Skip to content

Commit f94c01d

Browse files
committed
Merge remote-tracking branch 'origin/prerelease/develop-ga' into flexipage_wrapper_assess
2 parents f9b09e5 + f2a7598 commit f94c01d

File tree

16 files changed

+1904
-34
lines changed

16 files changed

+1904
-34
lines changed

src/commands/omnistudio/migration/migrate.ts

Lines changed: 82 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
*/
1010
import * as os from 'os';
1111
import { flags } from '@salesforce/command';
12-
import { Messages } from '@salesforce/core';
13-
import { ExecuteAnonymousResult } from 'jsforce';
12+
import { Connection, Messages } from '@salesforce/core';
1413
import OmniStudioBaseCommand from '../../basecommand';
1514
import { DataRaptorMigrationTool } from '../../../migration/dataraptor';
1615
import { DebugTimer, MigratedObject, MigratedRecordInfo } from '../../../utils';
@@ -24,8 +23,8 @@ import { generatePackageXml } from '../../../utils/generatePackageXml';
2423
import { OmnistudioOrgDetails, OrgUtils } from '../../../utils/orgUtils';
2524
import { Constants } from '../../../utils/constants/stringContants';
2625
import { OrgPreferences } from '../../../utils/orgPreferences';
27-
import { AnonymousApexRunner } from '../../../utils/apex/executor/AnonymousApexRunner';
2826
import { ProjectPathUtil } from '../../../utils/projectPathUtil';
27+
import { PostMigrate } from '../../../migration/postMigrate';
2928

3029
// Initialize Messages with the current plugin directory
3130
Messages.importMessagesDirectory(__dirname);
@@ -85,7 +84,6 @@ export default class Migrate extends OmniStudioBaseCommand {
8584
const migrateOnly = (this.flags.only || '') as string;
8685
const allVersions = this.flags.allversions || (false as boolean);
8786
const relatedObjects = (this.flags.relatedobjects || '') as string;
88-
8987
// this.org is guaranteed because requiresUsername=true, as opposed to supportsUsername
9088
const conn = this.org.getConnection();
9189
if (apiVersion) {
@@ -124,9 +122,10 @@ export default class Migrate extends OmniStudioBaseCommand {
124122
let projectPath: string;
125123
let objectsToProcess: string[] = [];
126124
let targetApexNamespace: string;
125+
const isExperienceBundleMetadataAPIProgramaticallyEnabled: { value: boolean } = { value: false };
127126
if (relatedObjects) {
128127
// To-Do: Add LWC to valid options when GA is released
129-
const validOptions = [Constants.Apex, Constants.FlexiPage];
128+
const validOptions = [Constants.Apex, Constants.ExpSites, Constants.FlexiPage];
130129
objectsToProcess = relatedObjects.split(',').map((obj) => obj.trim());
131130
// Validate input
132131
for (const obj of objectsToProcess) {
@@ -141,7 +140,15 @@ export default class Migrate extends OmniStudioBaseCommand {
141140
// Use ProjectPathUtil for APEX project folder selection (matches assess.ts logic)
142141
projectPath = await ProjectPathUtil.getProjectPath(messages, true);
143142
targetApexNamespace = await this.getTargetApexNamespace(objectsToProcess, targetApexNamespace);
144-
}
143+
await this.handleExperienceSitePrerequisites(
144+
objectsToProcess,
145+
conn,
146+
isExperienceBundleMetadataAPIProgramaticallyEnabled
147+
);
148+
Logger.logVerbose(
149+
'The objects to process after handleExpSitePrerequisite are ' + JSON.stringify(objectsToProcess)
150+
);
151+
} // TODO - What if general consent is no
145152
}
146153

147154
Logger.log(messages.getMessage('migrationInitialization', [String(namespace)]));
@@ -182,8 +189,20 @@ export default class Migrate extends OmniStudioBaseCommand {
182189
relatedObjectMigrationResult.flexipageAssessmentInfos
183190
);
184191

192+
// POST MIGRATION
185193
let actionItems = [];
186-
actionItems = await this.setDesignersToUseStandardDataModel(namespace);
194+
const postMigrate: PostMigrate = new PostMigrate(
195+
this.org,
196+
namespace,
197+
conn,
198+
this.logger,
199+
messages,
200+
this.ux,
201+
objectsToProcess
202+
);
203+
204+
actionItems = await postMigrate.setDesignersToUseStandardDataModel(namespace);
205+
await postMigrate.restoreExperienceAPIMetadataSettings(isExperienceBundleMetadataAPIProgramaticallyEnabled);
187206

188207
await ResultsBuilder.generateReport(
189208
objectMigrationResults,
@@ -201,27 +220,47 @@ export default class Migrate extends OmniStudioBaseCommand {
201220
return { objectMigrationResults };
202221
}
203222

204-
private async setDesignersToUseStandardDataModel(namespace: string): Promise<string[]> {
205-
const userActionMessage: string[] = [];
206-
try {
207-
Logger.logVerbose('Setting designers to use the standard data model');
208-
const apexCode = `
209-
${namespace}.OmniStudioPostInstallClass.useStandardDataModel();
210-
`;
211-
212-
const result: ExecuteAnonymousResult = await AnonymousApexRunner.run(this.org, apexCode);
213-
if (result?.success === false) {
214-
const message = result?.exceptionStackTrace;
215-
Logger.error(`Error occurred while setting designers to use the standard data model ${message}`);
216-
userActionMessage.push(messages.getMessage('manuallySwitchDesignerToStandardDataModel'));
217-
} else if (result?.success === true) {
218-
Logger.logVerbose('Successfully executed setDesignersToUseStandardDataModel');
223+
private async handleExperienceSitePrerequisites(
224+
objectsToProcess: string[],
225+
conn: Connection,
226+
isExperienceBundleMetadataAPIProgramaticallyEnabled: { value: boolean }
227+
): Promise<void> {
228+
if (objectsToProcess.includes(Constants.ExpSites)) {
229+
const expMetadataApiConsent = await this.getExpSiteMetadataEnableConsent();
230+
Logger.logVerbose(`The consent for exp site is ${expMetadataApiConsent}`);
231+
232+
if (expMetadataApiConsent === false) {
233+
Logger.warn('Consent for experience sites is not provided. Experience sites will not be processed');
234+
this.removeKeyFromRelatedObjectsToProcess(Constants.ExpSites, objectsToProcess);
235+
Logger.logVerbose(`Objects to process after removing expsite are ${JSON.stringify(objectsToProcess)}`);
236+
return;
237+
}
238+
239+
const isMetadataAPIPreEnabled = await OrgPreferences.isExperienceBundleMetadataAPIEnabled(conn);
240+
if (isMetadataAPIPreEnabled === true) {
241+
Logger.logVerbose('ExperienceBundle metadata api is already enabled');
242+
return;
219243
}
220-
} catch (ex) {
221-
Logger.error(`Exception occurred while setting designers to use the standard data model ${JSON.stringify(ex)}`);
222-
userActionMessage.push(messages.getMessage('manuallySwitchDesignerToStandardDataModel'));
244+
245+
Logger.logVerbose('ExperienceBundle metadata api needs to be programatically enabled');
246+
isExperienceBundleMetadataAPIProgramaticallyEnabled.value = await OrgPreferences.setExperienceBundleMetadataAPI(
247+
conn,
248+
true
249+
);
250+
if (isExperienceBundleMetadataAPIProgramaticallyEnabled.value === false) {
251+
this.removeKeyFromRelatedObjectsToProcess(Constants.ExpSites, objectsToProcess);
252+
Logger.warn('Since the api could not able enabled the experience sites would not be processed');
253+
}
254+
255+
Logger.logVerbose(`Objects to process are ${JSON.stringify(objectsToProcess)}`);
256+
}
257+
}
258+
259+
private removeKeyFromRelatedObjectsToProcess(keyToRemove: string, relatedObjects: string[]): void {
260+
const index = relatedObjects.indexOf(Constants.ExpSites);
261+
if (index > -1) {
262+
relatedObjects.splice(index, 1);
223263
}
224-
return userActionMessage;
225264
}
226265

227266
private async truncateObjects(migrationObjects: MigrationTool[], debugTimer: DebugTimer): Promise<MigratedObject[]> {
@@ -355,6 +394,23 @@ export default class Migrate extends OmniStudioBaseCommand {
355394
return consent;
356395
}
357396

397+
private async getExpSiteMetadataEnableConsent(): Promise<boolean> {
398+
let consent: boolean | null = null;
399+
400+
while (consent === null) {
401+
try {
402+
consent = await Logger.confirm(
403+
'By proceeding further, you hereby consent to enable digital experience metadata api(y/n). If y sites will be processed, if n expsites will not be processed'
404+
);
405+
} catch (error) {
406+
Logger.log(messages.getMessage('invalidYesNoResponse'));
407+
consent = null;
408+
}
409+
}
410+
411+
return consent;
412+
}
413+
358414
private mergeRecordAndUploadResults(
359415
migrationResults: MigrationResult,
360416
migrationTool: MigrationTool

src/migration/flexcard.ts

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,21 @@ import CardMappings from '../mappings/VlocityCard';
44
import { DebugTimer, QueryTools, SortDirection } from '../utils';
55
import { NetUtils } from '../utils/net';
66
import { BaseMigrationTool } from './base';
7-
import { MigrationResult, MigrationTool, ObjectMapping, UploadRecordResult } from './interfaces';
7+
import {
8+
MigrationResult,
9+
MigrationTool,
10+
ObjectMapping,
11+
UploadRecordResult,
12+
MigrationStorage,
13+
FlexcardStorage,
14+
} from './interfaces';
815
import { Connection, Messages } from '@salesforce/core';
916
import { UX } from '@salesforce/command';
1017
import { FlexCardAssessmentInfo } from '../../src/utils';
1118
import { Logger } from '../utils/logger';
1219
import { createProgressBar } from './base';
1320
import { Constants } from '../utils/constants/stringContants';
21+
import { StorageUtil } from '../utils/storageUtil';
1422

1523
export class CardMigrationTool extends BaseMigrationTool implements MigrationTool {
1624
static readonly VLOCITYCARD_NAME = 'VlocityCard__c';
@@ -75,6 +83,7 @@ export class CardMigrationTool extends BaseMigrationTool implements MigrationToo
7583
async migrate(): Promise<MigrationResult[]> {
7684
// Get All the Active VlocityCard__c records
7785
const cards = await this.getAllActiveCards();
86+
7887
Logger.log(this.messages.getMessage('foundFlexCardsToMigrate', [cards.length]));
7988

8089
const progressBar = createProgressBar('Migrating', 'Flexcard');
@@ -445,6 +454,9 @@ export class CardMigrationTool extends BaseMigrationTool implements MigrationToo
445454
await this.uploadCard(cards, card, cardsUploadInfo, originalRecords, uniqueNames);
446455
progressBar.update(++progressCounter);
447456
}
457+
458+
this.prepareStorageForFlexcards(cardsUploadInfo, originalRecords);
459+
448460
progressBar.stop();
449461

450462
return cardsUploadInfo;
@@ -526,8 +538,9 @@ export class CardMigrationTool extends BaseMigrationTool implements MigrationToo
526538
this.messages.getMessage('cardAuthorNameChangeMessage', [transformedCardAuthorName])
527539
);
528540
}
541+
542+
uploadResult.newName = transformedCardName;
529543
if (transformedCardName !== card['Name']) {
530-
uploadResult.newName = transformedCardName;
531544
uploadResult.warnings.unshift(this.messages.getMessage('cardNameChangeMessage', [transformedCardName]));
532545
}
533546

@@ -571,6 +584,52 @@ export class CardMigrationTool extends BaseMigrationTool implements MigrationToo
571584
}
572585
}
573586

587+
private prepareStorageForFlexcards(
588+
cardsUploadInfo: Map<string, UploadRecordResult>,
589+
originalRecords: Map<string, any>
590+
) {
591+
Logger.logVerbose('Started preparing storage for flexcards');
592+
let storage: MigrationStorage = StorageUtil.getOmnistudioMigrationStorage();
593+
594+
for (let key of Array.from(originalRecords.keys())) {
595+
try {
596+
let oldrecord = originalRecords.get(key);
597+
let newrecord = cardsUploadInfo.get(key);
598+
599+
Logger.logVerbose('Oldrecord - ' + JSON.stringify(oldrecord));
600+
Logger.logVerbose('Newrecord - ' + JSON.stringify(newrecord));
601+
602+
let value: FlexcardStorage = {
603+
name: newrecord['newName'],
604+
isDuplicate: false,
605+
};
606+
607+
if (newrecord.hasErrors) {
608+
value.error = newrecord.errors;
609+
value.migrationSuccess = false;
610+
} else {
611+
value.migrationSuccess = true;
612+
}
613+
614+
let finalKey = `${oldrecord['Name']}`;
615+
if (storage.fcStorage.has(finalKey)) {
616+
// Key already exists - handle accordingly
617+
Logger.logVerbose(`Key ${finalKey} already exists in flexcard storage`);
618+
value.isDuplicate = true;
619+
storage.fcStorage.set(finalKey, value);
620+
} else {
621+
// Key doesn't exist - safe to set
622+
storage.fcStorage.set(finalKey, value);
623+
}
624+
} catch (error) {
625+
Logger.logVerbose('Error occurred while processing key for flexcard storage');
626+
Logger.error(error);
627+
}
628+
629+
StorageUtil.printMigrationStorage();
630+
}
631+
}
632+
574633
private getChildCards(card: AnyJson): string[] {
575634
let childs = [];
576635
const definition = JSON.parse(card[this.namespacePrefix + 'Definition__c']);

src/migration/interfaces.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ export interface UploadRecordResult {
4343
warnings: string[];
4444
hasErrors: boolean;
4545
success?: boolean;
46+
type?: string;
47+
subtype?: string;
48+
language?: string;
4649
}
4750

4851
export interface MigrationResult {
@@ -118,3 +121,74 @@ export interface FlexiComponentInstanceProperty {
118121
name: string;
119122
value?: string;
120123
}
124+
125+
export interface ExpSitePageJson {
126+
// Index signature to allow any additional properties
127+
[key: string]: any;
128+
appPageId: string;
129+
componentName: string;
130+
dataProviders: unknown[]; // Replace 'any' with a specific type if known
131+
id: string;
132+
label: string;
133+
regions: ExpSiteRegion[];
134+
themeLayoutType: string;
135+
type: string;
136+
viewType: string;
137+
}
138+
139+
export interface ExpSiteRegion {
140+
// Index signature to allow any additional properties
141+
[key: string]: any;
142+
id: string;
143+
regionName: string;
144+
type: string;
145+
components?: ExpSiteComponent[]; // Optional, as some regions don't have components
146+
}
147+
148+
export interface ExpSiteComponent {
149+
// Index signature to allow any additional properties
150+
[key: string]: any;
151+
componentAttributes: ExpSiteComponentAttributes;
152+
componentName: string;
153+
id: string;
154+
renderPriority?: string;
155+
renditionMap: unknown; // Replace with better type if known
156+
type: string;
157+
subtype?: string;
158+
language?: string;
159+
}
160+
161+
export interface ExpSiteComponentAttributes {
162+
// Index signature to allow any additional string properties
163+
[key: string]: any;
164+
// This is a union of possible structures. You can separate them if needed.
165+
layout?: string;
166+
params?: string;
167+
standAlone?: boolean;
168+
target?: string;
169+
customHeadTags?: string;
170+
description?: string;
171+
title?: string;
172+
richTextValue?: string;
173+
}
174+
175+
export interface MigrationStorage {
176+
osStorage: Map<string, OmniScriptStorage>;
177+
fcStorage: Map<string, FlexcardStorage>;
178+
}
179+
180+
export interface Storage {
181+
migrationSuccess?: boolean;
182+
error?: string[];
183+
isDuplicate: boolean;
184+
}
185+
186+
export interface OmniScriptStorage extends Storage {
187+
type: string;
188+
subtype: string;
189+
language: string;
190+
}
191+
192+
export interface FlexcardStorage extends Storage {
193+
name: string;
194+
}

0 commit comments

Comments
 (0)