UNPKG

@pega/custom-dx-components

Version:

Utility for building custom UI components

563 lines (456 loc) 17.5 kB
import chalk from 'chalk'; import fs from 'fs'; import path from 'path'; import fetch from 'node-fetch'; import { join } from 'path'; import { Listr } from 'listr2'; import { addDebugLog, getHttpsAgent, getConfigDefaults, getPegaServerConfig, convertIntoPascalCase, getComponents, getComponentDirectoryPath, getDirectoryFiles, setConfigImportRelativePath, forceDefaultsUpdate, constructCompleteUrl } from '../../util.js'; import { TASKS_CONFIG_JSON_FILENAME, TEMP_PATH, PACKAGE_JSON_FILENAME, LIST_COMPONENT_LIBRARY_ARCHIVES_SERVICE_REST_ENDPOINT, COMPONENT_LIBRARY_ARCHIVE_SERVICE_REST_ENDPOINT, TOKEN_PATH} from '../../constants.js'; export const SOURCE_OF_COMPONENT_TYPES = { SERVER: 'Server', LOCAL: 'Local' }; export const setTasksConfig = async (libraryName, version, devBuild, currDir) => { addDebugLog("setTasksConfig", `libraryName: ${libraryName}, version: ${version}, devBuild: ${devBuild}`, ""); const pegaConfigJsonPath = join(currDir, TASKS_CONFIG_JSON_FILENAME); const sPegaData = fs.readFileSync(pegaConfigJsonPath, { encoding: 'utf8' }); const configData = JSON.parse(sPegaData); let serverConfig = configData['server-config']; serverConfig.devBuild = devBuild; let compConfig = configData['component']; compConfig.library = libraryName; compConfig.version = version; // update file // stringify "4", makes the json string look like JSON in the file, formatted instead of a single line fs.writeFileSync(pegaConfigJsonPath, JSON.stringify(configData, null, 4), { encoding: 'utf8',flag:'w' }); } export const setPackageOrg = async (organization, currDir) => { addDebugLog("setPackageOrg", "", ""); const packageJsonPath = join(currDir, PACKAGE_JSON_FILENAME); const sPackage = fs.readFileSync(packageJsonPath, { encoding: 'utf8'}); const packageData = JSON.parse(sPackage); packageData.organization = organization; // update file // stringify "4", makes the json string look like JSON in the file, formatted instead of a single line fs.writeFileSync(packageJsonPath, JSON.stringify(packageData, null, 4), { encoding: 'utf8',flag:'w' }); } export const updateConfig = async ( { oldComponentKey, newComponentKey, organization, library, version, currentDirectory, } ) => { addDebugLog("updateConfig", `oldComponentKey: ${oldComponentKey}, newComponentKey: ${newComponentKey}`, ""); let configData = fs.readFileSync(join(currentDirectory, "/config.json"), { encoding: 'utf8' }); configData = configData && JSON.parse(configData); configData.name = newComponentKey; if (configData.componentKey) configData.componentKey = newComponentKey; configData.organization = organization; configData.library = library; configData.version = version; // stringify "4", makes the json string look like JSON in the file, formatted instead of a single line fs.writeFileSync(join(currentDirectory, "/config.json"), JSON.stringify(configData, null, 4), { encoding: 'utf8',flag:'w' }); }; export const updateFile = async ( fileName, oldComponentKeyPC, newComponentKeyPC, currentDirectory, ) => { addDebugLog("updateFile", `fileName: ${fileName}, oldComponentKeyPC: ${oldComponentKeyPC}, newComponentKeyPC: ${newComponentKeyPC}, currentDirectory: ${currentDirectory}`, ""); let fileData = fs.readFileSync(join(currentDirectory, "/", fileName), { encoding: 'utf8' }); if (fileData.indexOf(oldComponentKeyPC) >= 0) { fileData = fileData.replaceAll(oldComponentKeyPC, newComponentKeyPC); fs.writeFileSync(join(currentDirectory, "/", fileName), fileData, { encoding: 'utf8',flag:'w' }); } }; export const renameDirectory = async(currentDirectory, targetDirectory) => { if (!fs.existsSync(targetDirectory)) { fs.mkdirSync(targetDirectory, { recursive: true }); } const targetFileList = await getDirectoryFiles(currentDirectory); for (const targetFile of targetFileList) { const currentFilePath = join(currentDirectory, targetFile); const targetFilePath = join(targetDirectory, targetFile); fs.copyFileSync(currentFilePath, targetFilePath) } // fs.rm(currentDirectory, { recursive: true }, err => { // if (err) { // throw err; // } // }); fs.rmSync(currentDirectory, { recursive: true, force: true, maxRetries: 2 }); } // rename the component export const renameComponent = async ( componentKey, library, version, currDir ) => { addDebugLog("renameComponent", `library: ${library}, componentKey: ${componentKey}`, ""); const componentName = componentKey.split("_")[2]; const configDef = getConfigDefaults(); const orgLibName = `${configDef.organization}_${library}`; const organization = configDef.organization; const newComponentKey = `${orgLibName}_${componentName}`; const oldComponentKey = componentKey; const newComponentKeyPC = convertIntoPascalCase(newComponentKey); const oldComponentKeyPC = convertIntoPascalCase(oldComponentKey); const currentDirectory = await getComponentDirectoryPath(componentKey, currDir); const targetDirectory = await getComponentDirectoryPath(newComponentKey, currDir); const targetFileList = await getDirectoryFiles(currentDirectory); for (const fileIndex in targetFileList) { const fileName = targetFileList[fileIndex]; if (fileName === "config.json") { await updateConfig( { oldComponentKey, newComponentKey, componentName, organization, library, version, currentDirectory, } ); } else { await updateFile( fileName, oldComponentKeyPC, newComponentKeyPC, currentDirectory ); } } // for // rename the directory await renameDirectory(currentDirectory, targetDirectory); return newComponentKey; }; export const renameComponents = async(compList, libraryName, version, compDir) => { for (const componentKey of compList) { await renameComponent(componentKey, libraryName, version, compDir); } } export const updateTempArchive = async(libraryName, version, devBuild) => { const currentDirectory = process.cwd(); const tempStoreDirectory = join(currentDirectory, TEMP_PATH); const configDef = getConfigDefaults(); // remove -dev if exists const justVersion = version.replace("-dev", ""); const tasks = new Listr( [ { title: 'Update tasks.config', task: async () => { await setTasksConfig(libraryName, justVersion, devBuild, tempStoreDirectory); } }, { title: 'Update package.json', task: async () => { await setPackageOrg(configDef.organization, tempStoreDirectory); } }, { title: `Renaming components`, task: async () => { const compDir = join(tempStoreDirectory, "src", "components"); const compList = await getComponents(compDir); await renameComponents(compList, libraryName, version, compDir); } } ], { exitOnError: true } ); await tasks.run().catch(err => { console.log(chalk.bold.red(err.toString())); process.exit(1); }); } export const getServerOrDirectoryQuestion = async () => { addDebugLog("getLibraryVersionQuestion", "", ""); return [ { name: 'importFrom', type: 'rawlist', message: `Import from`, choices: [{name: 'Local directory', value: 'Directory'}, {name: 'Server', value: 'Server'}], default: 'Directory' } ]; }; export const getFilePathQuestions = async () => { addDebugLog("getFilePathQuestions", "", ""); console.log("\nFile path examples:"); if (path.sep === "/") { console.log("\tMac OS/Unix:\t/Users/name/...\n"); } else { console.log("\tWindows:\tC:\\Users\\name\\...\n"); } // console.log(chalk.yellow("[Press arrows to search, press <tab> to select, press <enter> to return (after selecting via tab).]")); console.log(chalk.yellow("[Manual typing, give search time to find paths]\n")); const configDef = getConfigDefaults(); const currentDirectory = process.cwd(); const arPathParts = currentDirectory.split(path.sep); let defaultPath = join(arPathParts[0], arPathParts[1], arPathParts[2], configDef.importRelativePath); let startDirectory = join(arPathParts[0], arPathParts[1], arPathParts[2]); if (path.sep === "/") { defaultPath = "/".concat(defaultPath); startDirectory = "/".concat(startDirectory).concat("/"); } return [ { name: 'filePath', type: 'fuzzypath', message: 'Enter full file path of zip file location:', default: defaultPath, excludePath: nodePath => nodePath.startsWith('node_modules'), // excludePath :: (String) -> Bool // excludePath to exclude some paths from the file-system scan excludeFilter: nodePath => nodePath.indexOf('/.') >= 0, // excludeFilter :: (String) -> Bool // excludeFilter to exclude some paths from the final list, e.g. '.' itemType: 'directory', // itemType :: 'any' | 'directory' | 'file' // specify the type of nodes to display // default value: 'any' // example: itemType: 'file' - hides directories from the item list rootPath: startDirectory, // rootPath :: String // Root search directory suggestOnly: false, // suggestOnly :: Bool // Restrict prompt answer to available choices or use them as suggestions depthLimit: 2, // depthLimit :: integer >= 0 // Limit the depth of sub-folders to scan // Defaults to infinite depth if undefined } ]; }; export const getFileNameQuestions = async (zipFileList) => { addDebugLog("getFileNameQuestions", "", ""); return [ { name: 'fileName', type: 'rawlist', message: `Select zip file`, choices: zipFileList }, ]; }; export const updateSavedFilePath = async(filePath) => { const currentDirectory = process.cwd(); const arPathParts = currentDirectory.split(path.sep); let defaultPath = join(path.sep, arPathParts[0], arPathParts[1], arPathParts[2]); if (path.sep != "/") { // windows if (defaultPath.indexOf(path.sep) === 0) { // if start with \\, then remove defaultPath = defaultPath.substring(1); } if (filePath.indexOf(defaultPath) === 0) { let relativePath = ""; if (filePath !== defaultPath) { // same starting path, so we can save the relative relativePath = filePath.replace(defaultPath.concat(path.sep), ""); } await setConfigImportRelativePath(relativePath); await forceDefaultsUpdate(); } } else { // mac if (filePath.indexOf(defaultPath) === 0) { let relativePath = ""; if (filePath !== defaultPath) { // same starting path, so we can save the relative relativePath = filePath.replace(defaultPath.concat("/"), ""); } await setConfigImportRelativePath(relativePath); await forceDefaultsUpdate(); } } } async function fetchComponentLibraryArchivesFromInfinity(apiUrl, headers) { addDebugLog("fetchComponentLibraryArchivesFromInfinity", `apiUrl: ${apiUrl}`, ""); const serverConfig = await getPegaServerConfig(); return new Promise((resolve, reject) => { let status = 500; fetch(apiUrl, { method: 'GET', agent: getHttpsAgent(serverConfig), headers }) .then((response) => { status = response.status; if (status === 401) { throw new Error('Error occurred in authentication. Please regenerate using authenticate'); } else if (status === 404){ throw new Error('404: Server resource not found'); } else if (status === 405){ throw new Error('405: Server method not allowed'); } else if (status === 408){ throw new Error('408: Server timed out'); } return response.json(); }) .then((resp) => { // const respData = JSON.parse(resp); if (status === 401) { throw new Error('Error occurred in authentication. Please regenerate using authenticate'); // console.log(accessTokenUri, refreshToken); /* TODO - Handle refresh_token */ } else if (status === 200) { resolve(resp); } else { throw new Error(`${resp.message}`); } }) // eslint-disable-next-line prefer-promise-reject-errors .catch((e) => reject(`${chalk.bold.red(e)}`)); }); } export const getServerLibraryArchives = async() => { addDebugLog("getServerLibraryArchives", '', ""); const defaultPegaServerConfig = await getPegaServerConfig(); let url = constructCompleteUrl( defaultPegaServerConfig.server, LIST_COMPONENT_LIBRARY_ARCHIVES_SERVICE_REST_ENDPOINT ); return new Promise((resolve, reject) => { try { fs.readFile(TOKEN_PATH, 'utf8', (err, data) => { if (err) { // eslint-disable-next-line prefer-promise-reject-errors reject(`\n${chalk.bold.red('Error occurred in authentication. Please regenerate using authenticate')}`); } if (data) { const { access_token: accessToken, token_type: tokenType } = JSON.parse(data); const headers = {}; headers.Authorization = `${tokenType} ${accessToken}`; fetchComponentLibraryArchivesFromInfinity(url, headers) .then((response) => resolve(response)) .catch((e) => reject(e)); } }); } catch (error) { console.log(`\n${chalk.bold.red(error)}`); // eslint-disable-next-line prefer-promise-reject-errors reject(`${chalk.bold.red(error)}`); } }); } export const getLibraryArchiveQuestion = async (archiveVersions) => { addDebugLog("getLibraryArchiveQuestion", `archiveVersions: ${archiveVersions}`, ""); return [ { name: 'archiveData', type: 'rawlist', message: `Select an archive`, choices: archiveVersions } ]; }; async function fetchComponentLibraryArchiveFromInfinity(apiUrl, headers) { addDebugLog("fetchComponentLibraryArchivesFromInfinity", `apiUrl: ${apiUrl}`, ""); const serverConfig = await getPegaServerConfig(); return new Promise((resolve, reject) => { let status = 500; fetch(apiUrl, { method: 'GET', agent: getHttpsAgent(serverConfig), headers }) .then((response) => { status = response.status; if (status === 401) { throw new Error('Error occurred in authentication. Please regenerate using authenticate'); } else if (status === 404){ throw new Error('404: Server resource not found'); } else if (status === 405){ throw new Error('405: Server method not allowed'); } else if (status === 408){ throw new Error('408: Server timed out'); } return response.json(); }) .then((resp) => { // const respData = JSON.parse(resp); if (status === 401) { throw new Error('Error occurred in authentication. Please regenerate using authenticate'); // console.log(accessTokenUri, refreshToken); /* TODO - Handle refresh_token */ } else if (status === 200) { resolve(resp); } else { throw new Error(`${resp.message}`); } }) // eslint-disable-next-line prefer-promise-reject-errors .catch((e) => reject(`${chalk.bold.red(e)}`)); }); } export const getServerLibraryArchive = async(appName, fileName, fileType, ruleSet, ruleSetVersion) => { addDebugLog("getServerLibraryArchives", '', ""); const defaultPegaServerConfig = await getPegaServerConfig(); let url = constructCompleteUrl( defaultPegaServerConfig.server, COMPONENT_LIBRARY_ARCHIVE_SERVICE_REST_ENDPOINT ); let urlParams = `/app-name/${appName}/file-name/${fileName}/file-type/${fileType}/rulesetname/${ruleSet}/rulesetversion/${ruleSetVersion}`; url = url.concat(urlParams); return new Promise((resolve, reject) => { try { fs.readFile(TOKEN_PATH, 'utf8', (err, data) => { if (err) { // eslint-disable-next-line prefer-promise-reject-errors reject(`\n${chalk.bold.red('Error occurred in authentication. Please regenerate using authenticate')}`); } if (data) { const { access_token: accessToken, token_type: tokenType } = JSON.parse(data); const headers = {}; headers.Authorization = `${tokenType} ${accessToken}`; fetchComponentLibraryArchiveFromInfinity(url, headers) .then((response) => resolve(response)) .catch((e) => reject(e)); } }); } catch (error) { console.log(`\n${chalk.bold.red(error)}`); // eslint-disable-next-line prefer-promise-reject-errors reject(`${chalk.bold.red(error)}`); } }); }