diff --git a/.mocharc.json b/.mocharc.json index 2e6bae40..9d930a48 100644 --- a/.mocharc.json +++ b/.mocharc.json @@ -3,5 +3,7 @@ "watch-extensions": "ts", "recursive": true, "reporter": "spec", - "timeout": 5000 + "timeout": 5000, + "spec": "test/**/*.test.ts", + "ignore": ["node_modules/**"] } diff --git a/package.json b/package.json index 02b3e23c..60dec039 100644 --- a/package.json +++ b/package.json @@ -10,18 +10,20 @@ "@oclif/command": "^1", "@oclif/config": "^1", "@oclif/errors": "^1", - "@salesforce/command": "^4.2.1", - "@salesforce/core": "^2.37.1", + "@salesforce/command": "^5.3.9", + "@salesforce/core": "^8.6.4", "@types/jsdom": "^21.1.7", "@types/lodash.chunk": "^4.2.9", + "@types/mocha": "^10.0.9", "@types/shelljs": "^0.8.15", + "@xmldom/xmldom": "^0.7.5", "cheerio": "^1.0.0", "jsdom": "^25.0.0", "lodash.chunk": "^4.2.0", + "minimatch": "^10.0.1", "open": "^8.4.2", "shelljs": "^0.8.5", - "tslib": "^2", - "xmldom": "^0.6.0" + "tslib": "^2" }, "devDependencies": { "@babel/parser": "^7.25.6", @@ -30,7 +32,6 @@ "@oclif/plugin-help": "^3", "@oclif/test": "^1", "@salesforce/dev-config": "^2.1.2", - "@salesforce/dev-scripts": "^0", "@salesforce/plugin-command-reference": "^1.4.7", "@salesforce/prettier-config": "^0.0.3", "@salesforce/ts-sinon": "^1", @@ -52,14 +53,19 @@ "eslint-plugin-typescript": "^0", "globby": "^11", "husky": "^4.3.8", - "mocha": "^8.4.0", - "nyc": "^15.1.0", + "nyc": "^17.1.0", "prettier": "^2.8.8", "pretty-quick": "^3.3.1", "sinon": "10.0.0", "ts-node": "^10.9.2", "typescript": "^4.9.5" }, + "overrides": { + "semver": "7.5.3", + "marked": "^15.0.0", + "trim-newlines": "5.0.0", + "nanoid": "5.0.8" + }, "engines": { "node": ">=12.0.0" }, diff --git a/src/commands/omnistudio/migration/assess.ts b/src/commands/omnistudio/migration/assess.ts index dc5dc675..fc2814ec 100644 --- a/src/commands/omnistudio/migration/assess.ts +++ b/src/commands/omnistudio/migration/assess.ts @@ -1,6 +1,6 @@ import * as os from 'os'; import { flags } from '@salesforce/command'; -import { Messages } from '@salesforce/core'; +import { Messages, Org } from '@salesforce/core'; import OmniStudioBaseCommand from '../../basecommand'; import { AssessmentInfo } from '../../../utils/interfaces'; import { AssessmentReporter } from '../../../utils/resultsbuilder/assessmentReporter'; @@ -46,23 +46,25 @@ export default class Assess extends OmniStudioBaseCommand { const namespace = (this.flags.namespace || 'vlocity_ins') as string; const apiVersion = (this.flags.apiversion || '55.0') as string; const allVersions = (this.flags.allversions || false) as boolean; - const conn = this.org.getConnection(); - Logger.initialiseLogger(this.ux, this.logger); + const org = new Org(); + const conn = org.getConnection(); + Logger.initialiseLogger(this.ux, Logger.logger); + const logger = Logger.logger; const projectDirectory = OmnistudioRelatedObjectMigrationFacade.intializeProject(); conn.setApiVersion(apiVersion); - const lwcparser = new LwcMigration(projectDirectory, namespace, this.org); - const apexMigrator = new ApexMigration(projectDirectory, namespace, this.org); + const lwcparser = new LwcMigration(projectDirectory, namespace, org); + const apexMigrator = new ApexMigration(projectDirectory, namespace, org); const osMigrator = new OmniScriptMigrationTool( OmniScriptExportType.All, namespace, conn, - this.logger, + logger, messages, this.ux, allVersions ); - const flexMigrator = new CardMigrationTool(namespace, conn, this.logger, messages, this.ux, allVersions); - const drMigrator = new DataRaptorMigrationTool(namespace, conn, this.logger, messages, this.ux); + const flexMigrator = new CardMigrationTool(namespace, conn, logger, messages, this.ux, allVersions); + const drMigrator = new DataRaptorMigrationTool(namespace, conn, logger, messages, this.ux); this.logger.info(namespace); this.ux.log(`Using Namespace: ${namespace}`); diff --git a/src/commands/omnistudio/migration/info.ts b/src/commands/omnistudio/migration/info.ts index 416637a9..9db987a9 100644 --- a/src/commands/omnistudio/migration/info.ts +++ b/src/commands/omnistudio/migration/info.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unsafe-call */ /* * Copyright (c) 2020, salesforce.com, inc. * All rights reserved. @@ -6,7 +7,7 @@ */ import * as os from 'os'; import { flags, SfdxCommand } from '@salesforce/command'; -import { Messages, SfdxError } from '@salesforce/core'; +import { Messages, SfError } from '@salesforce/core'; import { AnyJson } from '@salesforce/ts-types'; // Initialize Messages with the current plugin directory @@ -66,7 +67,7 @@ export default class Org extends SfdxCommand { // Organization will always return one result, but this is an example of throwing an error // The output and --json will automatically be handled for you. if (!result.records || result.records.length <= 0) { - throw new SfdxError(messages.getMessage('errorNoOrgResults', [this.org.getOrgId()])); + throw new SfError(messages.getMessage('errorNoOrgResults', [this.org.getOrgId()])); } // Organization always only returns one result diff --git a/src/commands/omnistudio/migration/migrate.ts b/src/commands/omnistudio/migration/migrate.ts index 72b6fed2..14bc1b84 100644 --- a/src/commands/omnistudio/migration/migrate.ts +++ b/src/commands/omnistudio/migration/migrate.ts @@ -9,7 +9,7 @@ */ import * as os from 'os'; import { flags } from '@salesforce/command'; -import { Messages } from '@salesforce/core'; +import { Messages, Org } from '@salesforce/core'; import '../../../utils/prototypes'; import OmniStudioBaseCommand from '../../basecommand'; import { DataRaptorMigrationTool } from '../../../migration/dataraptor'; @@ -59,10 +59,11 @@ export default class Migrate extends OmniStudioBaseCommand { const migrateOnly = (this.flags.only || '') as string; const allVersions = this.flags.allversions || false; - Logger.initialiseLogger(this.ux, this.logger); - this.logger = Logger.logger; + Logger.initialiseLogger(this.ux, Logger.logger); + const logger = Logger.logger; // this.org is guaranteed because requiresUsername=true, as opposed to supportsUsername - const conn = this.org.getConnection(); + const org = new Org(); + const conn = org.getConnection(); conn.setApiVersion(apiVersion); // Let's time every step @@ -72,17 +73,9 @@ export default class Migrate extends OmniStudioBaseCommand { let migrationObjects: MigrationTool[] = []; if (!migrateOnly) { migrationObjects = [ - new DataRaptorMigrationTool(namespace, conn, this.logger, messages, this.ux), - new OmniScriptMigrationTool( - OmniScriptExportType.All, - namespace, - conn, - this.logger, - messages, - this.ux, - allVersions - ), - new CardMigrationTool(namespace, conn, this.logger, messages, this.ux, allVersions), + new DataRaptorMigrationTool(namespace, conn, logger, messages, this.ux), + new OmniScriptMigrationTool(OmniScriptExportType.All, namespace, conn, logger, messages, this.ux, allVersions), + new CardMigrationTool(namespace, conn, logger, messages, this.ux, allVersions), ]; } else { switch (migrateOnly) { @@ -92,7 +85,7 @@ export default class Migrate extends OmniStudioBaseCommand { OmniScriptExportType.OS, namespace, conn, - this.logger, + logger, messages, this.ux, allVersions @@ -105,7 +98,7 @@ export default class Migrate extends OmniStudioBaseCommand { OmniScriptExportType.IP, namespace, conn, - this.logger, + logger, messages, this.ux, allVersions @@ -113,10 +106,10 @@ export default class Migrate extends OmniStudioBaseCommand { ); break; case 'fc': - migrationObjects.push(new CardMigrationTool(namespace, conn, this.logger, messages, this.ux, allVersions)); + migrationObjects.push(new CardMigrationTool(namespace, conn, logger, messages, this.ux, allVersions)); break; case 'dr': - migrationObjects.push(new DataRaptorMigrationTool(namespace, conn, this.logger, messages, this.ux)); + migrationObjects.push(new DataRaptorMigrationTool(namespace, conn, logger, messages, this.ux)); break; default: throw new Error(messages.getMessage('invalidOnlyFlag')); @@ -174,7 +167,7 @@ export default class Migrate extends OmniStudioBaseCommand { namespace, migrateOnly, allVersions, - this.org + org ); const relatedObjectMigrationResult = omnistudioRelatedObjectsMigration.migrateAll(objectMigrationResults, []); generatePackageXml.createChangeList( diff --git a/src/migration/base.ts b/src/migration/base.ts index 982455f3..fd5a1336 100644 --- a/src/migration/base.ts +++ b/src/migration/base.ts @@ -12,13 +12,14 @@ export class BaseMigrationTool { protected readonly connection: Connection; protected readonly namespacePrefix: string; protected readonly logger: Logger; - protected readonly messages: Messages; + protected readonly messages: Messages; protected readonly ux: UX; - public constructor(namespace: string, connection: Connection, logger: Logger, messages: Messages, ux: UX) { + public constructor(namespace: string, connection: Connection, logger: Logger, messages: Messages, ux: UX) { this.namespace = namespace; this.connection = connection; this.logger = logger; + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment this.messages = messages; this.ux = ux; this.namespacePrefix = namespace ? namespace + '__' : ''; diff --git a/src/migration/flexcard.ts b/src/migration/flexcard.ts index ddec680d..0354ce33 100644 --- a/src/migration/flexcard.ts +++ b/src/migration/flexcard.ts @@ -18,7 +18,7 @@ export class CardMigrationTool extends BaseMigrationTool implements MigrationToo namespace: string, connection: Connection, logger: Logger, - messages: Messages, + messages: Messages, ux: UX, allVersions: boolean ) { diff --git a/src/migration/omniscript.ts b/src/migration/omniscript.ts index 9b0563ad..af691554 100644 --- a/src/migration/omniscript.ts +++ b/src/migration/omniscript.ts @@ -42,7 +42,7 @@ export class OmniScriptMigrationTool extends BaseMigrationTool implements Migrat namespace: string, connection: Connection, logger: Logger, - messages: Messages, + messages: Messages, ux: UX, allVersions: boolean ) { diff --git a/src/utils/apex/executor/AnonymousApexRunner.ts b/src/utils/apex/executor/AnonymousApexRunner.ts index c3227f34..f2281f93 100644 --- a/src/utils/apex/executor/AnonymousApexRunner.ts +++ b/src/utils/apex/executor/AnonymousApexRunner.ts @@ -1,8 +1,8 @@ import { Org } from '@salesforce/core'; -import { ExecuteAnonymousResult } from 'jsforce'; export class AnonymousApexRunner { - public static async run(org: Org, anonymousApex: string): Promise { - return org.getConnection().tooling.executeAnonymous(anonymousApex); + public static async run(org: Org, anonymousApex: string): Promise { + const connection = org.getConnection(); + return connection.tooling.executeAnonymous(anonymousApex); } } diff --git a/src/utils/lwcparser/xmlParser/XmlParser.ts b/src/utils/lwcparser/xmlParser/XmlParser.ts index fb5480a4..9b77f60c 100644 --- a/src/utils/lwcparser/xmlParser/XmlParser.ts +++ b/src/utils/lwcparser/xmlParser/XmlParser.ts @@ -6,13 +6,12 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/explicit-member-accessibility */ import * as fs from 'fs'; -import { DOMParser, XMLSerializer } from 'xmldom'; +import { DOMParser, XMLSerializer } from '@xmldom/xmldom'; import { FileConstant } from '../fileutils/FileConstant'; export class XmlParser { private xmlDoc: Document | null = null; private fileContent: string; - constructor(private filePath: string) { this.fileContent = fs.readFileSync(this.filePath, 'utf-8'); this.parseXml(this.fileContent); diff --git a/src/utils/net/index.ts b/src/utils/net/index.ts index f8968000..0ee1d3d6 100644 --- a/src/utils/net/index.ts +++ b/src/utils/net/index.ts @@ -2,134 +2,160 @@ import { Connection } from '@salesforce/core'; import chunk from 'lodash.chunk'; import { UploadRecordResult } from '../../migration/interfaces'; +import { HttpRequest, HttpMethods } from 'jsforce'; class NetUtils { - - private static readonly CHUNK_SIZE = 200; - - public static async create(connection: Connection, objectName: string, data: any[]): Promise> { - // Metadata API only accepts 200 records per request - const chunks = chunk(data, NetUtils.CHUNK_SIZE), - results = new Map(); - - for (let curr of chunks) { - const response = await this.request(connection, `composite/tree/${objectName}`, { records: curr }, RequestMethod.POST); - response.results.forEach(result => { - results.set(result.referenceId, { - ...result, - hasErrors: Array.isArray(result.errors) && result.errors.length > 0 - }); - }); - } - - return results; + private static readonly CHUNK_SIZE = 200; + + public static async create( + connection: Connection, + objectName: string, + data: any[] + ): Promise> { + // Metadata API only accepts 200 records per request + const chunks = chunk(data, NetUtils.CHUNK_SIZE), + results = new Map(); + + for (let curr of chunks) { + const response = await this.request( + connection, + `composite/tree/${objectName}`, + { records: curr }, + RequestMethod.POST + ); + response.results.forEach((result) => { + results.set(result.referenceId, { + ...result, + hasErrors: Array.isArray(result.errors) && result.errors.length > 0, + }); + }); } - public static async createOne(connection: Connection, objectName: string, referenceId: string, data: any): Promise { - - try { - const url = 'sobjects/' + objectName; - - const response = await this.request(connection, url, data, RequestMethod.POST); - return { ...response, referenceId, hasErrors: response.errors.length > 0 }; - - } catch (err) { - return { - referenceId, - hasErrors: true, - success: false, - errors: err, - warnings: [] - }; - } + return results; + } + + public static async createOne( + connection: Connection, + objectName: string, + referenceId: string, + data: any + ): Promise { + try { + const url = 'sobjects/' + objectName; + + const response = await this.request(connection, url, data, RequestMethod.POST); + return { ...response, referenceId, hasErrors: response.errors.length > 0 }; + } catch (err) { + return { + referenceId, + hasErrors: true, + success: false, + errors: err, + warnings: [], + }; } - - public static async updateOne(connection: Connection, objectName: string, referenceId: string, recordId: string, data: any): Promise { - - try { - const url = 'sobjects/' + objectName + '/' + recordId; - - await this.request(connection, url, data, RequestMethod.PATCH); - - return { - referenceId, - hasErrors: false, - success: true, - errors: [], - warnings: [] - } - - } catch (err) { - return { - referenceId, - hasErrors: true, - success: false, - errors: err, - warnings: [] - }; - } + } + + public static async updateOne( + connection: Connection, + objectName: string, + referenceId: string, + recordId: string, + data: any + ): Promise { + try { + const url = 'sobjects/' + objectName + '/' + recordId; + + await this.request(connection, url, data, RequestMethod.PATCH); + + return { + referenceId, + hasErrors: false, + success: true, + errors: [], + warnings: [], + }; + } catch (err) { + return { + referenceId, + hasErrors: true, + success: false, + errors: err, + warnings: [], + }; } - - public static async update(connection: Connection, data: any[]): Promise> { - // Metadata API only accepts 200 records per request - const chunks = chunk(data, NetUtils.CHUNK_SIZE), - results = new Map(); - - for (let curr of chunks) { - const response = await this.request(connection, 'composite/sobjects', { records: curr }, RequestMethod.PATCH); - - response.forEach(result => { - results.set(result.referenceId || result.id, { - ...result, - hasErrors: Array.isArray(result.errors) && result.errors.length > 0 - }); - }); - } - - return results; + } + + public static async update(connection: Connection, data: any[]): Promise> { + // Metadata API only accepts 200 records per request + const chunks = chunk(data, NetUtils.CHUNK_SIZE), + results = new Map(); + + for (let curr of chunks) { + const response = await this.request( + connection, + 'composite/sobjects', + { records: curr }, + RequestMethod.PATCH + ); + + response.forEach((result) => { + results.set(result.referenceId || result.id, { + ...result, + hasErrors: Array.isArray(result.errors) && result.errors.length > 0, + }); + }); } - public static async delete(connection: Connection, data: string[]): Promise { - // Metadata API only accepts 200 records per request - const chunks = chunk(data, NetUtils.CHUNK_SIZE); + return results; + } - for (let curr of chunks) { - const deleteUrl = 'composite/sobjects?allOrNone=true&ids=' + curr.join(','); + public static async delete(connection: Connection, data: string[]): Promise { + // Metadata API only accepts 200 records per request + const chunks = chunk(data, NetUtils.CHUNK_SIZE); - const response = await this.request(connection, deleteUrl, [], RequestMethod.DELETE); + for (let curr of chunks) { + const deleteUrl = 'composite/sobjects?allOrNone=true&ids=' + curr.join(','); - if (!response.every(r => r.success)) return false; - } + const response = await this.request(connection, deleteUrl, [], RequestMethod.DELETE); - return true; + if (!response.every((r) => r.success)) return false; } - public static async request(connection: Connection, url: string, data: any, method: RequestMethod): Promise { - - const apiVersion = connection.getApiVersion(); - const metadataApiUrl = `/services/data/v${apiVersion}/${url}`; - const request = { - method: method, - url: metadataApiUrl, - body: JSON.stringify(data) - } - - const response = await connection.request(request); - - return response; - } + return true; + } + + public static async request( + connection: Connection, + url: string, + data: any, + method: RequestMethod + ): Promise { + const apiVersion = connection.getApiVersion(); + const metadataApiUrl = `/services/data/v${apiVersion}/${url}`; + const httpMethod: HttpMethods = 'GET'; + const request: HttpRequest = { + method: httpMethod, + url: metadataApiUrl, + body: JSON.stringify(data), + }; + + const response = await connection.request(request); + + return response; + } } enum RequestMethod { - POST = 'post', - GET = 'get', - PATCH = 'patch', - DELETE = 'delete' + POST = 'post', + GET = 'get', + PATCH = 'patch', + DELETE = 'delete', } interface TreeResult { - hasErrors: boolean, - results: UploadRecordResult[] + hasErrors: boolean; + results: UploadRecordResult[]; } -export { NetUtils, RequestMethod, TreeResult } \ No newline at end of file +export { NetUtils, RequestMethod, TreeResult }; diff --git a/src/utils/query/index.ts b/src/utils/query/index.ts index e769b9ff..8c94031b 100644 --- a/src/utils/query/index.ts +++ b/src/utils/query/index.ts @@ -44,14 +44,14 @@ export class QueryTools { const query = QueryTools.buildCustomObjectQuery(namespace, objectName, fields); // Execute the query - let results = await connection.query(query); + let results = await connection.query>(query); if (results && results.totalSize > 0) { allrecords = results.records; // Load more pages while (results.nextRecordsUrl) { - results = await connection.queryMore(results.nextRecordsUrl); + results = await connection.queryMore>(results.nextRecordsUrl); results.records.forEach((row) => { allrecords.push(row); }); @@ -73,14 +73,14 @@ export class QueryTools { const query = QueryTools.buildCustomObjectQuery(namespace, objectName, fields, filters); // Execute the query - let results = await connection.query(query); + let results = await connection.query>(query); if (results && results.totalSize > 0) { allrecords = results.records; // Load more pages while (results.nextRecordsUrl) { - results = await connection.queryMore(results.nextRecordsUrl); + results = await connection.queryMore>(results.nextRecordsUrl); results.records.forEach((row) => { allrecords.push(row); }); @@ -110,14 +110,14 @@ export class QueryTools { } // Execute the query - let results = await connection.query(query); + let results = await connection.query>(query); if (results && results.totalSize > 0) { allrecords = results.records; // Load more pages while (results.nextRecordsUrl) { - results = await connection.queryMore(results.nextRecordsUrl); + results = await connection.queryMore>(results.nextRecordsUrl); results.records.forEach((row) => { allrecords.push(row); }); @@ -154,7 +154,7 @@ export class QueryTools { } // Execute the query - let results = await connection.query(query); + let results = await connection.query>(query); let allrecords = []; if (results && results.totalSize > 0) { @@ -162,7 +162,7 @@ export class QueryTools { // Load more pages while (results.nextRecordsUrl) { - results = await connection.queryMore(results.nextRecordsUrl); + results = await connection.queryMore>(results.nextRecordsUrl); results.records.forEach((row) => { allrecords.push(row); }); @@ -191,14 +191,14 @@ export class QueryTools { } // Execute the query - let results = await connection.query(query); + let results = await connection.query>(query); if (results && results.totalSize > 0) { allrecords = results.records; // Load more pages while (results.nextRecordsUrl) { - results = await connection.queryMore(results.nextRecordsUrl); + results = await connection.queryMore>(results.nextRecordsUrl); results.records.forEach((row) => { allrecords.push(row); }); diff --git a/test/utils/apex/executor/anonymousApexRunner.test.ts b/test/utils/apex/executor/anonymousApexRunner.test.ts index 9b271184..533de236 100644 --- a/test/utils/apex/executor/anonymousApexRunner.test.ts +++ b/test/utils/apex/executor/anonymousApexRunner.test.ts @@ -2,7 +2,7 @@ import { Org } from '@salesforce/core'; import sinon = require('sinon'); import { expect } from '@salesforce/command/lib/test'; -import { Connection, ExecuteAnonymousResult } from 'jsforce'; +import { Connection } from 'jsforce'; import { AnonymousApexRunner } from '../../../../src/utils/apex/executor/AnonymousApexRunner'; describe('AnonymusApexRunner', () => { @@ -30,7 +30,7 @@ describe('AnonymusApexRunner', () => { const executeAnonymousResultStub = sandboxStub .stub() - .returns(Promise.resolve({ success: true } as unknown as ExecuteAnonymousResult)); + .returns(Promise.resolve({ success: true } as unknown as AnonymousApexRunner)); sandboxStub.stub(Org.prototype.getConnection().tooling, 'executeAnonymous').callsFake(executeAnonymousResultStub); const result = await AnonymousApexRunner.run(org, anonymousApex); diff --git a/tsconfig.json b/tsconfig.json index ccbc4d84..4638b3b3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,5 +8,6 @@ "types": ["node", "mocha"], "skipLibCheck": true }, - "include": ["./src/**/*.ts"] + "include": ["./src/**/*.ts"], + "exclude": ["node_modules", "test/**/*.d.ts"] }