@salesforce/core 3.13.0 → 3.15.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/lib/config/config.d.ts +23 -1
  3. package/lib/config/config.js +28 -18
  4. package/lib/config/configAggregator.d.ts +37 -24
  5. package/lib/config/configAggregator.js +83 -41
  6. package/lib/config/envVars.js +3 -3
  7. package/lib/config/sandboxProcessCache.d.ts +15 -0
  8. package/lib/config/sandboxProcessCache.js +38 -0
  9. package/lib/exported.d.ts +5 -3
  10. package/lib/exported.js +8 -2
  11. package/lib/globalInfo/accessors/sandboxAccessor.d.ts +36 -0
  12. package/lib/globalInfo/accessors/sandboxAccessor.js +63 -0
  13. package/lib/globalInfo/globalInfoConfig.d.ts +2 -0
  14. package/lib/globalInfo/globalInfoConfig.js +5 -0
  15. package/lib/globalInfo/sfdxDataHandler.d.ts +12 -2
  16. package/lib/globalInfo/sfdxDataHandler.js +116 -25
  17. package/lib/globalInfo/types.d.ts +19 -1
  18. package/lib/globalInfo/types.js +1 -0
  19. package/lib/org/authInfo.d.ts +2 -1
  20. package/lib/org/authInfo.js +2 -1
  21. package/lib/org/connection.js +4 -4
  22. package/lib/org/org.d.ts +61 -39
  23. package/lib/org/org.js +261 -159
  24. package/lib/org/scratchOrgCache.d.ts +19 -0
  25. package/lib/org/scratchOrgCache.js +33 -0
  26. package/lib/org/scratchOrgCreate.d.ts +25 -16
  27. package/lib/org/scratchOrgCreate.js +110 -41
  28. package/lib/org/scratchOrgErrorCodes.d.ts +8 -2
  29. package/lib/org/scratchOrgErrorCodes.js +26 -3
  30. package/lib/org/scratchOrgInfoApi.d.ts +19 -8
  31. package/lib/org/scratchOrgInfoApi.js +91 -42
  32. package/lib/org/scratchOrgLifecycleEvents.d.ts +2 -0
  33. package/lib/org/scratchOrgLifecycleEvents.js +20 -1
  34. package/lib/org/scratchOrgSettingsGenerator.d.ts +7 -2
  35. package/lib/org/scratchOrgSettingsGenerator.js +1 -0
  36. package/lib/sfProject.js +1 -1
  37. package/lib/status/pollingClient.js +1 -0
  38. package/lib/testSetup.js +0 -2
  39. package/messages/org.md +9 -1
  40. package/messages/scratchOrgCreate.md +20 -0
  41. package/package.json +2 -2
package/lib/org/org.js CHANGED
@@ -14,7 +14,6 @@ const ts_types_1 = require("@salesforce/ts-types");
14
14
  const config_1 = require("../config/config");
15
15
  const configAggregator_1 = require("../config/configAggregator");
16
16
  const orgUsersConfig_1 = require("../config/orgUsersConfig");
17
- const sandboxOrgConfig_1 = require("../config/sandboxOrgConfig");
18
17
  const global_1 = require("../global");
19
18
  const lifecycleEvents_1 = require("../lifecycleEvents");
20
19
  const logger_1 = require("../logger");
@@ -23,6 +22,7 @@ const sfdc_1 = require("../util/sfdc");
23
22
  const webOAuthServer_1 = require("../webOAuthServer");
24
23
  const messages_1 = require("../messages");
25
24
  const globalInfo_1 = require("../globalInfo");
25
+ const pollingClient_1 = require("../status/pollingClient");
26
26
  const connection_1 = require("./connection");
27
27
  const authInfo_1 = require("./authInfo");
28
28
  const scratchOrgCreate_1 = require("./scratchOrgCreate");
@@ -35,11 +35,12 @@ const messages = messages_1.Messages.load('@salesforce/core', 'org', [
35
35
  'noDevHubFound',
36
36
  'notADevHub',
37
37
  'noUsernameFound',
38
- 'orgPollingTimeout',
39
38
  'sandboxDeleteFailed',
40
39
  'sandboxInfoCreateFailed',
41
40
  'sandboxNotFound',
42
41
  'scratchOrgNotFound',
42
+ 'AuthInfoOrgIdUndefined',
43
+ 'sandboxCreateNotComplete',
43
44
  ]);
44
45
  var OrgTypes;
45
46
  (function (OrgTypes) {
@@ -52,6 +53,7 @@ var SandboxEvents;
52
53
  SandboxEvents["EVENT_ASYNC_RESULT"] = "asyncResult";
53
54
  SandboxEvents["EVENT_RESULT"] = "result";
54
55
  SandboxEvents["EVENT_AUTH"] = "auth";
56
+ SandboxEvents["EVENT_RESUME"] = "resume";
55
57
  })(SandboxEvents = exports.SandboxEvents || (exports.SandboxEvents = {}));
56
58
  /**
57
59
  * Provides a way to manage a locally authenticated Org.
@@ -95,23 +97,88 @@ class Org extends kit_1.AsyncOptionalCreatable {
95
97
  * @param sandboxReq SandboxRequest options to create the sandbox with
96
98
  * @param options Wait: The amount of time to wait before timing out, Interval: The time interval between polling
97
99
  */
98
- async createSandbox(sandboxReq, options) {
99
- var _a;
100
- this.logger.debug('CreateSandbox called with SandboxRequest: %s ', sandboxReq);
100
+ async createSandbox(sandboxReq, options = {
101
+ wait: kit_1.Duration.minutes(6),
102
+ async: false,
103
+ interval: kit_1.Duration.seconds(30),
104
+ }) {
105
+ this.logger.debug('CreateSandbox called with SandboxRequest:', sandboxReq);
101
106
  const createResult = await this.connection.tooling.create('SandboxInfo', sandboxReq);
102
- this.logger.debug('Return from calling tooling.create: %s ', createResult);
107
+ this.logger.debug('Return from calling tooling.create:', createResult);
103
108
  if (Array.isArray(createResult) || !createResult.success) {
104
109
  throw messages.createError('sandboxInfoCreateFailed', [JSON.stringify(createResult)]);
105
110
  }
106
- const sandboxCreationProgress = await this.querySandboxProcess(createResult.id);
107
- this.logger.debug('Return from calling singleRecordQuery with tooling: %s', sandboxCreationProgress);
108
- const retries = options.wait ? options.wait.seconds / kit_1.Duration.seconds(30).seconds : 0;
109
- this.logger.debug('pollStatusAndAuth sandboxProcessObj %s, maxPollingRetries %i', sandboxCreationProgress, retries);
110
- const pollInterval = (_a = options.interval) !== null && _a !== void 0 ? _a : kit_1.Duration.seconds(30);
111
+ const sandboxCreationProgress = await this.querySandboxProcessBySandboxInfoId(createResult.id);
112
+ this.logger.debug('Return from calling singleRecordQuery with tooling:', sandboxCreationProgress);
113
+ const isAsync = !!options.async;
114
+ if (isAsync) {
115
+ // The user didn't want us to poll, so simply return the status
116
+ await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_ASYNC_RESULT, sandboxCreationProgress);
117
+ return sandboxCreationProgress;
118
+ }
119
+ const [wait, pollInterval] = this.validateWaitOptions(options);
120
+ this.logger.debug(`create - pollStatusAndAuth sandboxProcessObj, max wait time of ${wait.minutes} minutes`, sandboxCreationProgress);
121
+ return this.pollStatusAndAuth({
122
+ sandboxProcessObj: sandboxCreationProgress,
123
+ wait,
124
+ pollInterval,
125
+ });
126
+ }
127
+ /**
128
+ * resume a sandbox creation from a production org
129
+ * 'this' needs to be a production org with sandbox licenses available
130
+ *
131
+ * @param resumeSandboxRequest SandboxRequest options to create the sandbox with
132
+ * @param options Wait: The amount of time to wait (default: 30 minutes) before timing out,
133
+ * Interval: The time interval (default: 30 seconds) between polling
134
+ */
135
+ async resumeSandbox(resumeSandboxRequest, options = {
136
+ wait: kit_1.Duration.minutes(0),
137
+ async: false,
138
+ interval: kit_1.Duration.seconds(30),
139
+ }) {
140
+ var _a;
141
+ this.logger.debug('ResumeSandbox called with ResumeSandboxRequest:', resumeSandboxRequest);
142
+ let sandboxCreationProgress;
143
+ // seed the sandboxCreationProgress via the resumeSandboxRequest options
144
+ if (resumeSandboxRequest.SandboxName) {
145
+ sandboxCreationProgress = await this.querySandboxProcessBySandboxName(resumeSandboxRequest.SandboxName);
146
+ }
147
+ else if (resumeSandboxRequest.SandboxProcessObjId) {
148
+ sandboxCreationProgress = await this.querySandboxProcessById(resumeSandboxRequest.SandboxProcessObjId);
149
+ }
150
+ else {
151
+ throw messages.createError('sandboxNotFound', [
152
+ (_a = resumeSandboxRequest.SandboxName) !== null && _a !== void 0 ? _a : resumeSandboxRequest.SandboxProcessObjId,
153
+ ]);
154
+ }
155
+ this.logger.debug('Return from calling singleRecordQuery with tooling:', sandboxCreationProgress);
156
+ await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_RESUME, sandboxCreationProgress);
157
+ const [wait, pollInterval] = this.validateWaitOptions(options);
158
+ // if wait is 0, return the sandboxCreationProgress immediately
159
+ if (wait.seconds === 0) {
160
+ if (sandboxCreationProgress.Status === 'Completed') {
161
+ // check to see if sandbox can authenticate via sandboxAuth endpoint
162
+ const sandboxInfo = await this.sandboxSignupComplete(sandboxCreationProgress);
163
+ if (sandboxInfo) {
164
+ await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_AUTH, sandboxInfo);
165
+ try {
166
+ this.logger.debug('sandbox signup complete with', sandboxInfo);
167
+ await this.writeSandboxAuthFile(sandboxCreationProgress, sandboxInfo);
168
+ return sandboxCreationProgress;
169
+ }
170
+ catch (err) {
171
+ // eat the error, we don't want to throw an error if we can't write the file
172
+ }
173
+ }
174
+ }
175
+ await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_ASYNC_RESULT, sandboxCreationProgress);
176
+ throw messages.createError('sandboxCreateNotComplete');
177
+ }
178
+ this.logger.debug(`resume - pollStatusAndAuth sandboxProcessObj, max wait time of ${wait.minutes} minutes`, sandboxCreationProgress);
111
179
  return this.pollStatusAndAuth({
112
180
  sandboxProcessObj: sandboxCreationProgress,
113
- retries,
114
- shouldPoll: retries > 0,
181
+ wait,
115
182
  pollInterval,
116
183
  });
117
184
  }
@@ -156,9 +223,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
156
223
  return await orgUsersConfig_1.OrgUsersConfig.create(orgUsersConfig_1.OrgUsersConfig.getOptions(this.getOrgId()));
157
224
  }
158
225
  /**
159
- * Removes the scratch org config file at $HOME/.sfdx/[name].json, any project level org
160
- * files, all user auth files for the org, matching default config settings, and any
161
- * matching aliases.
226
+ * Cleans up all org related artifacts including users, sandbox config(if a sandbox and auth file.
162
227
  *
163
228
  * @param throwWhenRemoveFails Determines if the call should throw an error or fail silently.
164
229
  */
@@ -168,7 +233,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
168
233
  if (this.getConnection().isUsingAccessToken()) {
169
234
  return Promise.resolve();
170
235
  }
171
- await this.removeSandboxConfig(throwWhenRemoveFails);
236
+ await this.removeSandboxConfig();
172
237
  await this.removeUsers(throwWhenRemoveFails);
173
238
  await this.removeUsersConfig();
174
239
  // An attempt to remove this org's auth file occurs in this.removeUsersConfig. That's because this org's usersname is also
@@ -182,7 +247,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
182
247
  *
183
248
  */
184
249
  async isSandbox() {
185
- return !!(await this.getSandboxOrgConfigField(sandboxOrgConfig_1.SandboxOrgConfig.Fields.PROD_ORG_USERNAME));
250
+ return (await globalInfo_1.GlobalInfo.getInstance()).sandboxes.has(this.getOrgId());
186
251
  }
187
252
  /**
188
253
  * Check that this org is a scratch org by asking the dev hub if it knows about it.
@@ -491,25 +556,22 @@ class Org extends kit_1.AsyncOptionalCreatable {
491
556
  return this;
492
557
  }
493
558
  /**
494
- * Sets the key/value pair in the sandbox config for this org. For convenience `this` object is returned.
495
- *
559
+ * set the sandbox config related to this given org
496
560
  *
497
- * @param {key} The key for this value
498
- * @param {value} The value to save
561
+ * @param orgId {string} orgId of the sandbox
562
+ * @param config {SfSandbox} config of the sandbox
499
563
  */
500
- async setSandboxOrgConfigField(field, value) {
501
- const sandboxOrgConfig = await this.retrieveSandboxOrgConfig();
502
- sandboxOrgConfig.set(field, value);
503
- await sandboxOrgConfig.write();
564
+ async setSandboxConfig(orgId, config) {
565
+ (await globalInfo_1.GlobalInfo.getInstance()).sandboxes.set(orgId, config);
504
566
  return this;
505
567
  }
506
568
  /**
507
- * Returns an org config field. Returns undefined if the field is not set or invalid.
569
+ * get the sandbox config for the given orgId
570
+ *
571
+ * @param orgId {string} orgId of the sandbox
508
572
  */
509
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
510
- async getSandboxOrgConfigField(field) {
511
- const sandboxOrgConfig = await this.retrieveSandboxOrgConfig();
512
- return sandboxOrgConfig.get(field);
573
+ async getSandboxConfig(orgId) {
574
+ return (await globalInfo_1.GlobalInfo.getInstance()).sandboxes.get(orgId);
513
575
  }
514
576
  /**
515
577
  * Retrieves the highest api version that is supported by the target server instance. If the apiVersion configured for
@@ -566,6 +628,53 @@ class Org extends kit_1.AsyncOptionalCreatable {
566
628
  getConnection() {
567
629
  return this.connection;
568
630
  }
631
+ async supportsSourceTracking() {
632
+ if (this.isScratch()) {
633
+ return true;
634
+ }
635
+ const conn = this.getConnection();
636
+ try {
637
+ await conn.tooling.sobject('SourceMember').describe();
638
+ return true;
639
+ }
640
+ catch (err) {
641
+ if (err.message.includes('The requested resource does not exist')) {
642
+ return false;
643
+ }
644
+ throw err;
645
+ }
646
+ }
647
+ /**
648
+ * query SandboxProcess via sandbox name
649
+ *
650
+ * @param name SandboxName to query for
651
+ * @private
652
+ */
653
+ async querySandboxProcessBySandboxName(name) {
654
+ return await this.querySandboxProcess(`SandboxName='${name}'`);
655
+ }
656
+ /**
657
+ * query SandboxProcess via SandboxInfoId
658
+ *
659
+ * @param id SandboxInfoId to query for
660
+ * @private
661
+ */
662
+ async querySandboxProcessBySandboxInfoId(id) {
663
+ return await this.querySandboxProcess(`SandboxInfoId='${id}'`);
664
+ const queryStr = `SELECT Id, Status, SandboxName, SandboxInfoId, LicenseType, CreatedDate, CopyProgress, SandboxOrganization, SourceId, Description, EndDate FROM SandboxProcess WHERE SandboxInfoId='${id}' AND Status != 'D'`;
665
+ return await this.connection.singleRecordQuery(queryStr, {
666
+ tooling: true,
667
+ });
668
+ }
669
+ /**
670
+ * query SandboxProcess via Id
671
+ *
672
+ * @param id SandboxProcessId to query for
673
+ * @private
674
+ */
675
+ async querySandboxProcessById(id) {
676
+ return await this.querySandboxProcess(`Id='${id}'`);
677
+ }
569
678
  /**
570
679
  * Initialize async components.
571
680
  */
@@ -617,47 +726,53 @@ class Org extends kit_1.AsyncOptionalCreatable {
617
726
  * @private
618
727
  */
619
728
  async deleteSandbox(prodOrg) {
729
+ const sandbox = await this.getSandboxConfig(this.getOrgId());
620
730
  prodOrg !== null && prodOrg !== void 0 ? prodOrg : (prodOrg = await Org.create({
621
731
  aggregator: this.configAggregator,
622
- aliasOrUsername: await this.getSandboxOrgConfigField(sandboxOrgConfig_1.SandboxOrgConfig.Fields.PROD_ORG_USERNAME),
732
+ aliasOrUsername: sandbox === null || sandbox === void 0 ? void 0 : sandbox.prodOrgUsername,
623
733
  }));
624
- let result;
625
- // attempt to locate sandbox id by username
626
- try {
627
- // try to calculate sandbox name from the production org
628
- // production org: admin@integrationtesthub.org
629
- // this.getUsername: admin@integrationtesthub.org.dev1
630
- // sandboxName in Production: dev1
631
- const sandboxName = (this.getUsername() || '').split(`${prodOrg.getUsername()}.`)[1];
632
- if (!sandboxName) {
633
- this.logger.debug('Could not construct a sandbox name');
634
- throw new Error();
635
- }
636
- this.logger.debug(`attempting to locate sandbox with username ${sandboxName}`);
637
- result = await this.queryProduction(prodOrg, 'SandboxName', sandboxName);
638
- if (!result) {
639
- this.logger.debug(`Failed to find sandbox with username: ${sandboxName}`);
640
- throw new Error();
641
- }
642
- }
643
- catch {
644
- // if an error is thrown, don't panic yet. we'll try querying by orgId
645
- const trimmedId = sfdc_1.sfdc.trimTo15(this.getOrgId());
646
- this.logger.debug(`defaulting to trimming id from ${this.getOrgId()} to ${trimmedId}`);
734
+ let sandboxInfoId = sandbox === null || sandbox === void 0 ? void 0 : sandbox.sandboxInfoId;
735
+ if (!sandboxInfoId) {
736
+ let result;
647
737
  try {
648
- result = await this.queryProduction(prodOrg, 'SandboxOrganization', trimmedId);
738
+ // grab sandboxName from config or try to calculate from the sandbox username
739
+ const sandboxName = (sandbox === null || sandbox === void 0 ? void 0 : sandbox.sandboxName) || (this.getUsername() || '').split(`${prodOrg.getUsername()}.`)[1];
740
+ if (!sandboxName) {
741
+ this.logger.debug('Sandbox name is not available');
742
+ // jump to query by orgId
743
+ throw new Error();
744
+ }
745
+ this.logger.debug(`attempting to locate sandbox with sandbox ${sandboxName}`);
746
+ try {
747
+ result = await this.queryProduction(prodOrg, 'SandboxName', sandboxName);
748
+ }
749
+ catch (err) {
750
+ this.logger.debug(`Failed to find sandbox with sandbox name: ${sandboxName}`);
751
+ // jump to query by orgId
752
+ throw err;
753
+ }
649
754
  }
650
755
  catch {
651
- throw messages.createError('sandboxNotFound', [trimmedId]);
756
+ // if an error is thrown, don't panic yet. we'll try querying by orgId
757
+ const trimmedId = sfdc_1.sfdc.trimTo15(this.getOrgId());
758
+ this.logger.debug(`defaulting to trimming id from ${this.getOrgId()} to ${trimmedId}`);
759
+ try {
760
+ result = await this.queryProduction(prodOrg, 'SandboxOrganization', trimmedId);
761
+ sandboxInfoId = result.SandboxInfoId;
762
+ }
763
+ catch {
764
+ // eating exceptions when trying to find sandbox process record by orgId
765
+ // allows idempotent cleanup of sandbox orgs
766
+ this.logger.debug(`Failed find a SandboxProcess for the sandbox org: ${this.getOrgId()}`);
767
+ }
652
768
  }
653
769
  }
654
- // const deleteResult = await prodOrg.connection.tooling.delete('SandboxInfo', result.SandboxInfoId);
655
- const deleteResult = await this.destroySandbox(prodOrg, result.SandboxInfoId);
656
- this.logger.debug('Return from calling tooling.delete: %o ', deleteResult);
657
- await this.remove();
658
- if (Array.isArray(deleteResult) || !deleteResult.success) {
659
- throw messages.createError('sandboxDeleteFailed', [JSON.stringify(deleteResult)]);
770
+ if (sandboxInfoId) {
771
+ const deleteResult = await this.destroySandbox(prodOrg, sandboxInfoId);
772
+ this.logger.debug('Return from calling tooling.delete: ', deleteResult);
660
773
  }
774
+ // cleanup remaining artifacts
775
+ await this.remove();
661
776
  }
662
777
  /**
663
778
  * If this Org is a scratch org, calling this method will delete the scratch org from the DevHub and clean up any local files
@@ -725,12 +840,6 @@ class Org extends kit_1.AsyncOptionalCreatable {
725
840
  await config.unlink();
726
841
  }
727
842
  }
728
- /**
729
- * @ignore
730
- */
731
- async retrieveSandboxOrgConfig() {
732
- return await sandboxOrgConfig_1.SandboxOrgConfig.create(sandboxOrgConfig_1.SandboxOrgConfig.getOptions(this.getOrgId()));
733
- }
734
843
  async manageDelete(cb, dirPath, throwWhenRemoveFails) {
735
844
  return cb().catch((e) => {
736
845
  if (throwWhenRemoveFails) {
@@ -775,22 +884,16 @@ class Org extends kit_1.AsyncOptionalCreatable {
775
884
  }));
776
885
  await globalInfo.write();
777
886
  }
778
- /**
779
- * Remove an associate sandbox config.
780
- *
781
- * @param throwWhenRemoveFails true if manageDelete should throw or not if the deleted fails.
782
- */
783
- async removeSandboxConfig(throwWhenRemoveFails) {
784
- const sandboxOrgConfig = await this.retrieveSandboxOrgConfig();
785
- if (await sandboxOrgConfig.exists()) {
786
- await this.manageDelete(async () => await sandboxOrgConfig.unlink(), sandboxOrgConfig.getPath(), throwWhenRemoveFails);
787
- }
887
+ async removeSandboxConfig() {
888
+ const globalInfo = await globalInfo_1.GlobalInfo.getInstance();
889
+ globalInfo.sandboxes.unset(this.getOrgId());
890
+ await globalInfo.write();
788
891
  }
789
892
  async writeSandboxAuthFile(sandboxProcessObj, sandboxRes) {
790
- this.logger.debug('writeSandboxAuthFile sandboxProcessObj: %s, sandboxRes: %s', sandboxProcessObj, sandboxRes);
893
+ this.logger.debug(`writeSandboxAuthFile sandboxProcessObj: ${JSON.stringify(sandboxProcessObj)}, sandboxRes: ${JSON.stringify(sandboxRes)}`);
791
894
  if (sandboxRes.authUserName) {
792
895
  const productionAuthFields = this.connection.getAuthInfoFields();
793
- this.logger.debug('Result from getAuthInfoFields: AuthFields %s', productionAuthFields);
896
+ this.logger.debug('Result from getAuthInfoFields: AuthFields', productionAuthFields);
794
897
  // let's do headless auth via jwt (if we have privateKey) or web auth
795
898
  const oauth2Options = {
796
899
  loginUrl: sandboxRes.loginUrl,
@@ -802,16 +905,34 @@ class Org extends kit_1.AsyncOptionalCreatable {
802
905
  oauth2Options.redirectUri = `http://localhost:${await webOAuthServer_1.WebOAuthServer.determineOauthPort()}/OauthRedirect`;
803
906
  oauth2Options.authCode = sandboxRes.authCode;
804
907
  }
908
+ else {
909
+ oauth2Options.privateKey = productionAuthFields.privateKey;
910
+ oauth2Options.clientId = productionAuthFields.clientId;
911
+ }
805
912
  const authInfo = await authInfo_1.AuthInfo.create({
806
913
  username: sandboxRes.authUserName,
807
914
  oauth2Options,
808
915
  parentUsername: productionAuthFields.username,
809
916
  });
917
+ this.logger.debug('Creating AuthInfo for sandbox', sandboxRes.authUserName, productionAuthFields.username, oauth2Options);
918
+ // save auth info for new sandbox
810
919
  await authInfo.save();
811
- const sandboxOrg = await Org.create({ aliasOrUsername: authInfo.getUsername() });
812
- await sandboxOrg.setSandboxOrgConfigField(sandboxOrgConfig_1.SandboxOrgConfig.Fields.PROD_ORG_USERNAME,
813
- // we couldn't get this far into the process without a production org so username will be there
814
- productionAuthFields.username);
920
+ if (!authInfo.getFields().orgId) {
921
+ throw messages.createError('AuthInfoOrgIdUndefined');
922
+ }
923
+ // set the sandbox config value
924
+ const sfSandbox = {
925
+ sandboxUsername: sandboxRes.authUserName,
926
+ sandboxOrgId: authInfo.getFields().orgId,
927
+ prodOrgUsername: this.getUsername(),
928
+ sandboxName: sandboxProcessObj.SandboxName,
929
+ sandboxProcessId: sandboxProcessObj.Id,
930
+ sandboxInfoId: sandboxProcessObj.SandboxInfoId,
931
+ timestamp: new Date().toISOString(),
932
+ };
933
+ await this.setSandboxConfig(authInfo.getFields().orgId, sfSandbox);
934
+ const globalInfo = await globalInfo_1.GlobalInfo.getInstance();
935
+ await globalInfo.write();
815
936
  await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_RESULT, {
816
937
  sandboxProcessObj,
817
938
  sandboxRes,
@@ -822,84 +943,56 @@ class Org extends kit_1.AsyncOptionalCreatable {
822
943
  throw messages.createError('missingAuthUsername', [sandboxProcessObj.SandboxName]);
823
944
  }
824
945
  }
825
- /**
826
- * Polls for the new sandbox to be created - and will write the associated auth files
827
- *
828
- * @private
829
- * @param options
830
- * sandboxProcessObj: The in-progress sandbox signup request
831
- * retries: the number of retries to poll for every 30s
832
- * shouldPoll: wait for polling, or just return
833
- * pollInterval: Duration to sleep between poll events, default 30 seconds
834
- */
835
946
  async pollStatusAndAuth(options) {
836
- var _a;
837
- const { sandboxProcessObj, retries, shouldPoll, pollInterval } = options;
838
- this.logger.debug('PollStatusAndAuth called with SandboxProcessObject%s, retries %s', sandboxProcessObj, retries);
839
- const lifecycle = lifecycleEvents_1.Lifecycle.getInstance();
840
- let pollFinished = false;
947
+ this.logger.debug('PollStatusAndAuth called with SandboxProcessObject', options.sandboxProcessObj, options.wait.minutes, options.pollInterval.seconds);
948
+ let remainingWait = options.wait;
841
949
  let waitingOnAuth = false;
842
- const sandboxInfo = await this.sandboxSignupComplete(sandboxProcessObj);
843
- if (sandboxInfo) {
844
- await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_AUTH, sandboxInfo);
845
- try {
846
- this.logger.debug('sandbox signup complete with %s', sandboxInfo);
847
- await this.writeSandboxAuthFile(sandboxProcessObj, sandboxInfo);
848
- pollFinished = true;
849
- }
850
- catch (err) {
851
- const error = err;
852
- this.logger.debug('Exception while calling writeSandboxAuthFile %s', err);
853
- 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"))) {
854
- waitingOnAuth = true;
855
- }
856
- else {
857
- throw sfError_1.SfError.wrap(error);
950
+ const pollingClient = await pollingClient_1.PollingClient.create({
951
+ poll: async () => {
952
+ var _a;
953
+ const sandboxProcessObj = await this.querySandboxProcessBySandboxInfoId(options.sandboxProcessObj.SandboxInfoId);
954
+ // check to see if sandbox can authenticate via sandboxAuth endpoint
955
+ const sandboxInfo = await this.sandboxSignupComplete(sandboxProcessObj);
956
+ if (sandboxInfo) {
957
+ await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_AUTH, sandboxInfo);
958
+ try {
959
+ this.logger.debug('sandbox signup complete with', sandboxInfo);
960
+ await this.writeSandboxAuthFile(sandboxProcessObj, sandboxInfo);
961
+ return { completed: true, payload: sandboxProcessObj };
962
+ }
963
+ catch (err) {
964
+ const error = err;
965
+ this.logger.debug('Exception while calling writeSandboxAuthFile', err);
966
+ 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"))) {
967
+ waitingOnAuth = true;
968
+ }
969
+ else {
970
+ throw sfError_1.SfError.wrap(error);
971
+ }
972
+ }
858
973
  }
859
- }
860
- }
861
- if (!pollFinished) {
862
- if (retries > 0) {
863
- // emit the signup progress of the sandbox and query the production org again after waiting the interval
864
- await Promise.all([
865
- await lifecycle.emit(SandboxEvents.EVENT_STATUS, {
866
- sandboxProcessObj,
867
- interval: pollInterval.seconds,
868
- retries,
869
- waitingOnAuth,
870
- }),
871
- await (0, kit_1.sleep)(pollInterval),
872
- ]);
873
- const polledSandboxProcessObj = await this.querySandboxProcess(sandboxProcessObj.SandboxInfoId);
874
- return this.pollStatusAndAuth({
875
- sandboxProcessObj: polledSandboxProcessObj,
876
- retries: retries - 1,
877
- shouldPoll,
878
- pollInterval,
974
+ await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_STATUS, {
975
+ sandboxProcessObj,
976
+ remainingWait: remainingWait.seconds,
977
+ interval: options.pollInterval.seconds,
978
+ waitingOnAuth,
879
979
  });
880
- }
881
- else {
882
- if (shouldPoll) {
883
- // timed out on retries
884
- throw messages.createError('orgPollingTimeout', [sandboxProcessObj.Status]);
885
- }
886
- else {
887
- // The user didn't want us to poll, so simply return the status
888
- // simply report status and exit
889
- await lifecycle.emit(SandboxEvents.EVENT_ASYNC_RESULT, sandboxProcessObj);
890
- }
891
- }
892
- }
893
- return sandboxProcessObj;
980
+ remainingWait = kit_1.Duration.seconds(remainingWait.seconds - options.pollInterval.seconds);
981
+ return { completed: false, payload: sandboxProcessObj };
982
+ },
983
+ frequency: options.pollInterval,
984
+ timeout: options.wait,
985
+ });
986
+ return pollingClient.subscribe();
894
987
  }
895
988
  /**
896
- * query SandboxProcess via SandboxInfoId
989
+ * query SandboxProcess using supplied where clause
897
990
  *
898
- * @param id SandboxInfoId to query for
991
+ * @param where clause to query for
899
992
  * @private
900
993
  */
901
- async querySandboxProcess(id) {
902
- const queryStr = `SELECT Id, Status, SandboxName, SandboxInfoId, LicenseType, CreatedDate, CopyProgress, SandboxOrganization, SourceId, Description, EndDate FROM SandboxProcess WHERE SandboxInfoId='${id}' AND Status != 'D'`;
994
+ async querySandboxProcess(where) {
995
+ const queryStr = `SELECT Id, Status, SandboxName, SandboxInfoId, LicenseType, CreatedDate, CopyProgress, SandboxOrganization, SourceId, Description, EndDate FROM SandboxProcess WHERE ${where} AND Status != 'D'`;
903
996
  return await this.connection.singleRecordQuery(queryStr, {
904
997
  tooling: true,
905
998
  });
@@ -907,11 +1000,11 @@ class Org extends kit_1.AsyncOptionalCreatable {
907
1000
  /**
908
1001
  * determines if the sandbox has successfully been created
909
1002
  *
910
- * @param sandboxProcessObj sandbox signup progeress
1003
+ * @param sandboxProcessObj sandbox signup progress
911
1004
  * @private
912
1005
  */
913
1006
  async sandboxSignupComplete(sandboxProcessObj) {
914
- this.logger.debug('sandboxSignupComplete called with SandboxProcessObject %s', sandboxProcessObj);
1007
+ this.logger.debug('sandboxSignupComplete called with SandboxProcessObject', sandboxProcessObj);
915
1008
  if (!sandboxProcessObj.EndDate) {
916
1009
  return;
917
1010
  }
@@ -925,7 +1018,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
925
1018
  sandboxName: sandboxProcessObj.SandboxName,
926
1019
  callbackUrl,
927
1020
  };
928
- this.logger.debug('Calling sandboxAuth with SandboxUserAuthRequest %s', sandboxReq);
1021
+ this.logger.debug('Calling sandboxAuth with SandboxUserAuthRequest', sandboxReq);
929
1022
  const url = `${this.connection.tooling._baseUrl()}/sandboxAuth`;
930
1023
  const params = {
931
1024
  method: 'POST',
@@ -934,7 +1027,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
934
1027
  body: JSON.stringify(sandboxReq),
935
1028
  };
936
1029
  const result = await this.connection.tooling.request(params);
937
- this.logger.debug('Result of calling sandboxAuth %s', result);
1030
+ this.logger.debug('Result of calling sandboxAuth', result);
938
1031
  return result;
939
1032
  }
940
1033
  catch (err) {
@@ -942,7 +1035,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
942
1035
  // There are cases where the endDate is set before the sandbox has actually completed.
943
1036
  // In that case, the sandboxAuth call will throw a specific exception.
944
1037
  if ((error === null || error === void 0 ? void 0 : error.name) === 'INVALID_STATUS') {
945
- this.logger.debug('Error while authenticating the user %s', error === null || error === void 0 ? void 0 : error.toString());
1038
+ this.logger.debug('Error while authenticating the user', error === null || error === void 0 ? void 0 : error.toString());
946
1039
  }
947
1040
  else {
948
1041
  // If it fails for any unexpected reason, just pass that through
@@ -950,6 +1043,15 @@ class Org extends kit_1.AsyncOptionalCreatable {
950
1043
  }
951
1044
  }
952
1045
  }
1046
+ validateWaitOptions(options) {
1047
+ var _a, _b;
1048
+ const wait = (_a = options.wait) !== null && _a !== void 0 ? _a : kit_1.Duration.minutes(30);
1049
+ const interval = (_b = options.interval) !== null && _b !== void 0 ? _b : kit_1.Duration.seconds(30);
1050
+ let pollInterval = options.async ? kit_1.Duration.seconds(0) : interval;
1051
+ // pollInterval cannot be > wait.
1052
+ pollInterval = pollInterval.seconds > wait.seconds ? wait : pollInterval;
1053
+ return [wait, pollInterval];
1054
+ }
953
1055
  }
954
1056
  exports.Org = Org;
955
1057
  (function (Org) {
@@ -0,0 +1,19 @@
1
+ import { JsonMap } from '@salesforce/ts-types';
2
+ import { TTLConfig } from '../config/ttlConfig';
3
+ export declare type CachedOptions = {
4
+ hubUsername: string;
5
+ /** stores the scratch definition, including settings/objectSettings */
6
+ definitionjson: JsonMap;
7
+ hubBaseUrl: string;
8
+ /** may be required for auth*/
9
+ clientSecret?: string;
10
+ signupTargetLoginUrlConfig?: string;
11
+ apiVersion?: string;
12
+ alias?: string;
13
+ setDefault?: boolean;
14
+ };
15
+ export declare class ScratchOrgCache extends TTLConfig<TTLConfig.Options, CachedOptions> {
16
+ static getFileName(): string;
17
+ static getDefaultOptions(): TTLConfig.Options;
18
+ static unset(key: string): Promise<void>;
19
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ScratchOrgCache = void 0;
4
+ /*
5
+ * Copyright (c) 2020, 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
+ const kit_1 = require("@salesforce/kit");
11
+ const global_1 = require("../global");
12
+ const ttlConfig_1 = require("../config/ttlConfig");
13
+ class ScratchOrgCache extends ttlConfig_1.TTLConfig {
14
+ static getFileName() {
15
+ return 'scratch-create-cache.json';
16
+ }
17
+ static getDefaultOptions() {
18
+ return {
19
+ isGlobal: true,
20
+ isState: true,
21
+ filename: ScratchOrgCache.getFileName(),
22
+ stateFolder: global_1.Global.SF_STATE_FOLDER,
23
+ ttl: kit_1.Duration.days(1),
24
+ };
25
+ }
26
+ static async unset(key) {
27
+ const cache = await ScratchOrgCache.create();
28
+ cache.unset(key);
29
+ await cache.write();
30
+ }
31
+ }
32
+ exports.ScratchOrgCache = ScratchOrgCache;
33
+ //# sourceMappingURL=scratchOrgCache.js.map