@salesforce/core 2.33.1 → 2.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/LICENSE.txt +1 -1
- package/lib/connection.d.ts +5 -0
- package/lib/connection.js +15 -0
- package/lib/exported.d.ts +3 -1
- package/lib/exported.js +3 -1
- package/lib/org.d.ts +10 -0
- package/lib/org.js +12 -6
- package/lib/scratchOrgCreate.d.ts +43 -0
- package/lib/scratchOrgCreate.js +135 -0
- package/lib/scratchOrgErrorCodes.d.ts +4 -0
- package/lib/scratchOrgErrorCodes.js +53 -0
- package/lib/scratchOrgFeatureDeprecation.d.ts +26 -0
- package/lib/scratchOrgFeatureDeprecation.js +106 -0
- package/lib/scratchOrgInfoApi.d.ts +94 -0
- package/lib/scratchOrgInfoApi.js +331 -0
- package/lib/scratchOrgInfoGenerator.d.ts +62 -0
- package/lib/scratchOrgInfoGenerator.js +226 -0
- package/lib/scratchOrgSettingsGenerator.d.ts +56 -0
- package/lib/scratchOrgSettingsGenerator.js +208 -0
- package/lib/util/jsonXmlTools.d.ts +14 -0
- package/lib/util/jsonXmlTools.js +41 -0
- package/lib/util/mapKeys.d.ts +14 -0
- package/lib/util/mapKeys.js +48 -0
- package/lib/util/zipWriter.d.ts +14 -0
- package/lib/util/zipWriter.js +68 -0
- package/lib/webOAuthServer.js +1 -1
- package/messages/scratchOrgCreate.json +4 -0
- package/messages/scratchOrgErrorCodes.json +27 -0
- package/messages/scratchOrgFeatureDeprecation.json +5 -0
- package/messages/scratchOrgInfoApi.json +5 -0
- package/messages/scratchOrgInfoGenerator.json +7 -0
- package/package.json +9 -6
|
@@ -0,0 +1,331 @@
|
|
|
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
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.deploySettingsAndResolveUrl = exports.pollForScratchOrgInfo = exports.requestScratchOrgCreation = exports.authorizeScratchOrg = void 0;
|
|
10
|
+
const kit_1 = require("@salesforce/kit");
|
|
11
|
+
const ts_types_1 = require("@salesforce/ts-types");
|
|
12
|
+
const ts_retry_promise_1 = require("ts-retry-promise");
|
|
13
|
+
// Local
|
|
14
|
+
const org_1 = require("./org");
|
|
15
|
+
const logger_1 = require("./logger");
|
|
16
|
+
const mapKeys_1 = require("./util/mapKeys");
|
|
17
|
+
const authInfo_1 = require("./authInfo");
|
|
18
|
+
const messages_1 = require("./messages");
|
|
19
|
+
const sfdxError_1 = require("./sfdxError");
|
|
20
|
+
const sfdcUrl_1 = require("./util/sfdcUrl");
|
|
21
|
+
const myDomainResolver_1 = require("./status/myDomainResolver");
|
|
22
|
+
const scratchOrgErrorCodes_1 = require("./scratchOrgErrorCodes");
|
|
23
|
+
messages_1.Messages.importMessagesDirectory(__dirname);
|
|
24
|
+
const messages = messages_1.Messages.loadMessages('@salesforce/core', 'scratchOrgInfoApi');
|
|
25
|
+
/**
|
|
26
|
+
* Returns the url to be used to authorize into the new scratch org
|
|
27
|
+
*
|
|
28
|
+
* @param scratchOrgInfoComplete The completed ScratchOrgInfo
|
|
29
|
+
* @param hubOrgLoginUrl the hun org login url
|
|
30
|
+
* @param signupTargetLoginUrlConfig the login url
|
|
31
|
+
* @returns {string}
|
|
32
|
+
*/
|
|
33
|
+
const getOrgInstanceAuthority = function (scratchOrgInfoComplete, hubOrgLoginUrl, signupTargetLoginUrlConfig) {
|
|
34
|
+
const createdOrgInstance = scratchOrgInfoComplete.SignupInstance;
|
|
35
|
+
if (createdOrgInstance === 'utf8') {
|
|
36
|
+
return hubOrgLoginUrl;
|
|
37
|
+
}
|
|
38
|
+
let altUrl;
|
|
39
|
+
// For non-Falcon (ie - instance names not ending in -s) sandboxes, use the instance URL
|
|
40
|
+
if (createdOrgInstance && !createdOrgInstance.toLowerCase().endsWith('s')) {
|
|
41
|
+
altUrl = `https://${createdOrgInstance}.salesforce.com`;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
// For Falcon sandboxes, try the LoginURL instead; createdOrgInstance will not yield a valid URL
|
|
45
|
+
altUrl = scratchOrgInfoComplete.LoginUrl;
|
|
46
|
+
}
|
|
47
|
+
return signupTargetLoginUrlConfig !== null && signupTargetLoginUrlConfig !== void 0 ? signupTargetLoginUrlConfig : altUrl;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Returns OAuth2Options object
|
|
51
|
+
*
|
|
52
|
+
* @param hubOrg the environment hub org
|
|
53
|
+
* @param clientSecret The OAuth client secret. May be null for JWT OAuth flow.
|
|
54
|
+
* @param scratchOrgInfoComplete The completed ScratchOrgInfo which should contain an access token.
|
|
55
|
+
* @param retry auth retry attempts
|
|
56
|
+
* @param signupTargetLoginUrlConfig the login url
|
|
57
|
+
* @returns {OAuth2Options, number, number, number} options, retries, timeout, delay
|
|
58
|
+
*/
|
|
59
|
+
const buildOAuth2Options = async (options) => {
|
|
60
|
+
const logger = await logger_1.Logger.child('buildOAuth2Options');
|
|
61
|
+
const isJwtFlow = !!options.hubOrg.getConnection().getAuthInfoFields().privateKey;
|
|
62
|
+
const oauth2Options = {
|
|
63
|
+
loginUrl: getOrgInstanceAuthority(options.scratchOrgInfoComplete, options.hubOrg.getField(org_1.Org.Fields.LOGIN_URL), options.signupTargetLoginUrlConfig),
|
|
64
|
+
};
|
|
65
|
+
logger.debug(`isJwtFlow: ${isJwtFlow}`);
|
|
66
|
+
if (isJwtFlow && !process.env.SFDX_CLIENT_SECRET) {
|
|
67
|
+
oauth2Options.privateKeyFile = options.hubOrg.getConnection().getAuthInfoFields().privateKey;
|
|
68
|
+
const retries = (options === null || options === void 0 ? void 0 : options.retry) || kit_1.env.getNumber('SFDX_JWT_AUTH_RETRY_ATTEMPTS') || 0;
|
|
69
|
+
const timeoutInSeconds = kit_1.env.getNumber('SFDX_JWT_AUTH_RETRY_TIMEOUT') || 300;
|
|
70
|
+
const timeout = kit_1.Duration.seconds(timeoutInSeconds).milliseconds;
|
|
71
|
+
const delay = retries ? timeout / retries : 1000;
|
|
72
|
+
return {
|
|
73
|
+
options: oauth2Options,
|
|
74
|
+
retries,
|
|
75
|
+
timeout,
|
|
76
|
+
delay,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
// Web Server OAuth "auth code exchange" flow
|
|
81
|
+
if (process.env.SFDX_CLIENT_SECRET) {
|
|
82
|
+
oauth2Options.clientSecret = process.env.SFDX_CLIENT_SECRET;
|
|
83
|
+
}
|
|
84
|
+
else if (options.clientSecret) {
|
|
85
|
+
oauth2Options.clientSecret = options.clientSecret;
|
|
86
|
+
}
|
|
87
|
+
oauth2Options.redirectUri = options.scratchOrgInfoComplete.ConnectedAppCallbackUrl;
|
|
88
|
+
oauth2Options.authCode = options.scratchOrgInfoComplete.AuthCode;
|
|
89
|
+
return {
|
|
90
|
+
options: oauth2Options,
|
|
91
|
+
retries: 0,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Returns OAuth2Options object
|
|
97
|
+
*
|
|
98
|
+
* @param hubOrg the environment hub org
|
|
99
|
+
* @param username The OAuth client secret. May be null for JWT OAuth flow.
|
|
100
|
+
* @param oauth2Options The completed ScratchOrgInfo which should contain an access token.
|
|
101
|
+
* @param retries auth retry attempts
|
|
102
|
+
* @param timeout the login url
|
|
103
|
+
* @param delay the login url
|
|
104
|
+
* @returns {OAuth2Options, number, number, number} options, retries, timeout, delay
|
|
105
|
+
*/
|
|
106
|
+
const getAuthInfo = async (options) => {
|
|
107
|
+
const logger = await logger_1.Logger.child('getAuthInfo');
|
|
108
|
+
const retryAuthorize = ts_retry_promise_1.retryDecorator(async (opts) => authInfo_1.AuthInfo.create(opts), {
|
|
109
|
+
timeout: options.timeout,
|
|
110
|
+
delay: options.delay,
|
|
111
|
+
retries: options.retries,
|
|
112
|
+
});
|
|
113
|
+
if (options.retries) {
|
|
114
|
+
try {
|
|
115
|
+
return await retryAuthorize({
|
|
116
|
+
username: options.username,
|
|
117
|
+
parentUsername: options.hubOrg.getUsername(),
|
|
118
|
+
oauth2Options: options.oauth2Options,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
const error = err;
|
|
123
|
+
logger.error(error);
|
|
124
|
+
throw error.lastError || error;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
return authInfo_1.AuthInfo.create({
|
|
129
|
+
username: options.username,
|
|
130
|
+
parentUsername: options.hubOrg.getUsername(),
|
|
131
|
+
oauth2Options: options.oauth2Options,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* after we successfully signup an org we need to trade the auth token for access and refresh token.
|
|
137
|
+
*
|
|
138
|
+
* @param scratchOrgInfoComplete - The completed ScratchOrgInfo which should contain an access token.
|
|
139
|
+
* @param hubOrg - the environment hub org
|
|
140
|
+
* @param clientSecret - The OAuth client secret. May be null for JWT OAuth flow.
|
|
141
|
+
* @param signupTargetLoginUrlConfig - Login url
|
|
142
|
+
* @param retry - auth retry attempts
|
|
143
|
+
* @returns {Promise<AuthInfo>}
|
|
144
|
+
*/
|
|
145
|
+
const authorizeScratchOrg = async (options) => {
|
|
146
|
+
var _a;
|
|
147
|
+
const { scratchOrgInfoComplete, hubOrg, clientSecret, signupTargetLoginUrlConfig, retry: maxRetries } = options;
|
|
148
|
+
const logger = await logger_1.Logger.child('authorizeScratchOrg');
|
|
149
|
+
logger.debug(`scratchOrgInfoComplete: ${JSON.stringify(scratchOrgInfoComplete, null, 4)}`);
|
|
150
|
+
// 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
|
|
151
|
+
if (!hubOrg.isDevHubOrg()) {
|
|
152
|
+
await hubOrg.determineIfDevHubOrg(true);
|
|
153
|
+
}
|
|
154
|
+
const oAuth2Options = await buildOAuth2Options({
|
|
155
|
+
hubOrg,
|
|
156
|
+
clientSecret,
|
|
157
|
+
scratchOrgInfoComplete,
|
|
158
|
+
retry: maxRetries,
|
|
159
|
+
signupTargetLoginUrlConfig,
|
|
160
|
+
});
|
|
161
|
+
const authInfo = await getAuthInfo({
|
|
162
|
+
hubOrg,
|
|
163
|
+
username: scratchOrgInfoComplete.SignupUsername,
|
|
164
|
+
oauth2Options: oAuth2Options.options,
|
|
165
|
+
retries: oAuth2Options.retries,
|
|
166
|
+
timeout: oAuth2Options.timeout,
|
|
167
|
+
delay: oAuth2Options.delay,
|
|
168
|
+
});
|
|
169
|
+
await authInfo.save({
|
|
170
|
+
devHubUsername: hubOrg.getUsername(),
|
|
171
|
+
created: new Date((_a = scratchOrgInfoComplete.CreatedDate) !== null && _a !== void 0 ? _a : new Date()).valueOf().toString(),
|
|
172
|
+
expirationDate: scratchOrgInfoComplete.ExpirationDate,
|
|
173
|
+
clientId: scratchOrgInfoComplete.ConnectedAppConsumerKey,
|
|
174
|
+
createdOrgInstance: scratchOrgInfoComplete.SignupInstance,
|
|
175
|
+
isDevHub: false,
|
|
176
|
+
snapshot: scratchOrgInfoComplete.Snapshot,
|
|
177
|
+
});
|
|
178
|
+
return authInfo;
|
|
179
|
+
};
|
|
180
|
+
exports.authorizeScratchOrg = authorizeScratchOrg;
|
|
181
|
+
const checkOrgDoesntExist = async (scratchOrgInfo) => {
|
|
182
|
+
const usernameKey = Object.keys(scratchOrgInfo).find((key) => key.toUpperCase() === 'USERNAME');
|
|
183
|
+
if (!usernameKey) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const username = ts_types_1.getString(scratchOrgInfo, usernameKey);
|
|
187
|
+
if (username && username.length > 0) {
|
|
188
|
+
try {
|
|
189
|
+
await authInfo_1.AuthInfo.create({ username: username.toLowerCase() });
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
const sfdxError = sfdxError_1.SfdxError.wrap(error);
|
|
193
|
+
// if an AuthInfo couldn't be created that means no AuthFile exists.
|
|
194
|
+
if (sfdxError.name === 'NamedOrgNotFound') {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
// Something unexpected
|
|
198
|
+
throw sfdxError;
|
|
199
|
+
}
|
|
200
|
+
// An org file already exists
|
|
201
|
+
throw sfdxError_1.SfdxError.create('@salesforce/core', 'scratchOrgErrorCodes', 'C-1007');
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* This extracts orgPrefs/settings from the user input and performs a basic scratchOrgInfo request.
|
|
206
|
+
*
|
|
207
|
+
* @param hubOrg - the environment hub org
|
|
208
|
+
* @param scratchOrgRequest - An object containing the fields of the ScratchOrgInfo
|
|
209
|
+
* @param settings - An object containing org settings
|
|
210
|
+
* @returns {Promise<RecordResult>}
|
|
211
|
+
*/
|
|
212
|
+
const requestScratchOrgCreation = async (hubOrg, scratchOrgRequest, settings) => {
|
|
213
|
+
// If these were present, they were already used to initialize the scratchOrgSettingsGenerator.
|
|
214
|
+
// They shouldn't be submitted as part of the scratchOrgInfo.
|
|
215
|
+
delete scratchOrgRequest.settings;
|
|
216
|
+
delete scratchOrgRequest.objectSettings;
|
|
217
|
+
// We do not allow you to specify the old and the new way of doing post create settings
|
|
218
|
+
if (scratchOrgRequest.orgPreferences && settings.hasSettings()) {
|
|
219
|
+
// This is not allowed
|
|
220
|
+
throw new sfdxError_1.SfdxError('signupDuplicateSettingsSpecified');
|
|
221
|
+
}
|
|
222
|
+
// deprecated old style orgPreferences
|
|
223
|
+
if (scratchOrgRequest.orgPreferences) {
|
|
224
|
+
throw new sfdxError_1.SfdxError(messages.getMessage('deprecatedPrefFormat'));
|
|
225
|
+
}
|
|
226
|
+
const scratchOrgInfo = mapKeys_1.default(scratchOrgRequest, kit_1.upperFirst, true);
|
|
227
|
+
await checkOrgDoesntExist(scratchOrgInfo); // throw if it does exists.
|
|
228
|
+
try {
|
|
229
|
+
return await hubOrg.getConnection().sobject('ScratchOrgInfo').create(scratchOrgInfo);
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
// this is a jsforce error which contains the property "fields" which regular error don't
|
|
233
|
+
const jsForceError = error;
|
|
234
|
+
if (jsForceError.errorCode === 'REQUIRED_FIELD_MISSING') {
|
|
235
|
+
throw new sfdxError_1.SfdxError(messages.getMessage('signupFieldsMissing', [jsForceError.fields.toString()]));
|
|
236
|
+
}
|
|
237
|
+
throw sfdxError_1.SfdxError.wrap(jsForceError);
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
exports.requestScratchOrgCreation = requestScratchOrgCreation;
|
|
241
|
+
/**
|
|
242
|
+
* This retrieves the ScratchOrgInfo, polling until the status is Active or Error
|
|
243
|
+
*
|
|
244
|
+
* @param hubOrg
|
|
245
|
+
* @param scratchOrgInfoId - the id of the scratchOrgInfo that we are retrieving
|
|
246
|
+
* @param timeout - A Duration object
|
|
247
|
+
* @returns {Promise<ScratchOrgInfo>}
|
|
248
|
+
*/
|
|
249
|
+
const pollForScratchOrgInfo = async (hubOrg, scratchOrgInfoId,
|
|
250
|
+
// org:create specifies a default timeout of 6. This longer default is for other consumers
|
|
251
|
+
timeout = kit_1.Duration.minutes(15)) => {
|
|
252
|
+
const logger = await logger_1.Logger.child('scratchOrgInfoApi-pollForScratchOrgInfo');
|
|
253
|
+
logger.debug(`PollingTimeout in minutes: ${timeout.minutes}`);
|
|
254
|
+
const response = await ts_retry_promise_1.retry(async () => {
|
|
255
|
+
const resultInProgress = await hubOrg
|
|
256
|
+
.getConnection()
|
|
257
|
+
.sobject('ScratchOrgInfo')
|
|
258
|
+
.retrieve(scratchOrgInfoId);
|
|
259
|
+
logger.debug(`polling client result: ${JSON.stringify(resultInProgress, null, 4)}`);
|
|
260
|
+
// Once it's "done" we can return it
|
|
261
|
+
if (resultInProgress.Status === 'Active' || resultInProgress.Status === 'Error') {
|
|
262
|
+
return resultInProgress;
|
|
263
|
+
}
|
|
264
|
+
// all other statuses, OR lack of status (e.g. network errors) will cause a retry
|
|
265
|
+
throw new sfdxError_1.SfdxError(`Scratch org status is ${resultInProgress.Status}`);
|
|
266
|
+
}, {
|
|
267
|
+
retries: 'INFINITELY',
|
|
268
|
+
timeout: timeout.milliseconds,
|
|
269
|
+
delay: kit_1.Duration.seconds(2).milliseconds,
|
|
270
|
+
backoff: 'LINEAR',
|
|
271
|
+
maxBackOff: kit_1.Duration.seconds(30).milliseconds,
|
|
272
|
+
}).catch(() => {
|
|
273
|
+
throw new sfdxError_1.SfdxError(`The scratch org did not complete within ${timeout.minutes} minutes`, 'orgCreationTimeout', [
|
|
274
|
+
'Try your force:org:create command again with a longer --wait value',
|
|
275
|
+
]);
|
|
276
|
+
});
|
|
277
|
+
return scratchOrgErrorCodes_1.checkScratchOrgInfoForErrors(response, hubOrg.getUsername(), logger);
|
|
278
|
+
};
|
|
279
|
+
exports.pollForScratchOrgInfo = pollForScratchOrgInfo;
|
|
280
|
+
/**
|
|
281
|
+
* This authenticates into the newly created org and sets org preferences
|
|
282
|
+
*
|
|
283
|
+
* @param scratchOrgAuthInfo - an object containing the AuthInfo of the ScratchOrg
|
|
284
|
+
* @param apiVersion - the target api version
|
|
285
|
+
* @param orgSettings - The ScratchOrg settings
|
|
286
|
+
* @param scratchOrg - The scratchOrg Org info
|
|
287
|
+
* @returns {Promise<Optional<AuthInfo>>}
|
|
288
|
+
*/
|
|
289
|
+
const deploySettingsAndResolveUrl = async (scratchOrgAuthInfo, apiVersion, orgSettings, scratchOrg) => {
|
|
290
|
+
const logger = await logger_1.Logger.child('scratchOrgInfoApi-deploySettingsAndResolveUrl');
|
|
291
|
+
if (orgSettings.hasSettings()) {
|
|
292
|
+
// deploy the settings to the newly created scratch org
|
|
293
|
+
logger.debug(`deploying scratch org settings with apiVersion ${apiVersion}`);
|
|
294
|
+
try {
|
|
295
|
+
await orgSettings.createDeploy();
|
|
296
|
+
await orgSettings.deploySettingsViaFolder(scratchOrg, apiVersion);
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
throw sfdxError_1.SfdxError.wrap(error);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
const { instanceUrl } = scratchOrgAuthInfo.getFields();
|
|
303
|
+
if (instanceUrl) {
|
|
304
|
+
logger.debug(`processScratchOrgInfoResult - resultData.instanceUrl: ${instanceUrl}`);
|
|
305
|
+
const options = {
|
|
306
|
+
timeout: kit_1.Duration.minutes(3),
|
|
307
|
+
frequency: kit_1.Duration.seconds(10),
|
|
308
|
+
url: new sfdcUrl_1.SfdcUrl(instanceUrl),
|
|
309
|
+
};
|
|
310
|
+
try {
|
|
311
|
+
const resolver = await myDomainResolver_1.MyDomainResolver.create(options);
|
|
312
|
+
await resolver.resolve();
|
|
313
|
+
}
|
|
314
|
+
catch (error) {
|
|
315
|
+
const sfdxError = sfdxError_1.SfdxError.wrap(error);
|
|
316
|
+
logger.debug('processScratchOrgInfoResult - err: %s', error);
|
|
317
|
+
if (sfdxError.name === 'MyDomainResolverTimeoutError') {
|
|
318
|
+
sfdxError.setData({
|
|
319
|
+
orgId: scratchOrgAuthInfo.getFields().orgId,
|
|
320
|
+
username: scratchOrgAuthInfo.getFields().username,
|
|
321
|
+
instanceUrl,
|
|
322
|
+
});
|
|
323
|
+
logger.debug('processScratchOrgInfoResult - err data: %s', sfdxError.data);
|
|
324
|
+
}
|
|
325
|
+
throw sfdxError;
|
|
326
|
+
}
|
|
327
|
+
return scratchOrgAuthInfo;
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
exports.deploySettingsAndResolveUrl = deploySettingsAndResolveUrl;
|
|
331
|
+
//# sourceMappingURL=scratchOrgInfoApi.js.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Org } from './org';
|
|
2
|
+
import { SfdxProjectJson } from './sfdxProject';
|
|
3
|
+
import { ScratchOrgInfo } from './scratchOrgInfoApi';
|
|
4
|
+
declare type PartialScratchOrgInfo = Pick<ScratchOrgInfo, 'ConnectedAppConsumerKey' | 'AuthCode' | 'Snapshot' | 'Status' | 'LoginUrl' | 'SignupEmail' | 'SignupUsername' | 'SignupInstance' | 'Username'>;
|
|
5
|
+
export interface ScratchOrgInfoPayload extends PartialScratchOrgInfo {
|
|
6
|
+
orgName: string;
|
|
7
|
+
package2AncestorIds: string;
|
|
8
|
+
features: string | string[];
|
|
9
|
+
connectedAppConsumerKey: string;
|
|
10
|
+
namespace: string;
|
|
11
|
+
connectedAppCallbackUrl: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Generates the package2AncestorIds scratch org property
|
|
15
|
+
*
|
|
16
|
+
* @param scratchOrgInfo - the scratchOrgInfo passed in by the user
|
|
17
|
+
* @param projectJson - sfdxProjectJson
|
|
18
|
+
* @param hubOrg - the hub org, in case we need to do queries
|
|
19
|
+
*/
|
|
20
|
+
export declare const getAncestorIds: (scratchOrgInfo: ScratchOrgInfoPayload, projectJson: SfdxProjectJson, hubOrg: Org) => Promise<string>;
|
|
21
|
+
/**
|
|
22
|
+
* Takes in a scratchOrgInfo and fills in the missing fields
|
|
23
|
+
*
|
|
24
|
+
* @param hubOrg the environment hub org
|
|
25
|
+
* @param scratchOrgInfoPayload - the scratchOrgInfo passed in by the user
|
|
26
|
+
* @param nonamespace create the scratch org with no namespace
|
|
27
|
+
* @param ignoreAncestorIds true if the sfdx-project.json ancestorId keys should be ignored
|
|
28
|
+
*/
|
|
29
|
+
export declare const generateScratchOrgInfo: ({ hubOrg, scratchOrgInfoPayload, nonamespace, ignoreAncestorIds, }: {
|
|
30
|
+
hubOrg: Org;
|
|
31
|
+
scratchOrgInfoPayload: ScratchOrgInfoPayload;
|
|
32
|
+
nonamespace?: boolean | undefined;
|
|
33
|
+
ignoreAncestorIds?: boolean | undefined;
|
|
34
|
+
}) => Promise<ScratchOrgInfoPayload>;
|
|
35
|
+
/**
|
|
36
|
+
* Returns a valid signup json
|
|
37
|
+
*
|
|
38
|
+
* @param definitionjson org definition in JSON format
|
|
39
|
+
* @param definitionfile path to an org definition file
|
|
40
|
+
* @param connectedAppConsumerKey The connected app consumer key. May be null for JWT OAuth flow.
|
|
41
|
+
* @param durationdays duration of the scratch org (in days) (default:1, min:1, max:30)
|
|
42
|
+
* @param nonamespace create the scratch org with no namespace
|
|
43
|
+
* @param noancestors do not include second-generation package ancestors in the scratch org
|
|
44
|
+
* @param orgConfig overrides definitionjson
|
|
45
|
+
* @returns scratchOrgInfoPayload: ScratchOrgInfoPayload;
|
|
46
|
+
ignoreAncestorIds: boolean;
|
|
47
|
+
warnings: string[];
|
|
48
|
+
*/
|
|
49
|
+
export declare const getScratchOrgInfoPayload: (options: {
|
|
50
|
+
definitionjson?: string;
|
|
51
|
+
definitionfile?: string;
|
|
52
|
+
connectedAppConsumerKey?: string;
|
|
53
|
+
durationDays: number;
|
|
54
|
+
nonamespace?: boolean;
|
|
55
|
+
noancestors?: boolean;
|
|
56
|
+
orgConfig?: Record<string, unknown>;
|
|
57
|
+
}) => Promise<{
|
|
58
|
+
scratchOrgInfoPayload: ScratchOrgInfoPayload;
|
|
59
|
+
ignoreAncestorIds: boolean;
|
|
60
|
+
warnings: string[];
|
|
61
|
+
}>;
|
|
62
|
+
export {};
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getScratchOrgInfoPayload = exports.generateScratchOrgInfo = exports.getAncestorIds = void 0;
|
|
4
|
+
/*
|
|
5
|
+
* Copyright (c) 2021, salesforce.com, inc.
|
|
6
|
+
* All rights reserved.
|
|
7
|
+
* Licensed under the BSD 3-Clause license.
|
|
8
|
+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
9
|
+
*/
|
|
10
|
+
// Node
|
|
11
|
+
const fs_1 = require("fs");
|
|
12
|
+
// @salesforce
|
|
13
|
+
const kit_1 = require("@salesforce/kit");
|
|
14
|
+
const ts_types_1 = require("@salesforce/ts-types");
|
|
15
|
+
const sfdc_1 = require("./util/sfdc");
|
|
16
|
+
const sfdxProject_1 = require("./sfdxProject");
|
|
17
|
+
const webOAuthServer_1 = require("./webOAuthServer");
|
|
18
|
+
const messages_1 = require("./messages");
|
|
19
|
+
const sfdxError_1 = require("./sfdxError");
|
|
20
|
+
const scratchOrgFeatureDeprecation_1 = require("./scratchOrgFeatureDeprecation");
|
|
21
|
+
const defaultConnectedAppInfo = {
|
|
22
|
+
clientId: 'PlatformCLI',
|
|
23
|
+
legacyClientId: 'SalesforceDevelopmentExperience',
|
|
24
|
+
legacyClientSecret: '1384510088588713504',
|
|
25
|
+
};
|
|
26
|
+
messages_1.Messages.importMessagesDirectory(__dirname);
|
|
27
|
+
const messages = messages_1.Messages.loadMessages('@salesforce/core', 'scratchOrgInfoGenerator');
|
|
28
|
+
const SNAPSHOT_UNSUPPORTED_OPTIONS = [
|
|
29
|
+
'features',
|
|
30
|
+
'orgPreferences',
|
|
31
|
+
'edition',
|
|
32
|
+
'sourceOrg',
|
|
33
|
+
'settingsPath',
|
|
34
|
+
'releaseVersion',
|
|
35
|
+
'language',
|
|
36
|
+
];
|
|
37
|
+
// A validator function to ensure any options parameters entered by the user adhere
|
|
38
|
+
// to a allowlist of valid option settings. Because org:create allows options to be
|
|
39
|
+
// input either key=value pairs or within the definition file, this validator is
|
|
40
|
+
// executed within the ctor and also after parsing/normalization of the definition file.
|
|
41
|
+
const optionsValidator = (key, scratchOrgInfoPayload) => {
|
|
42
|
+
if (key.toLowerCase() === 'durationdays') {
|
|
43
|
+
throw new sfdxError_1.SfdxError('unrecognizedScratchOrgOption', 'durationDays');
|
|
44
|
+
}
|
|
45
|
+
if (key.toLowerCase() === 'snapshot') {
|
|
46
|
+
const foundInvalidFields = SNAPSHOT_UNSUPPORTED_OPTIONS.filter((invalidField) => invalidField in scratchOrgInfoPayload);
|
|
47
|
+
if (foundInvalidFields.length > 0) {
|
|
48
|
+
throw new sfdxError_1.SfdxError(messages.getMessage('unsupportedSnapshotOrgCreateOptions', [foundInvalidFields.join(', ')]), 'orgSnapshot');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Generates the package2AncestorIds scratch org property
|
|
54
|
+
*
|
|
55
|
+
* @param scratchOrgInfo - the scratchOrgInfo passed in by the user
|
|
56
|
+
* @param projectJson - sfdxProjectJson
|
|
57
|
+
* @param hubOrg - the hub org, in case we need to do queries
|
|
58
|
+
*/
|
|
59
|
+
const getAncestorIds = async (scratchOrgInfo, projectJson, hubOrg) => {
|
|
60
|
+
if (Reflect.has(scratchOrgInfo, 'package2AncestorIds')) {
|
|
61
|
+
throw new sfdxError_1.SfdxError(messages.getMessage('errorpackage2AncestorIdsKeyNotSupported'), 'DeprecationError');
|
|
62
|
+
}
|
|
63
|
+
const packagesWithAncestors = (await projectJson.getPackageDirectories())
|
|
64
|
+
// check that the package has any ancestor types (id or version)
|
|
65
|
+
.filter((packageDir) => packageDir.ancestorId || packageDir.ancestorVersion);
|
|
66
|
+
if (packagesWithAncestors.length === 0) {
|
|
67
|
+
return '';
|
|
68
|
+
}
|
|
69
|
+
const ancestorIds = await Promise.all(packagesWithAncestors.map(async (packageDir) => {
|
|
70
|
+
var _a, _b, _c;
|
|
71
|
+
// ancestorID can be 05i, or 04t, alias; OR "ancestorVersion": "4.6.0.1"
|
|
72
|
+
// 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
|
|
73
|
+
if (packageDir.ancestorVersion) {
|
|
74
|
+
if (!/^[0-9]+.[0-9]+.[0-9]+(.[0-9]+)?$/.test(packageDir.ancestorVersion)) {
|
|
75
|
+
throw sfdxError_1.SfdxError.create('@salesforce/core', 'scratchOrgInfoGenerator', 'errorInvalidAncestorVersionFormat', [
|
|
76
|
+
packageDir.ancestorVersion,
|
|
77
|
+
]);
|
|
78
|
+
}
|
|
79
|
+
// package can be an ID, but not according to docs
|
|
80
|
+
const packageAliases = projectJson.get('packageAliases');
|
|
81
|
+
const packageId = (_a = packageAliases[ts_types_1.ensureString(packageDir.package)]) !== null && _a !== void 0 ? _a : packageDir.package;
|
|
82
|
+
const [major, minor, patch] = packageDir.ancestorVersion.split('.');
|
|
83
|
+
let releasedAncestor;
|
|
84
|
+
try {
|
|
85
|
+
releasedAncestor = await hubOrg
|
|
86
|
+
.getConnection()
|
|
87
|
+
.singleRecordQuery(`SELECT Id, IsReleased FROM Package2Version WHERE Package2Id = '${packageId}' AND MajorVersion = ${major} AND MinorVersion = ${minor} AND PatchVersion = ${patch} and IsReleased = true`, { tooling: true });
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
throw new sfdxError_1.SfdxError(messages.getMessage('errorNoMatchingAncestor', [packageDir.ancestorVersion, packageDir.package]), 'ErrorNoMatchingAncestor', [messages.getMessage('errorAncestorNotReleased', [packageDir.ancestorVersion])]);
|
|
91
|
+
}
|
|
92
|
+
if (packageDir.ancestorId && packageDir.ancestorId !== releasedAncestor.Id) {
|
|
93
|
+
throw sfdxError_1.SfdxError.create('@salesforce/core', 'scratchOrgInfogenerator', 'ErrorAncestorIdVersionMismatch', [
|
|
94
|
+
packageDir.ancestorVersion,
|
|
95
|
+
packageDir.ancestorId,
|
|
96
|
+
]);
|
|
97
|
+
}
|
|
98
|
+
return releasedAncestor.Id;
|
|
99
|
+
}
|
|
100
|
+
if ((_b = packageDir === null || packageDir === void 0 ? void 0 : packageDir.ancestorId) === null || _b === void 0 ? void 0 : _b.startsWith('05i')) {
|
|
101
|
+
// if it's already a 05i return it, otherwise query for it
|
|
102
|
+
return packageDir.ancestorId;
|
|
103
|
+
}
|
|
104
|
+
if ((_c = packageDir === null || packageDir === void 0 ? void 0 : packageDir.ancestorId) === null || _c === void 0 ? void 0 : _c.startsWith('04t')) {
|
|
105
|
+
// query for the Id
|
|
106
|
+
return (await hubOrg
|
|
107
|
+
.getConnection()
|
|
108
|
+
.singleRecordQuery(`SELECT Id FROM Package2Version WHERE SubscriberPackageVersionId = '${packageDir.ancestorId}'`, { tooling: true })).Id;
|
|
109
|
+
}
|
|
110
|
+
// ancestorID can be an alias get it from projectJson
|
|
111
|
+
const packageAliases = projectJson.get('packageAliases');
|
|
112
|
+
if (packageDir.ancestorId && (packageAliases === null || packageAliases === void 0 ? void 0 : packageAliases[packageDir.ancestorId])) {
|
|
113
|
+
return packageAliases[packageDir.ancestorId];
|
|
114
|
+
}
|
|
115
|
+
throw new sfdxError_1.SfdxError(`Invalid ancestorId ${packageDir.ancestorId}`, 'InvalidAncestorId');
|
|
116
|
+
}));
|
|
117
|
+
return Array.from(new Set(ancestorIds)).join(';');
|
|
118
|
+
};
|
|
119
|
+
exports.getAncestorIds = getAncestorIds;
|
|
120
|
+
/**
|
|
121
|
+
* Takes in a scratchOrgInfo and fills in the missing fields
|
|
122
|
+
*
|
|
123
|
+
* @param hubOrg the environment hub org
|
|
124
|
+
* @param scratchOrgInfoPayload - the scratchOrgInfo passed in by the user
|
|
125
|
+
* @param nonamespace create the scratch org with no namespace
|
|
126
|
+
* @param ignoreAncestorIds true if the sfdx-project.json ancestorId keys should be ignored
|
|
127
|
+
*/
|
|
128
|
+
const generateScratchOrgInfo = async ({ hubOrg, scratchOrgInfoPayload, nonamespace, ignoreAncestorIds, }) => {
|
|
129
|
+
var _a, _b;
|
|
130
|
+
let sfdxProject;
|
|
131
|
+
try {
|
|
132
|
+
sfdxProject = await sfdxProject_1.SfdxProjectJson.create({});
|
|
133
|
+
}
|
|
134
|
+
catch (e) {
|
|
135
|
+
// project is not required
|
|
136
|
+
}
|
|
137
|
+
scratchOrgInfoPayload.orgName = (_a = scratchOrgInfoPayload.orgName) !== null && _a !== void 0 ? _a : 'Company';
|
|
138
|
+
scratchOrgInfoPayload.package2AncestorIds =
|
|
139
|
+
!ignoreAncestorIds && (sfdxProject === null || sfdxProject === void 0 ? void 0 : sfdxProject.hasPackages())
|
|
140
|
+
? await exports.getAncestorIds(scratchOrgInfoPayload, sfdxProject, hubOrg)
|
|
141
|
+
: '';
|
|
142
|
+
// Use the Hub org's client ID value, if one wasn't provided to us, or the default
|
|
143
|
+
if (!scratchOrgInfoPayload.connectedAppConsumerKey) {
|
|
144
|
+
scratchOrgInfoPayload.connectedAppConsumerKey =
|
|
145
|
+
(_b = hubOrg.getConnection().getAuthInfoFields().clientId) !== null && _b !== void 0 ? _b : defaultConnectedAppInfo.clientId;
|
|
146
|
+
}
|
|
147
|
+
if (!nonamespace && (sfdxProject === null || sfdxProject === void 0 ? void 0 : sfdxProject.get('namespace'))) {
|
|
148
|
+
scratchOrgInfoPayload.namespace = sfdxProject.get('namespace');
|
|
149
|
+
}
|
|
150
|
+
// we already have the info, and want to get rid of configApi, so this doesn't use that
|
|
151
|
+
scratchOrgInfoPayload.connectedAppCallbackUrl = `http://localhost:${await webOAuthServer_1.WebOAuthServer.determineOauthPort()}/OauthRedirect`;
|
|
152
|
+
return scratchOrgInfoPayload;
|
|
153
|
+
};
|
|
154
|
+
exports.generateScratchOrgInfo = generateScratchOrgInfo;
|
|
155
|
+
/**
|
|
156
|
+
* Returns a valid signup json
|
|
157
|
+
*
|
|
158
|
+
* @param definitionjson org definition in JSON format
|
|
159
|
+
* @param definitionfile path to an org definition file
|
|
160
|
+
* @param connectedAppConsumerKey The connected app consumer key. May be null for JWT OAuth flow.
|
|
161
|
+
* @param durationdays duration of the scratch org (in days) (default:1, min:1, max:30)
|
|
162
|
+
* @param nonamespace create the scratch org with no namespace
|
|
163
|
+
* @param noancestors do not include second-generation package ancestors in the scratch org
|
|
164
|
+
* @param orgConfig overrides definitionjson
|
|
165
|
+
* @returns scratchOrgInfoPayload: ScratchOrgInfoPayload;
|
|
166
|
+
ignoreAncestorIds: boolean;
|
|
167
|
+
warnings: string[];
|
|
168
|
+
*/
|
|
169
|
+
const getScratchOrgInfoPayload = async (options) => {
|
|
170
|
+
var _a;
|
|
171
|
+
let warnings = [];
|
|
172
|
+
// orgConfig input overrides definitionjson (-j option; hidden/deprecated)
|
|
173
|
+
const definitionJson = options.definitionjson ? JSON.parse(options.definitionjson) : {};
|
|
174
|
+
const orgConfigInput = { ...definitionJson, ...((_a = options.orgConfig) !== null && _a !== void 0 ? _a : {}) };
|
|
175
|
+
let scratchOrgInfoPayload = orgConfigInput;
|
|
176
|
+
// the -f option
|
|
177
|
+
if (options.definitionfile) {
|
|
178
|
+
try {
|
|
179
|
+
const fileData = await fs_1.promises.readFile(options.definitionfile, 'utf8');
|
|
180
|
+
const defFileContents = kit_1.parseJson(fileData);
|
|
181
|
+
// definitionjson and orgConfig override file input
|
|
182
|
+
scratchOrgInfoPayload = { ...defFileContents, ...orgConfigInput };
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
const error = err;
|
|
186
|
+
if (error.name === 'JsonParseError') {
|
|
187
|
+
throw new sfdxError_1.SfdxError(`An error occurred parsing ${options.definitionfile}`);
|
|
188
|
+
}
|
|
189
|
+
throw sfdxError_1.SfdxError.wrap(error);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// scratchOrgInfoPayload must be heads down camelcase.
|
|
193
|
+
const upperCaseKey = sfdc_1.sfdc.findUpperCaseKeys(scratchOrgInfoPayload);
|
|
194
|
+
if (upperCaseKey) {
|
|
195
|
+
throw new sfdxError_1.SfdxError('InvalidJsonCasing', upperCaseKey);
|
|
196
|
+
}
|
|
197
|
+
// Now run the fully resolved user input against the validator
|
|
198
|
+
Object.keys(scratchOrgInfoPayload).forEach((key) => {
|
|
199
|
+
optionsValidator(key, scratchOrgInfoPayload);
|
|
200
|
+
});
|
|
201
|
+
if (options.connectedAppConsumerKey) {
|
|
202
|
+
scratchOrgInfoPayload.connectedAppConsumerKey = options.connectedAppConsumerKey;
|
|
203
|
+
}
|
|
204
|
+
scratchOrgInfoPayload.durationDays = options.durationDays;
|
|
205
|
+
// Throw warnings for deprecated scratch org features.
|
|
206
|
+
const scratchOrgFeatureDeprecation = new scratchOrgFeatureDeprecation_1.ScratchOrgFeatureDeprecation();
|
|
207
|
+
// convert various supported array and string formats to a semi-colon-delimited string
|
|
208
|
+
if (scratchOrgInfoPayload.features) {
|
|
209
|
+
if (typeof scratchOrgInfoPayload.features === 'string') {
|
|
210
|
+
scratchOrgInfoPayload.features = scratchOrgInfoPayload.features.split(/[;,]/);
|
|
211
|
+
}
|
|
212
|
+
warnings = scratchOrgFeatureDeprecation.getFeatureWarnings(scratchOrgInfoPayload.features);
|
|
213
|
+
scratchOrgInfoPayload.features = scratchOrgInfoPayload.features.map((feature) => feature.trim());
|
|
214
|
+
scratchOrgInfoPayload.features = scratchOrgFeatureDeprecation
|
|
215
|
+
.filterDeprecatedFeatures(scratchOrgInfoPayload.features)
|
|
216
|
+
.join(';');
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
scratchOrgInfoPayload,
|
|
220
|
+
// Ignore ancestor ids only when 'nonamespace' or 'noancestors' options are specified
|
|
221
|
+
ignoreAncestorIds: options.nonamespace || options.noancestors || false,
|
|
222
|
+
warnings,
|
|
223
|
+
};
|
|
224
|
+
};
|
|
225
|
+
exports.getScratchOrgInfoPayload = getScratchOrgInfoPayload;
|
|
226
|
+
//# sourceMappingURL=scratchOrgInfoGenerator.js.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { JsonMap } from '@salesforce/ts-types';
|
|
2
|
+
import { Org } from './org';
|
|
3
|
+
import { ScratchOrgInfo } from './scratchOrgInfoApi';
|
|
4
|
+
export declare enum RequestStatus {
|
|
5
|
+
Pending = "Pending",
|
|
6
|
+
InProgress = "InProgress",
|
|
7
|
+
Succeeded = "Succeeded",
|
|
8
|
+
SucceededPartial = "SucceededPartial",
|
|
9
|
+
Failed = "Failed",
|
|
10
|
+
Canceling = "Canceling",
|
|
11
|
+
Canceled = "Canceled"
|
|
12
|
+
}
|
|
13
|
+
export interface ObjectSetting extends JsonMap {
|
|
14
|
+
sharingModel?: string;
|
|
15
|
+
defaultRecordType?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface BusinessProcessFileContent extends JsonMap {
|
|
18
|
+
fullName: string;
|
|
19
|
+
isActive: boolean;
|
|
20
|
+
values: [
|
|
21
|
+
{
|
|
22
|
+
fullName: string;
|
|
23
|
+
default?: boolean;
|
|
24
|
+
}
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Helper class for dealing with the settings that are defined in a scratch definition file. This class knows how to extract the
|
|
29
|
+
* settings from the definition, how to expand them into a MD directory and how to generate a package.xml.
|
|
30
|
+
*/
|
|
31
|
+
export default class SettingsGenerator {
|
|
32
|
+
private settingData?;
|
|
33
|
+
private objectSettingsData?;
|
|
34
|
+
private logger;
|
|
35
|
+
private writer;
|
|
36
|
+
private shapeDirName;
|
|
37
|
+
constructor();
|
|
38
|
+
/** extract the settings from the scratch def file, if they are present. */
|
|
39
|
+
extract(scratchDef: ScratchOrgInfo): Promise<void>;
|
|
40
|
+
/** True if we are currently tracking setting or object setting data. */
|
|
41
|
+
hasSettings(): boolean;
|
|
42
|
+
/** Create temporary deploy directory used to upload the scratch org shape.
|
|
43
|
+
* This will create the dir, all of the .setting files and minimal object files needed for objectSettings
|
|
44
|
+
*/
|
|
45
|
+
createDeploy(): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Deploys the settings to the org.
|
|
48
|
+
*/
|
|
49
|
+
deploySettingsViaFolder(scratchOrg: Org, apiVersion: string): Promise<void>;
|
|
50
|
+
private writeObjectSettingsIfNeeded;
|
|
51
|
+
private writeSettingsIfNeeded;
|
|
52
|
+
private createPackageXml;
|
|
53
|
+
private createObjectFileContent;
|
|
54
|
+
private createRecordTypeFileContent;
|
|
55
|
+
private createBusinessProcessFileContent;
|
|
56
|
+
}
|