Skip to content

Commit 4321696

Browse files
committed
feat: flexipage wrapper assess for LWCOmniWrapper
1 parent 788e3b6 commit 4321696

File tree

21 files changed

+1611
-124
lines changed

21 files changed

+1611
-124
lines changed

messages/assess.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,5 +114,16 @@
114114
"errorParsingData": "Error while parsing data for template",
115115
"errorGeneratingHTML": "Error while generating HTML from template for {} with properties {}",
116116
"unexpectedError": "Unexpected Assessment error",
117-
"errorEvaluatingExpression": "Error evaluating expression: {0}, {1}"
117+
"errorEvaluatingExpression": "Error evaluating expression: {0}, {1}",
118+
"retrievingFlexiPages": "Retrieving FlexiPages",
119+
"successfullyRetrievedFlexiPages": "Successfully retrieved %s FlexiPages",
120+
"assessingFlexiPages": "Assessing FlexiPages",
121+
"completedProcessingAllFlexiPages": "Completed processing all flexipage files. Total processed: %s",
122+
"completedProcessingFlexiPage": "Completed processing %s - Errors: %s",
123+
"processingFlexiPage": "Processing flexipage file: %s",
124+
"startingFlexiPageProcessing": "Starting to process flexipage: %s",
125+
"readFlexiPageContent": "Read file content, size: %s characters",
126+
"updatedModifiedContent": "Wrote modified content to file: %s",
127+
"generatedDiffForFile": "Generated diff for file: %s",
128+
"errorProcessingFlexiPage": "Error processing flexipage file: %s, %s"
118129
}

messages/migrate.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,5 +109,16 @@
109109
"errorParsingData": "Error while parsing data for template",
110110
"errorGeneratingHTML": "Error while generating HTML from template for {} with properties {}",
111111
"errorEvaluatingExpression": "Error evaluating expression: {0}, {1}",
112-
"manuallySwitchDesignerToStandardDataModel": "To complete the setup, please manually run the required Apex code to configure OmniStudio Designers to use the Standard Data Model <br> https://help.salesforce.com/s/articleView?id=xcloud.os_migrate_change_the_sobject_data_model_after_migration.htm&type=5"
112+
"manuallySwitchDesignerToStandardDataModel": "To complete the setup, please manually run the required Apex code to configure OmniStudio Designers to use the Standard Data Model <br> https://help.salesforce.com/s/articleView?id=xcloud.os_migrate_change_the_sobject_data_model_after_migration.htm&type=5",
113+
"retrievingFlexiPages": "Retrieving FlexiPages",
114+
"successfullyRetrievedFlexiPages": "Successfully retrieved %s FlexiPages",
115+
"migratingFlexiPages": "Migrating FlexiPages",
116+
"completedProcessingAllFlexiPages": "Completed processing all flexipage files. Total processed: %s",
117+
"completedProcessingFlexiPage": "Completed processing %s - Errors: %s",
118+
"processingFlexiPage": "Processing flexipage file: %s",
119+
"startingFlexiPageProcessing": "Starting to process flexipage: %s",
120+
"readFlexiPageContent": "Read file content, size: %s characters",
121+
"updatedModifiedContent": "Wrote modified content to file: %s",
122+
"generatedDiffForFile": "Generated diff for file: %s",
123+
"errorProcessingFlexiPage": "Error processing flexipage file: %s, %s"
113124
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"@types/jsdom": "^21.1.7",
1616
"@types/lodash.chunk": "^4.2.9",
1717
"@types/shelljs": "^0.8.15",
18+
"fast-xml-parser": "^5.2.5",
1819
"jsdom": "^25.0.0",
1920
"lodash.chunk": "^4.2.0",
2021
"open": "^8.4.2",

src/commands/omnistudio/migration/assess.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ export default class Assess extends OmniStudioBaseCommand {
106106
osAssessmentInfos: [],
107107
ipAssessmentInfos: [],
108108
},
109+
flexipageAssessmentInfos: [],
109110
};
110111

111112
Logger.log(messages.getMessage('assessmentInitialization', [String(namespace)]));
@@ -120,7 +121,7 @@ export default class Assess extends OmniStudioBaseCommand {
120121
// Assess related objects if specified
121122
if (relatedObjects) {
122123
// To-Do: Add LWC to valid options when GA is released
123-
const validOptions = [Constants.Apex];
124+
const validOptions = [Constants.Apex, Constants.FlexiPage];
124125
objectsToProcess = relatedObjects.split(',').map((obj) => obj.trim());
125126

126127
// Validate input
@@ -141,6 +142,7 @@ export default class Assess extends OmniStudioBaseCommand {
141142
const relatedObjectAssessmentResult = omnistudioRelatedObjectsMigration.assessAll(objectsToProcess);
142143
assesmentInfo.lwcAssessmentInfos = relatedObjectAssessmentResult.lwcAssessmentInfos;
143144
assesmentInfo.apexAssessmentInfos = relatedObjectAssessmentResult.apexAssessmentInfos;
145+
assesmentInfo.flexipageAssessmentInfos = relatedObjectAssessmentResult.flexipageAssessmentInfos;
144146
}
145147
try {
146148
orgs.rollbackFlags = await OrgPreferences.checkRollbackFlags(conn);

src/migration/base.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { UX } from '@salesforce/command';
22
import { Connection, Messages } from '@salesforce/core';
3-
import cliProgress from 'cli-progress';
3+
import * as cliProgress from 'cli-progress';
44
import { DebugTimer, QueryTools } from '../utils';
55
import { NetUtils } from '../utils/net';
66
import { Stringutil } from '../utils/StringValue/stringutil';
77
import { Logger } from '../utils/logger';
88
import { TransformData, UploadRecordResult } from './interfaces';
99

10-
export type ComponentType = 'Data Mapper' | 'Flexcard' | 'Omniscript and Integration Procedure';
10+
export type ComponentType = 'Data Mapper' | 'Flexcard' | 'Omniscript and Integration Procedure' | 'Flexipage';
1111

1212
/**
1313
* Creates a progress bar for migration/assessment operations

src/migration/interfaces.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,30 @@ export interface RelatedObjectMigrationResult {
9191
apexClasses: string[];
9292
lwcComponents: string[];
9393
}
94+
95+
export interface Flexipage {
96+
[key: string]: string | FlexiPageRegion[];
97+
flexiPageRegions: FlexiPageRegion[];
98+
}
99+
100+
export interface FlexiPageRegion {
101+
[key: string]: string | FlexiItemInstance[];
102+
itemInstances?: FlexiItemInstance[];
103+
}
104+
105+
export interface FlexiItemInstance {
106+
[key: string]: string | FlexiComponentInstance;
107+
componentInstance: FlexiComponentInstance;
108+
}
109+
110+
export interface FlexiComponentInstance {
111+
[key: string]: string | FlexiComponentInstanceProperty[];
112+
componentInstanceProperties: FlexiComponentInstanceProperty[];
113+
componentName: string;
114+
identifier?: string;
115+
}
116+
117+
export interface FlexiComponentInstanceProperty {
118+
name: string;
119+
value?: string;
120+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import * as fs from 'fs';
2+
import * as path from 'path';
3+
import * as shell from 'shelljs';
4+
import { Messages, Org } from '@salesforce/core';
5+
import { sfProject } from '../../utils/sfcli/project/sfProject';
6+
import { Logger } from '../../utils/logger';
7+
import { Constants } from '../../utils/constants/stringContants';
8+
import { FlexiPageAssessmentInfo } from '../../utils/interfaces';
9+
import { createProgressBar } from '../base';
10+
import { xmlUtil } from '../../utils/XMLUtil';
11+
import { FileDiffUtil } from '../../utils/lwcparser/fileutils/FileDiffUtil';
12+
import { transformFlexipageBundle } from '../../utils/flexipage/flexiPageTransformer';
13+
import { Flexipage } from '../interfaces';
14+
import { BaseRelatedObjectMigration } from './BaseRealtedObjectMigration';
15+
16+
export class FlexipageMigration extends BaseRelatedObjectMigration {
17+
private messages: Messages;
18+
19+
public constructor(projectPath: string, namespace: string, org: Org, messages: Messages) {
20+
super(projectPath, namespace, org);
21+
this.messages = messages;
22+
}
23+
24+
public processObjectType(): string {
25+
return Constants.FlexiPage;
26+
}
27+
28+
public assess(): FlexiPageAssessmentInfo[] {
29+
Logger.info(this.messages.getMessage('assessingFlexiPages'));
30+
return this.process('assess');
31+
}
32+
33+
public migrate(): FlexiPageAssessmentInfo[] {
34+
Logger.info(this.messages.getMessage('migratingFlexiPages'));
35+
return this.process('migrate');
36+
}
37+
38+
private process(mode: 'assess' | 'migrate'): FlexiPageAssessmentInfo[] {
39+
Logger.info(this.messages.getMessage('retrievingFlexiPages'));
40+
shell.cd(this.projectPath);
41+
sfProject.retrieve(Constants.FlexiPage, this.org.getUsername());
42+
const files = fs
43+
.readdirSync(path.join(this.projectPath, 'force-app', 'main', 'default', 'flexipages'))
44+
.filter((file) => file.endsWith('.xml'));
45+
Logger.info(this.messages.getMessage('successfullyRetrievedFlexiPages', [files.length]));
46+
const progressBar = createProgressBar('Migrating', 'Flexipage');
47+
progressBar.setTotal(files.length);
48+
const flexPageAssessmentInfos: FlexiPageAssessmentInfo[] = [];
49+
50+
for (const file of files) {
51+
Logger.logVerbose(this.messages.getMessage('processingFlexiPage', [file]));
52+
const filePath = path.join(this.projectPath, 'force-app', 'main', 'default', 'flexipages', file);
53+
try {
54+
const flexPageAssessmentInfo: FlexiPageAssessmentInfo = this.processFlexiPage(file, filePath, mode);
55+
flexPageAssessmentInfos.push(flexPageAssessmentInfo);
56+
Logger.logVerbose(
57+
this.messages.getMessage('completedProcessingFlexiPage', [
58+
file,
59+
JSON.stringify(flexPageAssessmentInfo.errors),
60+
])
61+
);
62+
} catch (error) {
63+
Logger.error(this.messages.getMessage('errorProcessingFlexiPage', [file, error]));
64+
flexPageAssessmentInfos.push({
65+
name: file,
66+
errors: [error instanceof Error ? error.message : JSON.stringify(error)],
67+
path: filePath,
68+
diff: '',
69+
status: 'Errors',
70+
});
71+
}
72+
progressBar.increment();
73+
}
74+
progressBar.stop();
75+
Logger.info(this.messages.getMessage('completedProcessingAllFlexiPages', [flexPageAssessmentInfos.length]));
76+
return flexPageAssessmentInfos;
77+
}
78+
79+
private processFlexiPage(fileName: string, filePath: string, mode: 'assess' | 'migrate'): FlexiPageAssessmentInfo {
80+
Logger.logVerbose(this.messages.getMessage('startingFlexiPageProcessing', [fileName]));
81+
const fileContent = fs.readFileSync(filePath, 'utf8');
82+
Logger.logVerbose(this.messages.getMessage('readFlexiPageContent', [fileContent.length]));
83+
84+
const parent: { FlexiPage: Flexipage } = xmlUtil.parse(fileContent) as { FlexiPage: Flexipage };
85+
const json = parent.FlexiPage;
86+
const transformedFlexiPage = transformFlexipageBundle(json, this.namespace);
87+
if (transformedFlexiPage === false) {
88+
return {
89+
name: fileName,
90+
errors: [],
91+
path: filePath,
92+
diff: '',
93+
status: 'No Changes',
94+
};
95+
}
96+
parent.FlexiPage = transformedFlexiPage as Flexipage;
97+
const modifiedContent = xmlUtil.build(parent);
98+
99+
if (mode === 'migrate') {
100+
fs.writeFileSync(filePath, modifiedContent);
101+
Logger.logVerbose(this.messages.getMessage('updatedModifiedContent', [filePath]));
102+
}
103+
104+
const diff = new FileDiffUtil().getFileDiff(fileName, fileContent, modifiedContent);
105+
Logger.logVerbose(this.messages.getMessage('generatedDiffForFile', [fileName]));
106+
107+
return {
108+
path: filePath,
109+
name: fileName,
110+
diff: JSON.stringify(diff),
111+
errors: [],
112+
status: 'Can be Automated',
113+
};
114+
}
115+
}

src/migration/related/OmnistudioRelatedObjectMigrationFacade.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,19 @@
44
/* eslint-disable @typescript-eslint/no-explicit-any */
55
import { Org, Messages } from '@salesforce/core';
66
import * as shell from 'shelljs';
7-
import { ApexAssessmentInfo, DebugTimer, LWCAssessmentInfo, RelatedObjectAssesmentInfo } from '../../utils';
7+
import {
8+
ApexAssessmentInfo,
9+
DebugTimer,
10+
FlexiPageAssessmentInfo,
11+
LWCAssessmentInfo,
12+
RelatedObjectAssesmentInfo,
13+
} from '../../utils';
814
import { sfProject } from '../../utils/sfcli/project/sfProject';
915
import { Logger } from '../../utils/logger';
1016
import { Constants } from '../../utils/constants/stringContants';
1117
import { ApexMigration } from './ApexMigration';
1218
import { LwcMigration } from './LwcMigration';
19+
import { FlexipageMigration } from './FlexipageMigration';
1320

1421
Messages.importMessagesDirectory(__dirname);
1522
const assessMessages = Messages.loadMessages('@salesforce/plugin-omnistudio-migration-tool', 'assess');
@@ -32,6 +39,7 @@ export default class OmnistudioRelatedObjectMigrationFacade {
3239
protected readonly projectPath: string;
3340
protected readonly apexMigration: ApexMigration;
3441
protected readonly lwcMigration: LwcMigration;
42+
protected readonly flexipageMigration: FlexipageMigration;
3543

3644
public constructor(
3745
namespace: string,
@@ -49,6 +57,7 @@ export default class OmnistudioRelatedObjectMigrationFacade {
4957

5058
// Initialize migration instances
5159
this.apexMigration = new ApexMigration(this.projectPath, this.namespace, this.org, targetApexNamespace);
60+
this.flexipageMigration = new FlexipageMigration(this.projectPath, this.namespace, this.org, assessMessages);
5261
// TODO: Uncomment code once MVP for migration is completed
5362
// this.lwcMigration = new LwcMigration(this.projectPath, this.namespace, this.org);
5463
}
@@ -89,12 +98,16 @@ export default class OmnistudioRelatedObjectMigrationFacade {
8998

9099
let apexAssessmentInfos: ApexAssessmentInfo[] = [];
91100
const lwcAssessmentInfos: LWCAssessmentInfo[] = [];
101+
let flexipageAssessmentInfos: FlexiPageAssessmentInfo[] = [];
92102

93103
// Proceed with processing logic
94104
try {
95105
if (relatedObjects.includes(Constants.Apex)) {
96106
apexAssessmentInfos = isMigration ? this.apexMigration.migrate() : this.apexMigration.assess();
97107
}
108+
if (relatedObjects.includes(Constants.FlexiPage)) {
109+
flexipageAssessmentInfos = isMigration ? this.flexipageMigration.migrate() : this.flexipageMigration.assess();
110+
}
98111
} catch (Error) {
99112
// Log the error
100113
Logger.error(JSON.stringify(Error));
@@ -117,7 +130,7 @@ export default class OmnistudioRelatedObjectMigrationFacade {
117130
Logger.debug(timer.toString());
118131

119132
// Return results needed for --json flag
120-
return { apexAssessmentInfos, lwcAssessmentInfos };
133+
return { apexAssessmentInfos, lwcAssessmentInfos, flexipageAssessmentInfos };
121134
}
122135

123136
public migrateAll(relatedObjects: string[]): RelatedObjectAssesmentInfo {

src/styles/reportGenerator.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,8 @@ html {
262262
border-radius: 4px;
263263
text-align: left;
264264
position: relative;
265+
display: flex;
266+
flex-direction: column;
265267
}
266268

267269
.closeButton {
@@ -278,6 +280,7 @@ html {
278280
align-items: center;
279281
justify-content: center;
280282
border-radius: 3px;
283+
padding-right: 0;
281284
}
282285

283286
.modalHeader {
@@ -289,6 +292,11 @@ html {
289292
}
290293

291294
.modalContent {
295+
overflow: auto;
296+
}
297+
298+
.modalContent p {
299+
margin: 0;
292300
margin-top: 20px;
293301
}
294302

src/utils/XMLUtil.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { XMLParser, XMLBuilder } from 'fast-xml-parser';
2+
3+
class XMLUtil {
4+
private parser: XMLParser;
5+
private builder: XMLBuilder;
6+
7+
public constructor() {
8+
this.parser = new XMLParser({
9+
ignoreAttributes: false,
10+
attributeNamePrefix: '@',
11+
isArray: (_tagName: string, jPath: string): boolean => {
12+
return ['FlexiPage.flexiPageRegions', 'FlexiPage.flexiPageRegions.itemInstances'].includes(jPath);
13+
},
14+
});
15+
this.builder = new XMLBuilder({
16+
ignoreAttributes: false,
17+
attributeNamePrefix: '@',
18+
format: true,
19+
indentBy: ' ',
20+
});
21+
}
22+
23+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
24+
public parse(xml: string): any {
25+
return this.parser.parse(xml);
26+
}
27+
28+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
29+
public build(json: any): string {
30+
return this.builder.build(json);
31+
}
32+
}
33+
34+
export const xmlUtil = new XMLUtil();

0 commit comments

Comments
 (0)