Skip to content

Commit f9ae63d

Browse files
Merge pull request #238 from salesforcecli/u/abhinavkumar2/using_shelljs
Apex DR IP reference updates
2 parents 024fe9e + c843e45 commit f9ae63d

File tree

11 files changed

+287
-158
lines changed

11 files changed

+287
-158
lines changed

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,21 @@
55
"author": "Salesforce",
66
"bugs": "https://github.com/forcedotcom/cli/issues",
77
"dependencies": {
8-
"@babel/parser": "^7.25.4",
98
"@apexdevtools/apex-parser": "^4.1.0",
9+
"@babel/parser": "^7.25.4",
1010
"@oclif/command": "^1",
1111
"@oclif/config": "^1",
1212
"@oclif/errors": "^1",
1313
"@salesforce/command": "^4.2.1",
1414
"@salesforce/core": "^2.37.1",
1515
"@types/jsdom": "^21.1.7",
1616
"@types/lodash.chunk": "^4.2.9",
17+
"@types/shelljs": "^0.8.15",
1718
"cheerio": "^1.0.0",
1819
"jsdom": "^25.0.0",
1920
"lodash.chunk": "^4.2.0",
2021
"open": "^8.4.2",
22+
"shelljs": "^0.8.5",
2123
"tslib": "^2",
2224
"xmldom": "^0.6.0"
2325
},
Lines changed: 47 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,173 +1,101 @@
11
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
22
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
33
/* eslint-disable @typescript-eslint/no-explicit-any */
4-
import * as os from 'os';
5-
import { Messages } from '@salesforce/core';
4+
import { Org } from '@salesforce/core';
65
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';
6+
import { DebugTimer, MigratedObject } from '../../../utils';
7+
import { RelatedObjectsMigrate } from '../../../migration/interfaces';
8+
import { sfProject } from '../../../utils/sfcli/project/sfProject';
9+
import { Logger } from '../../../utils/logger';
10+
import { ApexMigration } from '../../../migration/related/ApexMigration';
1711

1812
// Initialize Messages with the current plugin directory
19-
Messages.importMessagesDirectory(__dirname);
13+
// Messages.importMessagesDirectory(__dirname);
2014

2115
// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core,
2216
// 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');
17+
// const messages = Messages.loadMessages('@salesforce/plugin-omnistudio-related-object-migration-tool', 'migrate');
2418

25-
export default class OmnistudioRelatedObjectMigrationFacade extends OmniStudioBaseCommand {
26-
public static description = messages.getMessage('commandDescription');
27-
public static examples = messages.getMessage('examples').split(os.EOL);
19+
const defaultProjectName = 'omnistudio_migration';
20+
export default class OmnistudioRelatedObjectMigrationFacade {
21+
// public static description = messages.getMessage('commandDescription');
22+
// public static examples = messages.getMessage('examples').split(os.EOL);
2823
public static args = [{ name: 'file' }];
2924

3025
protected readonly namespace: string;
3126
protected readonly only: string;
3227
protected readonly allversions: boolean;
28+
protected readonly org: Org;
3329

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;
30+
public constructor(namespace: string, only: string, allversions: boolean, org: Org) {
31+
this.namespace = namespace;
32+
this.only = only;
33+
this.allversions = allversions;
34+
this.org = org;
4035
}
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-
36+
public migrateAll(migrationResult: MigratedObject[], relatedObjects: string[]): any {
4637
// Start the debug timer
4738
DebugTimer.getInstance().start();
4839

4940
// Declare an array of MigrationTool
50-
const migrationTools: MigrationTool[] = [];
51-
41+
const migrationTools: RelatedObjectsMigrate[] = [];
42+
const projectDirectory: string = this.intializeProject();
43+
const debugTimer = DebugTimer.getInstance();
44+
debugTimer.start();
5245
// Initialize migration tools based on the relatedObjects parameter
5346
if (relatedObjects.includes('lwc')) {
54-
migrationTools.push(this.createLWCComponentMigrationTool(namespace, conn));
47+
migrationTools.push(this.createLWCComponentMigrationTool(this.namespace, this.org));
5548
}
5649
if (relatedObjects.includes('labels')) {
57-
migrationTools.push(this.createCustomLabelMigrationTool(namespace, conn));
50+
migrationTools.push(this.createCustomLabelMigrationTool(this.namespace, this.org));
5851
}
5952
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'));
53+
migrationTools.push(this.createApexClassMigrationTool(projectDirectory));
6554
}
6655

6756
// 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()) {
57+
for (const migrationTool of migrationTools.reverse()) {
7458
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-
}
59+
migrationTool.migrateRelatedObjects(null, null);
60+
} catch (Error) {
61+
// Log the error
62+
Logger.logger.error(Error.message);
63+
return { migrationResult };
10764
}
10865
}
109-
66+
// Truncate existing objects if necessary
11067
// Stop the debug timer
111-
const timer = DebugTimer.getInstance().stop();
112-
113-
await ResultsBuilder.generate(objectMigrationResults, conn.instanceUrl);
68+
const timer = debugTimer.stop();
11469

11570
// Save timer to debug logger
116-
this.logger.debug(timer);
71+
Logger.logger.debug(timer);
11772

11873
// Return results needed for --json flag
119-
return { objectMigrationResults };
74+
return { migrationResult };
12075
}
12176

12277
// Factory methods to create instances of specific tools
123-
private createLWCComponentMigrationTool(namespace: string, conn: any): LWCComponentMigrationTool {
78+
private createLWCComponentMigrationTool(namespace: string, org: Org): RelatedObjectsMigrate {
12479
// Return an instance of LWCComponentMigrationTool when implemented
12580
throw new Error('LWCComponentMigrationTool implementation is not provided yet.');
12681
}
12782

128-
private createCustomLabelMigrationTool(namespace: string, conn: any): CustomLabelMigrationTool {
83+
private createCustomLabelMigrationTool(namespace: string, org: Org): RelatedObjectsMigrate {
12984
// Return an instance of CustomLabelMigrationTool when implemented
13085
throw new Error('CustomLabelMigrationTool implementation is not provided yet.');
13186
}
13287

133-
private createApexClassMigrationTool(namespace: string, conn: any): ApexClassMigrationTool {
88+
private createApexClassMigrationTool(projectPath: string): ApexMigration {
13489
// Return an instance of ApexClassMigrationTool when implemented
135-
throw new Error('ApexClassMigrationTool implementation is not provided yet.');
90+
return new ApexMigration(projectPath, this.namespace, this.org);
13691
}
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);
92+
private intializeProject(projectPath?: string): string {
93+
if (projectPath) {
94+
sfProject.create(defaultProjectName, projectPath);
95+
return projectPath + '/' + defaultProjectName;
96+
} else {
97+
sfProject.create(defaultProjectName);
98+
return process.cwd() + '/' + defaultProjectName;
16999
}
170-
171-
return mergedResults;
172100
}
173101
}

src/commands/omnistudio/migration/migrate.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { ResultsBuilder } from '../../../utils/resultsbuilder';
1919
import { CardMigrationTool } from '../../../migration/flexcard';
2020
import { OmniScriptExportType, OmniScriptMigrationTool } from '../../../migration/omniscript';
2121
import { Logger } from '../../../utils/logger';
22+
import OmnistudioRelatedObjectMigrationFacade from './OmnistudioRelatedObjectMigrationFacade';
2223

2324
// Initialize Messages with the current plugin directory
2425
Messages.importMessagesDirectory(__dirname);
@@ -56,6 +57,7 @@ export default class Migrate extends OmniStudioBaseCommand {
5657
const apiVersion = (this.flags.apiversion || '55.0') as string;
5758
const migrateOnly = (this.flags.only || '') as string;
5859
const allVersions = this.flags.allversions || false;
60+
5961
Logger.initialiseLogger(this.ux, this.logger);
6062
this.logger = Logger.logger;
6163
// this.org is guaranteed because requiresUsername=true, as opposed to supportsUsername
@@ -123,7 +125,6 @@ export default class Migrate extends OmniStudioBaseCommand {
123125
// Migrate individual objects
124126
const debugTimer = DebugTimer.getInstance();
125127
let objectMigrationResults: MigratedObject[] = [];
126-
127128
// We need to truncate the standard objects first
128129
let allTruncateComplete = true;
129130
for (const cls of migrationObjects.reverse()) {
@@ -168,6 +169,13 @@ export default class Migrate extends OmniStudioBaseCommand {
168169
// Stop the debug timer
169170
const timer = DebugTimer.getInstance().stop();
170171

172+
const omnistudioRelatedObjectsMigration = new OmnistudioRelatedObjectMigrationFacade(
173+
namespace,
174+
migrateOnly,
175+
allVersions,
176+
this.org
177+
);
178+
omnistudioRelatedObjectsMigration.migrateAll(objectMigrationResults, []);
171179
await ResultsBuilder.generate(objectMigrationResults, conn.instanceUrl);
172180

173181
// save timer to debug logger

src/migration/interfaces.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ export interface RelatedObjectsMigrate {
9191
* @param namespace The namespace used to identify objects.
9292
* @returns List of identified migration candidates as strings.
9393
*/
94-
identifyObjects(migrationResults: MigrationResult[], namespace: string): Promise<string[]>;
94+
identifyObjects(migrationResults: MigrationResult[]): Promise<JSON[]>;
9595

9696
/**
9797
* Private method to perform the migration of related objects based on the provided candidates.
@@ -100,15 +100,9 @@ export interface RelatedObjectsMigrate {
100100
* @param namespace The namespace used to perform the migration.
101101
* @param migrationCandidates List of candidates to migrate.
102102
*/
103-
migrateRelatedObjects(
104-
migrationResults: MigrationResult[],
105-
namespace: string,
106-
migrationCandidates: string[]
107-
): Promise<void>;
103+
migrateRelatedObjects(migrationResults: MigrationResult[], migrationCandidates: JSON[]): void;
108104
}
109105

110106
export type LWCComponentMigrationTool = MigrationTool;
111107

112108
export type CustomLabelMigrationTool = MigrationTool;
113-
114-
export type ApexClassMigrationTool = MigrationTool;
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import * as fs from 'fs';
2+
// import { RetrieveResult } from '@salesforce/source-deploy-retrieve';
3+
// import { sfcclicommand } from '../../utils/sfcli/commands/sfclicommand';
4+
import * as shell from 'shelljs';
5+
import { Org } from '@salesforce/core';
6+
import { ApexASTParser, MethodCall } from '../../utils/apex/parser/apexparser';
7+
import { MigrationResult, RelatedObjectsMigrate } from '../interfaces';
8+
import { sfProject } from '../../utils/sfcli/project/sfProject';
9+
import { fileutil, File } from '../../utils/file/fileutil';
10+
import { BaseRelatedObjectMigration } from './BaseRealtedObjectMigration';
11+
const APEXCLASS = 'Apexclass';
12+
const APEX_CLASS_PATH = '/force-app/main/default/classes';
13+
export class ApexMigration extends BaseRelatedObjectMigration implements RelatedObjectsMigrate {
14+
public identifyObjects(migrationResults: MigrationResult[]): Promise<JSON[]> {
15+
throw new Error('Method not implemented.');
16+
}
17+
public migrateRelatedObjects(migrationResults: MigrationResult[], migrationCandidates: JSON[]): void {
18+
this.migrate();
19+
}
20+
public migrate(): void {
21+
const pwd = shell.pwd();
22+
shell.cd(this.projectPath);
23+
const targetOrg: Org = this.org;
24+
sfProject.retrieve(APEXCLASS, targetOrg.getUsername());
25+
this.processApexFiles(this.projectPath);
26+
sfProject.deploy(APEXCLASS, targetOrg.getUsername());
27+
shell.cd(pwd);
28+
}
29+
public processApexFiles(dir: string): File[] {
30+
dir += APEX_CLASS_PATH;
31+
let files: File[] = [];
32+
files = fileutil.readFilesSync(dir);
33+
for (const file of files) {
34+
if (file.ext !== '.cls') continue;
35+
this.processApexFile(file);
36+
}
37+
return files;
38+
}
39+
40+
public processApexFile(file: File): void {
41+
const fileContent = fs.readFileSync(file.location, 'utf8');
42+
const interfaces = new Set<string>(['VlocityOpenInterface', 'VlocityOpenInterface2', 'Callable']);
43+
const methodCalls = new Set<MethodCall>();
44+
methodCalls.add(new MethodCall('process', 'DRGlobal', this.namespace));
45+
methodCalls.add(new MethodCall('processObjectsJSON', 'DRGlobal', this.namespace));
46+
const parser = new ApexASTParser(fileContent, interfaces, methodCalls, this.namespace);
47+
parser.parse();
48+
this.processApexFileforDRCalls(file, fileContent);
49+
}
50+
public processApexFileforDRCalls(file: File, fileContent: string): void {
51+
// fileContent = fileContent.replace(this.namespace + '.', 'omnistudio.');
52+
fs.writeFileSync(file.location, fileContent, 'utf-8');
53+
}
54+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Org } from '@salesforce/core';
2+
3+
export abstract class BaseRelatedObjectMigration {
4+
public projectPath: string;
5+
public namespace: string;
6+
public org: Org;
7+
8+
public constructor(projectPath: string, namespace: string, org: Org) {
9+
this.projectPath = projectPath;
10+
this.namespace = namespace;
11+
this.org = org;
12+
}
13+
}

0 commit comments

Comments
 (0)