@pega/custom-dx-components
Version:
Utility for building custom UI components
1,561 lines (1,253 loc) • 75.5 kB
JavaScript
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