Skip to content

Commit

Permalink
fix: change mdcoverage url for getCurrentApiVersion (#1191)
Browse files Browse the repository at this point in the history
* fix: change mdcoverage url for getCurrentApiVersion

* fix: getObject() no longer throws

* fix: use the services/data endpoint for api versions
  • Loading branch information
shetzel authored Dec 15, 2023
1 parent b914863 commit ff82bb9
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 10 deletions.
4 changes: 4 additions & 0 deletions messages/sdr.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ A StaticResource must have an associated .resource file, missing %s.resource-met

The %s operation is missing a job ID. Initialize an operation with an ID, or start a new job.

# missingApiVersion

Could not determine an API version to use for the generated manifest. Tried looking for sourceApiVersion in sfdx-project.json, apiVersion from config vars, and the highest apiVersion from the APEX REST endpoint. Using API version 58.0 as a last resort.

# invalid_xml_parsing

error parsing %s due to:\n message: %s\n line: %s\n code: %s
Expand Down
59 changes: 57 additions & 2 deletions src/collections/componentSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,16 @@
*/
/* eslint @typescript-eslint/unified-signatures:0 */
import { XMLBuilder } from 'fast-xml-parser';
import { AuthInfo, Connection, Logger, Messages, SfError } from '@salesforce/core';
import {
AuthInfo,
ConfigAggregator,
Connection,
Logger,
Messages,
OrgConfigProperties,
SfError,
SfProject,
} from '@salesforce/core';
import { isString } from '@salesforce/ts-types';
import {
MetadataApiDeploy,
Expand Down Expand Up @@ -369,7 +378,7 @@ export class ComponentSet extends LazyCollection<MetadataComponent> {
* @returns Object representation of a package manifest
*/
public async getObject(destructiveType?: DestructiveChangesType): Promise<PackageManifestObject> {
const version = this.sourceApiVersion ?? this.apiVersion ?? `${await getCurrentApiVersion()}.0`;
const version = await this.getApiVersion();

// If this ComponentSet has components marked for delete, we need to
// only include those components in a destructiveChanges.xml and
Expand Down Expand Up @@ -662,6 +671,52 @@ export class ComponentSet extends LazyCollection<MetadataComponent> {
}
return destructiveChangesTypes;
}

/**
* Returns an API version to use as the value of the `version` field
* in a manifest (package.xml) for MDAPI calls in the following order
* of preference:
*
* 1. this.sourceApiVersion
* 2. this.apiVersion
* 3. sourceApiVersion set in sfdx-project.json
* 4. apiVersion from ConfigAggregator (config files and Env Vars)
* 5. http call to apexrest endpoint for highest apiVersion
* 6. hardcoded value of "58.0" as a last resort
*
* @returns string The resolved API version to use in a manifest
*/
private async getApiVersion(): Promise<string> {
let version = this.sourceApiVersion ?? this.apiVersion;

if (!version) {
try {
const project = await SfProject.resolve();
const projectConfig = await project.resolveProjectConfig();
version = projectConfig?.sourceApiVersion as string;
} catch (e) {
// If there's any problem just move on to ConfigAggregator
}
}

if (!version) {
try {
version = ConfigAggregator.getValue(OrgConfigProperties.ORG_API_VERSION).value as string;
} catch (e) {
// If there's any problem just move on to the REST endpoint
}
}

if (!version) {
try {
version = `${await getCurrentApiVersion()}.0`;
} catch (e) {
version = '58.0';
this.logger.warn(messages.getMessage('missingApiVersion'));
}
}
return version;
}
}

const sourceKey = (component: SourceComponent): string => {
Expand Down
17 changes: 11 additions & 6 deletions src/registry/coverage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,17 @@ const getProxiedOptions = (url: string): OptionsOfTextResponseBody => ({
url,
});

export const getCurrentApiVersion = async (): Promise<number> =>
(
await got(getProxiedOptions('https://mdcoverage.secure.force.com/services/apexrest/report')).json<{
versions: { selected: number };
}>()
).versions.selected;
type ApiVersion = {
label: string;
url: string;
version: string;
};

export const getCurrentApiVersion = async (): Promise<number> => {
const apiVersionsUrl = 'https://dx-extended-coverage.my.salesforce-sites.com/services/data';
const lastVersionEntry = (await got(getProxiedOptions(apiVersionsUrl)).json<ApiVersion[]>()).pop() as ApiVersion;
return +lastVersionEntry.version;
};

export const getCoverage = async (apiVersion: number): Promise<CoverageObject> => {
const results = await Promise.allSettled(
Expand Down
98 changes: 96 additions & 2 deletions test/collections/componentSet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { join } from 'node:path';
import { MockTestOrgData, TestContext } from '@salesforce/core/lib/testSetup';
import { assert, expect } from 'chai';
import { SinonStub } from 'sinon';
import { AuthInfo, ConfigAggregator, Connection, Lifecycle, Messages } from '@salesforce/core';
import { AuthInfo, ConfigAggregator, Connection, Lifecycle, Messages, SfProject } from '@salesforce/core';
import {
ComponentSet,
ComponentSetBuilder,
Expand Down Expand Up @@ -676,8 +676,18 @@ describe('ComponentSet', () => {
});

describe('getObject', () => {
const sfOrgApiVersion = process.env.SF_ORG_API_VERSION;
let getCurrentApiVersionStub: SinonStub;

beforeEach(() => {
$$.SANDBOX.stub(coverage, 'getCurrentApiVersion').resolves(testApiVersion);
getCurrentApiVersionStub = $$.SANDBOX.stub(coverage, 'getCurrentApiVersion').resolves(testApiVersion);
});

afterEach(() => {
process.env.SF_ORG_API_VERSION = sfOrgApiVersion;
if (!sfOrgApiVersion) {
delete process.env.SF_ORG_API_VERSION;
}
});

it('should return an object representing the package manifest', async () => {
Expand All @@ -701,9 +711,11 @@ describe('ComponentSet', () => {
version: testApiVersionAsString,
},
});
expect(getCurrentApiVersionStub.calledOnce).to.be.true;
});

it('should allow the componentSet to set the apiVersion', async () => {
const resolveSpy = $$.SANDBOX.spy(SfProject.prototype, 'resolveProjectConfig');
const set = ComponentSet.fromSource({
fsPaths: ['.'],
registry: registryAccess,
Expand All @@ -725,6 +737,87 @@ describe('ComponentSet', () => {
version: testApiVersionAsString,
},
});
expect(resolveSpy.called).to.be.false;
expect(getCurrentApiVersionStub.called).to.be.false;
});

it('should get an API version from sfdx-project.json', async () => {
const sourceApiVersion = '58.0';
const set = ComponentSet.fromSource({
fsPaths: ['.'],
registry: registryAccess,
tree: manifestFiles.TREE,
});
const resolveStub = $$.SANDBOX.stub(SfProject.prototype, 'resolveProjectConfig').resolves({ sourceApiVersion });
expect(await set.getObject()).to.deep.equal({
Package: {
types: [
{
name: registry.types.customobjecttranslation.name,
members: ['a'],
},
{
name: registry.types.staticresource.name,
members: ['b', 'c'],
},
],
version: sourceApiVersion,
},
});
expect(resolveStub.calledOnce).to.be.true;
expect(getCurrentApiVersionStub.called).to.be.false;
});

it('should get an API version from env var', async () => {
const set = ComponentSet.fromSource({
fsPaths: ['.'],
registry: registryAccess,
tree: manifestFiles.TREE,
});
process.env.SF_ORG_API_VERSION = testApiVersionAsString;
expect(await set.getObject()).to.deep.equal({
Package: {
types: [
{
name: registry.types.customobjecttranslation.name,
members: ['a'],
},
{
name: registry.types.staticresource.name,
members: ['b', 'c'],
},
],
version: testApiVersionAsString,
},
});
expect(getCurrentApiVersionStub.called).to.be.false;
});

it('should default to hardcoded API version 58.0 as a last resort', async () => {
getCurrentApiVersionStub.reset();
const causeErr = new Error('HTTP 404 - mdcoverage url is down');
getCurrentApiVersionStub.throws(causeErr);
const set = ComponentSet.fromSource({
fsPaths: ['.'],
registry: registryAccess,
tree: manifestFiles.TREE,
});
expect(await set.getObject()).to.deep.equal({
Package: {
types: [
{
name: registry.types.customobjecttranslation.name,
members: ['a'],
},
{
name: registry.types.staticresource.name,
members: ['b', 'c'],
},
],
version: '58.0',
},
});
expect(getCurrentApiVersionStub.called).to.be.true;
});

it('should return an object representing destructive changes manifest', async () => {
Expand Down Expand Up @@ -798,6 +891,7 @@ describe('ComponentSet', () => {
version: set.sourceApiVersion,
},
});
expect(getCurrentApiVersionStub.called).to.be.false;
});

it('should interpret folder components as members of the type they are a container for', async () => {
Expand Down

0 comments on commit ff82bb9

Please sign in to comment.