@apistudio/apim-cli
Version:
CLI for API Management Products
189 lines (188 loc) • 9.65 kB
JavaScript
import fs from "fs";
import { AssetCache } from "../../cache/asset-cache.js";
import { COLON, COMMA } from "../../constants/app-constants.js";
import { equalsIgnoreCase, isNullOrUndefined } from "../common/data-helper.js";
import { getRandomFileName, getSubDirectory, isDirectory, isDirOrFileExists, isYamlFile, normalizePath, readFile, } from "../common/fs-helper.js";
import { showError, showInfo, showWarning } from "../common/message-helper.js";
import { readMultiYaml, readYaml } from "../common/yaml-helper.js";
import { checkForDependencyAssets, loadCacheWithProject, } from "./asset-cache-helper.js";
import { getTargetModelAssetKind, isValidAsset } from "./asset-helper.js";
import { getOtherProjectsNames } from "./root-dir-helper.js";
import { DebugManager } from "../../debug/debug-manager.js";
import { ADDING_DEPENDENCY, ASSERT_ADDED, ASSET_DEPENDENCIES, DEPENDENT_ASSETS_TO_BE_PROCESSED, DUPLICATE_ENTRIES_FOR_KIND, FOLLOWING, INSIDE_THE_PROJECT_PATH, INVALID_DIRECTORY, IS_FOUND_IN, KIND, METADATA_NAME, NAME, NO_ENTRIES_FOUND_FOR_KIND, NO_FURTHER_DEPENDENCY, REF, SEARCHING, THERE_ARE, VERSION, } from "../../constants/message-constants.js";
const addDependencyAsset = (file, zip, fileExtension = ".yml") => {
if (isNullOrUndefined(file)) {
return;
}
const fileName = getRandomFileName(fileExtension);
const filePath = normalizePath(`${file.parentPath}/${file.name}`);
zip.addLocalFile(filePath, "dependencies", fileName);
};
const hasAssetInGivenAssets = (assets, metadataToSearch, kindToSearch) => {
for (const asset of assets) {
if (!isValidAsset(asset)) {
continue;
}
if (isSameAsset(asset.metadata, metadataToSearch) &&
equalsIgnoreCase(kindToSearch, getTargetModelAssetKind(asset.kind))) {
return true;
}
}
return false;
};
// search for the dependency asset for the given asset ref value and project directory path
const searchAsset = (kindToSearch, assetRefValueToSearch, projectDirPath) => {
if (!isDirOrFileExists(projectDirPath) || !isDirectory(projectDirPath)) {
throw new Error(`${INVALID_DIRECTORY} ${projectDirPath}`);
}
const entries = fs.readdirSync(projectDirPath, {
withFileTypes: true,
recursive: true,
});
const metadataToSearch = fromAssetRefValue(assetRefValueToSearch);
const filteredEntries = entries.filter((entry) => {
if (entry.isDirectory()) {
return false;
}
if (!isYamlFile(entry.name)) {
return false;
}
const assets = readMultiYaml(normalizePath(`${entry.parentPath}/${entry.name}`), readFile(entry.parentPath, entry.name));
return hasAssetInGivenAssets(assets, metadataToSearch, kindToSearch);
});
if (filteredEntries.length > 1 &&
DebugManager.getInstance().isDebugEnabled()) {
showWarning(`${DUPLICATE_ENTRIES_FOR_KIND} - '${kindToSearch}', ${METADATA_NAME} '${metadataToSearch.name}' ${IS_FOUND_IN} '${projectDirPath}'`);
}
if (filteredEntries.length === 0 &&
DebugManager.getInstance().isDebugEnabled()) {
showWarning(`${NO_ENTRIES_FOUND_FOR_KIND} - '${kindToSearch}', ${METADATA_NAME} '${metadataToSearch.name}' ${IS_FOUND_IN} '${projectDirPath}'`);
return undefined;
}
return filteredEntries[0];
};
const isSameAsset = (input1, input2) => {
const isNamespaceAndNameEqual = input1.namespace === input2.namespace && input1.name === input2.name;
const isVersionEqual = (() => {
const version1 = Number(input1.version);
const version2 = Number(input2.version);
if (Number.isNaN(version1) && Number.isNaN(version2)) {
return input1.version === input2.version;
}
return version1 === version2;
})();
return isNamespaceAndNameEqual && isVersionEqual;
};
const fromAssetRefValue = (assetRefValue) => {
const split = assetRefValue.split(COLON);
if (split.length === 1) {
return {
name: split[0],
};
}
else if (split.length === 2) {
return {
name: split[0],
version: split[1],
};
}
return {
namespace: split[0],
name: split[1],
version: split[2],
};
};
const searchAndBundleDependency = (cachedUnProcessedAsset, rootDirPath, projects, zipFile) => {
try {
for (const project of projects) {
const projectDirPath = getSubDirectory(rootDirPath, project);
if (DebugManager.getInstance().isDebugEnabled()) {
showInfo(`\n\n ${SEARCHING}: ${KIND} - ${cachedUnProcessedAsset.kind} ${REF} - ${cachedUnProcessedAsset.ref} ${INSIDE_THE_PROJECT_PATH} '${projectDirPath}'`);
}
const result = searchAsset(cachedUnProcessedAsset.kind, cachedUnProcessedAsset.ref, projectDirPath);
if (!isNullOrUndefined(result)) {
const fileContent = readFile(result.parentPath, result.name);
const asset = readYaml(fileContent);
/* (*) add dependency to zip file*/
if (DebugManager.getInstance().isDebugEnabled()) {
showInfo(`${ADDING_DEPENDENCY}: ${KIND}-'${asset.metadata.namespace}', ${NAME}-'${asset.metadata.name}', ${VERSION}-'${asset.metadata.version}'`);
}
addDependencyAsset(result, zipFile);
showInfo(`${ASSERT_ADDED} ${asset.metadata.namespace}:${asset.metadata.name}:${asset.metadata.version}`);
/* (*) mark the added asset as processed */
AssetCache.getInstance().markAsProcessed(asset);
/* (*) check for any further dependencies from the current asset */
checkForDependencyAssets(asset);
return;
}
}
/* (*) if there are no assets found, mark this unprocessed asset as checked. */
AssetCache.getInstance().markUnProcessedAssetAsChecked(cachedUnProcessedAsset);
}
catch (error) {
throw new Error(`Failure in search asset: kind - ${cachedUnProcessedAsset.kind} ref - ${cachedUnProcessedAsset.ref} with error: ${error.message}`);
}
};
const loadDependenciesFromProjects = (rootDirPath, projects, zipFile) => {
const newlyAddedUnProcessedAssets = AssetCache.getInstance().getNewlyAddedUnProcessedAssets();
for (const newlyAddedUnProcessedAsset of newlyAddedUnProcessedAssets) {
searchAndBundleDependency(newlyAddedUnProcessedAsset, rootDirPath, projects, zipFile);
}
};
const checkAndLoadDependenciesFromProjects = (rootDirPath, projects, zipFile) => {
while (!haveCheckedUnProcessedAssets() && haveUnCheckedUnProcessedAssets()) {
const unProcessedAssets = AssetCache.getInstance().getNewlyAddedUnProcessedAssets();
if (DebugManager.getInstance().isDebugEnabled()) {
showInfo(`\n\n ${THERE_ARE} ${unProcessedAssets.size} ${DEPENDENT_ASSETS_TO_BE_PROCESSED}`);
// logging newly added dependencies
showInfo(`${FOLLOWING} ${unProcessedAssets.size} ${ASSET_DEPENDENCIES} `);
}
unProcessedAssets.forEach((unProcessedAsset) => {
if (DebugManager.getInstance().isDebugEnabled()) {
showInfo(`${KIND}: '${unProcessedAsset.kind}' ${REF}: ${unProcessedAsset.ref}`);
}
});
loadDependenciesFromProjects(rootDirPath, projects, zipFile);
}
};
const checkCacheState = () => {
if (haveCheckedUnProcessedAssets()) {
const unProcessedAssets = AssetCache.getInstance().getCheckedUnProcessedAssets();
showError(`Following ${unProcessedAssets.size} asset dependencies cannot be resolved:`);
unProcessedAssets.forEach((unProcessedAsset) => showError(`kind: '${unProcessedAsset.kind}' ref: ${unProcessedAsset.ref}`));
throw new Error("Dependency assets cannot be resolved");
}
if (!haveUnCheckedUnProcessedAssets()) {
const unProcessedAssets = AssetCache.getInstance().getNewlyAddedUnProcessedAssets();
if (unProcessedAssets.size === 0) {
if (DebugManager.getInstance().isDebugEnabled()) {
showInfo(`${NO_FURTHER_DEPENDENCY}`);
}
return;
}
}
};
const haveCheckedUnProcessedAssets = () => {
return AssetCache.getInstance().getCheckedUnProcessedAssets().size > 0;
};
const haveUnCheckedUnProcessedAssets = () => {
return AssetCache.getInstance().getNewlyAddedUnProcessedAssets().size > 0;
};
const checkAndLoadDependencies = (rootDirPath, projectNames, zipFile, excludeCurrProj = true) => {
/* (*) Parse the current project assets and update cache with processed and to be processed */
AssetCache.getInstance().clear();
loadCacheWithProject(zipFile);
/* (*) Check the current projects before checking in the other projects */
if (!excludeCurrProj) {
const currentProjects = new Set(projectNames.split(COMMA));
checkAndLoadDependenciesFromProjects(rootDirPath, currentProjects, zipFile);
/* (*) mark unprocessed asset if any as unchecked to make to search in other projects */
AssetCache.getInstance().markAllUnProcessedAssetAsUnchecked();
}
/* (*) Check and load refers to the dependencies directory */
const otherProjects = getOtherProjectsNames(rootDirPath, projectNames);
checkAndLoadDependenciesFromProjects(rootDirPath, otherProjects, zipFile);
/* (*) Check for cache state and throw error if some assets cannot be resolved*/
checkCacheState();
};
export { addDependencyAsset, checkAndLoadDependencies, fromAssetRefValue, isSameAsset, searchAsset, };