Skip to content

Commit 71ffc40

Browse files
Merge branch 'prerelease/alpha' into prerelease/alpha
2 parents 4826a3a + b31423d commit 71ffc40

File tree

13 files changed

+561
-89
lines changed

13 files changed

+561
-89
lines changed

messages/assess.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,5 +84,12 @@
8484
"fileUpdatedToAllowRemoteCalls": "File updated to allow remote calls",
8585
"fileUpdatedToAllowCalls": "File updated to allow calls",
8686
"fileImplementsVlocityOpenInterface": "file %s implements VlocityOpenInterface no changes will be applied",
87-
"methodCallBundleNameUpdated": "Method call bundle name will be updated in %s for class %s method %s"
88-
}
87+
"methodCallBundleNameUpdated": "Method call bundle name will be updated in %s for class %s method %s",
88+
"cardNameChangeMessage": "Card name will be changed from {0} to {1} to follow API naming standards",
89+
"authordNameChangeMessage": "Author name will be changed from {0} to {1} to follow API naming standards",
90+
"omniScriptNameChangeMessage": "OmniScript reference part {0} will be changed to {1} during migration.",
91+
"dataRaptorNameChangeMessage": "DataRaptor reference {0} will be changed to {1} during migration.",
92+
"integrationProcedureNameChangeMessage": "Integration Procedure reference {0} will be changed to {1} during migration.",
93+
"integrationProcedureManualUpdateMessage": "Integration Procedure reference {0} may need manual updates after migration."
94+
95+
}

messages/migrate.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,12 @@
7777
"packageSelectionPrompt": "Enter the number of the package to use (1-%s):",
7878
"invalidPackageSelection": "Invalid selection. Please enter a number between 1 and %s.",
7979
"selectedPackage": "Selected package: %s (Version: %s)"
80-
}
80+
"dataRaptorNameChangeMessage": "DataRaptor reference {0} will be changed to {1} during migration.",
81+
"integrationProcedureNameChangeMessage": "Integration Procedure reference {0} will be changed to {1} during migration.",
82+
"integrationProcedureManualUpdateMessage": "Integration Procedure reference {0} may need manual updates after migration.",
83+
"alreadyStandardModel": "The org is already on standard data model.",
84+
"cardAuthorNameChangeMessage": "Card author name has been modified to fit naming rules: {0}",
85+
"cardNameChangeMessage": "Card name has been modified to fit naming rules: {0}",
86+
"duplicateCardNameMessage": "Potential duplicate: Another card has the same name {0} after name cleaning. This may cause conflicts during migration",
87+
}
88+
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import fs from 'fs';
2+
import path from 'path';
3+
4+
const data = {};
5+
6+
function generateModulesHTML(modules: typeof data.modules): string {
7+
return modules.map(mod => `
8+
<div class="dashboard-module-card">
9+
<div>
10+
<div class="dashboard-module-title">${mod.title}</div>
11+
<div class="dashboard-module-total">${mod.total}</div>
12+
<div class="dashboard-module-row">
13+
With Warning and Errors
14+
<span class="dashboard-module-warn">${mod.withWarningAndErrors.toString().padStart(2, '0')}</span>
15+
</div>
16+
<div class="dashboard-module-row">
17+
No Migration needed
18+
<span class="dashboard-module-neutral">${mod.noMigrationNeeded.toString().padStart(2, '0')}</span>
19+
</div>
20+
<div class="dashboard-module-row">
21+
With No Errors (Automated)
22+
<span class="dashboard-module-success">${mod.withNoErrors.toString().padStart(2, '0')}</span>
23+
</div>
24+
</div>
25+
<button class="dashboard-module-btn">View Assessment Report</button>
26+
</div>
27+
`).join('\n');
28+
}
29+
30+
export function generateAssessmentHomePageHTML(): string {
31+
const templatePath = path.join(__dirname, '../templates/AssessmentHomePage.template.html');
32+
let template = fs.readFileSync(templatePath, 'utf-8');
33+
34+
template = template
35+
.replace('{{orgName}}', data.orgInfo.name)
36+
.replace('{{orgId}}', data.orgInfo.orgId)
37+
.replace('{{packageName}}', data.orgInfo.packageName)
38+
.replace('{{dataModelType}}', data.orgInfo.dataModelType)
39+
.replace('{{assessmentDateTime}}', data.orgInfo.assessmentDateTime)
40+
.replace('{{modules}}', generateModulesHTML(data.modules));
41+
42+
return template;
43+
}

src/migration/flexcard.ts

Lines changed: 228 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { UX } from '@salesforce/command';
1010
import { FlexCardAssessmentInfo } from '../../src/utils';
1111
import { Logger } from '../utils/logger';
1212
import { createProgressBar } from './base';
13+
import { Constants } from '../utils/constants/stringContants';
1314

1415
export class CardMigrationTool extends BaseMigrationTool implements MigrationTool {
1516
static readonly VLOCITYCARD_NAME = 'VlocityCard__c';
@@ -113,21 +114,53 @@ export class CardMigrationTool extends BaseMigrationTool implements MigrationToo
113114
const flexCardAssessmentInfos: FlexCardAssessmentInfo[] = [];
114115
let progressCounter = 0;
115116
const progressBar = createProgressBar('Assessing', 'Flexcard');
116-
progressBar.start(flexCards.length, progressCounter);
117+
progressBar.start(flexCards.length, progressCounter); const uniqueNames = new Set<string>();
118+
117119
const limitedFlexCards = flexCards.slice(0, 200);
118120

119121
// Now process each OmniScript and its elements
120122
for (const flexCard of limitedFlexCards) {
121-
Logger.info(this.messages.getMessage('processingFlexCard', [flexCard['Name']]));
123+
const flexCardName = flexCard['Name'];
124+
Logger.info(this.messages.getMessage('processingFlexCard', [flexCardName]));
122125
const flexCardAssessmentInfo: FlexCardAssessmentInfo = {
123-
name: flexCard['Name'],
126+
name: flexCardName,
124127
id: flexCard['Id'],
125128
dependenciesIP: [],
126129
dependenciesDR: [],
127130
dependenciesOS: [],
131+
dependenciesLWC: [],
128132
infos: [],
129133
warnings: [],
130134
};
135+
136+
// Check for name changes due to API naming requirements
137+
const originalName:string = flexCardName;
138+
const cleanedName:string = this.cleanName(originalName);
139+
if (cleanedName !== originalName) {
140+
flexCardAssessmentInfo.warnings.push(
141+
this.messages.getMessage('cardNameChangeMessage', [originalName, cleanedName])
142+
);
143+
}
144+
145+
// Check for duplicate names
146+
if (uniqueNames.has(cleanedName)) {
147+
flexCardAssessmentInfo.warnings.push(
148+
this.messages.getMessage('duplicateCardNameMessage', [cleanedName])
149+
);
150+
}
151+
uniqueNames.add(cleanedName);
152+
153+
// Check for author name changes
154+
const originalAuthor = flexCard[this.namespacePrefix + 'Author__c'];
155+
if (originalAuthor) {
156+
const cleanedAuthor = this.cleanName(originalAuthor);
157+
if (cleanedAuthor !== originalAuthor) {
158+
flexCardAssessmentInfo.warnings.push(
159+
this.messages.getMessage('authordNameChangeMessage', [originalAuthor, cleanedAuthor])
160+
);
161+
}
162+
}
163+
131164
this.updateDependencies(flexCard, flexCardAssessmentInfo);
132165
flexCardAssessmentInfos.push(flexCardAssessmentInfo);
133166
progressBar.update(++progressCounter);
@@ -136,16 +169,197 @@ export class CardMigrationTool extends BaseMigrationTool implements MigrationToo
136169
}
137170

138171
private updateDependencies(flexCard, flexCardAssessmentInfo): void {
139-
let dataSource = JSON.parse(flexCard[this.namespacePrefix + 'Datasource__c']);
140-
if (dataSource?.datasource) {
172+
let dataSource = JSON.parse(flexCard[this.namespacePrefix + 'Datasource__c'] || '{}');
173+
// Handle both camelCase and lowercase variants
174+
if (dataSource?.dataSource) {
141175
dataSource = dataSource.dataSource;
176+
} else if (dataSource?.datasource) {
177+
dataSource = dataSource.datasource;
178+
}
179+
180+
// Check if it's a DataRaptor source
181+
if (dataSource.type === Constants.DataRaptorComponentName) {
182+
const originalBundle = dataSource.value?.bundle;
183+
if (originalBundle) {
184+
const cleanedBundle:string = this.cleanName(originalBundle);
185+
flexCardAssessmentInfo.dependenciesDR.push(cleanedBundle);
186+
187+
// Add warning if DataRaptor name will change
188+
if (originalBundle !== cleanedBundle) {
189+
flexCardAssessmentInfo.warnings.push(
190+
this.messages.getMessage('dataRaptorNameChangeMessage', [originalBundle, cleanedBundle])
191+
);
192+
}
193+
}
194+
} else if (dataSource.type === Constants.IntegrationProcedurePluralName) {
195+
const originalIpMethod = dataSource.value?.ipMethod;
196+
if (originalIpMethod) {
197+
const parts = originalIpMethod.split('_');
198+
const cleanedParts = parts.map((p) => this.cleanName(p, true));
199+
const cleanedIpMethod = cleanedParts.join('_');
200+
201+
flexCardAssessmentInfo.dependenciesIP.push(cleanedIpMethod);
202+
203+
// Add warning if IP name will change
204+
if (originalIpMethod !== cleanedIpMethod) {
205+
flexCardAssessmentInfo.warnings.push(
206+
this.messages.getMessage('integrationProcedureNameChangeMessage', [originalIpMethod, cleanedIpMethod])
207+
);
208+
}
209+
210+
// Add warning for IP references with more than 2 parts (which potentially need manual updates)
211+
if (parts.length > 2) {
212+
flexCardAssessmentInfo.warnings.push(
213+
this.messages.getMessage('integrationProcedureManualUpdateMessage', [originalIpMethod])
214+
);
215+
}
216+
}
217+
}
218+
219+
// Check for OmniScript dependencies in the card's definition
220+
try {
221+
const definition = JSON.parse(flexCard[this.namespacePrefix + 'Definition__c'] || '{}');
222+
if (definition && definition.states) {
223+
for (const state of definition.states) {
224+
if (state.omniscripts && Array.isArray(state.omniscripts)) {
225+
for (const os of state.omniscripts) {
226+
if (os.type && os.subtype) {
227+
const osRef = `${os.type}_${os.subtype}_${os.language || 'English'}`;
228+
flexCardAssessmentInfo.dependenciesOS.push(osRef);
229+
}
230+
}
231+
}
232+
233+
// Also check for omniscripts referenced in component actions
234+
if (state.components) {
235+
for (const componentKey in state.components) {
236+
if (state.components.hasOwnProperty(componentKey)) {
237+
const component = state.components[componentKey];
238+
this.checkComponentForDependencies(component, flexCardAssessmentInfo);
239+
}
240+
}
241+
}
242+
}
243+
}
244+
} catch (err) {
245+
// Log the error but continue processing
246+
Logger.error(`Error parsing definition for card ${flexCard.Name}: ${err.message}`);
142247
}
143-
if (dataSource['type'] === 'DataRaptor') {
144-
flexCardAssessmentInfo.dependenciesDR.push(dataSource['value']['bundle']);
145-
} else if (dataSource.type === 'IntegrationProcedures') {
146-
flexCardAssessmentInfo.dependenciesIP.push(dataSource['value']['ipMethod']);
248+
}
249+
250+
private checkComponentForDependencies(component: any, flexCardAssessmentInfo: FlexCardAssessmentInfo): void {
251+
// Check if this component is an action element
252+
if (component.element === 'action' && component.property && component.property.actionList) {
253+
// Process each action in the actionList
254+
for (const action of component.property.actionList) {
255+
if (action.stateAction) {
256+
// Case 1: Direct OmniScript reference
257+
if (action.stateAction.type === Constants.OmniScriptComponentName && action.stateAction.omniType) {
258+
const omniType = action.stateAction.omniType;
259+
if (omniType.Name && typeof omniType.Name === 'string') {
260+
const originalName = omniType.Name;
261+
const parts = originalName.split('/');
262+
263+
if (parts.length >= 2) {
264+
// Check for name changes in each part
265+
const cleanedParts = parts.map((p) => this.cleanName(p));
266+
const cleanedName = cleanedParts.join('_');
267+
flexCardAssessmentInfo.dependenciesOS.push(cleanedName);
268+
269+
// Add warning if any part of the name will change
270+
for (let i = 0; i < parts.length; i++) {
271+
if (parts[i] !== cleanedParts[i]) {
272+
flexCardAssessmentInfo.warnings.push(
273+
this.messages.getMessage('omniScriptNameChangeMessage', [parts[i], cleanedParts[i]])
274+
);
275+
}
276+
}
277+
}
278+
}
279+
}
280+
281+
// Case 2: Flyout OmniScript reference
282+
else if (
283+
action.stateAction.type === 'Flyout' &&
284+
action.stateAction.flyoutType === Constants.OmniScriptPluralName &&
285+
action.stateAction.osName
286+
) {
287+
const osName = action.stateAction.osName;
288+
if (typeof osName === 'string') {
289+
// osName is typically in format "Omniscript/Testing/English"
290+
const originalName = osName;
291+
const parts = originalName.split('/');
292+
293+
if (parts.length >= 2) {
294+
// Check for name changes in each part
295+
const cleanedParts = parts.map((p) => this.cleanName(p));
296+
const cleanedName = cleanedParts.join('_');
297+
flexCardAssessmentInfo.dependenciesOS.push(cleanedName);
298+
299+
// Add warning if any part of the name will change
300+
for (let i = 0; i < parts.length; i++) {
301+
if (parts[i] !== cleanedParts[i]) {
302+
flexCardAssessmentInfo.warnings.push(
303+
this.messages.getMessage('omniScriptNameChangeMessage', [parts[i], cleanedParts[i]])
304+
);
305+
}
306+
}
307+
}
308+
}
309+
}
310+
}
311+
}
312+
}
313+
314+
// Check for Custom LWC component
315+
if (component.element === 'customLwc' && component.property) {
316+
// Check customlwcname property
317+
/*if (component.property.customlwcname) {
318+
flexCardAssessmentInfo.dependenciesLWC.push(component.property.customlwcname);
319+
} */
320+
321+
// Also check customLwcData if available (has more details)
322+
if (component.property.customLwcData) {
323+
const lwcData = component.property.customLwcData;
324+
325+
// Use DeveloperName as a more reliable identifier
326+
if (lwcData.DeveloperName) {
327+
const lwcName = lwcData.NamespacePrefix
328+
? `${lwcData.NamespacePrefix}.${lwcData.DeveloperName}`
329+
: lwcData.DeveloperName;
330+
331+
// Avoid duplicates
332+
if (!flexCardAssessmentInfo.dependenciesLWC.includes(lwcName)) {
333+
flexCardAssessmentInfo.dependenciesLWC.push(lwcName);
334+
}
335+
}
336+
}
337+
}
338+
339+
// Check standard component actions if they exist
340+
if (component.actions && Array.isArray(component.actions)) {
341+
for (const action of component.actions) {
342+
if (action.stateAction && action.stateAction.omniType) {
343+
const omniType = action.stateAction.omniType;
344+
if (omniType.Name && typeof omniType.Name === 'string') {
345+
const parts = omniType.Name.split('/');
346+
if (parts.length >= 2) {
347+
const osRef = parts.join('_');
348+
flexCardAssessmentInfo.dependenciesOS.push(osRef);
349+
}
350+
}
351+
}
352+
}
353+
}
354+
355+
// Check child components recursively
356+
if (component.children && Array.isArray(component.children)) {
357+
for (const child of component.children) {
358+
this.checkComponentForDependencies(child, flexCardAssessmentInfo);
359+
}
147360
}
148361
}
362+
149363
// Query all cards that are active
150364
private async getAllActiveCards(): Promise<AnyJson[]> {
151365
//DebugTimer.getInstance().lap('Query Vlocity Cards');
@@ -267,21 +481,21 @@ export class CardMigrationTool extends BaseMigrationTool implements MigrationToo
267481
uploadResult.warnings = uploadResult.warnings || [];
268482
if (transformedCardAuthorName !== card[this.namespacePrefix + 'Author__c']) {
269483
uploadResult.warnings.unshift(
270-
'WARNING: Card author name has been modified to fit naming rules: ' + transformedCardAuthorName
484+
this.messages.getMessage('cardAuthorNameChangeMessage', [transformedCardAuthorName])
271485
);
272486
}
273487
if (transformedCardName !== card['Name']) {
274488
uploadResult.newName = transformedCardName;
275489
uploadResult.warnings.unshift(
276-
'WARNING: Card name has been modified to fit naming rules: ' + transformedCardName
490+
this.messages.getMessage('cardNameChangeMessage', [transformedCardName])
277491
);
278492
}
279493

280494
if (uploadResult.id && invalidIpNames.size > 0) {
281495
const val = Array.from(invalidIpNames.entries())
282496
.map((e) => e[0])
283497
.join(', ');
284-
uploadResult.errors.push('Integration Procedure Actions will need manual updates, please verify: ' + val);
498+
uploadResult.errors.push(this.messages.getMessage('integrationProcedureManualUpdateMessage', [val]));
285499
}
286500

287501
cardsUploadInfo.set(recordId, uploadResult);
@@ -396,9 +610,9 @@ export class CardMigrationTool extends BaseMigrationTool implements MigrationToo
396610
const datasource = JSON.parse(mappedObject[CardMappings.Datasource__c] || '{}');
397611
if (datasource.dataSource) {
398612
const type = datasource.dataSource.type;
399-
if (type === 'DataRaptor') {
613+
if (type === Constants.DataRaptorComponentName) {
400614
datasource.dataSource.value.bundle = this.cleanName(datasource.dataSource.value.bundle);
401-
} else if (type === 'IntegrationProcedures') {
615+
} else if (type === Constants.IntegrationProcedurePluralName) {
402616
const ipMethod: string = datasource.dataSource.value.ipMethod || '';
403617

404618
const parts = ipMethod.split('_');

0 commit comments

Comments
 (0)