@pega/custom-dx-components
Version:
Utility for building custom UI components
493 lines (421 loc) • 16.4 kB
JavaScript
import chalk from 'chalk';
import fs from 'fs';
import path from 'path';
import { join } from 'path';
import { Listr } from 'listr2';
import {
addDebugLog,
getFetchAgent,
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',
dispatcher: getFetchAgent(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}`);
}
})
.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) {
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)}`);
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',
dispatcher: getFetchAgent(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}`);
}
})
.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) {
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)}`);
reject(`${chalk.bold.red(error)}`);
}
});
};