Skip to content

Commit 01d28d8

Browse files
Merge pull request #232 from salesforcecli/u/sushmita-verma/W-16276197
W-16276197 : Bootstrap the enhancement of the Migration Plugin
2 parents 561b973 + a684997 commit 01d28d8

File tree

3 files changed

+204
-0
lines changed

3 files changed

+204
-0
lines changed
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
2+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
3+
/* eslint-disable @typescript-eslint/no-explicit-any */
4+
import * as os from 'os';
5+
import { Messages } from '@salesforce/core';
6+
import '../../../utils/prototypes';
7+
import { IConfig } from '@oclif/config';
8+
import OmniStudioBaseCommand from '../../basecommand';
9+
import { DebugTimer, MigratedObject, MigratedRecordInfo } from '../../../utils';
10+
import { MigrationResult, MigrationTool } from '../../../migration/interfaces';
11+
import { ResultsBuilder } from '../../../utils/resultsbuilder';
12+
import {
13+
LWCComponentMigrationTool,
14+
CustomLabelMigrationTool,
15+
ApexClassMigrationTool,
16+
} from '../../../migration/interfaces';
17+
18+
// Initialize Messages with the current plugin directory
19+
Messages.importMessagesDirectory(__dirname);
20+
21+
// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core,
22+
// or any library that is using the messages framework can also be loaded this way.
23+
const messages = Messages.loadMessages('@salesforce/plugin-omnistudio-related-object-migration-tool', 'migrate');
24+
25+
export default class OmnistudioRelatedObjectMigrationFacade extends OmniStudioBaseCommand {
26+
public static description = messages.getMessage('commandDescription');
27+
public static examples = messages.getMessage('examples').split(os.EOL);
28+
public static args = [{ name: 'file' }];
29+
30+
protected readonly namespace: string;
31+
protected readonly only: string;
32+
protected readonly allversions: boolean;
33+
34+
public constructor(argv: string[], config: IConfig) {
35+
super(argv, config);
36+
const { flags } = this.parse(OmnistudioRelatedObjectMigrationFacade);
37+
this.namespace = flags.namespace;
38+
this.only = flags.only;
39+
this.allversions = flags.allversions;
40+
}
41+
public async migrateAll(migrationResult: MigrationResult, namespace: string, relatedObjects: string[]): Promise<any> {
42+
const apiVersion = '55.0'; // Define the API version or make it configurable
43+
const conn = this.org.getConnection();
44+
conn.setApiVersion(apiVersion);
45+
46+
// Start the debug timer
47+
DebugTimer.getInstance().start();
48+
49+
// Declare an array of MigrationTool
50+
const migrationTools: MigrationTool[] = [];
51+
52+
// Initialize migration tools based on the relatedObjects parameter
53+
if (relatedObjects.includes('lwc')) {
54+
migrationTools.push(this.createLWCComponentMigrationTool(namespace, conn));
55+
}
56+
if (relatedObjects.includes('labels')) {
57+
migrationTools.push(this.createCustomLabelMigrationTool(namespace, conn));
58+
}
59+
if (relatedObjects.includes('apex')) {
60+
migrationTools.push(this.createApexClassMigrationTool(namespace, conn));
61+
}
62+
63+
if (migrationTools.length === 0) {
64+
throw new Error(messages.getMessage('noMigrationToolsSelected'));
65+
}
66+
67+
// Proceed with migration logic
68+
const debugTimer = DebugTimer.getInstance();
69+
let objectMigrationResults: MigratedObject[] = [];
70+
71+
// Truncate existing objects if necessary
72+
let allTruncateComplete = true;
73+
for (const tool of migrationTools.reverse()) {
74+
try {
75+
this.ux.log('Cleaning: ' + tool.getName());
76+
debugTimer.lap('Cleaning: ' + tool.getName());
77+
await tool.truncate();
78+
} catch (ex: any) {
79+
allTruncateComplete = false;
80+
objectMigrationResults.push({
81+
name: tool.getName(),
82+
errors: [ex.message],
83+
});
84+
}
85+
}
86+
87+
if (allTruncateComplete) {
88+
for (const tool of migrationTools.reverse()) {
89+
try {
90+
this.ux.log('Migrating: ' + tool.getName());
91+
debugTimer.lap('Migrating: ' + tool.getName());
92+
const results = await tool.migrate();
93+
94+
objectMigrationResults = objectMigrationResults.concat(
95+
results.map((r) => ({
96+
name: r.name,
97+
data: this.mergeRecordAndUploadResults(r, tool),
98+
}))
99+
);
100+
} catch (ex: any) {
101+
this.logger.error(JSON.stringify(ex));
102+
objectMigrationResults.push({
103+
name: tool.getName(),
104+
errors: [ex.message],
105+
});
106+
}
107+
}
108+
}
109+
110+
// Stop the debug timer
111+
const timer = DebugTimer.getInstance().stop();
112+
113+
await ResultsBuilder.generate(objectMigrationResults, conn.instanceUrl);
114+
115+
// Save timer to debug logger
116+
this.logger.debug(timer);
117+
118+
// Return results needed for --json flag
119+
return { objectMigrationResults };
120+
}
121+
122+
// Factory methods to create instances of specific tools
123+
private createLWCComponentMigrationTool(namespace: string, conn: any): LWCComponentMigrationTool {
124+
// Return an instance of LWCComponentMigrationTool when implemented
125+
throw new Error('LWCComponentMigrationTool implementation is not provided yet.');
126+
}
127+
128+
private createCustomLabelMigrationTool(namespace: string, conn: any): CustomLabelMigrationTool {
129+
// Return an instance of CustomLabelMigrationTool when implemented
130+
throw new Error('CustomLabelMigrationTool implementation is not provided yet.');
131+
}
132+
133+
private createApexClassMigrationTool(namespace: string, conn: any): ApexClassMigrationTool {
134+
// Return an instance of ApexClassMigrationTool when implemented
135+
throw new Error('ApexClassMigrationTool implementation is not provided yet.');
136+
}
137+
138+
private mergeRecordAndUploadResults(
139+
migrationResults: MigrationResult,
140+
migrationTool: MigrationTool
141+
): MigratedRecordInfo[] {
142+
const mergedResults: MigratedRecordInfo[] = [];
143+
144+
for (const record of Array.from(migrationResults.records.values())) {
145+
const obj = {
146+
id: record['Id'],
147+
name: migrationTool.getRecordName(record),
148+
status: 'Skipped',
149+
errors: record['errors'],
150+
migratedId: undefined,
151+
warnings: [],
152+
migratedName: '',
153+
};
154+
155+
if (migrationResults.results.has(record['Id'])) {
156+
const recordResults = migrationResults.results.get(record['Id']);
157+
158+
let errors: any[] = obj.errors || [];
159+
errors = errors.concat(recordResults.errors || []);
160+
161+
obj.status = !recordResults || recordResults.hasErrors ? 'Error' : 'Complete';
162+
obj.errors = errors;
163+
obj.migratedId = recordResults.id;
164+
obj.warnings = recordResults.warnings;
165+
obj.migratedName = recordResults.newName;
166+
}
167+
168+
mergedResults.push(obj);
169+
}
170+
171+
return mergedResults;
172+
}
173+
}

src/migration/interfaces.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,33 @@ export interface NameTransformData {
8282
dupNameSet: Set<string>;
8383
nameWithSepcialCharactorSet: Set<string>;
8484
}
85+
86+
export interface RelatedObjectsMigrate {
87+
/**
88+
* Identifies migration candidates based on the provided migration results and namespace.
89+
*
90+
* @param migrationResults List of migration results to identify objects from.
91+
* @param namespace The namespace used to identify objects.
92+
* @returns List of identified migration candidates as strings.
93+
*/
94+
identifyObjects(migrationResults: MigrationResult[], namespace: string): Promise<string[]>;
95+
96+
/**
97+
* Private method to perform the migration of related objects based on the provided candidates.
98+
*
99+
* @param migrationResults List of migration results to use for migration.
100+
* @param namespace The namespace used to perform the migration.
101+
* @param migrationCandidates List of candidates to migrate.
102+
*/
103+
migrateRelatedObjects(
104+
migrationResults: MigrationResult[],
105+
namespace: string,
106+
migrationCandidates: string[]
107+
): Promise<void>;
108+
}
109+
110+
export type LWCComponentMigrationTool = MigrationTool;
111+
112+
export type CustomLabelMigrationTool = MigrationTool;
113+
114+
export type ApexClassMigrationTool = MigrationTool;

test/.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@ module.exports = {
2121
'@typescript-eslint/no-empty-function': 'off',
2222
// Easily return a promise in a mocked method.
2323
'@typescript-eslint/require-await': 'off',
24+
'@typescript-eslint/no-unsafe-assignment': 'off',
2425
},
2526
};

0 commit comments

Comments
 (0)