diff --git a/messages/assess.json b/messages/assess.json index 6e4ea73..ef21e25 100644 --- a/messages/assess.json +++ b/messages/assess.json @@ -25,5 +25,7 @@ "errorWhileActivatingCard": "Could not activate Card: ", "errorWhileUploadingCard": "An error ocurred while uploading Card: ", "errorWhileCreatingElements": "An error ocurred while saving OmniScript elements: ", - "allVersionsDescription": "Migrate all versions of a component" -} \ No newline at end of file + "allVersionsDescription": "Migrate all versions of a component", + "changeMessage": " %s will be changed from %s to %s", + "angularOSWarning": " Angular OmniScript will not be migrated, please convert this to LWC based Omniscript" +} diff --git a/src/commands/omnistudio/migration/assess.ts b/src/commands/omnistudio/migration/assess.ts index e97f6ab..dc5dc67 100644 --- a/src/commands/omnistudio/migration/assess.ts +++ b/src/commands/omnistudio/migration/assess.ts @@ -45,7 +45,7 @@ export default class Assess extends OmniStudioBaseCommand { DebugTimer.getInstance().start(); const namespace = (this.flags.namespace || 'vlocity_ins') as string; const apiVersion = (this.flags.apiversion || '55.0') as string; - const allVersions = true; + const allVersions = (this.flags.allversions || false) as boolean; const conn = this.org.getConnection(); Logger.initialiseLogger(this.ux, this.logger); const projectDirectory = OmnistudioRelatedObjectMigrationFacade.intializeProject(); diff --git a/src/commands/omnistudio/migration/migrate.ts b/src/commands/omnistudio/migration/migrate.ts index 05cc1db..72b6fed 100644 --- a/src/commands/omnistudio/migration/migrate.ts +++ b/src/commands/omnistudio/migration/migrate.ts @@ -20,6 +20,7 @@ import { CardMigrationTool } from '../../../migration/flexcard'; import { OmniScriptExportType, OmniScriptMigrationTool } from '../../../migration/omniscript'; import { Logger } from '../../../utils/logger'; import OmnistudioRelatedObjectMigrationFacade from '../../../migration/related/OmnistudioRelatedObjectMigrationFacade'; +import { generatePackageXml } from '../../../utils/generatePackageXml'; // Initialize Messages with the current plugin directory Messages.importMessagesDirectory(__dirname); @@ -175,7 +176,11 @@ export default class Migrate extends OmniStudioBaseCommand { allVersions, this.org ); - omnistudioRelatedObjectsMigration.migrateAll(objectMigrationResults, []); + const relatedObjectMigrationResult = omnistudioRelatedObjectsMigration.migrateAll(objectMigrationResults, []); + generatePackageXml.createChangeList( + relatedObjectMigrationResult.apexClasses, + relatedObjectMigrationResult.lwcComponents + ); await ResultsBuilder.generate(objectMigrationResults, conn.instanceUrl); // save timer to debug logger diff --git a/src/migration/base.ts b/src/migration/base.ts index bb4ee6f..982455f 100644 --- a/src/migration/base.ts +++ b/src/migration/base.ts @@ -3,6 +3,7 @@ import { Connection, Logger, Messages } from '@salesforce/core'; import { DebugTimer, QueryTools } from '../utils'; import { NetUtils } from '../utils/net'; +import { Stringutil } from '../utils/StringValue/stringutil'; import { TransformData, UploadRecordResult } from './interfaces'; export class BaseMigrationTool { @@ -57,8 +58,7 @@ export class BaseMigrationTool { } protected cleanName(name: string, allowUnderscores = false): string { - if (!name) return ''; - return allowUnderscores ? name.replace(/[^a-z0-9_]+/gi, '') : name.replace(/[^a-z0-9]+/gi, ''); + return Stringutil.cleanName(name, allowUnderscores); } protected async truncate(objectName: string): Promise { diff --git a/src/migration/dataraptor.ts b/src/migration/dataraptor.ts index 7ed2f76..795e99b 100644 --- a/src/migration/dataraptor.ts +++ b/src/migration/dataraptor.ts @@ -188,8 +188,8 @@ export class DataRaptorMigrationTool extends BaseMigrationTool implements Migrat DebugTimer.getInstance().lap('Query data raptors'); const dataRaptors = await this.getAllDataRaptors(); const dataRaptorAssessmentInfos = this.processDRComponents(dataRaptors); - this.ux.log('dataRaptorAssessmentInfos'); - this.ux.log(dataRaptorAssessmentInfos.toString()); + /* this.ux.log('dataRaptorAssessmentInfos'); + this.ux.log(dataRaptorAssessmentInfos.toString()); */ return dataRaptorAssessmentInfos; } catch (err) { this.ux.log(err); diff --git a/src/migration/interfaces.ts b/src/migration/interfaces.ts index 80459a0..1f89eb9 100644 --- a/src/migration/interfaces.ts +++ b/src/migration/interfaces.ts @@ -100,9 +100,16 @@ export interface RelatedObjectsMigrate { * @param namespace The namespace used to perform the migration. * @param migrationCandidates List of candidates to migrate. */ - migrateRelatedObjects(migrationResults: MigrationResult[], migrationCandidates: JSON[]): void; + migrateRelatedObjects(migrationResults: MigrationResult[], migrationCandidates: JSON[]): string[]; + + processObjectType(): string; } export type LWCComponentMigrationTool = MigrationTool; export type CustomLabelMigrationTool = MigrationTool; + +export interface RelatedObjectMigrationResult { + apexClasses: string[]; + lwcComponents: string[]; +} diff --git a/src/migration/omniscript.ts b/src/migration/omniscript.ts index b11aef6..d1a32e5 100644 --- a/src/migration/omniscript.ts +++ b/src/migration/omniscript.ts @@ -4,7 +4,14 @@ import { AnyJson } from '@salesforce/ts-types'; import OmniScriptMappings from '../mappings/OmniScript'; import ElementMappings from '../mappings/Element'; import OmniScriptDefinitionMappings from '../mappings/OmniScriptDefinition'; -import { DataRaptorAssessmentInfo, DebugTimer, FlexCardAssessmentInfo, QueryTools, SortDirection } from '../utils'; +import { + DataRaptorAssessmentInfo, + DebugTimer, + FlexCardAssessmentInfo, + nameLocation, + QueryTools, + SortDirection, +} from '../utils'; import { BaseMigrationTool } from './base'; import { MigrationResult, MigrationTool, TransformData, UploadRecordResult } from './interfaces'; import { ObjectMapping } from './interfaces'; @@ -13,6 +20,7 @@ import { Connection, Messages, Logger } from '@salesforce/core'; import { UX } from '@salesforce/command'; import { OSAssessmentInfo, OmniAssessmentInfo, IPAssessmentInfo } from '../../src/utils'; import { getAllFunctionMetadata, getReplacedString } from '../utils/formula/FormulaUtil'; +import { StringVal } from '../utils/StringValue/stringval'; export class OmniScriptMigrationTool extends BaseMigrationTool implements MigrationTool { private readonly exportType: OmniScriptExportType; @@ -190,30 +198,24 @@ export class OmniScriptMigrationTool extends BaseMigrationTool implements Migrat const osAssessmentInfos: OSAssessmentInfo[] = []; const ipAssessmentInfos: IPAssessmentInfo[] = []; - const limitedOmniscripts = omniscripts.slice(0, 200); - // Create a set to store existing OmniScript names and also extract DataRaptor and FlexCard names const existingOmniscriptNames = new Set(); const existingDataRaptorNames = new Set(dataRaptorAssessmentInfos.map((info) => info.name)); const existingFlexCardNames = new Set(flexCardAssessmentInfos.map((info) => info.name)); // First, collect all OmniScript names from the omniscripts array - for (const omniscript of limitedOmniscripts) { - const omniScriptName = `${omniscript[this.namespacePrefix + 'Name']}`; - existingOmniscriptNames.add(omniScriptName); - } - // Now process each OmniScript and its elements - for (const omniscript of limitedOmniscripts) { + for (const omniscript of omniscripts) { const elements = await this.getAllElementsForOmniScript(omniscript['Id']); - const dependencyIP: string[] = []; + const dependencyIP: nameLocation[] = []; const missingIP: string[] = []; - const dependencyDR: string[] = []; + const dependencyDR: nameLocation[] = []; const missingDR: string[] = []; - const dependencyOS: string[] = []; + const dependencyOS: nameLocation[] = []; const missingOS: string[] = []; - const dependenciesRA: string[] = []; + const dependenciesRA: nameLocation[] = []; + const dependenciesLWC: nameLocation[] = []; //const missingRA: string[] = []; for (const elem of elements) { @@ -223,12 +225,15 @@ export class OmniScriptMigrationTool extends BaseMigrationTool implements Migrat // Check for OmniScript dependencies if (type === 'OmniScript') { - const nameVal = `${elemName}_OmniScript`; + const nameVal = `${elemName}`; const type = propertySet['Type']; const subtype = propertySet['Sub Type']; const language = propertySet['Language']; const osName = type + '_' + subtype + '_' + language; - dependencyOS.push(osName + ' ( ' + nameVal + ' )
'); + dependencyOS.push({ + name: osName, + location: nameVal, + }); if (!existingOmniscriptNames.has(nameVal)) { missingOS.push(nameVal); } @@ -236,8 +241,8 @@ export class OmniScriptMigrationTool extends BaseMigrationTool implements Migrat // Check for Integration Procedure Action dependencies if (type === 'Integration Procedure Action') { - const nameVal = `${elemName}_Integration Procedure Action`; - dependencyIP.push(propertySet['integrationProcedureKey'] + ' (' + nameVal + ')
'); + const nameVal = `${elemName}`; + dependencyIP.push({ name: propertySet['integrationProcedureKey'], location: nameVal }); if (!existingOmniscriptNames.has(nameVal) && !existingFlexCardNames.has(nameVal)) { missingIP.push(nameVal); } @@ -245,33 +250,94 @@ export class OmniScriptMigrationTool extends BaseMigrationTool implements Migrat // Check for DataRaptor dependencies if (['DataRaptor Extract Action', 'DataRaptor Turbo Action', 'DataRaptor Post Action'].includes(type)) { - const nameVal = `${elemName}_${type}`; - dependencyDR.push(propertySet['bundle'] + ' ( ' + nameVal + ' )
'); + const nameVal = `${elemName}`; + dependencyDR.push({ name: propertySet['bundle'], location: nameVal }); if (!existingOmniscriptNames.has(nameVal) && !existingDataRaptorNames.has(nameVal)) { missingDR.push(nameVal); } } if (type === 'Remote Action') { - const nameVal = `${elemName}_${type}`; + const nameVal = `${elemName}`; const className = propertySet['remoteClass']; const methodName = propertySet['remoteMethod']; - dependenciesRA.push(className + '.' + methodName + ' (' + nameVal + ')
'); + dependenciesRA.push({ name: className + '.' + methodName, location: nameVal }); + } + // To handle radio , multiselect + if (propertySet['optionSource'] && propertySet['optionSource']['type'] === 'Custom') { + const nameVal = `${elemName}`; + dependenciesRA.push({ name: propertySet['optionSource']['source'], location: nameVal }); } - } - /*const recordName = `${omniscript[this.namespacePrefix + 'Type__c']}_` + - `${omniscript[this.namespacePrefix + 'SubType__c']}` + - (omniscript[this.namespacePrefix + 'Language__c'] ? `_${omniscript[this.namespacePrefix + 'Language__c']}` : '') + - `_${omniscript[this.namespacePrefix + 'Version__c']}`;*/ + if (type === 'Custom Lightning Web Component') { + const nameVal = `${elemName}`; + const lwcName = propertySet['lwcName']; + dependenciesLWC.push({ name: lwcName, location: nameVal }); + } + } const omniProcessType = omniscript[this.namespacePrefix + 'IsProcedure__c'] ? 'Integration Procedure' : 'OmniScript'; + const existingType = omniscript[this.namespacePrefix + 'Type__c']; + const existingTypeVal = new StringVal(existingType, 'type'); + const existingSubType = omniscript[this.namespacePrefix + 'SubType__c']; + const existingSubTypeVal = new StringVal(existingSubType, 'sub type'); + const omniScriptName = omniscript[this.namespacePrefix + 'Name']; + const existingOmniScriptNameVal = new StringVal(omniScriptName, 'name'); + + const warnings: string[] = []; + + const recordName = + `${existingTypeVal.cleanName()}_` + + `${existingSubTypeVal.cleanName()}` + + (omniscript[this.namespacePrefix + 'Language__c'] + ? `_${omniscript[this.namespacePrefix + 'Language__c']}` + : '') + + `_${omniscript[this.namespacePrefix + 'Version__c']}`; + + if (!existingTypeVal.isNameCleaned()) { + warnings.push( + this.messages.getMessage('changeMessage', [ + existingTypeVal.type, + existingTypeVal.val, + existingTypeVal.cleanName(), + ]) + ); + } + if (!existingSubTypeVal.isNameCleaned()) { + warnings.push( + this.messages.getMessage('changeMessage', [ + existingSubTypeVal.type, + existingSubTypeVal.val, + existingSubTypeVal.cleanName(), + ]) + ); + } + if (!existingOmniScriptNameVal.isNameCleaned()) { + warnings.push( + this.messages.getMessage('changeMessage', [ + existingOmniScriptNameVal.type, + existingOmniScriptNameVal.val, + existingOmniScriptNameVal.cleanName(), + ]) + ); + } + if (existingOmniscriptNames.has(recordName)) { + warnings.push(this.messages.getMessage('duplicatedName') + recordName); + } else { + existingOmniscriptNames.add(recordName); + } + if (omniProcessType === 'OmniScript') { + const type = omniscript[this.namespacePrefix + 'IsLwcEnabled__c'] ? 'LWC' : 'Angular'; + if (type === 'Angular') { + warnings.unshift(this.messages.getMessage('angularOSWarning')); + } const osAssessmentInfo: OSAssessmentInfo = { - name: omniscript['Name'], + name: recordName, + type: type, id: omniscript['Id'], dependenciesIP: dependencyIP, missingIP: [], @@ -280,22 +346,22 @@ export class OmniScriptMigrationTool extends BaseMigrationTool implements Migrat dependenciesOS: dependencyOS, missingOS: missingOS, dependenciesRemoteAction: dependenciesRA, + dependenciesLWC: dependenciesLWC, infos: [], - warnings: [], + warnings: warnings, errors: [], - path: '', }; osAssessmentInfos.push(osAssessmentInfo); } else { const ipAssessmentInfo: IPAssessmentInfo = { - name: omniscript['Name'], + name: recordName, id: omniscript['Id'], dependenciesIP: dependencyIP, dependenciesDR: dependencyDR, dependenciesOS: dependencyOS, dependenciesRemoteAction: dependenciesRA, infos: [], - warnings: [], + warnings: warnings, errors: [], path: '', }; diff --git a/src/migration/related/ApexMigration.ts b/src/migration/related/ApexMigration.ts index 47af865..7e521a7 100644 --- a/src/migration/related/ApexMigration.ts +++ b/src/migration/related/ApexMigration.ts @@ -37,20 +37,24 @@ export class ApexMigration extends BaseRelatedObjectMigration implements Related this.vlocityOpenInterface2 = new InterfaceImplements(VLOCITY_OPEN_INTERFACE2, this.namespace); this.vlocityOpenInterface = new InterfaceImplements(VLOCITY_OPEN_INTERFACE, this.namespace); } + public processObjectType(): string { + return 'apex'; + } public identifyObjects(migrationResults: MigrationResult[]): Promise { throw new Error('Method not implemented.'); } - public migrateRelatedObjects(migrationResults: MigrationResult[], migrationCandidates: JSON[]): void { - this.migrate(); + public migrateRelatedObjects(migrationResults: MigrationResult[], migrationCandidates: JSON[]): string[] { + return this.migrate(); } - public migrate(): void { + public migrate(): string[] { const pwd = shell.pwd(); shell.cd(this.projectPath); const targetOrg: Org = this.org; sfProject.retrieve(APEXCLASS, targetOrg.getUsername()); - this.processApexFiles(this.projectPath); + const apexAssessmentInfos = this.processApexFiles(this.projectPath); sfProject.deploy(APEXCLASS, targetOrg.getUsername()); shell.cd(pwd); + return this.mapTOName(apexAssessmentInfos); } public assess(): ApexAssessmentInfo[] { @@ -180,4 +184,10 @@ export class ApexMigration extends BaseRelatedObjectMigration implements Related } `; } + + private mapTOName(apexAssessmentInfos: ApexAssessmentInfo[]): string[] { + return apexAssessmentInfos.map((apexAssessmentInfo) => { + return apexAssessmentInfo.name; + }); + } } diff --git a/src/migration/related/LwcMigration.ts b/src/migration/related/LwcMigration.ts index 043c7a8..00dda8b 100644 --- a/src/migration/related/LwcMigration.ts +++ b/src/migration/related/LwcMigration.ts @@ -2,7 +2,7 @@ import * as shell from 'shelljs'; import { Org } from '@salesforce/core'; import { fileutil, File } from '../../utils/file/fileutil'; -import { MigrationResult } from '../interfaces'; +import { MigrationResult, RelatedObjectsMigrate } from '../interfaces'; import { sfProject } from '../../utils/sfcli/project/sfProject'; import { Logger } from '../../utils/logger'; import { FileProcessorFactory } from '../../utils/lwcparser/fileutils/FileProcessorFactory'; @@ -12,14 +12,16 @@ import { BaseRelatedObjectMigration } from './BaseRealtedObjectMigration'; const LWC_DIR_PATH = '/force-app/main/default/lwc'; const LWCTYPE = 'LightningComponentBundle'; -export class LwcMigration extends BaseRelatedObjectMigration { +export class LwcMigration extends BaseRelatedObjectMigration implements RelatedObjectsMigrate { + public processObjectType(): string { + return 'lwc'; + } public identifyObjects(migrationResults: MigrationResult[]): Promise { this.assessment(); throw new Error('Method not implemented.'); } - public migrateRelatedObjects(migrationResults: MigrationResult[], migrationCandidates: JSON[]): void { - this.migrate(); - this.processLwcFiles(this.projectPath); + public migrateRelatedObjects(migrationResults: MigrationResult[], migrationCandidates: JSON[]): string[] { + return this.mapToName(this.migrate()); } public assessment(): LWCAssessmentInfo[] { const type = 'assessment'; @@ -31,14 +33,16 @@ export class LwcMigration extends BaseRelatedObjectMigration { return this.processFiles(filesMap, type); } - public migrate(): void { + public migrate(): LWCAssessmentInfo[] { const pwd = shell.pwd(); shell.cd(this.projectPath); const targetOrg: Org = this.org; sfProject.retrieve(LWCTYPE, targetOrg.getUsername()); - this.processLwcFiles(this.projectPath); - // sfProject.deploy(LWCTYPE, targetOrg.getUsername()); + const filesMap = this.processLwcFiles(this.projectPath); + const LWCAssessmentInfos = this.processFiles(filesMap, 'migration'); + sfProject.deploy(LWCTYPE, targetOrg.getUsername()); shell.cd(pwd); + return LWCAssessmentInfos; } // This method is returning a Map of directory and list of file in directory @@ -97,4 +101,10 @@ export class LwcMigration extends BaseRelatedObjectMigration { private isValideFile(filename: string): boolean { return !filename.includes('_def') && !filename.includes('styleDefinition') && !filename.includes('definition'); } + + private mapToName(lwcAssessmentInfos: LWCAssessmentInfo[]): string[] { + return lwcAssessmentInfos.map((lwcAssessmentInfo) => { + return lwcAssessmentInfo.name; + }); + } } diff --git a/src/migration/related/OmnistudioRelatedObjectMigrationFacade.ts b/src/migration/related/OmnistudioRelatedObjectMigrationFacade.ts index cb3d835..6eb22dc 100644 --- a/src/migration/related/OmnistudioRelatedObjectMigrationFacade.ts +++ b/src/migration/related/OmnistudioRelatedObjectMigrationFacade.ts @@ -5,7 +5,7 @@ import { Org } from '@salesforce/core'; import '../../utils/prototypes'; import { DebugTimer, MigratedObject } from '../../utils'; -import { RelatedObjectsMigrate } from '../interfaces'; +import { RelatedObjectMigrationResult, RelatedObjectsMigrate } from '../interfaces'; import { sfProject } from '../../utils/sfcli/project/sfProject'; import { Logger } from '../../utils/logger'; import { ApexMigration } from './ApexMigration'; @@ -44,7 +44,7 @@ export default class OmnistudioRelatedObjectMigrationFacade { return process.cwd() + '/' + defaultProjectName; } } - public migrateAll(migrationResult: MigratedObject[], relatedObjects: string[]): any { + public migrateAll(migrationResult: MigratedObject[], relatedObjects: string[]): RelatedObjectMigrationResult { // Start the debug timer DebugTimer.getInstance().start(); @@ -63,15 +63,14 @@ export default class OmnistudioRelatedObjectMigrationFacade { if (relatedObjects.includes('apex')) { migrationTools.push(this.createApexClassMigrationTool(projectDirectory)); } - + const results = new Map(); // Proceed with migration logic - for (const migrationTool of migrationTools.reverse()) { + for (const migrationTool of migrationTools) { try { - migrationTool.migrateRelatedObjects(null, null); + results.set(migrationTool.processObjectType(), migrationTool.migrateRelatedObjects(null, null)); } catch (Error) { // Log the error Logger.logger.error(Error.message); - return { migrationResult }; } } // Truncate existing objects if necessary @@ -82,7 +81,7 @@ export default class OmnistudioRelatedObjectMigrationFacade { Logger.logger.debug(timer); // Return results needed for --json flag - return { migrationResult }; + return { apexClasses: results.get('apex'), lwcComponents: results.get('lwc') }; } // Factory methods to create instances of specific tools diff --git a/src/utils/StringValue/stringutil.ts b/src/utils/StringValue/stringutil.ts new file mode 100644 index 0000000..9eb4b8e --- /dev/null +++ b/src/utils/StringValue/stringutil.ts @@ -0,0 +1,6 @@ +export class Stringutil { + public static cleanName(name: string, allowUnderscores = false): string { + if (!name) return ''; + return allowUnderscores ? name.replace(/[^a-z0-9_]+/gi, '') : name.replace(/[^a-z0-9]+/gi, ''); + } +} diff --git a/src/utils/StringValue/stringval.ts b/src/utils/StringValue/stringval.ts new file mode 100644 index 0000000..30e2ce8 --- /dev/null +++ b/src/utils/StringValue/stringval.ts @@ -0,0 +1,18 @@ +import { Stringutil } from './stringutil'; + +export class StringVal { + public val: string; + public type: string; + + public constructor(val: string, type?: string) { + this.val = val; + this.type = type; + } + + public cleanName(allowUnderscores = false): string { + return Stringutil.cleanName(this.val, allowUnderscores); + } + public isNameCleaned(): boolean { + return !this.val || this.val === this.cleanName(); + } +} diff --git a/src/utils/generatePackageXml.ts b/src/utils/generatePackageXml.ts index dcf794d..cab1cd5 100644 --- a/src/utils/generatePackageXml.ts +++ b/src/utils/generatePackageXml.ts @@ -1,12 +1,13 @@ import * as fs from 'fs'; import * as path from 'path'; -// Method to generate package.xml with additional types -function createChangeList(apexClasses: string[], lwcComponents: string[]): void { - const apexXml = apexClasses.map((name) => `${name}`).join('\n '); - const lwcXml = lwcComponents.map((name) => `${name}`).join('\n '); +export class generatePackageXml { + // Method to generate package.xml with additional types + public static createChangeList(apexClasses: string[], lwcComponents: string[]): void { + const apexXml = generatePackageXml.getXmlElementforMembers(apexClasses, 'ApexClass'); + const lwcXml = generatePackageXml.getXmlElementforMembers(lwcComponents, 'LightningComponentBundle'); - const additionalTypes = ` + const additionalTypes = ` * OmniDataTransform @@ -25,56 +26,45 @@ function createChangeList(apexClasses: string[], lwcComponents: string[]): void `; - const packageXmlContent = ` + const packageXmlContent = ` - - ${apexXml} - ApexClass - - - ${lwcXml} - LightningComponentBundle - + ${apexXml} + ${lwcXml} ${additionalTypes} 57.0 `; - const filePath = path.join(__dirname, 'package.xml'); - fs.writeFileSync(filePath, packageXmlContent.trim()); -} + const filePath = path.join(__dirname, 'package.xml'); + fs.writeFileSync(filePath, packageXmlContent.trim()); + } -// Backup method without additional types -function backupChangeList(apexClasses: string[], lwcComponents: string[]): void { - const apexXml = apexClasses.map((name) => `${name}`).join('\n '); - const lwcXml = lwcComponents.map((name) => `${name}`).join('\n '); + // Backup method without additional types + public static backupChangeList(apexClasses: string[], lwcComponents: string[]): void { + const apexXml = generatePackageXml.getXmlElementforMembers(apexClasses, 'ApexClass'); + const lwcXml = generatePackageXml.getXmlElementforMembers(lwcComponents, 'LightningComponentBundle'); - const packageXmlContent = ` + const packageXmlContent = ` - ${apexXml} - ApexClass - - ${lwcXml} - LightningComponentBundle - 57.0 `; - const filePath = path.join(__dirname, 'backup-package.xml'); - fs.writeFileSync(filePath, packageXmlContent.trim()); -} - -// remove all this code later --- only for testing -const apexClasses = ['MyApexClass1', 'MyApexClass2']; -const lwcComponents = ['MyLwcComponent1', 'MyLwcComponent2']; - -// creating normal package.xml with additional types -createChangeList(apexClasses, lwcComponents); + const filePath = path.join(__dirname, 'backup-package.xml'); + fs.writeFileSync(filePath, packageXmlContent.trim()); + } -// creating backup-package.xml without additional types -backupChangeList(apexClasses, lwcComponents); + private static getXmlElementforMembers(members: string[], type: string): string { + if (!members || members.length === 0) return ''; + const membersTag = members.map((member) => `${member}`).join('\n '); + return ` + + ${membersTag} + ${type} + `; + } +} diff --git a/src/utils/interfaces.ts b/src/utils/interfaces.ts index db4ba1e..9fdbb0a 100644 --- a/src/utils/interfaces.ts +++ b/src/utils/interfaces.ts @@ -25,27 +25,27 @@ export interface LWCAssessmentInfo { export interface OSAssessmentInfo { name: string; id: string; - dependenciesIP: string[]; + dependenciesIP: nameLocation[]; missingIP: string[]; - dependenciesDR: string[]; + dependenciesDR: nameLocation[]; missingDR: string[]; - dependenciesOS: string[]; + dependenciesOS: nameLocation[]; missingOS: string[]; - dependenciesRemoteAction: string[]; - // missingRemoteAction: AnyJson[]; + dependenciesRemoteAction: nameLocation[]; + dependenciesLWC: nameLocation[]; + type: string; infos: string[]; warnings: string[]; errors: string[]; - path: string; } export interface IPAssessmentInfo { name: string; id: string; - dependenciesIP: string[]; - dependenciesDR: string[]; - dependenciesOS: string[]; - dependenciesRemoteAction: string[]; + dependenciesIP: nameLocation[]; + dependenciesDR: nameLocation[]; + dependenciesOS: nameLocation[]; + dependenciesRemoteAction: nameLocation[]; infos: string[]; warnings: string[]; errors: string[]; @@ -119,3 +119,8 @@ export interface FileParser { export interface FileProcessor { process(file: File, type: string, namespace: string): string; } + +export interface nameLocation { + name: string; + location: string; +} diff --git a/src/utils/json/jsonutil.ts b/src/utils/json/jsonutil.ts new file mode 100644 index 0000000..f342d7b --- /dev/null +++ b/src/utils/json/jsonutil.ts @@ -0,0 +1,46 @@ +/* eslint-disable no-prototype-builtins */ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +export class jsonutil { + // Recursive method to find a property in the JSON + public static findProperty(obj: any, propertyName: string): any { + if (obj === null || typeof obj !== 'object') { + return null; + } + + // Check if the property exists in the current object + if (propertyName in obj) { + return obj[propertyName]; + } + + // If it's an array, use findInArray + if (Array.isArray(obj)) { + return this.findInArray(obj, propertyName); + } + + // Otherwise, search through all keys + for (const key in obj) { + if (obj.hasOwnProperty(key)) { + const result = this.findProperty(obj[key], propertyName); + if (result !== null) { + return result; + } + } + } + + return null; // Property not found + } + + // Method to find a property in an array + public static findInArray(arr: any[], propertyName: string): any { + for (const item of arr) { + const result = jsonutil.findProperty(item, propertyName); + if (result !== null) { + return result; + } + } + return null; // Property not found in the array + } +} diff --git a/src/utils/resultsbuilder/IPAssessmentReporter.ts b/src/utils/resultsbuilder/IPAssessmentReporter.ts new file mode 100644 index 0000000..d94a45a --- /dev/null +++ b/src/utils/resultsbuilder/IPAssessmentReporter.ts @@ -0,0 +1,71 @@ +import { IPAssessmentInfo } from '../interfaces'; +import { reportingHelper } from './reportingHelper'; + +export class IPAssessmentReporter { + public static generateIPAssesment(ipAssessmentInfos: IPAssessmentInfo[], instanceUrl: string): string { + let tableBody = ''; + tableBody += '
Integration Procedure Components Assessment
'; + + for (const ipAssessmentInfo of ipAssessmentInfos) { + const row = ` + + +
${ipAssessmentInfo.name}
+ + + + + + ${reportingHelper.convertToBuletedList(ipAssessmentInfo.warnings)} + + + ${reportingHelper.decorate(ipAssessmentInfo.dependenciesIP)} + + + ${reportingHelper.decorate(ipAssessmentInfo.dependenciesDR)} + + + ${reportingHelper.decorate(ipAssessmentInfo.dependenciesRemoteAction)} + + `; + tableBody += row; + } + + return this.getIPAssessmentReport(tableBody); + } + private static getIPAssessmentReport(tableContent: string): string { + const tableBody = ` +
+ + + + + + + + + + + + + ${tableContent} + +
+
Name
+
+
ID
+
+
Summary
+
+
Integration Procedures Dependencies
+
+
Data Raptor dependencies
+
+
Remote Action Dependencies
+
+
`; + return tableBody; + } +} diff --git a/src/utils/resultsbuilder/OSAssessmentReporter.ts b/src/utils/resultsbuilder/OSAssessmentReporter.ts new file mode 100644 index 0000000..34b52c4 --- /dev/null +++ b/src/utils/resultsbuilder/OSAssessmentReporter.ts @@ -0,0 +1,89 @@ +import { OSAssessmentInfo } from '../interfaces'; +import { reportingHelper } from './reportingHelper'; + +export class OSAssesmentReporter { + public static generateOSAssesment(osAssessmentInfos: OSAssessmentInfo[], instanceUrl: string): string { + let tableBody = ''; + tableBody += '
Omniscript Components Assessment
'; + + for (const osAssessmentInfo of osAssessmentInfos) { + const row = ` + + +
${osAssessmentInfo.name}
+ + +
+ + + ${osAssessmentInfo.type} + + + ${reportingHelper.convertToBuletedList(osAssessmentInfo.warnings)} + + + ${reportingHelper.decorate(osAssessmentInfo.dependenciesOS)} + + + ${reportingHelper.decorate(osAssessmentInfo.dependenciesIP)} + + + ${reportingHelper.decorate(osAssessmentInfo.dependenciesDR)} + + + ${reportingHelper.decorate(osAssessmentInfo.dependenciesRemoteAction)} + + + ${reportingHelper.decorate(osAssessmentInfo.dependenciesLWC)} + + `; + tableBody += row; + } + + return this.getOSAssessmentReport(tableBody); + } + + private static getOSAssessmentReport(tableContent: string): string { + const tableBody = ` +
+ + + + + + + + + + + + + + + + ${tableContent} + +
+
Name
+
+
ID
+
+
Type
+
+
Summary
+
+
Omniscript Dependencies
+
+
Integration Procedures Dependencies
+
+
Data Raptor Dependencies
+
+
Remote Action dependencies
+
+
custom LWC Dependencies
+
+
`; + return tableBody; + } +} diff --git a/src/utils/resultsbuilder/assessmentReporter.ts b/src/utils/resultsbuilder/assessmentReporter.ts index a32c877..2eb3256 100644 --- a/src/utils/resultsbuilder/assessmentReporter.ts +++ b/src/utils/resultsbuilder/assessmentReporter.ts @@ -6,30 +6,108 @@ import { ApexAssessmentInfo, AssessmentInfo, LWCAssessmentInfo, - OSAssessmentInfo, - IPAssessmentInfo, OmniAssessmentInfo, FlexCardAssessmentInfo, DataRaptorAssessmentInfo, + nameLocation, } from '../interfaces'; +import { OSAssesmentReporter } from './OSAssessmentReporter'; +import { IPAssessmentReporter } from './IPAssessmentReporter'; export class AssessmentReporter { public static async generate(result: AssessmentInfo, instanceUrl: string): Promise { - let htmlBody = ''; + const basePath = process.cwd() + '/assessment_reports'; + fs.mkdirSync(basePath, { recursive: true }); + const omniscriptAssessmentFilePath = basePath + '/omniscript_assessment.html'; + const flexcardAssessmentFilePath = basePath + '/flexcard_assessment.html'; + const integrationProcedureAssessmentFilePath = basePath + '/integration_procedure_assessment.html'; + const dataMapperAssessmentFilePath = basePath + '/datamapper_assessment.html'; + const apexAssessmentFilePath = basePath + '/apex_assessment.html'; + const lwcAssessmentFilePath = basePath + '/lwc_assessment.html'; - htmlBody += '
' + this.generateOmniAssesment(result.omniAssessmentInfo, instanceUrl); - htmlBody += '
' + this.generateCardAssesment(result.flexCardAssessmentInfos, instanceUrl); - htmlBody += '
' + this.generateDRAssesment(result.dataRaptorAssessmentInfos, instanceUrl); - htmlBody += '
' + this.generateApexAssesment(result.apexAssessmentInfos); - htmlBody += '
' + this.generateLwcAssesment(result.lwcAssessmentInfos); + this.createDocument( + omniscriptAssessmentFilePath, + this.generateOmniAssesment(result.omniAssessmentInfo, instanceUrl) + ); + this.createDocument( + flexcardAssessmentFilePath, + this.generateCardAssesment(result.flexCardAssessmentInfos, instanceUrl) + ); + this.createDocument( + integrationProcedureAssessmentFilePath, + IPAssessmentReporter.generateIPAssesment(result.omniAssessmentInfo.ipAssessmentInfos, instanceUrl) + ); + this.createDocument( + dataMapperAssessmentFilePath, + this.generateDRAssesment(result.dataRaptorAssessmentInfos, instanceUrl) + ); + this.createDocument(apexAssessmentFilePath, this.generateApexAssesment(result.apexAssessmentInfos)); + this.createDocument(lwcAssessmentFilePath, this.generateLwcAssesment(result.lwcAssessmentInfos)); + const nameUrls = [ + { + name: 'omnscript assessment report', + location: 'omniscript_assessment.html', + }, + { + name: 'flexcard assessment report', + location: 'flexcard_assessment.html', + }, + { + name: 'Integration Procedure assessment report', + location: 'integration_procedure_assessment.html', + }, + { + name: 'DataMapper assessment report', + location: 'datamapper_assessment.html', + }, + { + name: 'Apex assessment report', + location: 'apex_assessment.html', + }, + { + name: 'LWC assessment report', + location: 'lwc_assessment.html', + }, + ]; + await this.createMasterDocument(nameUrls, basePath); + } - const doc = this.generateDocument(htmlBody); - const fileUrl = process.cwd() + '/assessmentresults.html'; - fs.writeFileSync(fileUrl, doc); + private static async createMasterDocument(reports: nameLocation[], basePath: string): Promise { + let listBody = ''; + for (const report of reports) { + listBody += `
  • + ${report.name} +
  • `; + } + const body = ` + + + + + + + SLDS Bulleted List + + +
    +

    Assessment Reports

    +
      + ${listBody} +
    +
    + + + `; + const fileUrl = basePath + '/assessmentresults.html'; + fs.writeFileSync(fileUrl, body); await open('file://' + fileUrl); } + private static createDocument(filePath: string, htmlBody: string): void { + const doc = this.generateDocument(htmlBody); + fs.writeFileSync(filePath, doc); + } private static generateLwcAssesment(lwcAssessmentInfos: LWCAssessmentInfo[]): string { let tableBody = ''; tableBody += ` @@ -87,88 +165,42 @@ export class AssessmentReporter { return this.getApexAssessmentReport(tableBody); } + private static getApexAssessmentReport(tableContent: string): string { + const tableBody = ` +
    + + + + + + + + + + + + ${tableContent} + +
    +
    Name
    +
    +
    File reference
    +
    +
    Diff
    +
    +
    Comments
    +
    +
    Errors
    +
    +
    `; + return tableBody; + } private static generateOmniAssesment(omniAssessmentInfo: OmniAssessmentInfo, instanceUrl: string): string { let htmlBody = ''; - - // htmlBody += '
    ' + this.generateLwcAssesment(result.lwcAssessmentInfos); - htmlBody += '
    ' + this.generateOSAssesment(omniAssessmentInfo.osAssessmentInfos, instanceUrl); - htmlBody += '
    ' + this.generateIPAssesment(omniAssessmentInfo.ipAssessmentInfos, instanceUrl); + htmlBody += '
    ' + OSAssesmentReporter.generateOSAssesment(omniAssessmentInfo.osAssessmentInfos, instanceUrl); return htmlBody; } - private static generateOSAssesment(osAssessmentInfos: OSAssessmentInfo[], instanceUrl: string): string { - let tableBody = ''; - tableBody += '
    Omniscript Components Assessment
    '; - - for (const osAssessmentInfo of osAssessmentInfos) { - const row = ` - - -
    ${osAssessmentInfo.name}
    - - - - - -
    ${osAssessmentInfo.dependenciesOS}
    - - - -
    ${osAssessmentInfo.dependenciesIP}
    - - - -
    ${osAssessmentInfo.dependenciesDR}
    - - - -
    ${osAssessmentInfo.dependenciesRemoteAction}
    - - `; - tableBody += row; - } - - return this.getOSAssessmentReport(tableBody); - } - - private static generateIPAssesment(ipAssessmentInfos: IPAssessmentInfo[], instanceUrl: string): string { - let tableBody = ''; - tableBody += '
    Integration Procedure Components Assessment
    '; - - for (const ipAssessmentInfo of ipAssessmentInfos) { - const row = ` - - -
    ${ipAssessmentInfo.name}
    - - -
    - - - -
    ${ipAssessmentInfo.dependenciesIP}
    - - -
    ${ipAssessmentInfo.dependenciesDR}
    - - -
    ${ipAssessmentInfo.dependenciesRemoteAction}
    - - `; - tableBody += row; - } - - return this.getIPAssessmentReport(tableBody); - } - private static generateCardAssesment(flexCardAssessmentInfos: FlexCardAssessmentInfo[], instanceUrl: string): string { let tableBody = ''; tableBody += '
    Flexcard Components Assessment
    '; @@ -241,114 +273,6 @@ export class AssessmentReporter { return document; } - private static getApexAssessmentReport(tableContent: string): string { - const tableBody = ` -
    - - - - - - - - - - - - ${tableContent} - -
    -
    Name
    -
    -
    File reference
    -
    -
    Diff
    -
    -
    Comments
    -
    -
    Errors
    -
    -
    `; - return tableBody; - } - - private static getOSAssessmentReport(tableContent: string): string { - const tableBody = ` -
    - - - - - - - - - - - - - - - - ${tableContent} - -
    -
    Name
    -
    -
    ID
    -
    -
    Omniscript Dependencies
    -
    -
    Integration Procedures Dependencies
    -
    -
    Data Raptor dependencies
    -
    -
    Remote Action dependencies
    -
    -
    `; - return tableBody; - } - - private static getIPAssessmentReport(tableContent: string): string { - const tableBody = ` -
    - - - - - - - - - - - - - ${tableContent} - -
    -
    Name
    -
    -
    ID
    -
    -
    Integration Procedures Dependencies
    -
    -
    Data Raptor pendencies
    -
    -
    Remote Action Dependencies
    -
    -
    `; - return tableBody; - } - private static getCardAssessmentReport(tableContent: string): string { const tableBody = `
    @@ -368,7 +292,7 @@ export class AssessmentReporter {
    Integration Procedures Dependencies
    -
    Data Raptor pendencies
    +
    Data Raptor Dependencies
    @@ -402,7 +326,7 @@ export class AssessmentReporter {
    Integration Procedures Dependencies
    -
    Data Raptor pendencies
    +
    Data Raptor Dependencies
    diff --git a/src/utils/resultsbuilder/reportingHelper.ts b/src/utils/resultsbuilder/reportingHelper.ts new file mode 100644 index 0000000..d478f7f --- /dev/null +++ b/src/utils/resultsbuilder/reportingHelper.ts @@ -0,0 +1,21 @@ +import { nameLocation } from '../interfaces'; + +export class reportingHelper { + public static decorate(nameLocations: nameLocation[]): string { + let htmlContent = '
      '; + for (const nameLoc of nameLocations) { + htmlContent += `
    • ${nameLoc.name}
    • `; + } + htmlContent += '
    '; + return htmlContent; + } + + public static convertToBuletedList(messages: string[]): string { + let htmlContent = '
      '; + for (const msg of messages) { + htmlContent += `
    • ${msg}
    • `; + } + htmlContent += '
    '; + return htmlContent; + } +}