/* * Copyright (c) 2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ const https = require('https'); const { StatusCode, StatusMessages } = require('./reporter'); async function mergeDataBetweenVersion(oldVersion, newVersion, allVersionUrl) { const oldVersionNumber = oldVersion.replace(/\./g, ''); const newVersionNumber = newVersion.replace(/\./g, ''); const url = allVersionUrl.replace('allVersion.json', ''); let versionArr = []; await downloadFiles(allVersionUrl).then((versionNumbers) => { versionArr = JSON.parse(versionNumbers); }).catch(err => { console.log('ERROR CODE:', err.code); }); if (versionArr.length === 0) { return []; } for (let i = 0; i < versionArr.length; i++) { const version = versionArr[i].replace(/\./g, ''); if (version < oldVersionNumber || version > newVersionNumber) { versionArr.splice(i, 1); i--; } } let orderVersionArr = versionArr.sort((a, b) => { return a.replace(/\.|.json/g, '') - b.replace(/\.|.json/g, ''); }); return formatExcelData(orderVersionArr, url); } function downloadFiles(allVersionUrl) { return new Promise((resolve, reject) => { getVersionsData(allVersionUrl, (versions) => { if (versions) { resolve(versions); } else { reject({ code: -1 }); } }); }); } function getLink(url, fileName) { return `${url}${fileName}.json`; } function getVersionsData(versionUrl, callback) { let json = ''; const SUCCESS_CODE = 200; const request = https.get(versionUrl, { timeout: 2000 }, function (res) { if (res.statusCode !== SUCCESS_CODE) { return; } res.on('data', function (d) { json += d; }); }).on('error', function (e) { console.log('ERROR:', e.message); }).on('close', () => { callback(json); }).on('timeout', () => { request.destroy(); }); } /** * 将最老版本changelog中函数有变化的数据作为初始数据,去跟其他版本的changelog数据进行合并。 * @param {Array} orderVersionArr * @param {String} url * @returns {Array} */ async function formatExcelData(orderVersionArr, url) { let allMergeData = []; const versionUrl = getLink(url, orderVersionArr[0]); let oldestVersionData = []; const MAX_LENGTH = 2; await downloadFiles(versionUrl).then(versionData => { oldestVersionData = JSON.parse(versionData); }).catch(err => { console.log('ERROR CODE:', err.code); }); if (orderVersionArr.length < MAX_LENGTH) { return allMergeData; } oldestVersionData.forEach(oldestData => { if (oldestData.newApi !== oldestData.oldApi) { oldestData.version = orderVersionArr[0]; allMergeData.push(oldestData); } }); await mergeAllData(orderVersionArr, url, allMergeData); return allMergeData; } /** * 将初始数据挨个跟下一个版本的changelog数据进行比较合并。 * * @param {Array} orderVersionArr * @param {String} url * @param {Array} allMergeData */ async function mergeAllData(orderVersionArr, url, allMergeData) { let index = 1; while (index < orderVersionArr.length) { const versionUrl = getLink(url, orderVersionArr[index]); let newVersionData = []; await downloadFiles(versionUrl).then(versionData => { newVersionData = JSON.parse(versionData); }).catch(err => { console.log('ERROR CODE:', err.code); }); mergeTwoVersionData(allMergeData, newVersionData, orderVersionArr[index]); index++; } } /** * 跟下一个版本的数据进行合并 * * @param {Array} allMergeData * @param {Array} newVersionData */ function mergeTwoVersionData(allMergeData, newVersionData, currentVersion) { let indexListSet = new Set(); for (let i = 0; i < allMergeData.length; i++) { const data = allMergeData[i]; for (let j = 0; j < newVersionData.length; j++) { if (!indexListSet.has(j) && data.newDtsName === newVersionData[j].oldDtsName && compareApiText(data.newApi, newVersionData[j].oldApi,) && data.newApi !== data.oldApi) { allMergeData[i].newApi = newVersionData[j].newApi; allMergeData[i].version = currentVersion; indexListSet.add(j); } } } newVersionData.forEach((data, index) => { if (!indexListSet.has(index)) { data.version = currentVersion; allMergeData.push(data); } }); } /** * 比较旧版本的newApi和新版本的oldApi,判断是否一样 * * @param {string} oldApiText * @param {string} newApiText * @returns {Boolean} */ function compareApiText(oldApiText, newApiText) { if (formatApi(oldApiText) === formatApi(newApiText) && formatApi(oldApiText) !== '') { return true; } return false; } /** * 格式化API定义内容,排除空格,换行,符号对比较API的影响。 * * @param {string} apiText * @returns {string} */ function formatApi(apiText) { if (!apiText) { return ''; } return apiText.replace(/\r|\n|\s+|\,|\;/g, ''); } /** * 将数组形式的changelog数据转换成Map * * @param {Array} dataInChangelogs * @returns {Map} */ function covertToMap(dataInChangelogs) { let dataMap = new Map(); dataInChangelogs.forEach(data => { const dataSignature = getSignature(data.newDtsName, data.oldApi); if (!dataMap.get(dataSignature)) { dataMap.set(dataSignature, [data]); } else { const dataArr = dataMap.get(dataSignature); dataArr.push(data); dataMap.set(dataSignature, dataArr); } }); return dataMap; } function getSignature(dtsName, apiText) { const handledDtsName = handleDtsName(dtsName).dtsPath; return `${handledDtsName}#${formatApi(apiText)}`; } function addChangelogLink(changelogsData, diffsData, diffs) { diffsData.forEach(diffData => { changelogsData.forEach(changelogData => { diffData.changelogs.add({ version: changelogData.version, url: changelogData.changelog }); if (changelogData.oldType !== changelogData.newType) { diffData.status = StatusMessages[StatusCode.CLASS_CHANGES]; diffData.StatusCode = StatusCode.CLASS_CHANGES; diffData.oldMessage = changelogData.oldApi; diffData.newMessage = changelogData.newApi; } if (changelogData.oldDtsName !== changelogData.newDtsName) { diffData.status = StatusMessages[StatusCode.DTS_CHANGED]; diffData.StatusCode = StatusCode.DTS_CHANGED; diffData.oldMessage = changelogData.oldDtsName; diffData.newMessage = changelogData.newDtsName; } if (changelogData.newApi === changelogData.oldApi || changelogData.newApi === 'N/A') { return; } if (diffData.statusCode === StatusCode.DELETE) { const newApiSignature = getSignature(changelogData.newDtsName, changelogData.newApi); diffs.delete(newApiSignature); diffData.status = StatusMessages[StatusCode.FUNCTION_CHANGES]; diffData.StatusCode = StatusCode.FUNCTION_CHANGES; diffData.oldMessage = changelogData.oldApi; diffData.newMessage = changelogData.newApi; } else if (diffData.statusCode === StatusCode.DELETE_CLASS) { diffData.status = StatusMessages[StatusCode.CLASS_CHANGES]; diffData.StatusCode = StatusCode.CLASS_CHANGES; diffData.oldMessage = changelogData.oldApi; diffData.newMessage = changelogData.newApi; } }); }); return diffsData; } function mergeDiffsAndChangelogs(changelogs, diffs) { changelogs.forEach((data, dataSignature) => { const diffsData = diffs.get(dataSignature); if (diffsData) { diffs.set(dataSignature, addChangelogLink(data, diffsData, diffs)); changelogs.delete(dataSignature); return; } data.forEach(changelogData => { const newApiSignature = getSignature(changelogData.newDtsName, changelogData.newApi); const diffsData = diffs.get(newApiSignature); if (!diffsData) { return; } diffsData.forEach(diffData => { diffData.changelog.add(changelogData.changelog); }); changelogs.delete(dataSignature); }); }); changelogs.forEach((changelogData, signature) => { changelogData.forEach(changelogApi => { if (diffs.get(signature)) { diffs.get(signature).push(formatChangelogApi(changelogApi)); } else { diffs.set(signature, [formatChangelogApi(changelogApi)]); } }); }); return diffs; } function formatChangelogApi(changelogApi) { const filePathObj = handleDtsName(changelogApi.newDtsName); return { packageName: '', className: changelogApi.newType, rawText: changelogApi.newApi, dtsName: filePathObj.dtsPath, hint: '', changelogs: [{ version: changelogApi.version, url: changelogApi.changelog }], statusCode: StatusCode.CHANGELOG, status: StatusMessages[StatusCode.CHANGELOG], oldMessage: changelogApi.oldApi, newMessage: changelogApi.newApi }; } function handleDtsName(dtsName) { let packageName = dtsName; let dtsPath = dtsName; if (dtsName.indexOf('api/@internal/component/ets') > -1) { packageName === 'ArkUI'; dtsPath.replace('api/@internal/component/ets', 'component'); } else if (dtsName.indexOf('api/@internal/ets') > -1) { packageName = dtsName.replace('api/@internal/ets', 'api/@internal/full'); dtsPath = packageName; } return { packageName, dtsPath }; } async function mergeDiffWithChangeLog(diffs, oldVersion, newVersion, allVersionUrl) { const dataInChangelogs = await mergeDataBetweenVersion(oldVersion, newVersion, allVersionUrl); const dataMapInChangelogs = covertToMap(dataInChangelogs); return mergeDiffsAndChangelogs(dataMapInChangelogs, diffs); } exports.applyChangeLogs = mergeDiffWithChangeLog;