salesforce-alm
Version:
This package contains tools, and APIs, for an improved salesforce.com developer experience.
200 lines (198 loc) • 10 kB
JavaScript
"use strict";
/*
* Copyright (c) 2020, 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
*/
Object.defineProperty(exports, "__esModule", { value: true });
// Local
const core_1 = require("@salesforce/core");
const almError = require("../core/almError");
const messages = require("../messages");
const srcDevUtil = require("../core/srcDevUtil");
const logApi = require("../core/logApi");
const remoteSourceTrackingService_1 = require("../source/remoteSourceTrackingService");
const scratchOrgInfoApi_1 = require("./scratchOrgInfoApi");
const SettingsGenerator = require("./scratchOrgSettingsGenerator");
const scratchOrgInfoGenerator_1 = require("./scratchOrgInfoGenerator");
const scratchOrgFeatureDeprecation_1 = require("./scratchOrgFeatureDeprecation");
// 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, value, scratchOrgInfoPayload) => {
if (key.toLowerCase() === 'durationdays') {
throw almError('unrecognizedScratchOrgOption', 'durationDays');
}
if (key.toLowerCase() === 'snapshot') {
const foundInvalidFields = [];
ScratchOrgCreateCommand.SNAPSHOT_UNSUPPORTED_OPTIONS.forEach((invalidField) => {
if (scratchOrgInfoPayload.hasOwnProperty(invalidField)) {
foundInvalidFields.push(invalidField);
}
});
if (foundInvalidFields.length > 0) {
const msg = messages().getMessage('unsupportedSnapshotOrgCreateOptions', [foundInvalidFields.join(', ')], 'orgSnapshot');
throw new Error(msg);
}
}
};
/**
* constructs a create command helper
*
* @param force - the force api
* @constructor
*/
class ScratchOrgCreateCommand {
constructor(hubOrg, flags, varargs, configAggregator) {
this.hubOrg = hubOrg;
this.flags = flags;
this.varargs = varargs;
this.configAggregator = configAggregator;
this.settingsGenerator = new SettingsGenerator();
}
/**
* executes the command. this is a protocol style function intended to be represented by other commands.
*
* @param cliContext - the cli context
* @param stdinValues - param values obtained from stdin
* @returns {Promise}
*/
async execute(clientSecret) {
var _a, _b;
this.logger = await core_1.Logger.child('scratchOrgCreateCommand');
this.logger.debug('scratchOrgCreateCommand: execute');
this.scratchOrgInfo = await this.getScratchOrgInfo();
// gets the scratch org settings (will use in both signup paths AND to deploy the settings)
await this.settingsGenerator.extract(this.scratchOrgInfo);
this.logger.debug(`the scratch org def file has settings: ${this.settingsGenerator.hasSettings()}`);
// creates the scratch org info in the devhub
const scratchOrgInfoRequestResult = await scratchOrgInfoApi_1.requestScratchOrgCreation(this.hubOrg, this.scratchOrgInfo, this.settingsGenerator);
if (scratchOrgInfoRequestResult.success === true) {
this.scratchOrgInfoId = scratchOrgInfoRequestResult.id;
this.logger.debug(`scratch org has recordId ${this.scratchOrgInfoId}`);
}
const scratchOrgInfoResult = await scratchOrgInfoApi_1.pollForScratchOrgInfo(this.hubOrg, this.scratchOrgInfoId, this.flags.wait);
let signupTargetLoginUrlConfig;
try {
const project = await core_1.SfdxProject.resolve();
const projectJson = await project.resolveProjectConfig();
signupTargetLoginUrlConfig = projectJson.signupTargetLoginUrl;
}
catch {
// a project isn't required for org:create
}
const scratchOrgAuthInfo = await scratchOrgInfoApi_1.authorizeScratchOrg({
scratchOrgInfoComplete: scratchOrgInfoResult,
hubOrg: this.hubOrg,
clientSecret,
setAsDefault: this.flags.setdefaultusername,
alias: this.flags.setalias,
signupTargetLoginUrlConfig,
retry: this.flags.retry || 0,
});
// we'll need this scratch org connection later;
this.scratchOrg = await core_1.Org.create({ connection: await core_1.Connection.create({ authInfo: scratchOrgAuthInfo }) });
const orgData = await scratchOrgInfoApi_1.deploySettingsAndResolveUrl(scratchOrgAuthInfo, (_b = (_a = this.flags.apiversion) !== null && _a !== void 0 ? _a : this.configAggregator.getPropertyValue('apiVersion')) !== null && _b !== void 0 ? _b : (await this.scratchOrg.retrieveMaxApiVersion()), this.settingsGenerator);
this.logger.trace('Settings deployed to org');
/** updating the revision num to zero during org:creation if source members are created during org:create.This only happens for some specific scratch org definition file.*/
await this.updateRevisionCounterToZero();
// initialize the maxRevision.json file.
try {
await remoteSourceTrackingService_1.RemoteSourceTrackingService.getInstance({ username: this.scratchOrg.getUsername() });
}
catch (err) {
// Do nothing. If org:create is not executed within sfdx project, allow the org to be created without errors.
this.logger.debug(`Failed to create the maxRevision.json file due to the error : ${err.message}`);
}
// emit postorgcreate event for hook
const postOrgCreateHookInfo = [orgData]
.map((result) => result.getFields())
.map((element) => ({
accessToken: element.accessToken,
clientId: element.clientId,
created: element.created,
createdOrgInstance: element.createdOrgInstance,
devHubUsername: element.devHubUsername,
expirationDate: element.expirationDate,
instanceUrl: element.instanceUrl,
loginUrl: element.loginUrl,
orgId: element.orgId,
username: element.username,
}))[0];
await core_1.Lifecycle.getInstance().emit('postorgcreate', postOrgCreateHookInfo);
return { orgId: orgData.getFields().orgId, username: this.scratchOrg.getUsername() };
}
// Returns a valid signup json object
async getScratchOrgInfo() {
// Varargs input overrides definitionjson (-j option; hidden/deprecated)
const definitionJson = this.flags.definitionjson ? JSON.parse(this.flags.definitionjson) : {};
const orgConfigInput = { ...definitionJson, ...(this.varargs || {}) };
let scratchOrgInfoPayload = orgConfigInput;
// the -f option
if (this.flags.definitionfile) {
try {
const defFileContents = (await core_1.fs.readJson(this.flags.definitionfile));
// definitionjson and varargs override file input
scratchOrgInfoPayload = { ...defFileContents, ...orgConfigInput };
}
catch (err) {
const thrownErr = srcDevUtil.processReadAndParseJsonFileError(err, this.flags.definitionfile);
throw thrownErr;
}
}
// scratchOrgInfoPayload must be heads down camelcase.
const upperCaseKey = core_1.sfdc.findUpperCaseKeys(scratchOrgInfoPayload);
if (upperCaseKey) {
throw almError('InvalidJsonCasing', [upperCaseKey, JSON.stringify(scratchOrgInfoPayload, null, 4)]);
}
// Now run the fully resolved user input against the validator
Object.entries(scratchOrgInfoPayload).forEach(([key, value]) => {
optionsValidator(key, value, scratchOrgInfoPayload);
});
// the -i option
if (this.flags.clientid) {
scratchOrgInfoPayload.connectedAppConsumerKey = this.flags.clientid;
}
// the -d option
scratchOrgInfoPayload.durationDays = this.flags.durationdays;
// Ignore ancestor ids only when 'nonamespace' or 'noancestors' options are specified
const ignoreAncestorIds = this.flags.nonamespace || this.flags.noancestors || false;
// Throw warnings for deprecated scratch org features.
const scratchOrgFeatureDeprecation = new scratchOrgFeatureDeprecation_1.ScratchOrgFeatureDeprecation();
scratchOrgFeatureDeprecation.getFeatureWarnings(scratchOrgInfoPayload.features).forEach((warning) => {
logApi.warnUser(this.flags, warning);
});
return scratchOrgInfoGenerator_1.generateScratchOrgInfo({
hubOrg: this.hubOrg,
scratchOrgInfoPayload,
nonamespace: this.flags.nonamespace,
ignoreAncestorIds,
});
}
async updateRevisionCounterToZero() {
const conn = this.scratchOrg.getConnection();
const queryResult = await conn.tooling.sobject('SourceMember').find({ RevisionCounter: { $gt: 0 } }, ['Id']);
try {
await conn.tooling
.sobject('SourceMember')
.update(queryResult.map((record) => ({ Id: record.Id, RevisionCounter: 0 })));
}
catch (err) {
const message = messages().getMessage('SourceStatusResetFailure', [this.scratchOrg.getOrgId(), this.scratchOrg.getUsername()], 'signup');
throw new core_1.SfdxError(message, 'SourceStatusResetFailure');
}
}
}
exports.default = ScratchOrgCreateCommand;
ScratchOrgCreateCommand.SNAPSHOT_UNSUPPORTED_OPTIONS = [
'features',
'orgPreferences',
'edition',
'sourceOrg',
'settingsPath',
'releaseVersion',
'language',
];
//# sourceMappingURL=scratchOrgCreateCommand.js.map