Skip to content

Commit 8dd71ca

Browse files
authored
Merge pull request #438 from sf-kishore-kurri/u/kkurri/W-19700286
feat: @W-19700286: [OMT] [Migration] Implementation of Guardrail and validation
2 parents 6e18027 + 55fd750 commit 8dd71ca

File tree

8 files changed

+586
-4
lines changed

8 files changed

+586
-4
lines changed

messages/migrate.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,8 @@
2525
"errorWhileActivatingCard": "Could not activate Card: ",
2626
"errorWhileUploadingCard": "An error ocurred while uploading Card: ",
2727
"errorWhileCreatingElements": "An error ocurred while saving OmniScript elements: ",
28-
"allVersionsDescription": "Migrate all versions of a component"
28+
"allVersionsDescription": "Migrate all versions of a component",
29+
"noPackageInstalled": "No managed package found in your org.",
30+
"alreadyStandardModel": "Migration cannot proceed for the org %s as the org already uses the standard data model. Contact Salesforce support.",
31+
"invalidNamespace": "Invalid namespace value provided: %s. Try again with the correct namespace value."
2932
}

src/commands/omnistudio/migration/migrate.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { Messages } from '@salesforce/core';
1313
import '../../../utils/prototypes';
1414
import OmniStudioBaseCommand from '../../basecommand';
1515
import { DataRaptorMigrationTool } from '../../../migration/dataraptor';
16-
import { DebugTimer, MigratedObject, MigratedRecordInfo } from '../../../utils';
16+
import { DebugTimer, MigratedObject, MigratedRecordInfo, OrgUtils } from '../../../utils';
1717
import { MigrationResult, MigrationTool } from '../../../migration/interfaces';
1818
import { ResultsBuilder } from '../../../utils/resultsbuilder';
1919
import { CardMigrationTool } from '../../../migration/flexcard';
@@ -62,6 +62,17 @@ export default class Migrate extends OmniStudioBaseCommand {
6262
const conn = this.org.getConnection();
6363
conn.setApiVersion(apiVersion);
6464

65+
// Get org details and validate before proceeding with migration
66+
const orgs = await OrgUtils.getOrgDetails(conn, namespace);
67+
68+
if (orgs.omniStudioOrgPermissionEnabled) {
69+
Logger.error(messages.getMessage('alreadyStandardModel', [orgs.orgDetails.Id]));
70+
return;
71+
} else if (!orgs.hasValidNamespace) {
72+
Logger.error(messages.getMessage('invalidNamespace', [namespace]));
73+
return;
74+
}
75+
6576
// Let's time every step
6677
DebugTimer.getInstance().start();
6778

src/utils/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
export * from './query';
22
export * from './logging/debugtimer';
33
export * from './interfaces';
4+
export * from './orgUtils';
5+
export * from './orgPreferences';

src/utils/logger.ts

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,107 @@
11
import { UX } from '@salesforce/command';
22
import { Logger as SfLogger } from '@salesforce/core';
3+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
4+
// @ts-ignore
5+
import { FileLogger } from './logger/fileLogger';
36

47
export class Logger {
58
private static sfUX: UX;
69
private static sfLogger: SfLogger;
10+
private static verbose = false;
711

8-
public static initialiseLogger(ux: UX, logger: SfLogger): Logger {
12+
public static initialiseLogger(ux: UX, logger: SfLogger, command?: string, verbose?: boolean): Logger {
913
Logger.sfUX = ux;
1014
Logger.sfLogger = logger;
15+
Logger.verbose = verbose || false;
16+
FileLogger.initialize(command || 'default');
1117
return Logger;
1218
}
1319

20+
public static setVerbose(isVerbose: boolean): void {
21+
Logger.verbose = isVerbose;
22+
}
23+
24+
public static getVerbose(): boolean {
25+
return Logger.verbose;
26+
}
27+
28+
public static logVerbose(message: string): void {
29+
if (Logger.verbose && Logger.sfUX) {
30+
Logger.sfUX.log(message);
31+
}
32+
FileLogger.writeLog('VERBOSE', message);
33+
}
34+
35+
public static captureVerboseData(message: string, data: unknown): void {
36+
if (Logger.verbose) {
37+
FileLogger.writeLog('VERBOSE DATA', `${message}: ${JSON.stringify(data)}`);
38+
}
39+
}
40+
1441
public static get logger(): SfLogger {
1542
return Logger.sfLogger;
1643
}
44+
1745
public static get ux(): UX {
1846
return Logger.sfUX;
1947
}
48+
49+
public static log(message: string): void {
50+
if (Logger.sfUX) {
51+
Logger.sfUX.log(message);
52+
}
53+
FileLogger.writeLog('INFO', message);
54+
}
55+
56+
public static warn(message: string): void {
57+
if (Logger.sfUX) {
58+
Logger.sfUX.warn(message);
59+
}
60+
FileLogger.writeLog('WARN', message);
61+
}
62+
63+
public static error(message: string | Error, error?: Error): void {
64+
if (Logger.sfUX) {
65+
if (message instanceof Error) {
66+
Logger.sfUX.error(`\x1b[31m${message.message}\n${message.stack}\x1b[0m`);
67+
} else {
68+
if (error) {
69+
Logger.sfUX.error(`\x1b[31m${error.message}\n${error.stack}\x1b[0m`);
70+
} else {
71+
Logger.sfUX.error(`\x1b[31m${message}\x1b[0m`);
72+
}
73+
}
74+
}
75+
FileLogger.writeLog('ERROR', message instanceof Error ? `${message.message}\n${message.stack}` : message);
76+
}
77+
78+
public static debug(message: string): void {
79+
if (Logger.sfLogger) {
80+
Logger.sfLogger.debug(message);
81+
}
82+
FileLogger.writeLog('DEBUG', message);
83+
}
84+
85+
public static info(message: string): void {
86+
if (Logger.sfLogger) {
87+
Logger.sfLogger.info(message);
88+
}
89+
FileLogger.writeLog('INFO', message);
90+
}
91+
92+
public static confirm(message: string): Promise<boolean> {
93+
if (Logger.sfUX) {
94+
FileLogger.writeLog('CONFIRM', message);
95+
return Logger.sfUX.confirm(message);
96+
}
97+
return Promise.resolve(false);
98+
}
99+
100+
public static prompt(message: string): Promise<string> {
101+
if (Logger.sfUX) {
102+
FileLogger.writeLog('PROMPT', message);
103+
return Logger.sfUX.prompt(message);
104+
}
105+
return Promise.resolve('');
106+
}
20107
}

src/utils/logger/fileLogger.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import * as fs from 'fs';
2+
import * as path from 'path';
3+
import { Logger } from '../logger';
4+
5+
export class FileLogger {
6+
private static logDir = path.resolve(process.cwd(), 'logs');
7+
private static currentLogFile: string | null = null;
8+
private static isInitialized = false;
9+
10+
public static initialize(command = 'default'): void {
11+
if (this.isInitialized) {
12+
return;
13+
}
14+
15+
try {
16+
// Create logs directory if it doesn't exist
17+
if (!fs.existsSync(this.logDir)) {
18+
fs.mkdirSync(this.logDir, { recursive: true });
19+
}
20+
21+
// Create log file with timestamp and command name
22+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
23+
this.currentLogFile = path.join(this.logDir, `${timestamp}_${command}.log`);
24+
25+
// Create an empty log file to ensure it exists
26+
fs.writeFileSync(this.currentLogFile, '');
27+
28+
this.isInitialized = true;
29+
Logger.logVerbose(`Log file initialized at: ${this.currentLogFile}`);
30+
} catch (error: unknown) {
31+
const err = error as Error;
32+
Logger.error(`Error initializing FileLogger: ${err.message}\n${err.stack}`);
33+
// Fallback to console logging if file system operations fail
34+
this.currentLogFile = null;
35+
}
36+
}
37+
38+
public static writeLog(level: string, message: string): void {
39+
// Initialize with default command if not already initialized
40+
if (!this.isInitialized) {
41+
this.initialize();
42+
}
43+
44+
if (!this.currentLogFile) {
45+
// Fallback to console logging if file logging is not available
46+
Logger.log(`[${level}] ${message}`);
47+
return;
48+
}
49+
50+
const timestamp = new Date().toISOString();
51+
const logEntry = `[${timestamp}] [${level}] ${message}\n`;
52+
53+
try {
54+
fs.appendFileSync(this.currentLogFile, logEntry);
55+
} catch (error: unknown) {
56+
const err = error as Error;
57+
Logger.error(`Error writing to log file: ${err.message}\n${err.stack}`);
58+
// Fallback to console logging
59+
Logger.log(`[${level}] ${message}`);
60+
}
61+
}
62+
}

src/utils/orgPreferences.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { Connection } from '@salesforce/core';
2+
import { Logger } from './logger';
3+
4+
/**
5+
* Class to manage OmniStudio organization preferences
6+
*
7+
* @class OrgPreferences
8+
* @description Provides functionality to enable OmniStudio preferences and check rollback flags
9+
*/
10+
export class OrgPreferences {
11+
/**
12+
* Checks if OmniStudio designers are already using the standard data model for the specific package.
13+
*
14+
* @public
15+
* @static
16+
* @async
17+
* @param {Connection} connection - Salesforce connection instance
18+
* @param {string} namespaceToModify - The namespace to check for standard designer
19+
* @throws {Error} If checking the standard designer status fails
20+
* @returns {Promise<boolean>} True if standard designer is enabled, false otherwise
21+
*/
22+
23+
public static async isStandardDesignerEnabled(connection: Connection, namespaceToModify: string): Promise<boolean> {
24+
try {
25+
const query = `SELECT DeveloperName, Value FROM OmniInteractionConfig
26+
WHERE DeveloperName IN ('TheFirstInstalledOmniPackage', 'InstalledIndustryPackage')`;
27+
28+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
29+
const result = await connection.query(query);
30+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
31+
if (result?.totalSize > 0) {
32+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
33+
const records = result.records as Array<{ DeveloperName: string; Value: string }>;
34+
35+
for (const record of records) {
36+
if (record.Value === namespaceToModify) {
37+
return true;
38+
}
39+
}
40+
return false;
41+
} else {
42+
return false;
43+
}
44+
} catch (error) {
45+
// TODO: What should be the default behavior if the query fails?
46+
const errMsg = error instanceof Error ? error.message : String(error);
47+
Logger.error(`Error checking standard designer for namespace ${namespaceToModify}: ${errMsg}`);
48+
return false;
49+
}
50+
}
51+
}

0 commit comments

Comments
 (0)