UNPKG

@apistudio/apim-cli

Version:

CLI for API Management Products

289 lines (250 loc) 9.62 kB
import path from 'path'; import AdmZip from 'adm-zip'; import {showError, showInfo} from '../common/message-helper.js'; import { readMultiYaml } from '../common/yaml-helper.js'; import { readFile } from '../common/fs-helper.js'; import {searchAssetByKind} from './asset-searchByKind-helper.js'; import { getAllProjectNames } from './root-dir-helper.js'; import Table from 'cli-table3'; import { GatewayResponseAPI } from '../../model/studio/deploy-response-model.js'; import { INVALID_OR_EMPTY_DEPLOYMENT_RESPONSES, NO_VALID_API_ASSET_FOUND, ADDING_ENDPOINT_FILE_FAILED, TESTING_LIST_ALL_PROJECTS, CHECKING_ALL_PROJECTS, IDENTIFIED_PROJECTS, FAILED_TO_BUILD_TEST_ASSETS, ERROR_IN_COMBINING_TEST_ASSET, LINE, TEST_STARTED, PROCESS_ENDPOINT_RESPONSE, PROCESS_GATEWAY_RESPONSE, PROCESS_ENDPOINT_RESPONSE_SUCCESS, ENDPOINT_TEST, COMBINING_TEST_ZIP, COMBINING_TEST_ZIP_SUCCESS, BUILDING_TEST, BUILDING_TEST_SUCCESS, JSON_FILE_MISSING, UPDATE_ENDPOINT_FAILED, ASSETS_BUILD_FAILED, APIENDPOINTS, } from '../../constants/message-constants.js'; import { ENDPOINT_FILE, TEST } from '../../constants/app-constants.js'; import { Test } from '@apic/api-model/test/Test.js'; import {BuildTestAssetsResult,executeBuildTestAssets} from '../../testers/project/projects-asset-testers.js'; import { DebugManager } from '../../debug/debug-manager.js'; import { KindEnums } from '@apic/api-model/common/StudioEnums.js'; import { buildAssets } from '../../actions/helpers/build-action-helper.js'; import { executeDeployment } from '../../deployers/project/projects-deployer.js'; import { GatewaysJson } from '@apic/studio-shared'; export type APIEndpoints = Record<string, string[]>; /** * @param jsonObject - The JSON object to be constructed. * @param key - The key to add to the JSON object. * @param value - The array of endpoints to add. */ export interface BuildTestProjectsResult { zipBuffer: Buffer; apiReference: Record<string,string>; } const constructJSON = (jsonObject: APIEndpoints,key: string,value: string[]): void => { jsonObject[key] = value; }; export const constructKey = (response: GatewayResponseAPI): string => { return `${response.namespace}:${response.assetName}:${response.version}`; }; export const createJSONBuffer = (apiGatewayEndpoints: APIEndpoints): Buffer => { return Buffer.from(JSON.stringify(apiGatewayEndpoints, null, 2), 'utf-8'); }; export const getAPIRefToBuild = (testFilePath: string): string[] | null => { const apiReferences : string[] = []; const parentDirPath = path.dirname(testFilePath); const testFileName = path.basename(testFilePath); const yamlContent = readFile(parentDirPath, testFileName); const parsedYamls = readMultiYaml<Test>(testFileName, yamlContent); parsedYamls.forEach((parsedYaml) => { const ref = parsedYaml?.spec?.api?.$ref; if (parsedYaml.kind?.toLowerCase() === TEST && ref) { apiReferences.push(ref); } }) return apiReferences; }; export const processEndpointFromResponse = (responses: GatewayResponseAPI[]): Buffer => { const apiGatewayEndpoints: APIEndpoints = {}; if(DebugManager.getInstance().isDebugEnabled()){ showInfo(PROCESS_ENDPOINT_RESPONSE); } if (!Array.isArray(responses) || responses.length === 0) { showError(INVALID_OR_EMPTY_DEPLOYMENT_RESPONSES); throw new Error(INVALID_OR_EMPTY_DEPLOYMENT_RESPONSES); } responses.forEach((response) => { if(DebugManager.getInstance().isDebugEnabled()){ showInfo(`${PROCESS_GATEWAY_RESPONSE}${response.name}`); } const key = constructKey(response); if (apiGatewayEndpoints[key]) { apiGatewayEndpoints[key] = [ ...new Set([...apiGatewayEndpoints[key], ...response.gatewayEndpoints]), ]; } else { constructJSON(apiGatewayEndpoints, key, response.gatewayEndpoints); } }); if(DebugManager.getInstance().isDebugEnabled()){ showInfo(PROCESS_ENDPOINT_RESPONSE_SUCCESS); } return createJSONBuffer(apiGatewayEndpoints); }; export const addEndpointToZip = (testZipBuffer: Buffer,endpointFile: Buffer): Buffer => { try { if(DebugManager.getInstance().isDebugEnabled()){ showInfo(ENDPOINT_TEST); } const testZipBufferWithEndpoint = new AdmZip(testZipBuffer); testZipBufferWithEndpoint.addFile(ENDPOINT_FILE, endpointFile); return testZipBufferWithEndpoint.toBuffer(); } catch (error) { showError(ADDING_ENDPOINT_FILE_FAILED); throw new Error( `${ADDING_ENDPOINT_FILE_FAILED} ${(error as Error).message}` ); } }; const addFileToZip = (combinedZip: AdmZip, entry: AdmZip.IZipEntry) => { if (!combinedZip.getEntry(entry.entryName)) { combinedZip.addFile(entry.entryName, entry.getData()); } }; const displayErrorMessage = (result: BuildTestAssetsResult, project: string) => { if (!result || !result.zipBuffer) { showError(`${FAILED_TO_BUILD_TEST_ASSETS} ${project}`); throw new Error(`${FAILED_TO_BUILD_TEST_ASSETS} ${project}`); } }; export const updateEndpointZip = async (finalZipBuffer: Buffer, newEndpoints: Buffer): Promise<Buffer> => { try { const zip = new AdmZip(finalZipBuffer); const jsonEntry = zip.getEntry(ENDPOINT_FILE); if (!jsonEntry) { throw new Error(JSON_FILE_MISSING); } const existingEndpointsBuffer = jsonEntry.getData(); const existingEndpoints: APIEndpoints = JSON.parse(existingEndpointsBuffer.toString('utf-8')); const newEndpointsData: APIEndpoints = JSON.parse(newEndpoints.toString('utf-8')); const updatedEndpoints = { ...existingEndpoints, ...newEndpointsData }; const updatedEndpointsBuffer = createJSONBuffer(updatedEndpoints); zip.updateFile(ENDPOINT_FILE, updatedEndpointsBuffer); return zip.toBuffer(); } catch (error) { showError(`${UPDATE_ENDPOINT_FAILED} ${(error as Error).message}`); throw new Error(`${UPDATE_ENDPOINT_FAILED} ${(error as Error).message}`); } }; export const combineTestAsset = async (rootDir: string, assetsToTest: Record<string, string>): Promise<BuildTestProjectsResult> => { try { if (DebugManager.getInstance().isDebugEnabled()) { showInfo(COMBINING_TEST_ZIP); } const combinedZip = new AdmZip(); const apiReferences: Record<string, string> = {}; for (const project in assetsToTest) { if (Object.prototype.hasOwnProperty.call(assetsToTest, project)) { if (DebugManager.getInstance().isDebugEnabled()) { showInfo(`${BUILDING_TEST} ${project}`); } const metadata = assetsToTest[project]; const result = await testAssets(rootDir, project, metadata); displayErrorMessage(result, project); if (DebugManager.getInstance().isDebugEnabled()) { showInfo(`${BUILDING_TEST_SUCCESS} ${project}`); } const projectZip = new AdmZip(result.zipBuffer); projectZip.getEntries().forEach((entry) => { addFileToZip(combinedZip, entry); }); apiReferences[project] = result.apiReference; } } if (DebugManager.getInstance().isDebugEnabled()) { showInfo(COMBINING_TEST_ZIP_SUCCESS); } return { zipBuffer: combinedZip.toBuffer(), apiReference: apiReferences }; } catch (error) { showError(`${ERROR_IN_COMBINING_TEST_ASSET} ${(error as Error).message}`); throw error; } }; export const findProjectForApi = (apiReference: Record<string, string>, notFoundApisList: string[]): Record<string, string> => { const apiReferenceMap: Record<string, string> = {}; for (const [project, reference] of Object.entries(apiReference)) { const apiList = reference.split(',').map(api => api.trim()); for (const api of notFoundApisList) { if (apiList.includes(api)) { apiReferenceMap[project] = apiReferenceMap[project] ? `${apiReferenceMap[project]},${api}` : api; } } } return apiReferenceMap; }; export const buildAndDeployAssets = async ( localDir: string, assetsToBuildAndDeploy: Record<string, string>, gatewayJson: GatewaysJson ): Promise<{ buildBuffer: Buffer; deploymentResult: GatewayResponseAPI[] }> => { const combinedZip = new AdmZip(); for (const [project, metadata] of Object.entries(assetsToBuildAndDeploy)) { try { const resultBuffer = await buildAssets(metadata, localDir, project); const projectZip = new AdmZip(resultBuffer); projectZip.getEntries().forEach((entry) => { combinedZip.addFile(entry.entryName, entry.getData()); }); } catch (error) { showError(`${ASSETS_BUILD_FAILED} ${project}: ${(error as Error).message}`); } } const combinedZipBuffer = combinedZip.toBuffer(); const deploymentResult = await executeDeployment(gatewayJson, combinedZipBuffer); return { buildBuffer: combinedZipBuffer, deploymentResult }; }; export const testProjects = async (all: boolean, rootDir: string,projects: string): Promise<Record<string, string>> => { if (all) { showInfo(TESTING_LIST_ALL_PROJECTS + rootDir); showInfo(CHECKING_ALL_PROJECTS + rootDir); projects = getAllProjectNames(rootDir); showInfo(IDENTIFIED_PROJECTS + projects); } return searchAssetByKind(KindEnums.Test,rootDir, projects); }; export const testAssets = (rootDir: string,projects: string,assets: string): Promise<BuildTestAssetsResult> => { showInfo(LINE); showInfo(TEST_STARTED); showInfo(LINE); return executeBuildTestAssets(rootDir, projects, assets); }; export const formattedEndpoints = (endpoints: Record<string, string[]>): void => { const endPointsTable = new Table({ head: ['APIs', 'Gateway Endpoints'], style: { head: ['blue'], border: ['yellow'], }, }); Object.entries(endpoints).forEach(([api, urls]) => { endPointsTable.push([api, urls.join('\n')]); }); showInfo(APIENDPOINTS); console.log(endPointsTable.toString()); };