@salesforce/core 3.7.0 → 3.7.4

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.
Files changed (51) hide show
  1. package/CHANGELOG.md +92 -2
  2. package/LICENSE.txt +1 -1
  3. package/lib/config/config.js +15 -15
  4. package/lib/config/configAggregator.js +4 -4
  5. package/lib/config/configFile.js +9 -9
  6. package/lib/config/configStore.js +12 -13
  7. package/lib/config/envVars.js +5 -5
  8. package/lib/config/keychainConfig.js +1 -1
  9. package/lib/crypto/crypto.js +6 -6
  10. package/lib/crypto/keyChainImpl.js +6 -5
  11. package/lib/crypto/secureBuffer.js +1 -1
  12. package/lib/deviceOauthService.js +5 -4
  13. package/lib/exported.d.ts +1 -1
  14. package/lib/exported.js +4 -2
  15. package/lib/globalInfo/globalInfoConfig.js +1 -1
  16. package/lib/globalInfo/sfdxDataHandler.js +11 -11
  17. package/lib/lifecycleEvents.d.ts +38 -1
  18. package/lib/lifecycleEvents.js +73 -2
  19. package/lib/logger.js +13 -12
  20. package/lib/messages.js +10 -9
  21. package/lib/org/authInfo.d.ts +2 -6
  22. package/lib/org/authInfo.js +44 -41
  23. package/lib/org/connection.js +14 -11
  24. package/lib/org/org.d.ts +121 -9
  25. package/lib/org/org.js +373 -36
  26. package/lib/org/orgConfigProperties.js +1 -1
  27. package/lib/org/permissionSetAssignment.js +3 -3
  28. package/lib/org/user.js +20 -20
  29. package/lib/schema/printer.js +18 -18
  30. package/lib/schema/validator.js +8 -8
  31. package/lib/sfdxError.d.ts +1 -1
  32. package/lib/sfdxError.js +3 -2
  33. package/lib/sfdxProject.js +15 -13
  34. package/lib/status/myDomainResolver.js +3 -3
  35. package/lib/status/pollingClient.js +2 -2
  36. package/lib/status/streamingClient.d.ts +2 -3
  37. package/lib/status/streamingClient.js +16 -22
  38. package/lib/status/types.d.ts +89 -0
  39. package/lib/status/types.js +18 -0
  40. package/lib/testSetup.d.ts +6 -4
  41. package/lib/testSetup.js +25 -25
  42. package/lib/util/cache.js +3 -3
  43. package/lib/util/fs.js +12 -12
  44. package/lib/util/sfdc.js +3 -3
  45. package/lib/util/sfdcUrl.d.ts +2 -1
  46. package/lib/util/sfdcUrl.js +16 -8
  47. package/lib/webOAuthServer.js +7 -7
  48. package/messages/core.json +3 -3
  49. package/messages/core.md +1 -1
  50. package/messages/org.md +36 -0
  51. package/package.json +38 -45
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', ['notADevHub', 'noUsernameFound']);
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) {