@salesforce/core 3.7.1 → 3.7.5
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 +96 -2
- package/LICENSE.txt +1 -1
- package/lib/config/config.js +15 -15
- package/lib/config/configAggregator.js +4 -4
- package/lib/config/configFile.js +9 -9
- package/lib/config/configStore.js +12 -13
- package/lib/config/envVars.d.ts +20 -5
- package/lib/config/envVars.js +75 -10
- package/lib/config/keychainConfig.js +1 -1
- package/lib/crypto/crypto.js +6 -6
- package/lib/crypto/keyChainImpl.js +6 -5
- package/lib/crypto/secureBuffer.js +1 -1
- package/lib/deviceOauthService.js +5 -4
- package/lib/exported.d.ts +2 -2
- package/lib/exported.js +6 -2
- package/lib/globalInfo/globalInfoConfig.js +1 -1
- package/lib/globalInfo/sfdxDataHandler.js +11 -11
- package/lib/lifecycleEvents.d.ts +38 -1
- package/lib/lifecycleEvents.js +73 -2
- package/lib/logger.js +13 -12
- package/lib/messages.js +10 -9
- package/lib/org/authInfo.d.ts +2 -6
- package/lib/org/authInfo.js +44 -41
- package/lib/org/connection.js +14 -11
- package/lib/org/org.d.ts +121 -9
- package/lib/org/org.js +373 -36
- package/lib/org/orgConfigProperties.js +1 -1
- package/lib/org/permissionSetAssignment.js +3 -3
- package/lib/org/user.js +20 -20
- package/lib/schema/printer.js +18 -18
- package/lib/schema/validator.js +8 -8
- package/lib/sfdxError.d.ts +1 -1
- package/lib/sfdxError.js +3 -2
- package/lib/sfdxProject.js +15 -13
- package/lib/status/myDomainResolver.js +3 -3
- package/lib/status/pollingClient.d.ts +0 -1
- package/lib/status/pollingClient.js +2 -2
- package/lib/status/streamingClient.d.ts +2 -3
- package/lib/status/streamingClient.js +16 -22
- package/lib/status/types.d.ts +89 -0
- package/lib/status/types.js +18 -0
- package/lib/testSetup.d.ts +3 -2
- package/lib/testSetup.js +25 -25
- package/lib/util/cache.js +3 -3
- package/lib/util/fs.js +12 -12
- package/lib/util/sfdc.js +3 -3
- package/lib/util/sfdcUrl.d.ts +2 -1
- package/lib/util/sfdcUrl.js +16 -8
- package/lib/webOAuthServer.js +7 -7
- package/messages/core.json +3 -3
- package/messages/core.md +1 -1
- package/messages/envVars.md +53 -13
- package/messages/org.md +36 -0
- package/package.json +39 -46
package/lib/org/org.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.Org = void 0;
|
|
9
|
+
exports.Org = exports.SandboxEvents = exports.OrgTypes = void 0;
|
|
10
10
|
const path_1 = require("path");
|
|
11
11
|
const kit_1 = require("@salesforce/kit");
|
|
12
12
|
const ts_types_1 = require("@salesforce/ts-types");
|
|
@@ -21,11 +21,37 @@ const fs_1 = require("../util/fs");
|
|
|
21
21
|
const sfdc_1 = require("../util/sfdc");
|
|
22
22
|
const globalInfo_1 = require("../globalInfo");
|
|
23
23
|
const messages_1 = require("../messages");
|
|
24
|
+
const lifecycleEvents_1 = require("../lifecycleEvents");
|
|
25
|
+
const webOAuthServer_1 = require("../webOAuthServer");
|
|
24
26
|
const connection_1 = require("./connection");
|
|
25
27
|
const authInfo_1 = require("./authInfo");
|
|
26
28
|
const orgConfigProperties_1 = require("./orgConfigProperties");
|
|
27
29
|
messages_1.Messages.importMessagesDirectory(__dirname);
|
|
28
|
-
const messages = messages_1.Messages.load('@salesforce/core', 'org', [
|
|
30
|
+
const messages = messages_1.Messages.load('@salesforce/core', 'org', [
|
|
31
|
+
'deleteOrgHubError',
|
|
32
|
+
'insufficientAccessToDelete',
|
|
33
|
+
'missingAuthUsername',
|
|
34
|
+
'noDevHubFound',
|
|
35
|
+
'notADevHub',
|
|
36
|
+
'noUsernameFound',
|
|
37
|
+
'orgPollingTimeout',
|
|
38
|
+
'sandboxDeleteFailed',
|
|
39
|
+
'sandboxInfoCreateFailed',
|
|
40
|
+
'sandboxNotFound',
|
|
41
|
+
'scratchOrgNotFound',
|
|
42
|
+
]);
|
|
43
|
+
var OrgTypes;
|
|
44
|
+
(function (OrgTypes) {
|
|
45
|
+
OrgTypes["Scratch"] = "scratch";
|
|
46
|
+
OrgTypes["Sandbox"] = "sandbox";
|
|
47
|
+
})(OrgTypes = exports.OrgTypes || (exports.OrgTypes = {}));
|
|
48
|
+
var SandboxEvents;
|
|
49
|
+
(function (SandboxEvents) {
|
|
50
|
+
SandboxEvents["EVENT_STATUS"] = "status";
|
|
51
|
+
SandboxEvents["EVENT_ASYNC_RESULT"] = "asyncResult";
|
|
52
|
+
SandboxEvents["EVENT_RESULT"] = "result";
|
|
53
|
+
SandboxEvents["EVENT_AUTH"] = "auth";
|
|
54
|
+
})(SandboxEvents = exports.SandboxEvents || (exports.SandboxEvents = {}));
|
|
29
55
|
/**
|
|
30
56
|
* Provides a way to manage a locally authenticated Org.
|
|
31
57
|
*
|
|
@@ -61,6 +87,33 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
61
87
|
this.status = Org.Status.UNKNOWN;
|
|
62
88
|
this.options = options || {};
|
|
63
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* create a sandbox from a production org
|
|
92
|
+
* 'this' needs to be a production org with sandbox licenses available
|
|
93
|
+
*
|
|
94
|
+
* @param sandboxReq SandboxRequest options to create the sandbox with
|
|
95
|
+
* @param options Wait: The amount of time to wait before timing out, Interval: The time interval between polling
|
|
96
|
+
*/
|
|
97
|
+
async createSandbox(sandboxReq, options) {
|
|
98
|
+
var _a;
|
|
99
|
+
this.logger.debug('CreateSandbox called with SandboxRequest: %s ', sandboxReq);
|
|
100
|
+
const createResult = await this.connection.tooling.create('SandboxInfo', sandboxReq);
|
|
101
|
+
this.logger.debug('Return from calling tooling.create: %s ', createResult);
|
|
102
|
+
if (Array.isArray(createResult) || !createResult.success) {
|
|
103
|
+
throw messages.createError('sandboxInfoCreateFailed', [JSON.stringify(createResult)]);
|
|
104
|
+
}
|
|
105
|
+
const sandboxCreationProgress = await this.querySandboxProcess(createResult.id);
|
|
106
|
+
this.logger.debug('Return from calling singleRecordQuery with tooling: %s', sandboxCreationProgress);
|
|
107
|
+
const retries = options.wait ? options.wait.seconds / kit_1.Duration.seconds(30).seconds : 0;
|
|
108
|
+
this.logger.debug('pollStatusAndAuth sandboxProcessObj %s, maxPollingRetries %i', sandboxCreationProgress, retries);
|
|
109
|
+
const pollInterval = (_a = options.interval) !== null && _a !== void 0 ? _a : kit_1.Duration.seconds(30);
|
|
110
|
+
return this.pollStatusAndAuth({
|
|
111
|
+
sandboxProcessObj: sandboxCreationProgress,
|
|
112
|
+
retries,
|
|
113
|
+
shouldPoll: retries > 0,
|
|
114
|
+
pollInterval,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
64
117
|
/**
|
|
65
118
|
* Clean all data files in the org's data path. Usually <workspace>/.sfdx/orgs/<username>.
|
|
66
119
|
*
|
|
@@ -71,11 +124,11 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
71
124
|
let dataPath;
|
|
72
125
|
try {
|
|
73
126
|
const rootFolder = await config_1.Config.resolveRootFolder(false);
|
|
74
|
-
dataPath = path_1.join(rootFolder, global_1.Global.SFDX_STATE_FOLDER, orgDataPath ? orgDataPath : 'orgs');
|
|
127
|
+
dataPath = (0, path_1.join)(rootFolder, global_1.Global.SFDX_STATE_FOLDER, orgDataPath ? orgDataPath : 'orgs');
|
|
75
128
|
this.logger.debug(`cleaning data for path: ${dataPath}`);
|
|
76
129
|
}
|
|
77
130
|
catch (err) {
|
|
78
|
-
if (err.name === 'InvalidProjectWorkspaceError') {
|
|
131
|
+
if (err instanceof Error && err.name === 'InvalidProjectWorkspaceError') {
|
|
79
132
|
// If we aren't in a project dir, we can't clean up data files.
|
|
80
133
|
// If the user unlink this org outside of the workspace they used it in,
|
|
81
134
|
// data files will be left over.
|
|
@@ -113,6 +166,13 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
113
166
|
// So, just in case no users are added to this org we will try the remove again.
|
|
114
167
|
await this.removeAuth();
|
|
115
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Check if org is a sandbox org by checking its SandboxOrgConfig.
|
|
171
|
+
*
|
|
172
|
+
*/
|
|
173
|
+
async isSandbox() {
|
|
174
|
+
return !!(await this.getSandboxOrgConfigField(sandboxOrgConfig_1.SandboxOrgConfig.Fields.PROD_ORG_USERNAME));
|
|
175
|
+
}
|
|
116
176
|
/**
|
|
117
177
|
* Check that this org is a scratch org by asking the dev hub if it knows about it.
|
|
118
178
|
*
|
|
@@ -133,12 +193,12 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
133
193
|
const DEV_HUB_SOQL = `SELECT CreatedDate,Edition,ExpirationDate FROM ActiveScratchOrg WHERE ScratchOrg='${trimmedId}'`;
|
|
134
194
|
try {
|
|
135
195
|
const results = await devHubConnection.query(DEV_HUB_SOQL);
|
|
136
|
-
if (ts_types_1.getNumber(results, 'records.length') !== 1) {
|
|
196
|
+
if ((0, ts_types_1.getNumber)(results, 'records.length') !== 1) {
|
|
137
197
|
throw new sfdxError_1.SfdxError('No results', 'NoResultsError');
|
|
138
198
|
}
|
|
139
199
|
}
|
|
140
200
|
catch (err) {
|
|
141
|
-
if (err.name === 'INVALID_TYPE') {
|
|
201
|
+
if (err instanceof Error && err.name === 'INVALID_TYPE') {
|
|
142
202
|
throw messages.createError('notADevHub', [devHubConnection.getUsername()]);
|
|
143
203
|
}
|
|
144
204
|
throw err;
|
|
@@ -153,7 +213,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
153
213
|
return this;
|
|
154
214
|
}
|
|
155
215
|
else if (this.getField(Org.Fields.DEV_HUB_USERNAME)) {
|
|
156
|
-
const devHubUsername = ts_types_1.ensureString(this.getField(Org.Fields.DEV_HUB_USERNAME));
|
|
216
|
+
const devHubUsername = (0, ts_types_1.ensureString)(this.getField(Org.Fields.DEV_HUB_USERNAME));
|
|
157
217
|
return Org.create({
|
|
158
218
|
connection: await connection_1.Connection.create({
|
|
159
219
|
authInfo: await authInfo_1.AuthInfo.create({ username: devHubUsername }),
|
|
@@ -170,13 +230,43 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
170
230
|
*/
|
|
171
231
|
isDevHubOrg() {
|
|
172
232
|
const isDevHub = this.getField(Org.Fields.IS_DEV_HUB);
|
|
173
|
-
if (ts_types_1.isBoolean(isDevHub)) {
|
|
233
|
+
if ((0, ts_types_1.isBoolean)(isDevHub)) {
|
|
174
234
|
return isDevHub;
|
|
175
235
|
}
|
|
176
236
|
else {
|
|
177
237
|
return false;
|
|
178
238
|
}
|
|
179
239
|
}
|
|
240
|
+
/**
|
|
241
|
+
* Will delete 'this' instance remotely and any files locally
|
|
242
|
+
*
|
|
243
|
+
* @param controllingOrg username or Org that 'this.devhub' or 'this.production' refers to. AKA a DevHub for a scratch org, or a Production Org for a sandbox
|
|
244
|
+
*/
|
|
245
|
+
async deleteFrom(controllingOrg) {
|
|
246
|
+
if (typeof controllingOrg === 'string') {
|
|
247
|
+
controllingOrg = await Org.create({
|
|
248
|
+
aggregator: this.configAggregator,
|
|
249
|
+
aliasOrUsername: controllingOrg,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
if (await this.isSandbox()) {
|
|
253
|
+
await this.deleteSandbox(controllingOrg);
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
await this.deleteScratchOrg(controllingOrg);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Will delete 'this' instance remotely and any files locally
|
|
261
|
+
*/
|
|
262
|
+
async delete() {
|
|
263
|
+
if (await this.isSandbox()) {
|
|
264
|
+
await this.deleteSandbox();
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
await this.deleteScratchOrg();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
180
270
|
/**
|
|
181
271
|
* Returns `true` if the org is a Dev Hub.
|
|
182
272
|
*
|
|
@@ -189,7 +279,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
189
279
|
*/
|
|
190
280
|
async determineIfDevHubOrg(forceServerCheck = false) {
|
|
191
281
|
const cachedIsDevHub = this.getField(Org.Fields.IS_DEV_HUB);
|
|
192
|
-
if (!forceServerCheck && ts_types_1.isBoolean(cachedIsDevHub)) {
|
|
282
|
+
if (!forceServerCheck && (0, ts_types_1.isBoolean)(cachedIsDevHub)) {
|
|
193
283
|
return cachedIsDevHub;
|
|
194
284
|
}
|
|
195
285
|
if (this.isDevHubOrg()) {
|
|
@@ -205,7 +295,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
205
295
|
catch (err) {
|
|
206
296
|
/* Not a dev hub */
|
|
207
297
|
}
|
|
208
|
-
const username = ts_types_1.ensure(this.getUsername());
|
|
298
|
+
const username = (0, ts_types_1.ensure)(this.getUsername());
|
|
209
299
|
const authInfo = await authInfo_1.AuthInfo.create({ username });
|
|
210
300
|
await authInfo.save({ isDevHub });
|
|
211
301
|
// Reset the connection with the updated auth file
|
|
@@ -221,7 +311,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
221
311
|
*/
|
|
222
312
|
isScratch() {
|
|
223
313
|
const isScratch = this.getField(Org.Fields.IS_SCRATCH);
|
|
224
|
-
if (ts_types_1.isBoolean(isScratch)) {
|
|
314
|
+
if ((0, ts_types_1.isBoolean)(isScratch)) {
|
|
225
315
|
return isScratch;
|
|
226
316
|
}
|
|
227
317
|
else {
|
|
@@ -243,22 +333,6 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
243
333
|
}
|
|
244
334
|
return cache;
|
|
245
335
|
}
|
|
246
|
-
/**
|
|
247
|
-
* Returns `true` if the org is a sandbox.
|
|
248
|
-
*
|
|
249
|
-
* **Note** This relies on a cached value in the auth file. If that property
|
|
250
|
-
* is not cached, this method will **always return false even if the org is a
|
|
251
|
-
* sandbox**. If you need accuracy, use the {@link Org.determineIfDevHubOrg} method.
|
|
252
|
-
*/
|
|
253
|
-
isSandbox() {
|
|
254
|
-
const isSandbox = this.getField(Org.Fields.IS_SANDBOX);
|
|
255
|
-
if (ts_types_1.isBoolean(isSandbox)) {
|
|
256
|
-
return isSandbox;
|
|
257
|
-
}
|
|
258
|
-
else {
|
|
259
|
-
return false;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
336
|
/**
|
|
263
337
|
* Returns `true` if the org is a sandbox.
|
|
264
338
|
*
|
|
@@ -323,8 +397,8 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
323
397
|
async readUserAuthFiles() {
|
|
324
398
|
const config = await this.retrieveOrgUsersConfig();
|
|
325
399
|
const contents = await config.read();
|
|
326
|
-
const thisUsername = ts_types_1.ensure(this.getUsername());
|
|
327
|
-
const usernames = ts_types_1.ensureJsonArray(contents.usernames || [thisUsername]);
|
|
400
|
+
const thisUsername = (0, ts_types_1.ensure)(this.getUsername());
|
|
401
|
+
const usernames = (0, ts_types_1.ensureJsonArray)(contents.usernames || [thisUsername]);
|
|
328
402
|
return Promise.all(usernames.map((username) => {
|
|
329
403
|
if (username === thisUsername) {
|
|
330
404
|
return authInfo_1.AuthInfo.create({
|
|
@@ -332,7 +406,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
332
406
|
});
|
|
333
407
|
}
|
|
334
408
|
else {
|
|
335
|
-
return authInfo_1.AuthInfo.create({ username: ts_types_1.ensureString(username) });
|
|
409
|
+
return authInfo_1.AuthInfo.create({ username: (0, ts_types_1.ensureString)(username) });
|
|
336
410
|
}
|
|
337
411
|
}));
|
|
338
412
|
}
|
|
@@ -357,18 +431,18 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
357
431
|
if (!auth) {
|
|
358
432
|
throw new sfdxError_1.SfdxError('Missing auth info', 'MissingAuthInfo');
|
|
359
433
|
}
|
|
360
|
-
const authInfo = ts_types_1.isString(auth) ? await authInfo_1.AuthInfo.create({ username: auth }) : auth;
|
|
434
|
+
const authInfo = (0, ts_types_1.isString)(auth) ? await authInfo_1.AuthInfo.create({ username: auth }) : auth;
|
|
361
435
|
this.logger.debug(`adding username ${authInfo.getFields().username}`);
|
|
362
436
|
const orgConfig = await this.retrieveOrgUsersConfig();
|
|
363
437
|
const contents = await orgConfig.read();
|
|
364
438
|
// TODO: This is kind of screwy because contents values can be `AnyJson | object`...
|
|
365
439
|
// needs config refactoring to improve
|
|
366
440
|
const usernames = contents.usernames || [];
|
|
367
|
-
if (!ts_types_1.isArray(usernames)) {
|
|
441
|
+
if (!(0, ts_types_1.isArray)(usernames)) {
|
|
368
442
|
throw new sfdxError_1.SfdxError('Usernames is not an array', 'UnexpectedDataFormat');
|
|
369
443
|
}
|
|
370
444
|
let shouldUpdate = false;
|
|
371
|
-
const thisUsername = ts_types_1.ensure(this.getUsername());
|
|
445
|
+
const thisUsername = (0, ts_types_1.ensure)(this.getUsername());
|
|
372
446
|
if (!usernames.includes(thisUsername)) {
|
|
373
447
|
usernames.push(thisUsername);
|
|
374
448
|
shouldUpdate = true;
|
|
@@ -395,7 +469,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
395
469
|
if (!auth) {
|
|
396
470
|
throw new sfdxError_1.SfdxError('Missing auth info', 'MissingAuthInfoError');
|
|
397
471
|
}
|
|
398
|
-
const authInfo = ts_types_1.isString(auth) ? await authInfo_1.AuthInfo.create({ username: auth }) : auth;
|
|
472
|
+
const authInfo = (0, ts_types_1.isString)(auth) ? await authInfo_1.AuthInfo.create({ username: auth }) : auth;
|
|
399
473
|
this.logger.debug(`removing username ${authInfo.getFields().username}`);
|
|
400
474
|
const orgConfig = await this.retrieveOrgUsersConfig();
|
|
401
475
|
const contents = await orgConfig.read();
|
|
@@ -502,7 +576,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
502
576
|
}
|
|
503
577
|
this.connection = await connection_1.Connection.create({
|
|
504
578
|
// If no username is provided or resolvable from an alias, AuthInfo will throw an SfdxError.
|
|
505
|
-
authInfo: await authInfo_1.AuthInfo.create({ username }),
|
|
579
|
+
authInfo: await authInfo_1.AuthInfo.create({ username, isDevHub: this.options.isDevHub }),
|
|
506
580
|
});
|
|
507
581
|
}
|
|
508
582
|
else {
|
|
@@ -516,6 +590,105 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
516
590
|
getDefaultOptions() {
|
|
517
591
|
throw new sfdxError_1.SfdxError('Not Supported', 'NotSupportedError');
|
|
518
592
|
}
|
|
593
|
+
async queryProduction(org, field, value) {
|
|
594
|
+
return org.connection.singleRecordQuery(`SELECT SandboxInfoId FROM SandboxProcess WHERE ${field} ='${value}' AND Status NOT IN ('D', 'E')`, { tooling: true });
|
|
595
|
+
}
|
|
596
|
+
async destorySandbox(org, id) {
|
|
597
|
+
return org.getConnection().tooling.delete('SandboxInfo', id);
|
|
598
|
+
}
|
|
599
|
+
async destoryScratchOrg(org, id) {
|
|
600
|
+
return org.getConnection().delete('ActiveScratchOrg', id);
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* this method will delete the sandbox org from the production org and clean up any local files
|
|
604
|
+
*
|
|
605
|
+
* @param prodOrg - Production org associated with this sandbox
|
|
606
|
+
* @private
|
|
607
|
+
*/
|
|
608
|
+
async deleteSandbox(prodOrg) {
|
|
609
|
+
prodOrg !== null && prodOrg !== void 0 ? prodOrg : (prodOrg = await Org.create({
|
|
610
|
+
aggregator: this.configAggregator,
|
|
611
|
+
aliasOrUsername: await this.getSandboxOrgConfigField(sandboxOrgConfig_1.SandboxOrgConfig.Fields.PROD_ORG_USERNAME),
|
|
612
|
+
}));
|
|
613
|
+
let result;
|
|
614
|
+
// attempt to locate sandbox id by username
|
|
615
|
+
try {
|
|
616
|
+
// try to calculate sandbox name from the production org
|
|
617
|
+
// production org: admin@integrationtesthub.org
|
|
618
|
+
// this.getUsername: admin@integrationtesthub.org.dev1
|
|
619
|
+
// sandboxName in Production: dev1
|
|
620
|
+
const sandboxName = (this.getUsername() || '').split(`${prodOrg.getUsername()}.`)[1];
|
|
621
|
+
if (!sandboxName) {
|
|
622
|
+
this.logger.debug('Could not construct a sandbox name');
|
|
623
|
+
throw new Error();
|
|
624
|
+
}
|
|
625
|
+
this.logger.debug(`attempting to locate sandbox with username ${sandboxName}`);
|
|
626
|
+
result = await this.queryProduction(prodOrg, 'SandboxName', sandboxName);
|
|
627
|
+
if (!result) {
|
|
628
|
+
this.logger.debug(`Failed to find sandbox with username: ${sandboxName}`);
|
|
629
|
+
throw new Error();
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
catch {
|
|
633
|
+
// if an error is thrown, don't panic yet. we'll try querying by orgId
|
|
634
|
+
const trimmedId = sfdc_1.sfdc.trimTo15(this.getOrgId());
|
|
635
|
+
this.logger.debug(`defaulting to trimming id from ${this.getOrgId()} to ${trimmedId}`);
|
|
636
|
+
try {
|
|
637
|
+
result = await this.queryProduction(prodOrg, 'SandboxOrganization', trimmedId);
|
|
638
|
+
}
|
|
639
|
+
catch {
|
|
640
|
+
throw messages.createError('sandboxNotFound', [trimmedId]);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
// const deleteResult = await prodOrg.connection.tooling.delete('SandboxInfo', result.SandboxInfoId);
|
|
644
|
+
const deleteResult = await this.destorySandbox(prodOrg, result.SandboxInfoId);
|
|
645
|
+
this.logger.debug('Return from calling tooling.delete: %o ', deleteResult);
|
|
646
|
+
await this.remove();
|
|
647
|
+
if (Array.isArray(deleteResult) || !deleteResult.success) {
|
|
648
|
+
throw messages.createError('sandboxDeleteFailed', [JSON.stringify(deleteResult)]);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* If this Org is a scratch org, calling this method will delete the scratch org from the DevHub and clean up any local files
|
|
653
|
+
*
|
|
654
|
+
* @param devHub - optional DevHub Org of the to-be-deleted scratch org
|
|
655
|
+
* @private
|
|
656
|
+
*/
|
|
657
|
+
async deleteScratchOrg(devHub) {
|
|
658
|
+
// if we didn't get a devHub, we'll get it from the this org
|
|
659
|
+
devHub !== null && devHub !== void 0 ? devHub : (devHub = await this.getDevHubOrg());
|
|
660
|
+
if (!devHub) {
|
|
661
|
+
throw messages.createError('noDevHubFound');
|
|
662
|
+
}
|
|
663
|
+
if (devHub.getOrgId() === this.getOrgId()) {
|
|
664
|
+
// we're attempting to delete a DevHub
|
|
665
|
+
throw messages.createError('deleteOrgHubError');
|
|
666
|
+
}
|
|
667
|
+
try {
|
|
668
|
+
const devHubConn = devHub.getConnection();
|
|
669
|
+
const username = this.getUsername();
|
|
670
|
+
const activeScratchOrgRecordId = (await devHubConn.singleRecordQuery(`SELECT Id FROM ActiveScratchOrg WHERE SignupUsername='${username}'`)).Id;
|
|
671
|
+
this.logger.trace(`found matching ActiveScratchOrg with SignupUsername: ${username}. Deleting...`);
|
|
672
|
+
await this.destoryScratchOrg(devHub, activeScratchOrgRecordId);
|
|
673
|
+
await this.remove();
|
|
674
|
+
}
|
|
675
|
+
catch (err) {
|
|
676
|
+
this.logger.info(err instanceof Error ? err.message : err);
|
|
677
|
+
if (err instanceof Error && (err.name === 'INVALID_TYPE' || err.name === 'INSUFFICIENT_ACCESS_OR_READONLY')) {
|
|
678
|
+
// most likely from devHubConn.delete
|
|
679
|
+
this.logger.info('Insufficient privilege to access ActiveScratchOrgs.');
|
|
680
|
+
throw messages.createError('insufficientAccessToDelete');
|
|
681
|
+
}
|
|
682
|
+
if (err instanceof Error && err.name === connection_1.SingleRecordQueryErrors.NoRecords) {
|
|
683
|
+
// most likely from singleRecordQuery
|
|
684
|
+
this.logger.info('The above error can be the result of deleting an expired or already deleted org.');
|
|
685
|
+
this.logger.info('attempting to cleanup the auth file');
|
|
686
|
+
await this.removeAuth();
|
|
687
|
+
throw messages.createError('scratchOrgNotFound');
|
|
688
|
+
}
|
|
689
|
+
throw err;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
519
692
|
/**
|
|
520
693
|
* Delete an auth info file from the local file system and any related cache information for
|
|
521
694
|
* this Org. You don't want to call this method directly. Instead consider calling Org.remove()
|
|
@@ -548,7 +721,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
548
721
|
async retrieveSandboxOrgConfig() {
|
|
549
722
|
return await sandboxOrgConfig_1.SandboxOrgConfig.create(sandboxOrgConfig_1.SandboxOrgConfig.getOptions(this.getOrgId()));
|
|
550
723
|
}
|
|
551
|
-
manageDelete(cb, dirPath, throwWhenRemoveFails) {
|
|
724
|
+
async manageDelete(cb, dirPath, throwWhenRemoveFails) {
|
|
552
725
|
return cb().catch((e) => {
|
|
553
726
|
if (throwWhenRemoveFails) {
|
|
554
727
|
throw e;
|
|
@@ -610,6 +783,170 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
610
783
|
await this.manageDelete(async () => await sandboxOrgConfig.unlink(), sandboxOrgConfig.getPath(), throwWhenRemoveFails);
|
|
611
784
|
}
|
|
612
785
|
}
|
|
786
|
+
async writeSandboxAuthFile(sandboxProcessObj, sandboxRes) {
|
|
787
|
+
this.logger.debug('writeSandboxAuthFile sandboxProcessObj: %s, sandboxRes: %s', sandboxProcessObj, sandboxRes);
|
|
788
|
+
if (sandboxRes.authUserName) {
|
|
789
|
+
const productionAuthFields = this.connection.getAuthInfoFields();
|
|
790
|
+
this.logger.debug('Result from getAuthInfoFields: AuthFields %s', productionAuthFields);
|
|
791
|
+
// let's do headless auth via jwt (if we have privateKey) or web auth
|
|
792
|
+
const oauth2Options = {
|
|
793
|
+
loginUrl: sandboxRes.loginUrl,
|
|
794
|
+
instanceUrl: sandboxRes.instanceUrl,
|
|
795
|
+
username: sandboxRes.authUserName,
|
|
796
|
+
};
|
|
797
|
+
// If we don't have a privateKey then we assume it's web auth.
|
|
798
|
+
if (!productionAuthFields.privateKey) {
|
|
799
|
+
oauth2Options.redirectUri = `http://localhost:${await webOAuthServer_1.WebOAuthServer.determineOauthPort()}/OauthRedirect`;
|
|
800
|
+
oauth2Options.authCode = sandboxRes.authCode;
|
|
801
|
+
}
|
|
802
|
+
const authInfo = await authInfo_1.AuthInfo.create({
|
|
803
|
+
username: sandboxRes.authUserName,
|
|
804
|
+
oauth2Options,
|
|
805
|
+
parentUsername: productionAuthFields.username,
|
|
806
|
+
});
|
|
807
|
+
await authInfo.save();
|
|
808
|
+
const sandboxOrg = await Org.create({ aliasOrUsername: authInfo.getUsername() });
|
|
809
|
+
await sandboxOrg.setSandboxOrgConfigField(sandboxOrgConfig_1.SandboxOrgConfig.Fields.PROD_ORG_USERNAME,
|
|
810
|
+
// we couldn't get this far into the process without a production org so username will be there
|
|
811
|
+
productionAuthFields.username);
|
|
812
|
+
await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_RESULT, {
|
|
813
|
+
sandboxProcessObj,
|
|
814
|
+
sandboxRes,
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
else {
|
|
818
|
+
// no authed sandbox user, error
|
|
819
|
+
throw messages.createError('missingAuthUsername', [sandboxProcessObj.SandboxName]);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* Polls for the new sandbox to be created - and will write the associated auth files
|
|
824
|
+
*
|
|
825
|
+
* @private
|
|
826
|
+
* @param options
|
|
827
|
+
* sandboxProcessObj: The in-progress sandbox signup request
|
|
828
|
+
* retries: the number of retries to poll for every 30s
|
|
829
|
+
* shouldPoll: wait for polling, or just return
|
|
830
|
+
* pollInterval: Duration to sleep between poll events, default 30 seconds
|
|
831
|
+
*/
|
|
832
|
+
async pollStatusAndAuth(options) {
|
|
833
|
+
var _a;
|
|
834
|
+
const { sandboxProcessObj, retries, shouldPoll, pollInterval } = options;
|
|
835
|
+
this.logger.debug('PollStatusAndAuth called with SandboxProcessObject%s, retries %s', sandboxProcessObj, retries);
|
|
836
|
+
const lifecycle = lifecycleEvents_1.Lifecycle.getInstance();
|
|
837
|
+
let pollFinished = false;
|
|
838
|
+
let waitingOnAuth = false;
|
|
839
|
+
const sandboxInfo = await this.sandboxSignupComplete(sandboxProcessObj);
|
|
840
|
+
if (sandboxInfo) {
|
|
841
|
+
await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_AUTH, sandboxInfo);
|
|
842
|
+
try {
|
|
843
|
+
this.logger.debug('sandbox signup complete with %s', sandboxInfo);
|
|
844
|
+
await this.writeSandboxAuthFile(sandboxProcessObj, sandboxInfo);
|
|
845
|
+
pollFinished = true;
|
|
846
|
+
}
|
|
847
|
+
catch (err) {
|
|
848
|
+
const error = err;
|
|
849
|
+
this.logger.debug('Exception while calling writeSandboxAuthFile %s', err);
|
|
850
|
+
if ((error === null || error === void 0 ? void 0 : error.name) === 'JWTAuthError' && ((_a = error === null || error === void 0 ? void 0 : error.stack) === null || _a === void 0 ? void 0 : _a.includes("user hasn't approved"))) {
|
|
851
|
+
waitingOnAuth = true;
|
|
852
|
+
}
|
|
853
|
+
else {
|
|
854
|
+
throw sfdxError_1.SfdxError.wrap(error);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
if (!pollFinished) {
|
|
859
|
+
if (retries > 0) {
|
|
860
|
+
// emit the signup progress of the sandbox and query the production org again after waiting the interval
|
|
861
|
+
await Promise.all([
|
|
862
|
+
await lifecycle.emit(SandboxEvents.EVENT_STATUS, {
|
|
863
|
+
sandboxProcessObj,
|
|
864
|
+
interval: pollInterval.seconds,
|
|
865
|
+
retries,
|
|
866
|
+
waitingOnAuth,
|
|
867
|
+
}),
|
|
868
|
+
await (0, kit_1.sleep)(pollInterval),
|
|
869
|
+
]);
|
|
870
|
+
const polledSandboxProcessObj = await this.querySandboxProcess(sandboxProcessObj.SandboxInfoId);
|
|
871
|
+
return this.pollStatusAndAuth({
|
|
872
|
+
sandboxProcessObj: polledSandboxProcessObj,
|
|
873
|
+
retries: retries - 1,
|
|
874
|
+
shouldPoll,
|
|
875
|
+
pollInterval,
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
else {
|
|
879
|
+
if (shouldPoll) {
|
|
880
|
+
// timed out on retries
|
|
881
|
+
throw messages.createError('orgPollingTimeout', [sandboxProcessObj.Status]);
|
|
882
|
+
}
|
|
883
|
+
else {
|
|
884
|
+
// The user didn't want us to poll, so simply return the status
|
|
885
|
+
// simply report status and exit
|
|
886
|
+
await lifecycle.emit(SandboxEvents.EVENT_ASYNC_RESULT, sandboxProcessObj);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
return sandboxProcessObj;
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* query SandboxProcess via SandboxInfoId
|
|
894
|
+
*
|
|
895
|
+
* @param id SandboxInfoId to query for
|
|
896
|
+
* @private
|
|
897
|
+
*/
|
|
898
|
+
async querySandboxProcess(id) {
|
|
899
|
+
const queryStr = `SELECT Id, Status, SandboxName, SandboxInfoId, LicenseType, CreatedDate, CopyProgress, SandboxOrganization, SourceId, Description, EndDate FROM SandboxProcess WHERE SandboxInfoId='${id}' AND Status != 'D'`;
|
|
900
|
+
return await this.connection.singleRecordQuery(queryStr, {
|
|
901
|
+
tooling: true,
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* determines if the sandbox has successfully been created
|
|
906
|
+
*
|
|
907
|
+
* @param sandboxProcessObj sandbox signup progeress
|
|
908
|
+
* @private
|
|
909
|
+
*/
|
|
910
|
+
async sandboxSignupComplete(sandboxProcessObj) {
|
|
911
|
+
this.logger.debug('sandboxSignupComplete called with SandboxProcessObject %s', sandboxProcessObj);
|
|
912
|
+
if (!sandboxProcessObj.EndDate) {
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
try {
|
|
916
|
+
// call server side /sandboxAuth API to auth the sandbox org user with the connected app
|
|
917
|
+
const authFields = this.connection.getAuthInfoFields();
|
|
918
|
+
const callbackUrl = `http://localhost:${await webOAuthServer_1.WebOAuthServer.determineOauthPort()}/OauthRedirect`;
|
|
919
|
+
const sandboxReq = {
|
|
920
|
+
// the sandbox signup has been completed on production, we have production clientId by this point
|
|
921
|
+
clientId: authFields.clientId,
|
|
922
|
+
sandboxName: sandboxProcessObj.SandboxName,
|
|
923
|
+
callbackUrl,
|
|
924
|
+
};
|
|
925
|
+
this.logger.debug('Calling sandboxAuth with SandboxUserAuthRequest %s', sandboxReq);
|
|
926
|
+
const url = `${this.connection.tooling._baseUrl()}/sandboxAuth`;
|
|
927
|
+
const params = {
|
|
928
|
+
method: 'POST',
|
|
929
|
+
url,
|
|
930
|
+
headers: { 'Content-Type': 'application/json' },
|
|
931
|
+
body: JSON.stringify(sandboxReq),
|
|
932
|
+
};
|
|
933
|
+
const result = await this.connection.tooling.request(params);
|
|
934
|
+
this.logger.debug('Result of calling sandboxAuth %s', result);
|
|
935
|
+
return result;
|
|
936
|
+
}
|
|
937
|
+
catch (err) {
|
|
938
|
+
const error = err;
|
|
939
|
+
// There are cases where the endDate is set before the sandbox has actually completed.
|
|
940
|
+
// In that case, the sandboxAuth call will throw a specific exception.
|
|
941
|
+
if ((error === null || error === void 0 ? void 0 : error.name) === 'INVALID_STATUS') {
|
|
942
|
+
this.logger.debug('Error while authenticating the user %s', error === null || error === void 0 ? void 0 : error.toString());
|
|
943
|
+
}
|
|
944
|
+
else {
|
|
945
|
+
// If it fails for any unexpected reason, just pass that through
|
|
946
|
+
throw sfdxError_1.SfdxError.wrap(error);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
}
|
|
613
950
|
}
|
|
614
951
|
exports.Org = Org;
|
|
615
952
|
(function (Org) {
|
|
@@ -9,7 +9,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
9
9
|
exports.ORG_CONFIG_ALLOWED_PROPERTIES = exports.OrgConfigProperties = void 0;
|
|
10
10
|
const path_1 = require("path");
|
|
11
11
|
const messages_1 = require("../messages");
|
|
12
|
-
messages_1.Messages.importMessagesDirectory(path_1.join(__dirname));
|
|
12
|
+
messages_1.Messages.importMessagesDirectory((0, path_1.join)(__dirname));
|
|
13
13
|
const messages = messages_1.Messages.load('@salesforce/core', 'config', ['targetOrg', 'targetDevHub']);
|
|
14
14
|
var OrgConfigProperties;
|
|
15
15
|
(function (OrgConfigProperties) {
|
|
@@ -61,7 +61,7 @@ class PermissionSetAssignment {
|
|
|
61
61
|
query += ` AND NamespacePrefix='${nsPrefix}'`;
|
|
62
62
|
}
|
|
63
63
|
const result = await this.org.getConnection().query(query);
|
|
64
|
-
const permissionSetId = ts_types_1.getString(result, 'records[0].Id');
|
|
64
|
+
const permissionSetId = (0, ts_types_1.getString)(result, 'records[0].Id');
|
|
65
65
|
if (!permissionSetId) {
|
|
66
66
|
if (nsPrefix) {
|
|
67
67
|
throw messages.createError('assignCommandPermissionSetNotFoundForNSError', [permSetName, nsPrefix]);
|
|
@@ -77,8 +77,8 @@ class PermissionSetAssignment {
|
|
|
77
77
|
const createResponse = await this.org
|
|
78
78
|
.getConnection()
|
|
79
79
|
.sobject('PermissionSetAssignment')
|
|
80
|
-
.create(kit_1.mapKeys(assignment, (value, key) => kit_1.upperFirst(key)));
|
|
81
|
-
if (ts_types_1.hasArray(createResponse, 'errors') && createResponse.errors.length > 0) {
|
|
80
|
+
.create((0, kit_1.mapKeys)(assignment, (value, key) => (0, kit_1.upperFirst)(key)));
|
|
81
|
+
if ((0, ts_types_1.hasArray)(createResponse, 'errors') && createResponse.errors.length > 0) {
|
|
82
82
|
let message = messages.getMessage('errorsEncounteredCreatingAssignment');
|
|
83
83
|
const errors = createResponse.errors;
|
|
84
84
|
if (errors && errors.length > 0) {
|