• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16const https = require('https');
17const { StatusCode, StatusMessages } = require('./reporter');
18
19async function mergeDataBetweenVersion(oldVersion, newVersion, allVersionUrl) {
20  const oldVersionNumber = oldVersion.replace(/\./g, '');
21  const newVersionNumber = newVersion.replace(/\./g, '');
22  const url = allVersionUrl.replace('allVersion.json', '');
23  let versionArr = [];
24  await downloadFiles(allVersionUrl).then((versionNumbers) => {
25    versionArr = JSON.parse(versionNumbers);
26  }).catch(err => {
27    console.log('ERROR CODE:', err.code);
28  });
29
30  if (versionArr.length === 0) {
31    return [];
32  }
33
34  for (let i = 0; i < versionArr.length; i++) {
35    const version = versionArr[i].replace(/\./g, '');
36    if (version < oldVersionNumber || version > newVersionNumber) {
37      versionArr.splice(i, 1);
38      i--;
39    }
40  }
41  let orderVersionArr = versionArr.sort((a, b) => {
42    return a.replace(/\.|.json/g, '') - b.replace(/\.|.json/g, '');
43  });
44  return formatExcelData(orderVersionArr, url);
45}
46
47function downloadFiles(allVersionUrl) {
48  return new Promise((resolve, reject) => {
49    getVersionsData(allVersionUrl, (versions) => {
50      if (versions) {
51        resolve(versions);
52      } else {
53        reject({ code: -1 });
54      }
55    });
56  });
57}
58
59function getLink(url, fileName) {
60  return `${url}${fileName}.json`;
61}
62
63function getVersionsData(versionUrl, callback) {
64  let json = '';
65  const SUCCESS_CODE = 200;
66  const request = https.get(versionUrl, { timeout: 2000 }, function (res) {
67    if (res.statusCode !== SUCCESS_CODE) {
68      return;
69    }
70    res.on('data', function (d) {
71      json += d;
72    });
73  }).on('error', function (e) {
74    console.log('ERROR:', e.message);
75  }).on('close', () => {
76    callback(json);
77  }).on('timeout', () => {
78    request.destroy();
79  });
80}
81
82/**
83 * 将最老版本changelog中函数有变化的数据作为初始数据,去跟其他版本的changelog数据进行合并。
84 * @param {Array} orderVersionArr
85 * @param {String} url
86 * @returns {Array}
87 */
88async function formatExcelData(orderVersionArr, url) {
89  let allMergeData = [];
90  const versionUrl = getLink(url, orderVersionArr[0]);
91  let oldestVersionData = [];
92  const MAX_LENGTH = 2;
93  await downloadFiles(versionUrl).then(versionData => {
94    oldestVersionData = JSON.parse(versionData);
95  }).catch(err => {
96    console.log('ERROR CODE:', err.code);
97  });
98
99  if (orderVersionArr.length < MAX_LENGTH) {
100    return allMergeData;
101  }
102
103  oldestVersionData.forEach(oldestData => {
104    if (oldestData.newApi !== oldestData.oldApi) {
105      oldestData.version = orderVersionArr[0];
106      allMergeData.push(oldestData);
107    }
108  });
109  await mergeAllData(orderVersionArr, url, allMergeData);
110  return allMergeData;
111}
112
113/**
114 * 将初始数据挨个跟下一个版本的changelog数据进行比较合并。
115 *
116 * @param {Array} orderVersionArr
117 * @param {String} url
118 * @param {Array} allMergeData
119 */
120async function mergeAllData(orderVersionArr, url, allMergeData) {
121  let index = 1;
122  while (index < orderVersionArr.length) {
123    const versionUrl = getLink(url, orderVersionArr[index]);
124    let newVersionData = [];
125    await downloadFiles(versionUrl).then(versionData => {
126      newVersionData = JSON.parse(versionData);
127    }).catch(err => {
128      console.log('ERROR CODE:', err.code);
129    });
130    mergeTwoVersionData(allMergeData, newVersionData, orderVersionArr[index]);
131    index++;
132  }
133
134}
135
136/**
137 * 跟下一个版本的数据进行合并
138 *
139 * @param {Array} allMergeData
140 * @param {Array} newVersionData
141 */
142function mergeTwoVersionData(allMergeData, newVersionData, currentVersion) {
143  let indexListSet = new Set();
144  for (let i = 0; i < allMergeData.length; i++) {
145    const data = allMergeData[i];
146    for (let j = 0; j < newVersionData.length; j++) {
147      if (!indexListSet.has(j) && data.newDtsName === newVersionData[j].oldDtsName &&
148        compareApiText(data.newApi, newVersionData[j].oldApi,) && data.newApi !== data.oldApi) {
149        allMergeData[i].newApi = newVersionData[j].newApi;
150        allMergeData[i].version = currentVersion;
151        indexListSet.add(j);
152      }
153    }
154  }
155
156  newVersionData.forEach((data, index) => {
157    if (!indexListSet.has(index)) {
158      data.version = currentVersion;
159      allMergeData.push(data);
160    }
161  });
162}
163
164/**
165 * 比较旧版本的newApi和新版本的oldApi,判断是否一样
166 *
167 * @param {string} oldApiText
168 * @param {string} newApiText
169 * @returns {Boolean}
170 */
171function compareApiText(oldApiText, newApiText) {
172  if (formatApi(oldApiText) === formatApi(newApiText) && formatApi(oldApiText) !== '') {
173    return true;
174  }
175  return false;
176}
177
178/**
179 * 格式化API定义内容,排除空格,换行,符号对比较API的影响。
180 *
181 * @param {string} apiText
182 * @returns {string}
183 */
184function formatApi(apiText) {
185  if (!apiText) {
186    return '';
187  }
188  return apiText.replace(/\r|\n|\s+|\,|\;/g, '');
189}
190
191/**
192 * 将数组形式的changelog数据转换成Map
193 *
194 * @param {Array} dataInChangelogs
195 * @returns {Map}
196 */
197function covertToMap(dataInChangelogs) {
198  let dataMap = new Map();
199  dataInChangelogs.forEach(data => {
200    const dataSignature = getSignature(data.newDtsName, data.oldApi);
201    if (!dataMap.get(dataSignature)) {
202      dataMap.set(dataSignature, [data]);
203    } else {
204      const dataArr = dataMap.get(dataSignature);
205      dataArr.push(data);
206      dataMap.set(dataSignature, dataArr);
207    }
208  });
209  return dataMap;
210}
211
212function getSignature(dtsName, apiText) {
213  const handledDtsName = handleDtsName(dtsName).dtsPath;
214  return `${handledDtsName}#${formatApi(apiText)}`;
215}
216
217function addChangelogLink(changelogsData, diffsData, diffs) {
218  diffsData.forEach(diffData => {
219    changelogsData.forEach(changelogData => {
220      diffData.changelogs.add({
221        version: changelogData.version,
222        url: changelogData.changelog
223      });
224
225      if (changelogData.oldType !== changelogData.newType) {
226        diffData.status = StatusMessages[StatusCode.CLASS_CHANGES];
227        diffData.StatusCode = StatusCode.CLASS_CHANGES;
228        diffData.oldMessage = changelogData.oldApi;
229        diffData.newMessage = changelogData.newApi;
230      }
231
232      if (changelogData.oldDtsName !== changelogData.newDtsName) {
233        diffData.status = StatusMessages[StatusCode.DTS_CHANGED];
234        diffData.StatusCode = StatusCode.DTS_CHANGED;
235        diffData.oldMessage = changelogData.oldDtsName;
236        diffData.newMessage = changelogData.newDtsName;
237      }
238
239      if (changelogData.newApi === changelogData.oldApi || changelogData.newApi === 'N/A') {
240
241        return;
242      }
243
244      if (diffData.statusCode === StatusCode.DELETE) {
245        const newApiSignature = getSignature(changelogData.newDtsName, changelogData.newApi);
246        diffs.delete(newApiSignature);
247        diffData.status = StatusMessages[StatusCode.FUNCTION_CHANGES];
248        diffData.StatusCode = StatusCode.FUNCTION_CHANGES;
249        diffData.oldMessage = changelogData.oldApi;
250        diffData.newMessage = changelogData.newApi;
251      } else if (diffData.statusCode === StatusCode.DELETE_CLASS) {
252        diffData.status = StatusMessages[StatusCode.CLASS_CHANGES];
253        diffData.StatusCode = StatusCode.CLASS_CHANGES;
254        diffData.oldMessage = changelogData.oldApi;
255        diffData.newMessage = changelogData.newApi;
256      }
257    });
258  });
259  return diffsData;
260}
261
262function mergeDiffsAndChangelogs(changelogs, diffs) {
263  changelogs.forEach((data, dataSignature) => {
264    const diffsData = diffs.get(dataSignature);
265    if (diffsData) {
266      diffs.set(dataSignature, addChangelogLink(data, diffsData, diffs));
267      changelogs.delete(dataSignature);
268      return;
269    }
270    data.forEach(changelogData => {
271      const newApiSignature = getSignature(changelogData.newDtsName, changelogData.newApi);
272      const diffsData = diffs.get(newApiSignature);
273      if (!diffsData) {
274        return;
275      }
276      diffsData.forEach(diffData => {
277        diffData.changelog.add(changelogData.changelog);
278      });
279      changelogs.delete(dataSignature);
280    });
281
282  });
283
284  changelogs.forEach((changelogData, signature) => {
285    changelogData.forEach(changelogApi => {
286      if (diffs.get(signature)) {
287        diffs.get(signature).push(formatChangelogApi(changelogApi));
288      } else {
289        diffs.set(signature, [formatChangelogApi(changelogApi)]);
290      }
291    });
292  });
293
294  return diffs;
295}
296
297function formatChangelogApi(changelogApi) {
298  const filePathObj = handleDtsName(changelogApi.newDtsName);
299  return {
300    packageName: '',
301    className: changelogApi.newType,
302    rawText: changelogApi.newApi,
303    dtsName: filePathObj.dtsPath,
304    hint: '',
305    changelogs: [{
306      version: changelogApi.version,
307      url: changelogApi.changelog
308    }],
309    statusCode: StatusCode.CHANGELOG,
310    status: StatusMessages[StatusCode.CHANGELOG],
311    oldMessage: changelogApi.oldApi,
312    newMessage: changelogApi.newApi
313  };
314}
315
316function handleDtsName(dtsName) {
317  let packageName = dtsName;
318  let dtsPath = dtsName;
319  if (dtsName.indexOf('api/@internal/component/ets') > -1) {
320    packageName === 'ArkUI';
321    dtsPath.replace('api/@internal/component/ets', 'component');
322  } else if (dtsName.indexOf('api/@internal/ets') > -1) {
323    packageName = dtsName.replace('api/@internal/ets', 'api/@internal/full');
324    dtsPath = packageName;
325  }
326  return { packageName, dtsPath };
327}
328
329
330async function mergeDiffWithChangeLog(diffs, oldVersion, newVersion, allVersionUrl) {
331  const dataInChangelogs = await mergeDataBetweenVersion(oldVersion, newVersion, allVersionUrl);
332  const dataMapInChangelogs = covertToMap(dataInChangelogs);
333  return mergeDiffsAndChangelogs(dataMapInChangelogs, diffs);
334}
335
336exports.applyChangeLogs = mergeDiffWithChangeLog;