UNPKG

15.6 kBJavaScriptView Raw
1"use strict";
2/*
3 * Copyright (c) 2021, salesforce.com, inc.
4 * All rights reserved.
5 * Licensed under the BSD 3-Clause license.
6 * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
7 */
8Object.defineProperty(exports, "__esModule", { value: true });
9exports.deploySettingsAndResolveUrl = exports.pollForScratchOrgInfo = exports.requestScratchOrgCreation = exports.authorizeScratchOrg = void 0;
10const kit_1 = require("@salesforce/kit");
11const ts_types_1 = require("@salesforce/ts-types");
12const ts_retry_promise_1 = require("ts-retry-promise");
13// Local
14const org_1 = require("./org");
15const logger_1 = require("./logger");
16const mapKeys_1 = require("./util/mapKeys");
17const authInfo_1 = require("./authInfo");
18const messages_1 = require("./messages");
19const sfdxError_1 = require("./sfdxError");
20const sfdcUrl_1 = require("./util/sfdcUrl");
21const pollingClient_1 = require("./status/pollingClient");
22const myDomainResolver_1 = require("./status/myDomainResolver");
23const scratchOrgErrorCodes_1 = require("./scratchOrgErrorCodes");
24messages_1.Messages.importMessagesDirectory(__dirname);
25const messages = messages_1.Messages.loadMessages('@salesforce/core', 'scratchOrgInfoApi');
26/**
27 * Returns the url to be used to authorize into the new scratch org
28 *
29 * @param scratchOrgInfoComplete The completed ScratchOrgInfo
30 * @param hubOrgLoginUrl the hun org login url
31 * @param signupTargetLoginUrlConfig the login url
32 * @returns {string}
33 */
34const getOrgInstanceAuthority = function (scratchOrgInfoComplete, hubOrgLoginUrl, signupTargetLoginUrlConfig) {
35 const createdOrgInstance = scratchOrgInfoComplete.SignupInstance;
36 if (createdOrgInstance === 'utf8') {
37 return hubOrgLoginUrl;
38 }
39 let altUrl;
40 // For non-Falcon (ie - instance names not ending in -s) sandboxes, use the instance URL
41 if (createdOrgInstance && !createdOrgInstance.toLowerCase().endsWith('s')) {
42 altUrl = `https://${createdOrgInstance}.salesforce.com`;
43 }
44 else {
45 // For Falcon sandboxes, try the LoginURL instead; createdOrgInstance will not yield a valid URL
46 altUrl = scratchOrgInfoComplete.LoginUrl;
47 }
48 return signupTargetLoginUrlConfig !== null && signupTargetLoginUrlConfig !== void 0 ? signupTargetLoginUrlConfig : altUrl;
49};
50/**
51 * Returns OAuth2Options object
52 *
53 * @param hubOrg the environment hub org
54 * @param clientSecret The OAuth client secret. May be null for JWT OAuth flow.
55 * @param scratchOrgInfoComplete The completed ScratchOrgInfo which should contain an access token.
56 * @param retry auth retry attempts
57 * @param signupTargetLoginUrlConfig the login url
58 * @returns {OAuth2Options, number, number, number} options, retries, timeout, delay
59 */
60const buildOAuth2Options = async (options) => {
61 const logger = await logger_1.Logger.child('buildOAuth2Options');
62 const isJwtFlow = !!options.hubOrg.getConnection().getAuthInfoFields().privateKey;
63 const oauth2Options = {
64 loginUrl: getOrgInstanceAuthority(options.scratchOrgInfoComplete, options.hubOrg.getField(org_1.Org.Fields.LOGIN_URL), options.signupTargetLoginUrlConfig),
65 };
66 logger.debug(`isJwtFlow: ${isJwtFlow}`);
67 if (isJwtFlow && !process.env.SFDX_CLIENT_SECRET) {
68 oauth2Options.privateKeyFile = options.hubOrg.getConnection().getAuthInfoFields().privateKey;
69 const retries = (options === null || options === void 0 ? void 0 : options.retry) || kit_1.env.getNumber('SFDX_JWT_AUTH_RETRY_ATTEMPTS') || 0;
70 const timeoutInSeconds = kit_1.env.getNumber('SFDX_JWT_AUTH_RETRY_TIMEOUT') || 300;
71 const timeout = kit_1.Duration.seconds(timeoutInSeconds).milliseconds;
72 const delay = retries ? timeout / retries : 1000;
73 return {
74 options: oauth2Options,
75 retries,
76 timeout,
77 delay,
78 };
79 }
80 else {
81 // Web Server OAuth "auth code exchange" flow
82 if (process.env.SFDX_CLIENT_SECRET) {
83 oauth2Options.clientSecret = process.env.SFDX_CLIENT_SECRET;
84 }
85 else if (options.clientSecret) {
86 oauth2Options.clientSecret = options.clientSecret;
87 }
88 oauth2Options.redirectUri = options.scratchOrgInfoComplete.ConnectedAppCallbackUrl;
89 oauth2Options.authCode = options.scratchOrgInfoComplete.AuthCode;
90 return {
91 options: oauth2Options,
92 retries: 0,
93 };
94 }
95};
96/**
97 * Returns OAuth2Options object
98 *
99 * @param hubOrg the environment hub org
100 * @param username The OAuth client secret. May be null for JWT OAuth flow.
101 * @param oauth2Options The completed ScratchOrgInfo which should contain an access token.
102 * @param retries auth retry attempts
103 * @param timeout the login url
104 * @param delay the login url
105 * @returns {OAuth2Options, number, number, number} options, retries, timeout, delay
106 */
107const getAuthInfo = async (options) => {
108 const logger = await logger_1.Logger.child('getAuthInfo');
109 const retryAuthorize = ts_retry_promise_1.retryDecorator(async (opts) => authInfo_1.AuthInfo.create(opts), {
110 timeout: options.timeout,
111 delay: options.delay,
112 retries: options.retries,
113 });
114 if (options.retries) {
115 try {
116 return await retryAuthorize({
117 username: options.username,
118 parentUsername: options.hubOrg.getUsername(),
119 oauth2Options: options.oauth2Options,
120 });
121 }
122 catch (err) {
123 const error = err;
124 logger.error(error);
125 throw error.lastError || error;
126 }
127 }
128 else {
129 return authInfo_1.AuthInfo.create({
130 username: options.username,
131 parentUsername: options.hubOrg.getUsername(),
132 oauth2Options: options.oauth2Options,
133 });
134 }
135};
136/**
137 * after we successfully signup an org we need to trade the auth token for access and refresh token.
138 *
139 * @param scratchOrgInfoComplete - The completed ScratchOrgInfo which should contain an access token.
140 * @param hubOrg - the environment hub org
141 * @param clientSecret - The OAuth client secret. May be null for JWT OAuth flow.
142 * @param signupTargetLoginUrlConfig - Login url
143 * @param retry - auth retry attempts
144 * @returns {Promise<AuthInfo>}
145 */
146const authorizeScratchOrg = async (options) => {
147 var _a;
148 const { scratchOrgInfoComplete, hubOrg, clientSecret, signupTargetLoginUrlConfig, retry: maxRetries } = options;
149 const logger = await logger_1.Logger.child('authorizeScratchOrg');
150 logger.debug(`scratchOrgInfoComplete: ${JSON.stringify(scratchOrgInfoComplete, null, 4)}`);
151 // if we didn't have it marked as a devhub but just successfully used it as one, this will update the authFile, fix cache, etc
152 if (!hubOrg.isDevHubOrg()) {
153 await hubOrg.determineIfDevHubOrg(true);
154 }
155 const oAuth2Options = await buildOAuth2Options({
156 hubOrg,
157 clientSecret,
158 scratchOrgInfoComplete,
159 retry: maxRetries,
160 signupTargetLoginUrlConfig,
161 });
162 const authInfo = await getAuthInfo({
163 hubOrg,
164 username: scratchOrgInfoComplete.SignupUsername,
165 oauth2Options: oAuth2Options.options,
166 retries: oAuth2Options.retries,
167 timeout: oAuth2Options.timeout,
168 delay: oAuth2Options.delay,
169 });
170 await authInfo.save({
171 devHubUsername: hubOrg.getUsername(),
172 created: new Date((_a = scratchOrgInfoComplete.CreatedDate) !== null && _a !== void 0 ? _a : new Date()).valueOf().toString(),
173 expirationDate: scratchOrgInfoComplete.ExpirationDate,
174 clientId: scratchOrgInfoComplete.ConnectedAppConsumerKey,
175 createdOrgInstance: scratchOrgInfoComplete.SignupInstance,
176 isDevHub: false,
177 snapshot: scratchOrgInfoComplete.Snapshot,
178 });
179 return authInfo;
180};
181exports.authorizeScratchOrg = authorizeScratchOrg;
182const checkOrgDoesntExist = async (scratchOrgInfo) => {
183 const usernameKey = Object.keys(scratchOrgInfo).find((key) => key.toUpperCase() === 'USERNAME');
184 if (!usernameKey) {
185 return;
186 }
187 const username = ts_types_1.getString(scratchOrgInfo, usernameKey);
188 if (username && username.length > 0) {
189 try {
190 await authInfo_1.AuthInfo.create({ username: username.toLowerCase() });
191 }
192 catch (error) {
193 const sfdxError = sfdxError_1.SfdxError.wrap(error);
194 // if an AuthInfo couldn't be created that means no AuthFile exists.
195 if (sfdxError.name === 'NamedOrgNotFound') {
196 return;
197 }
198 // Something unexpected
199 throw sfdxError;
200 }
201 // An org file already exists
202 throw sfdxError_1.SfdxError.create('@salesforce/core', 'scratchOrgErrorCodes', 'C-1007');
203 }
204};
205/**
206 * This extracts orgPrefs/settings from the user input and performs a basic scratchOrgInfo request.
207 *
208 * @param hubOrg - the environment hub org
209 * @param scratchOrgRequest - An object containing the fields of the ScratchOrgInfo
210 * @param settings - An object containing org settings
211 * @returns {Promise<RecordResult>}
212 */
213const requestScratchOrgCreation = async (hubOrg, scratchOrgRequest, settings) => {
214 // If these were present, they were already used to initialize the scratchOrgSettingsGenerator.
215 // They shouldn't be submitted as part of the scratchOrgInfo.
216 delete scratchOrgRequest.settings;
217 delete scratchOrgRequest.objectSettings;
218 // We do not allow you to specify the old and the new way of doing post create settings
219 if (scratchOrgRequest.orgPreferences && settings.hasSettings()) {
220 // This is not allowed
221 throw new sfdxError_1.SfdxError('signupDuplicateSettingsSpecified');
222 }
223 // deprecated old style orgPreferences
224 if (scratchOrgRequest.orgPreferences) {
225 throw new sfdxError_1.SfdxError(messages.getMessage('deprecatedPrefFormat'));
226 }
227 const scratchOrgInfo = mapKeys_1.default(scratchOrgRequest, kit_1.upperFirst, true);
228 await checkOrgDoesntExist(scratchOrgInfo); // throw if it does exists.
229 try {
230 return await hubOrg.getConnection().sobject('ScratchOrgInfo').create(scratchOrgInfo);
231 }
232 catch (error) {
233 // this is a jsforce error which contains the property "fields" which regular error don't
234 const jsForceError = error;
235 if (jsForceError.errorCode === 'REQUIRED_FIELD_MISSING') {
236 throw new sfdxError_1.SfdxError(messages.getMessage('signupFieldsMissing', [jsForceError.fields.toString()]));
237 }
238 throw sfdxError_1.SfdxError.wrap(jsForceError);
239 }
240};
241exports.requestScratchOrgCreation = requestScratchOrgCreation;
242/**
243 * This retrieves the ScratchOrgInfo, polling until the status is Active or Error
244 *
245 * @param hubOrg
246 * @param scratchOrgInfoId - the id of the scratchOrgInfo that we are retrieving
247 * @param timeout - A Duration object
248 * @returns {Promise<ScratchOrgInfo>}
249 */
250const pollForScratchOrgInfo = async (hubOrg, scratchOrgInfoId,
251// org:create specifies a default timeout of 6. This longer default is for other consumers
252timeout = kit_1.Duration.minutes(15)) => {
253 const logger = await logger_1.Logger.child('scratchOrgInfoApi-pollForScratchOrgInfo');
254 logger.debug(`PollingTimeout in minutes: ${timeout.minutes}`);
255 const pollingOptions = {
256 async poll() {
257 try {
258 const resultInProgress = await hubOrg
259 .getConnection()
260 .sobject('ScratchOrgInfo')
261 .retrieve(scratchOrgInfoId);
262 logger.debug(`polling client result: ${JSON.stringify(resultInProgress, null, 4)}`);
263 // Once it's "done" we can return it
264 if (resultInProgress.Status === 'Active' || resultInProgress.Status === 'Error') {
265 return {
266 completed: true,
267 payload: resultInProgress,
268 };
269 }
270 logger.debug(`Scratch org status is ${resultInProgress.Status}`);
271 return {
272 completed: false,
273 };
274 }
275 catch (error) {
276 logger.debug(`An error occurred trying to retrieve scratchOrgInfo for ${scratchOrgInfoId}`);
277 logger.debug(`Error: ${error.message}`);
278 logger.debug('Re-trying deploy check again....');
279 return {
280 completed: false,
281 };
282 }
283 },
284 timeout,
285 frequency: kit_1.Duration.seconds(1),
286 timeoutErrorName: 'ScratchOrgInfoTimeoutError',
287 };
288 const client = await pollingClient_1.PollingClient.create(pollingOptions);
289 try {
290 const resultInProgress = await client.subscribe();
291 return scratchOrgErrorCodes_1.checkScratchOrgInfoForErrors(resultInProgress, hubOrg.getUsername(), logger);
292 }
293 catch (error) {
294 const err = error;
295 if (err.message) {
296 throw sfdxError_1.SfdxError.wrap(err);
297 }
298 throw new sfdxError_1.SfdxError(`The scratch org did not complete within ${timeout.minutes} minutes`, 'orgCreationTimeout', [
299 'Try your force:org:create command again with a longer --wait value',
300 ]);
301 }
302};
303exports.pollForScratchOrgInfo = pollForScratchOrgInfo;
304/**
305 * This authenticates into the newly created org and sets org preferences
306 *
307 * @param scratchOrgAuthInfo - an object containing the AuthInfo of the ScratchOrg
308 * @param apiVersion - the target api version
309 * @param orgSettings - The ScratchOrg settings
310 * @param scratchOrg - The scratchOrg Org info
311 * @returns {Promise<Optional<AuthInfo>>}
312 */
313const deploySettingsAndResolveUrl = async (scratchOrgAuthInfo, apiVersion, orgSettings, scratchOrg) => {
314 const logger = await logger_1.Logger.child('scratchOrgInfoApi-deploySettingsAndResolveUrl');
315 if (orgSettings.hasSettings()) {
316 // deploy the settings to the newly created scratch org
317 logger.debug(`deploying scratch org settings with apiVersion ${apiVersion}`);
318 try {
319 await orgSettings.createDeploy();
320 await orgSettings.deploySettingsViaFolder(scratchOrg, apiVersion);
321 }
322 catch (error) {
323 throw sfdxError_1.SfdxError.wrap(error);
324 }
325 }
326 const { instanceUrl } = scratchOrgAuthInfo.getFields();
327 if (instanceUrl) {
328 logger.debug(`processScratchOrgInfoResult - resultData.instanceUrl: ${instanceUrl}`);
329 const options = {
330 timeout: kit_1.Duration.minutes(3),
331 frequency: kit_1.Duration.seconds(10),
332 url: new sfdcUrl_1.SfdcUrl(instanceUrl),
333 };
334 try {
335 const resolver = await myDomainResolver_1.MyDomainResolver.create(options);
336 await resolver.resolve();
337 }
338 catch (error) {
339 const sfdxError = sfdxError_1.SfdxError.wrap(error);
340 logger.debug('processScratchOrgInfoResult - err: %s', error);
341 if (sfdxError.name === 'MyDomainResolverTimeoutError') {
342 sfdxError.setData({
343 orgId: scratchOrgAuthInfo.getFields().orgId,
344 username: scratchOrgAuthInfo.getFields().username,
345 instanceUrl,
346 });
347 logger.debug('processScratchOrgInfoResult - err data: %s', sfdxError.data);
348 }
349 throw sfdxError;
350 }
351 return scratchOrgAuthInfo;
352 }
353};
354exports.deploySettingsAndResolveUrl = deploySettingsAndResolveUrl;
355//# sourceMappingURL=scratchOrgInfoApi.js.map
\No newline at end of file