UNPKG

@pega/custom-dx-components

Version:

Utility for building custom UI components

1,561 lines (1,253 loc) 75.5 kB
import fs from 'fs'; import path from 'path'; import { join } from 'path'; import { promisify } from 'util'; import https from 'https'; import crypto from 'node:crypto'; import { createRequire } from 'module'; import {jwtDecode} from 'jwt-decode'; import archiver from 'archiver'; import {unarchive} from 'unarchive'; import chalk from 'chalk'; import mustache from 'mustache'; import pascalCase from 'pascalcase'; import fetch from 'node-fetch'; // import checkGit from 'check-git'; import * as currentPath from './currentPath.cjs'; import { COMPONENTS_DIRECTORY_PATH, COMPONENTS_CONFIG_FILE_NAME, TASKS_CONFIG_JSON_FILENAME, INPUT_CONFIG_JSON_FILENAME, PACKAGE_JSON_FILENAME, TOKEN_PATH, OOTB_COMPONENT_SERVICE_REST_ENDPOINT, DELETE_COMPONENT_SERVICE_REST_ENDPOINT, LP_DELETE_COMPONENT_SERVICE_REST_ENDPOINT, OOTB_COMPONENTS, USE_PROMOTED_WEB_PACK, BOOTSTRAP_CONFIG, LIBRARY_BASED, USE_INPUT_CONFIG, SHARED_DIRECTORY, SHOW_DEBUG, COMPONENTS_PATH, ARCHIVES_PATH, BIN_ARCHIVES_PATH, TEMP_PATH, JEST_TESTS_FUNCTIONAL_PATH, IMPORT_RELATIVE_PATH, EXPORT_RELATIVE_PATH } from './constants.js'; const access = promisify(fs.access); let showDebug = false; let debugLevel = 1; let haveDependencyDifference = false; let addAboutData = true; // updated upon start let defaultConfigs = { serverType: "", isLibraryBased: false, devBuild: true, library: "", organization: "", currentOrgLib: "", version: "", buildVersion: "", displayLibVersion: "", showDebug: false, importRelativePath: "", exportRelativePath: "", useInputConfig: false }; let pegaConfig = null; let pegaData = null; let sPegaData; let sPegaServerConfig; export const modAddAboutData = (addInAboutData) => { // used by jest to not put in to match assets without aboutData addAboutData = addInAboutData; } export const addDebugLog = (sFunctionName, sLogData, sIncDec) => { let sLog; if (showDebug) { if (sIncDec === "-") { debugLevel--; } sLog = `>> log ${debugLevel} `; for (let i =0; i < debugLevel; i++) { sLog = sLog.concat("-"); } sLog = sLog.concat(sFunctionName).concat(": ").concat(sLogData); console.log(`${chalk.hex('#A0522D')(sLog)}`); if (sIncDec === "+") { debugLevel++; } } } export const setUpDefaults = async () => { // defaults, start up function will set these up // so that won't have to continously hit files to get data // thus saving time. let retrievedConfigData = { serverType: "", isLibraryBased: false, devBuild: true, library: "", organization: "", currentOrgLib: "", version: "", buildVersion: "", displayLibVersion: "", showDebug: false, importRelativePath: "", exportRelativePath: "", useInputConfig: false }; // retrievedConfigData const currentDirectory = process.cwd(); const pegaConfigJsonPath = join(currentDirectory, TASKS_CONFIG_JSON_FILENAME); try { // force to throw error and not console log await checkPathAccess(pegaConfigJsonPath, {}, true); let data = fs.readFileSync(pegaConfigJsonPath, { encoding: 'utf8' }); data = JSON.parse(data); if (data[IMPORT_RELATIVE_PATH]) { retrievedConfigData.importRelativePath = data[IMPORT_RELATIVE_PATH]; } if (data[EXPORT_RELATIVE_PATH]) { retrievedConfigData.exportRelativePath = data[EXPORT_RELATIVE_PATH]; } if (data[LIBRARY_BASED]) { retrievedConfigData.isLibraryBased = data[LIBRARY_BASED]; } if (data[USE_INPUT_CONFIG]) { retrievedConfigData.useInputConfig = data[USE_INPUT_CONFIG]; } retrievedConfigData.library = data.component.library; retrievedConfigData.version = data.component.version; const { organization } = JSON.parse(fs.readFileSync(path.resolve('package.json'), 'utf8')); retrievedConfigData.organization = organization; if (data["showDebug"]) { retrievedConfigData.showDebug = data.showDebug; showDebug = data.showDebug; } retrievedConfigData.serverType = data["server-config"].serverType; retrievedConfigData.devBuild = data["server-config"].devBuild; retrievedConfigData.buildVersion = retrievedConfigData.devBuild ? `${retrievedConfigData.version}-dev` : retrievedConfigData.version; retrievedConfigData.currentOrgLib = `${retrievedConfigData.organization}_${retrievedConfigData.library}`; retrievedConfigData.displayLibVersion = `${retrievedConfigData.currentOrgLib }/${retrievedConfigData.buildVersion}`; } catch (ex) { // when can't find it, always be false, assume no library based. // This is IMPORTANT for init (npx install) return false; } return JSON.parse(JSON.stringify(retrievedConfigData)); } export const convertYNToBoolean = (inputYN) => { switch (inputYN) { case 'Y': case 'y': case 'Yes': case 'YES': case 't': case 'true': case 'True': case 'TRUE': return true; } return false; } export const getInputConfigForCommand = async (commandName) => { // retrievedConfigData const currentDirectory = process.cwd(); const inputConfigJsonPath = join(currentDirectory, INPUT_CONFIG_JSON_FILENAME); const isLibraryBased = getLibraryBased(); try { // force to throw error and not console log await checkPathAccess(inputConfigJsonPath, {}, true); let data = fs.readFileSync(inputConfigJsonPath, { encoding: 'utf8' }); data = JSON.parse(data); if (isLibraryBased) { const libraryCommands = data["libraryMode"]; if (libraryCommands[commandName]) { return libraryCommands[commandName]; } } else { const standardCommands = data["standard"]; if (standardCommands[commandName]) { return standardCommands[commandName]; } } } catch (ex) { // when can't find it, always be false, assume no library based. // This is IMPORTANT for init (npx install) return {}; } return {}; } // initialize // await setUpDefaults(); export const checkPathAccess = async (path, options = {}, throwError = false) => { const { errorMessage } = options; try { await access(path, fs.constants.R_OK); } catch (err) { if (throwError) { if (showDebug) addDebugLog("checkPathAccess", "path: ".concat(path), "+"); if (showDebug) addDebugLog("checkPathAccess", "Error: ".concat(err), ""); if (showDebug) addDebugLog("checkPathAccess", "END", "-"); throw new Error(err); } else { if (showDebug) addDebugLog("checkPathAccess", "path: ".concat(path), "+"); const message = errorMessage || `${chalk.red.bold('ERROR')} Could not able to access path - ${path}`; console.error(message); if (showDebug) addDebugLog("checkPathAccess", "END", "-"); process.exit(1); } } }; export const getUseWebPackPromotion = async () => { if (showDebug) addDebugLog("getUseWebPackPromotion", "", ""); const currentDirectory = process.cwd(); const pegaConfigJsonPath = join(currentDirectory, TASKS_CONFIG_JSON_FILENAME); try { // force to throw error and not console log await checkPathAccess(pegaConfigJsonPath, {}, true); } catch (ex) { // when can't find it, always be false, assume no webpack override. // This is IMPORTANT for init (npx install) return false; } let data = fs.readFileSync(pegaConfigJsonPath, { encoding: 'utf8' }); data = JSON.parse(data); if (!data[COMPONENTS_DIRECTORY_PATH]) { console.error( `${chalk.red.bold('ERROR')} Could not able find components directory path in config.json` ); process.exit(1); } if (data[USE_PROMOTED_WEB_PACK] == null) { // value doesn't exist in task config, so add it with default of false data[USE_PROMOTED_WEB_PACK] = false; await updateUseWebPackPromotion(false); } return data[USE_PROMOTED_WEB_PACK]; } export const updateUseWebPackPromotion = async ( useValue = false ) => { if (showDebug) addDebugLog("updateUseWebPackPromotion", "", ""); const currentDirectory = process.cwd(); const pegaConfigJsonPath = join(currentDirectory, TASKS_CONFIG_JSON_FILENAME); await checkPathAccess(pegaConfigJsonPath); let data = fs.readFileSync(pegaConfigJsonPath, { encoding: 'utf8' }); data = JSON.parse(data); if (!data[COMPONENTS_DIRECTORY_PATH]) { console.error( `${chalk.red.bold('ERROR')} Could not able find components directory path in config.json` ); process.exit(1); } data["usePromotedWebPack"] = useValue; fs.writeFileSync(pegaConfigJsonPath, JSON.stringify(data, null, 4), { encoding: 'utf8',flag:'w' }); } export const getAboutData = async (currentOrgLib, buildVersion, dateStamp) => { if (showDebug) addDebugLog("getAboutData", `currentOrgLib: ${currentOrgLib}, buildVersion: ${buildVersion}, dateStamp: ${dateStamp}`) const tokenAndStaticServer = await getC11NB2STokenAndStaticServer(); if (tokenAndStaticServer.C11NB2S === undefined) { // console.log(chalk.yellowBright("Need to authenticate, missing services token.\nLibrary requires authentication to acquire a token to build.")); return `${currentOrgLib} ${buildVersion}, ${dateStamp}`; } const jwt = jwtDecode(tokenAndStaticServer.C11NB2S); let infinityVersion= jwt.infinity_version; infinityVersion = infinityVersion.replace("8.", ""); return `${currentOrgLib} ${buildVersion}, ${dateStamp}, ${infinityVersion}`; } export const checkJWTExpiration = async () => { const tokenAndStaticServer = await getC11NB2STokenAndStaticServer(); if (tokenAndStaticServer.C11NB2S === undefined) { console.log(chalk.redBright("Need to authenticate, missing services token.\nBuilding a library requires authentication to acquire a token to build.")); process.exit(1); } const jwt = jwtDecode(tokenAndStaticServer.C11NB2S); const { exp, customer_org } = jwt; if (exp < (new Date().getTime() + 1) / 1000) { console.log(chalk.redBright("JWT token has expired, please reauthenticate.")); process.exit(1); } // check that there is customer_org if (!customer_org || customer_org === "") { console.log(chalk.red(`PegaInfinity server DSS setting ${chalk.redBright.bold(`C11nCCv2CustomerOrg`)} is NOT set.`)); console.log(chalk.red("Please update DSS setting, save and then reauthenticate.")); process.exit(1); } } export const getLibraryBased = () => { if (showDebug) addDebugLog("getLibraryBased", "", ""); // const currentDirectory = process.cwd(); // const pegaConfigJsonPath = join(currentDirectory, TASKS_CONFIG_JSON_FILENAME); // try { // // force to throw error and not console log // await checkPathAccess(pegaConfigJsonPath, {}, true); // } // catch (ex) { // // when can't find it, always be false, assume no library based. // // This is IMPORTANT for init (npx install) // return false; // } // let data = fs.readFileSync(pegaConfigJsonPath, { encoding: 'utf8' }); // data = JSON.parse(data); // if (data[LIBRARY_BASED]) { // if (showDebug) addDebugLog("getLibraryBased", `based: ${data[LIBRARY_BASED]}`, ""); // if (showDebug) addDebugLog("getLibraryBased", "END", "-"); // return data[LIBRARY_BASED]; // } // else { // if (showDebug) addDebugLog("getLibraryBased", "END", "-"); // return false; // } return defaultConfigs.isLibraryBased; } export const getUseInputConfig = () => { if (showDebug) addDebugLog("getUseInputConfig", "", ""); return defaultConfigs.useInputConfig; } export const setLibraryBased = async (isLibraryBased) => { if (showDebug) addDebugLog("setLibraryBased", "", ""); const currentDirectory = process.cwd(); const pegaConfigJsonPath = join(currentDirectory, TASKS_CONFIG_JSON_FILENAME); try { // force to throw error and not console log await checkPathAccess(pegaConfigJsonPath, {}, true); } catch (ex) { // when can't find it, always be false, assume no library based. // This is IMPORTANT for init (npx install) return false; } let data = fs.readFileSync(pegaConfigJsonPath, { encoding: 'utf8' }); data = JSON.parse(data); if (data[LIBRARY_BASED] != undefined) { data[LIBRARY_BASED] = isLibraryBased; // update file // stringify "4", makes the json string look like JSON in the file, formated instead of a single line try { fs.writeFileSync(pegaConfigJsonPath, JSON.stringify(data, null, 4), { encoding: 'utf8',flag:'w' }); } catch (err) { console.error(err); } //await forceDefaultsUpdate(); defaultConfigs.isLibraryBased = isLibraryBased; // reset pegaData = null; pegaConfig = null; } } export const getShowDebug = () => { // const currentDirectory = process.cwd(); // const pegaConfigJsonPath = join(currentDirectory, TASKS_CONFIG_JSON_FILENAME); // try { // // force to throw error and not console log // await checkPathAccess(pegaConfigJsonPath, {}, true); // } // catch (ex) { // // when can't find it, always be false, assume no library based. // // This is IMPORTANT for init (npx install) // return false; // } // let data = fs.readFileSync(pegaConfigJsonPath, { encoding: 'utf8' }); // data = JSON.parse(data); // if (data[SHOW_DEBUG]) { // return data[SHOW_DEBUG]; // } // else return false; return defaultConfigs.showDebug; } export const deleteLocalComponent = async componentKey => { if (showDebug) addDebugLog("deleteLocalComponent", `componentKey: ${componentKey}`, ""); const currentDirectory = process.cwd(); const pegaConfigJsonPath = join(currentDirectory, TASKS_CONFIG_JSON_FILENAME); await checkPathAccess(pegaConfigJsonPath); let data = fs.readFileSync(pegaConfigJsonPath, { encoding: 'utf8' }); data = JSON.parse(data); if (!data[COMPONENTS_DIRECTORY_PATH]) { console.error( `${chalk.red.bold('ERROR')} Could not able find components directory path in config.json` ); process.exit(1); } const directory = join(currentDirectory, data[COMPONENTS_DIRECTORY_PATH], componentKey); try { fs.rmSync(directory, { recursive: true }); console.log(`${chalk.red.bold(componentKey)} is deleted from Local`); } catch (err) { console.log('no file'); throw new Error(`No such file ${componentKey}`); } }; export const getHttpsAgent = serverConfig => { const agentOptions = { rejectUnauthorized: false }; if (serverConfig.legacyTLS) { agentOptions.secureOptions = crypto.constants.SSL_OP_LEGACY_SERVER_CONNECT; } return new https.Agent(agentOptions); }; export const deleteServerComponent = async componentKey => { if (showDebug) addDebugLog("deleteServerComponent", `componentKey: ${componentKey}`, ""); const serverConfig = await getPegaServerConfig(); const { server, user, password } = serverConfig; const url = constructCompleteUrl(server, DELETE_COMPONENT_SERVICE_REST_ENDPOINT); const [componentName, rulesetName, rulesetVersion] = componentKey.split('~|~'); const deleteUrl = `${url}/${componentName}/rulesetname/${rulesetName}/rulesetversion/${rulesetVersion}`; try { const OauthData = fs.readFileSync(TOKEN_PATH, 'utf8'); if (OauthData) { const { access_token: accessToken, token_type: tokenType, refresh_token: refreshToken } = JSON.parse(OauthData); fetch(deleteUrl, { method: 'DELETE', agent: getHttpsAgent(serverConfig), headers: { Authorization: `${tokenType} ${accessToken}` } }) .then(response => response.text()) .then(resp => { let respData; try { respData = JSON.parse(resp); } catch (e) { console.log(chalk.bold.redBright(`Failure : ${resp}`)); process.exit(1); } if (respData.status == 200) { console.log(chalk.bold.green(`Success : ${respData.message}`)); } else { throw new Error(`${respData.message}`); } }) .catch(e => Promise.reject(`${chalk.bold.red(e)}`)); } } catch (error) { console.log(`\n${chalk.bold.red(error)}`); } }; export const copyFileToShared = async ( fileName, fileContents) => { if (showDebug) addDebugLog("copyFileToShared", `fileName: ${fileName}`, ""); const currentDirectory = process.cwd(); const pegaConfigJsonPath = join(currentDirectory, TASKS_CONFIG_JSON_FILENAME); await checkPathAccess(pegaConfigJsonPath); let data = fs.readFileSync(pegaConfigJsonPath, { encoding: 'utf8' }); data = JSON.parse(data); if (!data[COMPONENTS_DIRECTORY_PATH]) { console.error( `${chalk.red.bold('ERROR')} Could not find components directory path in config.json` ); process.exit(1); } const directory = join(currentDirectory, data[COMPONENTS_DIRECTORY_PATH]); const sharedDirectory = join(directory, SHARED_DIRECTORY); // if shared doesn't exist, create it if (!fs.existsSync(sharedDirectory)) { fs.mkdirSync(sharedDirectory, { recursive: true }); } const filePath = join(sharedDirectory, fileName); if (!fs.existsSync(filePath)) { fs.writeFileSync(filePath, fileContents); } }; export const getComponents = async (overrideDirectory = null) => { if (showDebug) addDebugLog("getComponents", "", ""); const currentDirectory = process.cwd(); const pegaConfigJsonPath = join(currentDirectory, TASKS_CONFIG_JSON_FILENAME); await checkPathAccess(pegaConfigJsonPath); let data = fs.readFileSync(pegaConfigJsonPath, { encoding: 'utf8' }); data = JSON.parse(data); if (!data[COMPONENTS_DIRECTORY_PATH]) { console.error( `${chalk.red.bold('ERROR')} Could not find components directory path in config.json` ); process.exit(1); } const directory = overrideDirectory || join(currentDirectory, data[COMPONENTS_DIRECTORY_PATH]); return fs .readdirSync(directory, { withFileTypes: true }) .filter(dirent => dirent.isDirectory() && dirent.name !== SHARED_DIRECTORY) .map(dirent => dirent.name); }; export const getTopLevelConfigFiles = async (directory) => { if (showDebug) addDebugLog("getTopLevelConfigFiles", `directory: ${directory}`, ""); if (fs.existsSync(directory)) { return fs .readdirSync(directory, { withFileTypes: true }) .filter(dirent => !dirent.isDirectory()) .map(dirent => dirent.name); } else { return []; } } export const removeTopLevelConfigFiles = async (directory) => { if (showDebug) addDebugLog("removeTopLevelConfigFiles", `directory: ${directory}`, ""); const configFiles = await getTopLevelConfigFiles(directory); if (configFiles && configFiles.length > 0) { for (const configF of configFiles) { // don't remove these files, otherwise, remove it if (configF !== "package-lock.json" && configF !== "package.json" && configF !== "tasks.config.json" && configF !== "input.config.json" && configF !== ".gitignore" && configF !== ".npmrc" && configF !== ".prettierignore") { fs.rmSync( join(directory, configF), { recursive: true, force: true, maxRetries: 2 }); } } } } export const restoreTopLevelConfigFiles = async(directory) => { if (showDebug) addDebugLog("restoreTopLevelConfigFiles", `directory: ${directory}`, ""); const currentDirectory = process.cwd(); const tempDirectory = join(currentDirectory, TEMP_PATH); const configFiles = await getTopLevelConfigFiles(tempDirectory); if (configFiles && configFiles.length > 0) { for (const configF of configFiles) { if (configF !== "package-lock.json" && configF !== "package.json") { fs.copyFileSync(join(tempDirectory, configF), join(directory, configF)); } } } } export const getTemplateConfigFiles = async () => { if (showDebug) addDebugLog("getTemplateConfigFiles", ``, ""); const currentDirectory = process.cwd(); const initDir = join(currentDirectory, "node_modules", "@pega", "custom-dx-components", "src", "tasks", "init"); const templateDir = join(initDir, "template"); if (fs.existsSync(templateDir)) { return fs .readdirSync(templateDir, { withFileTypes: true }) .filter(dirent => !dirent.isDirectory()) .map(dirent => dirent.name); } else { return []; } } export const restoreTopLevelConfigFilesFromTemplate = async(clearDep = true) => { if (showDebug) addDebugLog("restoreTopLevelConfigFilesFromTemplate", ``, ""); // we can't loose devDep that is part of install, so go get the template mustache and push // those in, replacing any changes there, but allowing additional, an keeping it updated // so won't loose new function if an old archive const currentDirectory = process.cwd(); const initDir = join(currentDirectory, "node_modules", "@pega", "custom-dx-components", "src", "tasks", "init"); const templateDir = join(initDir, "template"); const packageTemplateJsonPath = path.join(templateDir, 'package.json.mustache'); const currentPackagePath = join(currentDirectory, PACKAGE_JSON_FILENAME); let currentPackageData; // reset haveDependencyDifference = false; try { currentPackageData = fs.readFileSync(currentPackagePath); } catch (ex) { console.log($chalk.red(`\nUnable to retrieve current package.json.`)) process.exit(1); } let cData = JSON.parse(currentPackageData); let retrievedTemplateData; try { retrievedTemplateData = fs.readFileSync(packageTemplateJsonPath); } catch (ex) { console.log($chalk.red(`\nUnable to retrieve package.json.mustache from code.`)) process.exit(1); } // copy template dependecies and devDependencies const tData = JSON.parse(retrievedTemplateData); // check to see if any dependency changes const sDep = JSON.stringify(tData.dependencies); const sDevDep = JSON.stringify(tData.devDependencies); const sCurDep = JSON.stringify(cData.dependencies); const sCurDevDep = JSON.stringify(cData.devDependencies); // any changes, update haveDependencyDifference variable if (sDep != sCurDep) { haveDependencyDifference = true; } else if (sDevDep != sCurDevDep) { haveDependencyDifference = true; } await removeTopLevelConfigFiles(currentDirectory); // clear out if (clearDep) { cData.dependencies = {}; cData.devDependencies = {}; } const tDep = tData.dependencies; for (const [key, value] of Object.entries(tDep)) { cData.dependencies[key] = value; } const tDevDep = tData.devDependencies; for (const [key, value] of Object.entries(tDevDep)) { cData.devDependencies[key] = value; } // update current package json file fs.writeFileSync(currentPackagePath, JSON.stringify(cData, null, 4), { encoding: 'utf8',flag:'w' }); // restore from template const templateFiles = await getTemplateConfigFiles(); if (templateFiles && templateFiles.length > 0) { for (const tFile of templateFiles) { if (tFile !== "package.json.mustache" && tFile !== "templateNPMRC.txt" && tFile !== "tasks.config.json") { fs.copyFileSync(join(templateDir, tFile), join(currentDirectory, tFile)); } } } } export const getSubComponents = async (directory, type) => { if (showDebug) addDebugLog("getDirectoryFiles", `directory: ${directory}, type: ${type}`, ""); const subDirectory = join(directory, type); return fs .readdirSync(subDirectory, { withFileTypes: true }) .filter(dirent => dirent.isDirectory()) .map(dirent => dirent.name); }; export const getDirectoryFiles = async directory => { if (showDebug) addDebugLog("getDirectoryFiles", `directory: ${directory}`, ""); return fs .readdirSync(directory, { withFileTypes: true }) .filter(dirent => !dirent.isDirectory()) .map(dirent => dirent.name); }; export const getComponentsObj = async () => { if (showDebug) addDebugLog("getComponentsObj", "", "+"); const compList = []; // const componentList = await getDirectoryFiles(directory); const componentList = await getComponents(); if (componentList.length > 0) { compList.push( ...componentList.map(name => { const container = {}; container.name = name; container.value = name; if (showDebug) addDebugLog("getComponentsObj", "container: " + JSON.stringify(container), ""); return container; }) ); } if (showDebug) addDebugLog("getComponentsObj", "END", "-"); return compList; }; export const getPegaConfig = async (force = false) => { if (showDebug) { addDebugLog("getPegaConfig", "", "+"); } if (!pegaData || force) { if (showDebug) { addDebugLog("getPegaConfig", "retrieveData", ""); } const currentDirectory = process.cwd(); const pegaConfigJsonPath = join(currentDirectory, TASKS_CONFIG_JSON_FILENAME); await checkPathAccess(pegaConfigJsonPath); sPegaData = fs.readFileSync(pegaConfigJsonPath, { encoding: 'utf8' }); pegaData = JSON.parse(sPegaData); } if (showDebug) { addDebugLog("getPegaConfig", "pegaData: " + sPegaData, ""); } if (showDebug) { addDebugLog("getPegaConfig", "END", "-"); } return pegaData; }; export const getPegaServerConfig = async () => { if (showDebug) addDebugLog("getPegaServerConfig", "", "+"); if (!pegaConfig) { pegaConfig = await getPegaConfig(); sPegaServerConfig = JSON.stringify(pegaConfig['server-config']); } if (showDebug) addDebugLog("getPegaServerConfig", "server config:" + sPegaServerConfig, ""); if (showDebug) addDebugLog("getPegaServerConfig", "END", "-"); return pegaConfig['server-config']; }; // contains all the config, task config, etc. created upon start export const getConfigDefaults = () => { if (showDebug) addDebugLog("getConfigDefaults", `defaults: ${JSON.stringify(defaultConfigs)}`, ""); return defaultConfigs; } export const forceDefaultsUpdate = async () => { defaultConfigs = await setUpDefaults(); } export const updateServerConfigRuleSetAndVersion = async (rulesetName, rulesetVersion) => { if (showDebug) addDebugLog("updateServerConfigRuleSetAndVersion", `rulesetName: ${rulesetName}, rulesetVersion: ${rulesetVersion}`, ""); const currentDirectory = process.cwd(); const pegaConfigJsonPath = join(currentDirectory, TASKS_CONFIG_JSON_FILENAME); let configData = await getPegaConfig(true); let serverConfig = configData['server-config']; serverConfig.rulesetName = rulesetName; serverConfig.rulesetVersion = rulesetVersion; // update file // stringify "4", makes the json string look like JSON in the file, formated instead of a single line fs.writeFileSync(pegaConfigJsonPath, JSON.stringify(configData, null, 4), { encoding: 'utf8',flag:'w' }); } export const updateComponentDefaultLibrary = async (library) => { if (showDebug) addDebugLog("updateComponentDefaultLibrary", `library: ${library}`, ""); const currentDirectory = process.cwd(); const pegaConfigJsonPath = join(currentDirectory, TASKS_CONFIG_JSON_FILENAME); await checkPathAccess(pegaConfigJsonPath); let config = await getPegaConfig(true); let componentsDefault = config.component; componentsDefault.library = library; fs.writeFileSync(pegaConfigJsonPath, JSON.stringify(config, null, 4), { encoding: 'utf8',flag:'w' }); } export const updateComponentDefaultVersion = async (version) => { if (showDebug) addDebugLog("updateComponentDefaultVersion", `version: ${version}`, ""); const currentDirectory = process.cwd(); const pegaConfigJsonPath = join(currentDirectory, TASKS_CONFIG_JSON_FILENAME); await checkPathAccess(pegaConfigJsonPath); let config = await getPegaConfig(true); let componentsDefault = config.component; componentsDefault.version = version; fs.writeFileSync(pegaConfigJsonPath, JSON.stringify(config, null, 4), { encoding: 'utf8',flag:'w' }); } export const getConfigDevBuild = async() => { if (showDebug) addDebugLog("getConfigDevBuild", "", ""); // const serverConfig = await getPegaServerConfig(); // return serverConfig.devBuild; return defaultConfigs.devBuild; } export const setConfigDevBuild = async (devBuild) => { if (showDebug) addDebugLog("setConfigDevBuild", `devBuild: ${devBuild}`, ""); const currentDirectory = process.cwd(); const pegaConfigJsonPath = join(currentDirectory, TASKS_CONFIG_JSON_FILENAME); let configData = await getPegaConfig(true); let serverConfig = configData['server-config']; serverConfig.devBuild = devBuild; // update file // stringify "4", makes the json string look like JSON in the file, formated instead of a single line fs.writeFileSync(pegaConfigJsonPath, JSON.stringify(configData, null, 4), { encoding: 'utf8',flag:'w' }); } export const setConfigImportRelativePath = async (importRelativePath) => { if (showDebug) addDebugLog("setConfigImportRelativePath", `importRelativePath: ${importRelativePath}`, ""); const currentDirectory = process.cwd(); const pegaConfigJsonPath = join(currentDirectory, TASKS_CONFIG_JSON_FILENAME); let configData = await getPegaConfig(true); configData[IMPORT_RELATIVE_PATH] = importRelativePath; // update file // stringify "4", makes the json string look like JSON in the file, formated instead of a single line fs.writeFileSync(pegaConfigJsonPath, JSON.stringify(configData, null, 4), { encoding: 'utf8',flag:'w' }); } export const setConfigExportRelativePath = async (exportRelativePath) => { if (showDebug) addDebugLog("setConfigExportRelativePath", `exportRelativePath: ${exportRelativePath}`, ""); const currentDirectory = process.cwd(); const pegaConfigJsonPath = join(currentDirectory, TASKS_CONFIG_JSON_FILENAME); let configData = await getPegaConfig(true); configData[EXPORT_RELATIVE_PATH] = exportRelativePath; // update file // stringify "4", makes the json string look like JSON in the file, formated instead of a single line fs.writeFileSync(pegaConfigJsonPath, JSON.stringify(configData, null, 4), { encoding: 'utf8',flag:'w' }); } export const updateDefaultOrganization = async (organization) => { if (showDebug) addDebugLog("updateDefaultOrganization", `organization: ${organization}`, ""); let packageData = JSON.parse(fs.readFileSync(path.resolve('package.json'), 'utf-8')); packageData.organization = organization; fs.writeFileSync(path.resolve('package.json'), JSON.stringify(packageData, null, 4), { encoding: 'utf8',flag:'w' }); } export const updateConfigVersion = async (componentKey, buildVersion, currentOrgLib) => { if (showDebug) addDebugLog("updateConfigVersion", `componentKey: ${componentKey}, buildVersion: ${buildVersion}, currentOrgLib: ${currentOrgLib}`, ""); const componentDirectory = await getComponentDirectoryPath(componentKey); const configFileName = 'config.json'; const configFile = path.join(componentDirectory, configFileName); let configData; try { configData = fs.readFileSync(path.resolve(configFile), 'utf8'); } catch (err) { // for now, if now file just end console.log(`${chalk.bold.yellow(`Config file not available for: ${componentKey}`)}`); return; } const currentDate = new Date(Date.now()); const dateStamp = currentDate.toDateString(); const aboutData = await getAboutData(currentOrgLib, buildVersion, dateStamp); let data; try { data = JSON.parse(configData); //find "About" and create or update const properties = data.properties; if (properties) { let foundProp = null; // check to see if PegaAboutData exists. If customer decides to put // data under PegaAboutData, we won't change the order // otherise, will put at bottom for (let i in properties) { if (properties[i].name && properties[i].name === "PegaAboutData") { foundProp = properties[i]; break; } } if (addAboutData) { if (foundProp) { foundProp.label = aboutData; } else { // don't have "About" const aboutLabelJSON = { "format": "LABEL", "label": "About", "name": "PegaAboutLabel", "variant": "h3" }; const aboutDataJSON = { "format": "LABEL", "label": aboutData, "name": "PegaAboutData", "variant": "primary" } properties.push(aboutLabelJSON); properties.push(aboutDataJSON); } } } } catch (er) { throw new Error(`Invalid schema json in config.json: ${er}`); } data.version = buildVersion; if (addAboutData) { data.buildDate = dateStamp; } // write back file fs.writeFileSync(configFile, JSON.stringify(data, null, 4), { encoding: 'utf8',flag:'w' }); }; export const getServerType = () => { if (showDebug) addDebugLog("getServerType", "", "+"); // const defaultPegaServerConfig = await getPegaServerConfig(); // if (showDebug) addDebugLog("getServerType", "serverType:" + defaultPegaServerConfig.serverType, ""); if (showDebug) addDebugLog("getServerType", "END", "-"); // return defaultPegaServerConfig.serverType; return defaultConfigs.serverType; } export const setServerType = async(serverType) => { if (showDebug) addDebugLog("setServerType", `serverType: ${serverType}`, ""); const currentDirectory = process.cwd(); const pegaConfigJsonPath = join(currentDirectory, TASKS_CONFIG_JSON_FILENAME); let configData = await getPegaConfig(true); let serverConfig = configData['server-config']; serverConfig.serverType = serverType; // update file // stringify "4", makes the json string look like JSON in the file, formated instead of a single line fs.writeFileSync(pegaConfigJsonPath, JSON.stringify(configData, null, 4), { encoding: 'utf8',flag:'w' }); // reset pegaData = null; pegaConfig = null; await forceDefaultsUpdate(); } export const getSrcDirectoryPath = async () => { const currentDirectory = process.cwd(); return join(currentDirectory, "src"); } export const getComponentDirectoryPath = async (componentKey, overrideDir = null) => { if (showDebug) addDebugLog("getComponentDirectoryPath", "", "+"); const currentDirectory = process.cwd(); const pegaConfigJsonPath = join(currentDirectory, TASKS_CONFIG_JSON_FILENAME); await checkPathAccess(pegaConfigJsonPath); let data = fs.readFileSync(pegaConfigJsonPath, { encoding: 'utf8' }); data = JSON.parse(data); if (!data[COMPONENTS_DIRECTORY_PATH]) { console.error( `${chalk.red.bold('ERROR')} Could not able find components directory path in config.json` ); process.exit(1); } if (showDebug) addDebugLog("getComponentDirectoryPath", "path: " + join(currentDirectory, data[COMPONENTS_DIRECTORY_PATH], componentKey), ""); if (showDebug) addDebugLog("getComponentDirectoryPath", "END", "-"); return overrideDir ? join(overrideDir, componentKey) : join(currentDirectory, data[COMPONENTS_DIRECTORY_PATH], componentKey); }; export const compileMustacheTemplate = (file, data) => { if (showDebug) addDebugLog("compileMustacheTemplate", `file: ${file}`, ""); const content = fs.readFileSync(file, 'utf8'); return mustache.render(content, data); }; export const isPascalCase = string => { return string === pascalCase(string); }; export const convertIntoPascalCase = string => { return pascalCase(string); }; export const constructCompleteUrl = (baseServer, endPoint) => { if (showDebug) addDebugLog("constructCompleteUrl", "url: " + baseServer.endsWith('/') ? `${baseServer}${endPoint}` : `${baseServer}/${endPoint}`, ""); return baseServer.endsWith('/') ? `${baseServer}${endPoint}` : `${baseServer}/${endPoint}`; }; export const sanitize = str => { /* Allow only numbers and case insensitive alphabets */ str = str.replace(/[^a-zA-Z0-9 ]/g, ''); /* spaces will be replaced by - */ str = str.replace(/\s+/g, '-'); return str; }; export const validateSemver = str => { /* basic semver version validation - 0.0.1-dev */ const regex = /^[0-9]\d*\.\d+\.\d+(?:-[a-zA-Z0-9]+)?$/g; return regex.test(str); }; export const validateRulesetVersion = str => { /* Ruleset version range - 01-99 */ if (str.indexOf('00') !== -1) { return false; } /* basic ruleset version validation - 01-01-01 */ const regex = /^\d\d-\d\d-\d\d$/g; return regex.test(str); }; export const standardizeStr = (str, length) => { return str.padEnd(length); }; export const showVersion = async () => { // get package version, so can display at start // const require = createRequire(import.meta.url); const require = createRequire(currentPath.default); const pData = require('@pega/custom-dx-components/package.json'); console.log(chalk.green(`DX Component Builder v${pData.version}`)); showDebug = getShowDebug(); if (getLibraryBased() && getServerType() === "launchpad") { console.log(`\nLaunchpad commands only supported for ${chalk.bold.green('non library mode')} components.\n`); process.exit(); } // do a check of node version, it should be 18 or greater const arNodeVersion = process.versions.node.split('.'); const nodeMajorVersion = parseInt(arNodeVersion[0]); const nodeMinorVersion = parseInt(arNodeVersion[1]); if (nodeMajorVersion < 18) { console.log( chalk.redBright( `DX Component Builder - SDK requires node v18 or greater, current version: ${process.version}` ) ); process.exit(1); } // const hasGit = checkGit(process.cwd()); // if (!hasGit) { // console.log(chalk.redBright('DX Component Builder - Git needs to be installed.')); // process.exit(1); // } }; export const getOOTBComponents = async () => { if (showDebug) addDebugLog("getOOTBComponents", "", ""); return new Promise(resolve => { fs.readFile(OOTB_COMPONENTS, 'utf-8', async (error, ootbComponentsData) => { if (error) { try { const serverConfig = await getPegaServerConfig(); const { server } = serverConfig; const url = constructCompleteUrl(server, OOTB_COMPONENT_SERVICE_REST_ENDPOINT); const OauthData = fs.readFileSync(TOKEN_PATH, 'utf8'); if (OauthData) { const { access_token: accessToken, token_type: tokenType } = JSON.parse(OauthData); fetch(url, { method: 'GET', agent: getHttpsAgent(serverConfig), headers: { Authorization: `${tokenType} ${accessToken}` } }) .then(response => response.text()) .then(data => { if (data.charAt() === '[') { data = data.slice(1, -1); data = data.replace(/\s/g, ''); fs.writeFile(OOTB_COMPONENTS, data, err => { if (err) { console.error(err); } resolve(data.split(',')); }); } else { data = JSON.parse(data); if (data && data.errors) { const errMessages = data.errors; errMessages.forEach(msgArr => { throw new Error(`Failed with error - ${JSON.stringify(msgArr.message)}`); }); } } }) // eslint-disable-next-line prefer-promise-reject-errors .catch(e => Promise.reject(`${chalk.bold.red(e)}`)); } } catch (e) { throw new Error('Error occurred in validation', e); } } else { resolve(ootbComponentsData.split(',')); } }); }); }; export const deleteInfinityServerComponent = async componentKey => { if (showDebug) addDebugLog("deleteInfinityServerComponent", `componentKey: ${componentKey}`, ""); const serverConfig = await getPegaServerConfig(); const { server } = serverConfig; const url = constructCompleteUrl(server, DELETE_COMPONENT_SERVICE_REST_ENDPOINT); const [componentName, rulesetName, rulesetVersion] = componentKey.split('~|~'); const deleteUrl = `${url}/${componentName}/rulesetname/${rulesetName}/rulesetversion/${rulesetVersion}`; try { const OauthData = fs.readFileSync(TOKEN_PATH, 'utf8'); let status = 500; if (OauthData) { const { access_token: accessToken, token_type: tokenType } = JSON.parse(OauthData); const response = await fetch(deleteUrl, { method: 'DELETE', agent: getHttpsAgent(serverConfig), headers: { Authorization: `${tokenType} ${accessToken}` } }); status = response.status; if (!response.ok) { 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 === 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'); } else if (response.status === 403) { throw new Error( 'Error forbidden: User does not have privileges to Publish.' ); } } try { const respData = await response.json(); console.log( chalk.bold.green(`Success : ${respData.message}`) ); if (status === 500) { status = 999; throw new Error(respData.message); } } catch (err) { if (status === 500) { throw new Error( `Error occurred in authentication. Please regenerate using authenticate` ); } else if (status === 999) { throw new Error (err); } } } } catch (error) { throw new Error(error); } }; export const deleteLaunchpadServerComponent = async componentKey => { if (showDebug) addDebugLog("deleteLaunchpadServerComponent", `componentKey: ${componentKey}`, ""); const { server, isolationId } = await getPegaServerConfig(); const deleteUrl = constructCompleteUrl( server, LP_DELETE_COMPONENT_SERVICE_REST_ENDPOINT.replace('{isolationId}', isolationId || 'undefined') ); const [componentName] = componentKey.split('~|~'); try { const OauthData = fs.readFileSync(TOKEN_PATH, 'utf8'); let status = 500; if (OauthData) { const { access_token: accessToken, token_type: tokenType } = JSON.parse(OauthData); const headers = { Authorization: `${tokenType} ${accessToken}`, cookie: `Pega-AAT=${accessToken}`, 'Content-Type': 'application/json' }; const apiBody = { type: 'CustomComponent', componentName }; const response = await fetch(deleteUrl, { method: 'DELETE', agent: new https.Agent({ rejectUnauthorized: false }), headers, body: JSON.stringify(apiBody) }); status = response.status; if (!response.ok) { 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 === 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'); } else if (response.status === 403) { throw new Error( 'Error forbidden: User does not have privileges to Publish.' ); } } try { const respData = await response.text(); console.log( chalk.bold.green(`Success : ${respData}`) ); if (status === 500) { status = 999; throw new Error(respData.message); } } catch (err) { if (status === 500) { throw new Error( `Error occurred in authentication. Please regenerate using authenticate` ); } else if (status === 999) { throw new Error (err); } } } } catch (error) { throw new Error(error); } }; export const getBootstrapConfig = async (restServerUrl, authorizationHeader) => { if (showDebug) addDebugLog("getBootstrapConfig", `restServerUrl: ${restServerUrl}`, ""); const serverConfig = await getPegaServerConfig(); return fetch(`${restServerUrl}/${BOOTSTRAP_CONFIG}`, { method: 'GET', agent: getHttpsAgent(serverConfig), headers: new Headers({ Authorization: authorizationHeader }) }).then((response) => { if (response.ok) { return response.json(); } throw Error('D_pxBootstrapConfig load failed.', { cause: response.status }); }); }; export const updateC11NB2SForInfinity = async () => { if (showDebug) addDebugLog("updateC11NB2SForInfinity", "", ""); const serverConfig = await getPegaServerConfig(); const { server } = serverConfig; const OAuthData = fs.readFileSync(TOKEN_PATH, 'utf8'); if (OAuthData) { const { access_token: accessToken, token_type: tokenType } = JSON.parse(OAuthData); const bootstrapJSON = await getBootstrapConfig(server, `${tokenType} ${accessToken}`); if (bootstrapJSON) { const bootstrap = JSON.parse(bootstrapJSON.pyConfigJSON); const objOauthData = JSON.parse(OAuthData); objOauthData["C11NB2S"] = bootstrap.tokens.C11NB2S; objOauthData["appStaticContentServer"] = bootstrap.serviceConfig.appStaticContentServer; // write out file fs.writeFileSync(TOKEN_PATH, JSON.stringify(objOauthData), { encoding: 'utf8',flag:'w' }); } } }; export const updateC11NB2SForLaunchpad = async () => { }; export const getC11NB2STokenAndStaticServer = async () => { if (showDebug) addDebugLog("getC11NB2STokenAndStaticServer", "", ""); const serverConfig = await getPegaServerConfig(); const { server } = serverConfig; let OAuthData; try { OAuthData = fs.readFileSync(TOKEN_PATH, 'utf8'); } catch (ex) { return ({}); } if (OAuthData) { const { C11NB2S, appStaticContentServer } = JSON.parse(OAuthData) if (C11NB2S) { return ({ C11NB2S, appStaticContentServer}); } } return ({}); }; export const getLocalLibraries = async () => { if (showDebug) addDebugLog("getLocalLibraries", "" , ""); const currentDirectory = process.cwd(); const configDef = getConfigDefaults(); const libFilter = configDef.organization.concat("_"); return fs .readdirSync(currentDirectory, { withFileTypes: true }) .filter(dirent => dirent.isDirectory() && dirent.name.startsWith(libFilter)) .map(dirent => dirent.name); } export const getLocalLibraryVersions = async (libraryName, currentVersion = "0.0.0") => { if (showDebug) addDebugLog("getLocalLibraryVersions", `libraryName: ${libraryName}` , ""); const currentDirectory = process.cwd(); const directory = join(currentDirectory, libraryName); return fs .readdirSync(directory, { withFileTypes: true }) .filter(dirent => dirent.isDirectory() && dirent.name != currentVersion) .map(dirent => dirent.name); }; export const getLocalArchivedLibraryVersions = async (libraryName, currentVersion = "0.0.0") => { if (showDebug) addDebugLog("getLocalArchivedLibraryVersions", `libraryName: ${libraryName}` , ""); const currentDirectory = process.cwd(); const archiveDirectory = join (currentDirectory, ARCHIVES_PATH, `${libraryName}`); return fs .readdirSync(archiveDirectory, { withFileTypes: true }) .filter(dirent => dirent.isDirectory() && dirent.name != currentVersion) .map(dirent => dirent.name); }; export const getLocalArchivedLibrraryVersionData = async(libraryName, libraryVersion) => { if (showDebug) addDebugLog("getLocalArchivedLibrraryVersionData", `libraryName: ${libraryName}, libraryVersion: ${libraryVersion}`, ""); const currentDirectory = process.cwd(); const archiveDirectory = join (currentDirectory, ARCHIVES_PATH, `${libraryName}`, `${libraryVersion}`); const componentConfigFile = join (archiveDirectory, COMPONENTS_CONFIG_FILE_NAME); let data = fs.readFileSync(componentConfigFile, { encoding: 'utf8' }) return data; } export const getLocalLibraryVersionData = async (libraryName, libraryVersion) => { if (showDebug) addDebugLog("getLocalLibraryVersionDate", `libraryName: ${libraryName}, libraryVersion: ${libraryVersion}` , ""); const currentDirectory = process.cwd(); const libDir = join(currentDirectory, libraryName, libraryVersion); const libDataFile = join(libDir, COMPONENTS_CONFIG_FILE_NAME); let data = fs.readFileSync(libDataFile, { encoding: 'utf8' }) return data; }; export const deleteLocalLibrary = async (libraryName) => { if (showDebug) addDebugLog("deleteLocalLibrary", `libraryName: ${libraryName}` , ""); const currentDirectory = process.cwd(); const directory = join(currentDirectory, libraryName); // for some odd reason, it errors out with an error in "err", but it does delete // all the contents. The try/catch then calls again to delete the top level try { if (fs.existsSync(directory)) { fs.rmSync(directory, { recursive: true, force: true, maxRetries: 2 }); console.log(`Library: ${chalk.green.bold(libraryName)} is deleted from Local`); } else { console.log(`Library: ${chalk.red.bold(libraryName)} does not exist.`); } } catch (ex) { if (fs.existsSync(directory)) { fs.rmSync(directory, { recursive: true, force: true, maxRetries: 2 }); console.log(`Library: ${chalk.green.bold(libraryName)} is deleted from Local`); } else { console.log(`Library: ${chalk.red.bold(libraryName)} does not exist.`); } } }; export const