diff --git a/CHANGELOG.md b/CHANGELOG.md index 9dcd5dff..2ab0474e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## [1.17.16](https://github.com/salesforcecli/plugin-deploy-retrieve/compare/1.17.15...1.17.16) (2023-09-18) + +### Bug Fixes + +- disallow undefined xml ([#757](https://github.com/salesforcecli/plugin-deploy-retrieve/issues/757)) ([42d9409](https://github.com/salesforcecli/plugin-deploy-retrieve/commit/42d9409a7b46e5e6dd5f466a2e1235d83aabe152)) + +## [1.17.15](https://github.com/salesforcecli/plugin-deploy-retrieve/compare/1.17.14...1.17.15) (2023-09-18) + +### Bug Fixes + +- display deploy ID not validation ID ([#748](https://github.com/salesforcecli/plugin-deploy-retrieve/issues/748)) ([6797b6d](https://github.com/salesforcecli/plugin-deploy-retrieve/commit/6797b6d02cf2c9ab9ccac3b8c1483b4834cfa1b5)) + ## [1.17.14](https://github.com/salesforcecli/plugin-deploy-retrieve/compare/1.17.13...1.17.14) (2023-09-16) ### Bug Fixes diff --git a/package.json b/package.json index eedebb8e..abf0785b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@salesforce/plugin-deploy-retrieve", "description": "deploy and retrieve commands for sf", - "version": "1.17.14", + "version": "1.17.16", "author": "Salesforce", "bugs": "https://github.com/forcedotcom/cli/issues", "dependencies": { @@ -10,7 +10,7 @@ "@salesforce/core": "^5.2.7", "@salesforce/kit": "^3.0.9", "@salesforce/sf-plugins-core": "^3.1.22", - "@salesforce/source-deploy-retrieve": "^9.7.13", + "@salesforce/source-deploy-retrieve": "^9.7.15", "@salesforce/source-tracking": "^4.2.12", "chalk": "^4.1.2", "shelljs": "^0.8.5", diff --git a/src/commands/project/deploy/quick.ts b/src/commands/project/deploy/quick.ts index 9fa690ed..31c33da7 100644 --- a/src/commands/project/deploy/quick.ts +++ b/src/commands/project/deploy/quick.ts @@ -83,34 +83,38 @@ export default class DeployMetadataQuick extends SfCommand { public async run(): Promise { const [{ flags }, cache] = await Promise.all([this.parse(DeployMetadataQuick), DeployCache.create()]); + // This is the ID of the validation request const jobId = cache.resolveLatest(flags['use-most-recent'], flags['job-id'], false); const deployOpts = cache.get(jobId) ?? ({} as DeployOptions); const org = flags['target-org'] ?? (await Org.create({ aliasOrUsername: deployOpts['target-org'] })); const api = await resolveApi(this.configAggregator); - await org.getConnection(flags['api-version']).metadata.deployRecentValidation({ id: jobId, rest: api === 'REST' }); - this.log(`Deploy ID: ${bold(jobId)}`); + // This is the ID of the deploy (of the validated metadata) + const deployId = await org + .getConnection(flags['api-version']) + .metadata.deployRecentValidation({ id: jobId, rest: api === 'REST' }); + this.log(`Deploy ID: ${bold(deployId)}`); if (flags.async) { - const asyncFormatter = new AsyncDeployResultFormatter(jobId, this.config.bin); + const asyncFormatter = new AsyncDeployResultFormatter(deployId, this.config.bin); if (!this.jsonEnabled()) asyncFormatter.display(); return asyncFormatter.getJson(); } - const result = await poll(org, jobId, flags.wait); + const result = await poll(org, deployId, flags.wait); const formatter = new DeployResultFormatter(result, flags); if (!this.jsonEnabled()) formatter.display(); - await DeployCache.update(jobId, { status: result.response.status }); + await DeployCache.update(deployId, { status: result.response.status }); process.exitCode = determineExitCode(result); if (result.response.status === RequestStatus.Succeeded) { this.log(); - this.logSuccess(messages.getMessage('info.QuickDeploySuccess', [jobId])); + this.logSuccess(messages.getMessage('info.QuickDeploySuccess', [deployId])); } else { - this.log(messages.getMessage('error.QuickDeployFailure', [jobId, result.response.status])); + this.log(messages.getMessage('error.QuickDeployFailure', [deployId, result.response.status])); } return formatter.getJson(); diff --git a/src/utils/previewOutput.ts b/src/utils/previewOutput.ts index 62e088cc..158957e0 100644 --- a/src/utils/previewOutput.ts +++ b/src/utils/previewOutput.ts @@ -21,7 +21,7 @@ import { import { filePathsFromMetadataComponent } from '@salesforce/source-deploy-retrieve/lib/src/utils/filePathGenerator'; import { SourceTracking } from '@salesforce/source-tracking'; -import { isSourceComponent } from './types'; +import { isSourceComponentWithXml } from './types'; Messages.importMessagesDirectory(__dirname); const messages = Messages.loadMessages('@salesforce/plugin-deploy-retrieve', 'previewMessages'); @@ -64,7 +64,7 @@ const resolvePaths = (filenames: string[]): Array ({ fullName: sc.fullName, type: sc.type.name, path: ensureAbsolutePath(sc.xml) })); // dedupe by xml path return Array.from(new Map(sourceComponents.map((sc) => [sc.path, sc])).values()); diff --git a/src/utils/types.ts b/src/utils/types.ts index 4dfec7cc..0e316ad6 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -101,8 +101,19 @@ export type Formatter = { }; /** validates source component with fullname, type, and xml props */ -export const isSourceComponent = (sc: unknown): sc is SourceComponent & { xml: string } => - isObject(sc) && 'fullName' in sc && 'type' in sc && 'xml' in sc; +export const isSourceComponent = (sc: unknown): sc is SourceComponent => + isObject(sc) && + 'type' in sc && + typeof sc.type === 'object' && + sc.type !== null && + 'name' in sc.type && + typeof sc.type.name === 'string' && + 'fullName' in sc && + // (typeof sc.fullName === 'string' || typeof sc.fullName === 'function'); + typeof sc.fullName === 'string'; + +export const isSourceComponentWithXml = (sc: unknown): sc is SourceComponent & { xml: string } => + isSourceComponent(sc) && 'xml' in sc && typeof sc.xml === 'string'; export const isSdrFailure = (fileResponse: FileResponse): fileResponse is FileResponseFailure => fileResponse.state === ComponentStatus.Failed; diff --git a/test/utils/types.test.ts b/test/utils/types.test.ts new file mode 100644 index 00000000..19fa572f --- /dev/null +++ b/test/utils/types.test.ts @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2023, salesforce.com, inc. + * All rights reserved. + * Licensed under the BSD 3-Clause license. + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ + +import { expect, config } from 'chai'; +import { SourceComponent, RegistryAccess } from '@salesforce/source-deploy-retrieve'; +import { isSourceComponent, isSourceComponentWithXml } from '../../src/utils/types'; + +config.truncateThreshold = 0; + +const reg = new RegistryAccess(); +const type = reg.getTypeByName('ApexClass'); + +describe('isSourceComponent (type guard)', () => { + describe('good', () => { + it('full, correct definition', () => { + expect({ fullName: 'foo', type, xml: 'fooXml', content: 'fooContent' }).to.satisfy(isSourceComponent); + }); + it('SC constructed with xml', () => { + expect(new SourceComponent({ name: 'foo', type, xml: 'classes/foo.cls' })).to.satisfy(isSourceComponent); + }); + it('SC constructed with no xml', () => { + const sc = new SourceComponent({ name: 'foo', type }); + // console.log(sc); + // console.log(typeof sc.fullName); + expect(sc).to.satisfy(isSourceComponent); + }); + }); + describe('bad', () => { + it('object is undefined', () => { + expect(undefined).to.not.satisfy(isSourceComponent); + }); + it('empty object', () => { + expect({}).to.not.satisfy(isSourceComponent); + }); + + it('object.type is set to undefined', () => { + expect({ fullName: 'foo', type: undefined, xml: 'fooXml' }).to.not.satisfy(isSourceComponent); + }); + }); +}); + +describe('isSourceComponentWithXml (type guard)', () => { + describe('good', () => { + it('full, correct definition', () => { + expect({ fullName: 'foo', type, xml: 'fooXml', content: 'fooContent' }).to.satisfy(isSourceComponentWithXml); + }); + it('SC constructed with xml', () => { + expect(new SourceComponent({ name: 'foo', type, xml: 'classes/foo.cls' })).to.satisfy(isSourceComponentWithXml); + }); + }); + describe('bad', () => { + it('object is undefined', () => { + expect(undefined).to.not.satisfy(isSourceComponentWithXml); + }); + it('empty object', () => { + expect({}).to.not.satisfy(isSourceComponentWithXml); + }); + it('object.xml is undefined', () => { + expect({ fullName: 'foo', type: 'fooType', content: 'fooContent' }).to.not.satisfy(isSourceComponentWithXml); + }); + it('object.type is set to undefined', () => { + expect({ fullName: 'foo', type: undefined, xml: 'fooXml' }).to.not.satisfy(isSourceComponentWithXml); + }); + it('SC constructed with no xml', () => { + expect(new SourceComponent({ name: 'foo', type })).to.not.satisfy(isSourceComponentWithXml); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index 8991e225..1bafaf80 100644 --- a/yarn.lock +++ b/yarn.lock @@ -956,15 +956,15 @@ strip-ansi "6.0.1" ts-retry-promise "^0.7.0" -"@salesforce/core@^5.2.0", "@salesforce/core@^5.2.6", "@salesforce/core@^5.2.7": - version "5.2.9" - resolved "https://registry.yarnpkg.com/@salesforce/core/-/core-5.2.9.tgz#812478061d766cdff28f7a0e8abefc9de562465a" - integrity sha512-ZWNxHnCPGT1pcJe1bjeRjd8VAeHELK4fftt/2WO+ZsPFHZnzmdowz2Th407v04et+uIzL0Z6+qOaRY/bZr5tLA== +"@salesforce/core@^5.2.0", "@salesforce/core@^5.2.6", "@salesforce/core@^5.2.7", "@salesforce/core@^5.2.9": + version "5.2.10" + resolved "https://registry.yarnpkg.com/@salesforce/core/-/core-5.2.10.tgz#7f4824ddcef108b6ccec24434260c620de39012e" + integrity sha512-Xj1QRajmHWgl0ahivjKFGKJlGXwe9yFOZ3PwF91qEupGbO74XrCJ8OUM7EVlk53LKy9LlPZQFuy2ATX9MyEDKA== dependencies: "@salesforce/kit" "^3.0.11" "@salesforce/schemas" "^1.6.0" "@salesforce/ts-types" "^2.0.7" - "@types/semver" "^7.5.1" + "@types/semver" "^7.5.2" ajv "^8.12.0" change-case "^4.1.2" faye "^1.4.0" @@ -1121,12 +1121,12 @@ chalk "^4" inquirer "^8.2.5" -"@salesforce/source-deploy-retrieve@^9.7.13", "@salesforce/source-deploy-retrieve@^9.7.2", "@salesforce/source-deploy-retrieve@^9.7.8": - version "9.7.13" - resolved "https://registry.yarnpkg.com/@salesforce/source-deploy-retrieve/-/source-deploy-retrieve-9.7.13.tgz#85777222e157a621eb00fcef0487c682b7e3d5f7" - integrity sha512-QRhdepll3+ED9w4clI/RvcSLtuSbTzFPZenzrvJ2R7Pg5gXRZQIJrphEkAnY7oteVBBUxPVdYgbeuQxeoi98dg== +"@salesforce/source-deploy-retrieve@^9.7.15", "@salesforce/source-deploy-retrieve@^9.7.2", "@salesforce/source-deploy-retrieve@^9.7.8": + version "9.7.15" + resolved "https://registry.yarnpkg.com/@salesforce/source-deploy-retrieve/-/source-deploy-retrieve-9.7.15.tgz#a891e95101816a7e33854bc42ba21fafbdb1728e" + integrity sha512-av9Ojd9B/uegLNJth41aVPvAVPh8N0CllzfiDG5qbtDXW7WV/bqgWjiiiGc1U6RWnJ9OmQZNL/EjaSX0UPhiLA== dependencies: - "@salesforce/core" "^5.2.7" + "@salesforce/core" "^5.2.9" "@salesforce/kit" "^3.0.11" "@salesforce/ts-types" "^2.0.7" fast-levenshtein "^3.0.0" @@ -1558,7 +1558,7 @@ dependencies: "@types/node" "*" -"@types/semver@^7.3.12", "@types/semver@^7.5.1": +"@types/semver@^7.3.12", "@types/semver@^7.5.2": version "7.5.2" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.2.tgz#31f6eec1ed7ec23f4f05608d3a2d381df041f564" integrity sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw==