@@ -10,6 +10,7 @@ import { UX } from '@salesforce/command';
1010import { FlexCardAssessmentInfo } from '../../src/utils' ;
1111import { Logger } from '../utils/logger' ;
1212import { createProgressBar } from './base' ;
13+ import { Constants } from '../utils/constants/stringContants' ;
1314
1415export 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