@salesforce/core
Version:
Core libraries to interact with SFDX projects, orgs, and APIs.
226 lines • 12 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getScratchOrgInfoPayload = exports.generateScratchOrgInfo = exports.getAncestorIds = void 0;
/*
* Copyright (c) 2021, 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
*/
// Node
const fs_1 = require("fs");
// @salesforce
const kit_1 = require("@salesforce/kit");
const ts_types_1 = require("@salesforce/ts-types");
const sfdc_1 = require("./util/sfdc");
const sfdxProject_1 = require("./sfdxProject");
const webOAuthServer_1 = require("./webOAuthServer");
const messages_1 = require("./messages");
const sfdxError_1 = require("./sfdxError");
const scratchOrgFeatureDeprecation_1 = require("./scratchOrgFeatureDeprecation");
const defaultConnectedAppInfo = {
clientId: 'PlatformCLI',
legacyClientId: 'SalesforceDevelopmentExperience',
legacyClientSecret: '1384510088588713504',
};
messages_1.Messages.importMessagesDirectory(__dirname);
const messages = messages_1.Messages.loadMessages('@salesforce/core', 'scratchOrgInfoGenerator');
const SNAPSHOT_UNSUPPORTED_OPTIONS = [
'features',
'orgPreferences',
'edition',
'sourceOrg',
'settingsPath',
'releaseVersion',
'language',
];
// A validator function to ensure any options parameters entered by the user adhere
// to a allowlist of valid option settings. Because org:create allows options to be
// input either key=value pairs or within the definition file, this validator is
// executed within the ctor and also after parsing/normalization of the definition file.
const optionsValidator = (key, scratchOrgInfoPayload) => {
if (key.toLowerCase() === 'durationdays') {
throw new sfdxError_1.SfdxError('unrecognizedScratchOrgOption', 'durationDays');
}
if (key.toLowerCase() === 'snapshot') {
const foundInvalidFields = SNAPSHOT_UNSUPPORTED_OPTIONS.filter((invalidField) => invalidField in scratchOrgInfoPayload);
if (foundInvalidFields.length > 0) {
throw new sfdxError_1.SfdxError(messages.getMessage('unsupportedSnapshotOrgCreateOptions', [foundInvalidFields.join(', ')]), 'orgSnapshot');
}
}
};
/**
* Generates the package2AncestorIds scratch org property
*
* @param scratchOrgInfo - the scratchOrgInfo passed in by the user
* @param projectJson - sfdxProjectJson
* @param hubOrg - the hub org, in case we need to do queries
*/
const getAncestorIds = async (scratchOrgInfo, projectJson, hubOrg) => {
if (Reflect.has(scratchOrgInfo, 'package2AncestorIds')) {
throw new sfdxError_1.SfdxError(messages.getMessage('errorpackage2AncestorIdsKeyNotSupported'), 'DeprecationError');
}
const packagesWithAncestors = (await projectJson.getPackageDirectories())
// check that the package has any ancestor types (id or version)
.filter((packageDir) => packageDir.ancestorId || packageDir.ancestorVersion);
if (packagesWithAncestors.length === 0) {
return '';
}
const ancestorIds = await Promise.all(packagesWithAncestors.map(async (packageDir) => {
var _a, _b, _c;
// ancestorID can be 05i, or 04t, alias; OR "ancestorVersion": "4.6.0.1"
// according to docs, 05i is not ok: https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev2gp_config_file.htm
if (packageDir.ancestorVersion) {
if (!/^[0-9]+.[0-9]+.[0-9]+(.[0-9]+)?$/.test(packageDir.ancestorVersion)) {
throw sfdxError_1.SfdxError.create('@salesforce/core', 'scratchOrgInfoGenerator', 'errorInvalidAncestorVersionFormat', [
packageDir.ancestorVersion,
]);
}
// package can be an ID, but not according to docs
const packageAliases = projectJson.get('packageAliases');
const packageId = (_a = packageAliases[ts_types_1.ensureString(packageDir.package)]) !== null && _a !== void 0 ? _a : packageDir.package;
const [major, minor, patch] = packageDir.ancestorVersion.split('.');
let releasedAncestor;
try {
releasedAncestor = await hubOrg
.getConnection()
.singleRecordQuery(`SELECT Id, IsReleased FROM Package2Version WHERE Package2Id = '${packageId}' AND MajorVersion = ${major} AND MinorVersion = ${minor} AND PatchVersion = ${patch} and IsReleased = true`, { tooling: true });
}
catch (err) {
throw new sfdxError_1.SfdxError(messages.getMessage('errorNoMatchingAncestor', [packageDir.ancestorVersion, packageDir.package]), 'ErrorNoMatchingAncestor', [messages.getMessage('errorAncestorNotReleased', [packageDir.ancestorVersion])]);
}
if (packageDir.ancestorId && packageDir.ancestorId !== releasedAncestor.Id) {
throw sfdxError_1.SfdxError.create('@salesforce/core', 'scratchOrgInfogenerator', 'ErrorAncestorIdVersionMismatch', [
packageDir.ancestorVersion,
packageDir.ancestorId,
]);
}
return releasedAncestor.Id;
}
if ((_b = packageDir === null || packageDir === void 0 ? void 0 : packageDir.ancestorId) === null || _b === void 0 ? void 0 : _b.startsWith('05i')) {
// if it's already a 05i return it, otherwise query for it
return packageDir.ancestorId;
}
if ((_c = packageDir === null || packageDir === void 0 ? void 0 : packageDir.ancestorId) === null || _c === void 0 ? void 0 : _c.startsWith('04t')) {
// query for the Id
return (await hubOrg
.getConnection()
.singleRecordQuery(`SELECT Id FROM Package2Version WHERE SubscriberPackageVersionId = '${packageDir.ancestorId}'`, { tooling: true })).Id;
}
// ancestorID can be an alias get it from projectJson
const packageAliases = projectJson.get('packageAliases');
if (packageDir.ancestorId && (packageAliases === null || packageAliases === void 0 ? void 0 : packageAliases[packageDir.ancestorId])) {
return packageAliases[packageDir.ancestorId];
}
throw new sfdxError_1.SfdxError(`Invalid ancestorId ${packageDir.ancestorId}`, 'InvalidAncestorId');
}));
return Array.from(new Set(ancestorIds)).join(';');
};
exports.getAncestorIds = getAncestorIds;
/**
* Takes in a scratchOrgInfo and fills in the missing fields
*
* @param hubOrg the environment hub org
* @param scratchOrgInfoPayload - the scratchOrgInfo passed in by the user
* @param nonamespace create the scratch org with no namespace
* @param ignoreAncestorIds true if the sfdx-project.json ancestorId keys should be ignored
*/
const generateScratchOrgInfo = async ({ hubOrg, scratchOrgInfoPayload, nonamespace, ignoreAncestorIds, }) => {
var _a, _b;
let sfdxProject;
try {
sfdxProject = await sfdxProject_1.SfdxProjectJson.create({});
}
catch (e) {
// project is not required
}
scratchOrgInfoPayload.orgName = (_a = scratchOrgInfoPayload.orgName) !== null && _a !== void 0 ? _a : 'Company';
scratchOrgInfoPayload.package2AncestorIds =
!ignoreAncestorIds && (sfdxProject === null || sfdxProject === void 0 ? void 0 : sfdxProject.hasPackages())
? await exports.getAncestorIds(scratchOrgInfoPayload, sfdxProject, hubOrg)
: '';
// Use the Hub org's client ID value, if one wasn't provided to us, or the default
if (!scratchOrgInfoPayload.connectedAppConsumerKey) {
scratchOrgInfoPayload.connectedAppConsumerKey =
(_b = hubOrg.getConnection().getAuthInfoFields().clientId) !== null && _b !== void 0 ? _b : defaultConnectedAppInfo.clientId;
}
if (!nonamespace && (sfdxProject === null || sfdxProject === void 0 ? void 0 : sfdxProject.get('namespace'))) {
scratchOrgInfoPayload.namespace = sfdxProject.get('namespace');
}
// we already have the info, and want to get rid of configApi, so this doesn't use that
scratchOrgInfoPayload.connectedAppCallbackUrl = `http://localhost:${await webOAuthServer_1.WebOAuthServer.determineOauthPort()}/OauthRedirect`;
return scratchOrgInfoPayload;
};
exports.generateScratchOrgInfo = generateScratchOrgInfo;
/**
* Returns a valid signup json
*
* @param definitionjson org definition in JSON format
* @param definitionfile path to an org definition file
* @param connectedAppConsumerKey The connected app consumer key. May be null for JWT OAuth flow.
* @param durationdays duration of the scratch org (in days) (default:1, min:1, max:30)
* @param nonamespace create the scratch org with no namespace
* @param noancestors do not include second-generation package ancestors in the scratch org
* @param orgConfig overrides definitionjson
* @returns scratchOrgInfoPayload: ScratchOrgInfoPayload;
ignoreAncestorIds: boolean;
warnings: string[];
*/
const getScratchOrgInfoPayload = async (options) => {
var _a;
let warnings = [];
// orgConfig input overrides definitionjson (-j option; hidden/deprecated)
const definitionJson = options.definitionjson ? JSON.parse(options.definitionjson) : {};
const orgConfigInput = { ...definitionJson, ...((_a = options.orgConfig) !== null && _a !== void 0 ? _a : {}) };
let scratchOrgInfoPayload = orgConfigInput;
// the -f option
if (options.definitionfile) {
try {
const fileData = await fs_1.promises.readFile(options.definitionfile, 'utf8');
const defFileContents = kit_1.parseJson(fileData);
// definitionjson and orgConfig override file input
scratchOrgInfoPayload = { ...defFileContents, ...orgConfigInput };
}
catch (err) {
const error = err;
if (error.name === 'JsonParseError') {
throw new sfdxError_1.SfdxError(`An error occurred parsing ${options.definitionfile}`);
}
throw sfdxError_1.SfdxError.wrap(error);
}
}
// scratchOrgInfoPayload must be heads down camelcase.
const upperCaseKey = sfdc_1.sfdc.findUpperCaseKeys(scratchOrgInfoPayload);
if (upperCaseKey) {
throw new sfdxError_1.SfdxError('InvalidJsonCasing', upperCaseKey);
}
// Now run the fully resolved user input against the validator
Object.keys(scratchOrgInfoPayload).forEach((key) => {
optionsValidator(key, scratchOrgInfoPayload);
});
if (options.connectedAppConsumerKey) {
scratchOrgInfoPayload.connectedAppConsumerKey = options.connectedAppConsumerKey;
}
scratchOrgInfoPayload.durationDays = options.durationDays;
// Throw warnings for deprecated scratch org features.
const scratchOrgFeatureDeprecation = new scratchOrgFeatureDeprecation_1.ScratchOrgFeatureDeprecation();
// convert various supported array and string formats to a semi-colon-delimited string
if (scratchOrgInfoPayload.features) {
if (typeof scratchOrgInfoPayload.features === 'string') {
scratchOrgInfoPayload.features = scratchOrgInfoPayload.features.split(/[;,]/);
}
warnings = scratchOrgFeatureDeprecation.getFeatureWarnings(scratchOrgInfoPayload.features);
scratchOrgInfoPayload.features = scratchOrgInfoPayload.features.map((feature) => feature.trim());
scratchOrgInfoPayload.features = scratchOrgFeatureDeprecation
.filterDeprecatedFeatures(scratchOrgInfoPayload.features)
.join(';');
}
return {
scratchOrgInfoPayload,
// Ignore ancestor ids only when 'nonamespace' or 'noancestors' options are specified
ignoreAncestorIds: options.nonamespace || options.noancestors || false,
warnings,
};
};
exports.getScratchOrgInfoPayload = getScratchOrgInfoPayload;
//# sourceMappingURL=scratchOrgInfoGenerator.js.map