@salesforce/core 2.32.0 → 2.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ## [2.33.0](https://github.com/forcedotcom/sfdx-core/compare/v2.32.0...v2.33.0) (2021-12-14)
6
+
7
+ ### Features
8
+
9
+ - sandbox creation ([314dcf1](https://github.com/forcedotcom/sfdx-core/commit/314dcf164d93bd0e3a5f4763a4dfcdde7d0cb14a))
10
+
5
11
  ## [2.32.0](https://github.com/forcedotcom/sfdx-core/compare/v2.31.1...v2.32.0) (2021-12-14)
6
12
 
7
13
  ### Features
package/lib/exported.d.ts CHANGED
@@ -18,7 +18,7 @@ export { SfdcUrl } from './util/sfdcUrl';
18
18
  export { getJwtAudienceUrl } from './util/getJwtAudienceUrl';
19
19
  export { Fields, FieldValue, LoggerLevel, LoggerLevelValue, LogLine, LoggerOptions, LoggerStream, Logger, } from './logger';
20
20
  export { Messages } from './messages';
21
- export { Org } from './org';
21
+ export { Org, SandboxProcessObject, StatusEvent, SandboxEvents, SandboxUserAuthResponse, SandboxUserAuthRequest, SandboxRequest, OrgTypes, ResultEvent, } from './org';
22
22
  export { PackageDir, NamedPackageDir, PackageDirDependency, SfdxProject, SfdxProjectJson } from './sfdxProject';
23
23
  export { SchemaPrinter } from './schema/printer';
24
24
  export { SchemaValidator } from './schema/validator';
package/lib/exported.js CHANGED
@@ -16,7 +16,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
17
17
  };
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.PermissionSetAssignment = exports.User = exports.REQUIRED_FIELDS = exports.DefaultUserFields = exports.MyDomainResolver = exports.StreamingClient = exports.CometClient = exports.PollingClient = exports.SfdxErrorConfig = exports.SfdxError = exports.SchemaValidator = exports.SchemaPrinter = exports.SfdxProjectJson = exports.SfdxProject = exports.Org = exports.Messages = exports.Logger = exports.LoggerLevel = exports.getJwtAudienceUrl = exports.SfdcUrl = exports.WebOAuthServer = exports.Lifecycle = exports.Global = exports.Mode = exports.SFDX_HTTP_HEADERS = exports.Connection = exports.AuthRemover = exports.OAuth2WithVerifier = exports.AuthInfo = exports.ConfigAggregator = exports.Config = exports.OrgUsersConfig = exports.DeviceOauthService = exports.BaseConfigStore = exports.ConfigGroup = exports.ConfigFile = exports.AuthInfoConfig = exports.AliasGroup = exports.Aliases = void 0;
19
+ exports.PermissionSetAssignment = exports.User = exports.REQUIRED_FIELDS = exports.DefaultUserFields = exports.MyDomainResolver = exports.StreamingClient = exports.CometClient = exports.PollingClient = exports.SfdxErrorConfig = exports.SfdxError = exports.SchemaValidator = exports.SchemaPrinter = exports.SfdxProjectJson = exports.SfdxProject = exports.OrgTypes = exports.SandboxEvents = exports.Org = exports.Messages = exports.Logger = exports.LoggerLevel = exports.getJwtAudienceUrl = exports.SfdcUrl = exports.WebOAuthServer = exports.Lifecycle = exports.Global = exports.Mode = exports.SFDX_HTTP_HEADERS = exports.Connection = exports.AuthRemover = exports.OAuth2WithVerifier = exports.AuthInfo = exports.ConfigAggregator = exports.Config = exports.OrgUsersConfig = exports.DeviceOauthService = exports.BaseConfigStore = exports.ConfigGroup = exports.ConfigFile = exports.AuthInfoConfig = exports.AliasGroup = exports.Aliases = void 0;
20
20
  const messages_1 = require("./messages");
21
21
  messages_1.Messages.importMessagesDirectory(__dirname);
22
22
  var aliases_1 = require("./config/aliases");
@@ -64,6 +64,8 @@ var messages_2 = require("./messages");
64
64
  Object.defineProperty(exports, "Messages", { enumerable: true, get: function () { return messages_2.Messages; } });
65
65
  var org_1 = require("./org");
66
66
  Object.defineProperty(exports, "Org", { enumerable: true, get: function () { return org_1.Org; } });
67
+ Object.defineProperty(exports, "SandboxEvents", { enumerable: true, get: function () { return org_1.SandboxEvents; } });
68
+ Object.defineProperty(exports, "OrgTypes", { enumerable: true, get: function () { return org_1.OrgTypes; } });
67
69
  var sfdxProject_1 = require("./sfdxProject");
68
70
  Object.defineProperty(exports, "SfdxProject", { enumerable: true, get: function () { return sfdxProject_1.SfdxProject; } });
69
71
  Object.defineProperty(exports, "SfdxProjectJson", { enumerable: true, get: function () { return sfdxProject_1.SfdxProjectJson; } });
package/lib/org.d.ts CHANGED
@@ -1,10 +1,61 @@
1
- import { AsyncCreatable } from '@salesforce/kit';
1
+ import { AsyncCreatable, Duration } from '@salesforce/kit';
2
2
  import { AnyJson, JsonMap, Optional } from '@salesforce/ts-types';
3
3
  import { AuthFields, AuthInfo } from './authInfo';
4
4
  import { ConfigAggregator } from './config/configAggregator';
5
5
  import { OrgUsersConfig } from './config/orgUsersConfig';
6
6
  import { SandboxOrgConfig } from './config/sandboxOrgConfig';
7
7
  import { Connection } from './connection';
8
+ export declare enum OrgTypes {
9
+ Scratch = "scratch",
10
+ Sandbox = "sandbox"
11
+ }
12
+ export interface StatusEvent {
13
+ sandboxProcessObj: SandboxProcessObject;
14
+ interval: number;
15
+ retries: number;
16
+ waitingOnAuth: boolean;
17
+ }
18
+ export interface ResultEvent {
19
+ sandboxProcessObj: SandboxProcessObject;
20
+ sandboxRes: SandboxUserAuthResponse;
21
+ }
22
+ export interface SandboxUserAuthRequest {
23
+ sandboxName: string;
24
+ clientId: string;
25
+ callbackUrl: string;
26
+ }
27
+ export declare enum SandboxEvents {
28
+ EVENT_STATUS = "status",
29
+ EVENT_ASYNC_RESULT = "asyncResult",
30
+ EVENT_RESULT = "result",
31
+ EVENT_AUTH = "auth"
32
+ }
33
+ export interface SandboxUserAuthResponse {
34
+ authUserName: string;
35
+ authCode: string;
36
+ instanceUrl: string;
37
+ loginUrl: string;
38
+ }
39
+ export interface SandboxProcessObject {
40
+ Id: string;
41
+ Status: string;
42
+ SandboxName: string;
43
+ SandboxInfoId: string;
44
+ LicenseType: string;
45
+ CreatedDate: string;
46
+ SandboxOrganization?: string;
47
+ CopyProgress?: number;
48
+ SourceId?: string;
49
+ Description?: string;
50
+ ApexClassId?: string;
51
+ EndDate?: string;
52
+ }
53
+ export declare type SandboxRequest = {
54
+ SandboxName: string;
55
+ LicenseType?: string;
56
+ SourceId?: string;
57
+ Description?: string;
58
+ };
8
59
  /**
9
60
  * Provides a way to manage a locally authenticated Org.
10
61
  *
@@ -41,6 +92,17 @@ export declare class Org extends AsyncCreatable<Org.Options> {
41
92
  * @ignore
42
93
  */
43
94
  constructor(options: Org.Options);
95
+ /**
96
+ * create a sandbox from a production org
97
+ * 'this' needs to be a production org with sandbox licenses available
98
+ *
99
+ * @param sandboxReq SandboxRequest options to create the sandbox with
100
+ * @param options Wait: The amount of time to wait before timing out, Interval: The time interval between polling
101
+ */
102
+ createSandbox(sandboxReq: SandboxRequest, options: {
103
+ wait?: Duration;
104
+ interval?: Duration;
105
+ }): Promise<SandboxProcessObject>;
44
106
  /**
45
107
  * Clean all data files in the org's data path. Usually <workspace>/.sfdx/orgs/<username>.
46
108
  *
@@ -233,6 +295,32 @@ export declare class Org extends AsyncCreatable<Org.Options> {
233
295
  * @param throwWhenRemoveFails true if manageDelete should throw or not if the deleted fails.
234
296
  */
235
297
  private removeSandboxConfig;
298
+ private writeSandboxAuthFile;
299
+ /**
300
+ * Polls for the new sandbox to be created - and will write the associated auth files
301
+ *
302
+ * @private
303
+ * @param options
304
+ * sandboxProcessObj: The in-progress sandbox signup request
305
+ * retries: the number of retries to poll for every 30s
306
+ * shouldPoll: wait for polling, or just return
307
+ * pollInterval: Duration to sleep between poll events, default 30 seconds
308
+ */
309
+ private pollStatusAndAuth;
310
+ /**
311
+ * query SandboxProcess via SandboxInfoId
312
+ *
313
+ * @param id SandboxInfoId to query for
314
+ * @private
315
+ */
316
+ private querySandboxProcess;
317
+ /**
318
+ * determines if the sandbox has successfully been created
319
+ *
320
+ * @param sandboxProcessObj sandbox signup progeress
321
+ * @private
322
+ */
323
+ private sandboxSignupComplete;
236
324
  }
237
325
  export declare namespace Org {
238
326
  /**
package/lib/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");
@@ -19,10 +19,24 @@ const orgUsersConfig_1 = require("./config/orgUsersConfig");
19
19
  const sandboxOrgConfig_1 = require("./config/sandboxOrgConfig");
20
20
  const connection_1 = require("./connection");
21
21
  const global_1 = require("./global");
22
+ const lifecycleEvents_1 = require("./lifecycleEvents");
22
23
  const logger_1 = require("./logger");
23
24
  const sfdxError_1 = require("./sfdxError");
24
25
  const fs_1 = require("./util/fs");
25
26
  const sfdc_1 = require("./util/sfdc");
27
+ const webOAuthServer_1 = require("./webOAuthServer");
28
+ var OrgTypes;
29
+ (function (OrgTypes) {
30
+ OrgTypes["Scratch"] = "scratch";
31
+ OrgTypes["Sandbox"] = "sandbox";
32
+ })(OrgTypes = exports.OrgTypes || (exports.OrgTypes = {}));
33
+ var SandboxEvents;
34
+ (function (SandboxEvents) {
35
+ SandboxEvents["EVENT_STATUS"] = "status";
36
+ SandboxEvents["EVENT_ASYNC_RESULT"] = "asyncResult";
37
+ SandboxEvents["EVENT_RESULT"] = "result";
38
+ SandboxEvents["EVENT_AUTH"] = "auth";
39
+ })(SandboxEvents = exports.SandboxEvents || (exports.SandboxEvents = {}));
26
40
  /**
27
41
  * Provides a way to manage a locally authenticated Org.
28
42
  *
@@ -58,6 +72,33 @@ class Org extends kit_1.AsyncCreatable {
58
72
  this.status = Org.Status.UNKNOWN;
59
73
  this.options = options;
60
74
  }
75
+ /**
76
+ * create a sandbox from a production org
77
+ * 'this' needs to be a production org with sandbox licenses available
78
+ *
79
+ * @param sandboxReq SandboxRequest options to create the sandbox with
80
+ * @param options Wait: The amount of time to wait before timing out, Interval: The time interval between polling
81
+ */
82
+ async createSandbox(sandboxReq, options) {
83
+ var _a;
84
+ this.logger.debug('CreateSandbox called with SandboxRequest: %s ', sandboxReq);
85
+ const createResult = await this.connection.tooling.create('SandboxInfo', sandboxReq);
86
+ this.logger.debug('Return from calling tooling.create: %s ', createResult);
87
+ if (Array.isArray(createResult) || !createResult.success) {
88
+ throw sfdxError_1.SfdxError.create('@salesforce/core', 'org', 'SandboxInfoCreateFailed', [JSON.stringify(createResult)]);
89
+ }
90
+ const sandboxCreationProgress = await this.querySandboxProcess(createResult.id);
91
+ this.logger.debug('Return from calling singleRecordQuery with tooling: %s', sandboxCreationProgress);
92
+ const retries = options.wait ? options.wait.seconds / kit_1.Duration.seconds(30).seconds : 0;
93
+ this.logger.debug('pollStatusAndAuth sandboxProcessObj %s, maxPollingRetries %i', sandboxCreationProgress, retries);
94
+ const pollInterval = (_a = options.interval) !== null && _a !== void 0 ? _a : kit_1.Duration.seconds(30);
95
+ return this.pollStatusAndAuth({
96
+ sandboxProcessObj: sandboxCreationProgress,
97
+ retries,
98
+ shouldPoll: retries > 0,
99
+ pollInterval,
100
+ });
101
+ }
61
102
  /**
62
103
  * Clean all data files in the org's data path. Usually <workspace>/.sfdx/orgs/<username>.
63
104
  *
@@ -594,6 +635,7 @@ class Org extends kit_1.AsyncCreatable {
594
635
  *
595
636
  * @param throwWhenRemoveFails true if manageDelete should throw or not if the deleted fails.
596
637
  */
638
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
597
639
  async removeUsers(throwWhenRemoveFails) {
598
640
  this.logger.debug(`Removing users associate with org: ${this.getOrgId()}`);
599
641
  const config = await this.retrieveOrgUsersConfig();
@@ -637,6 +679,167 @@ class Org extends kit_1.AsyncCreatable {
637
679
  await this.manageDelete(async () => await sandboxOrgConfig.unlink(), sandboxOrgConfig.getPath(), throwWhenRemoveFails);
638
680
  }
639
681
  }
682
+ async writeSandboxAuthFile(sandboxProcessObj, sandboxRes) {
683
+ this.logger.debug('writeSandboxAuthFile sandboxProcessObj: %s, sandboxRes: %s', sandboxProcessObj, sandboxRes);
684
+ if (sandboxRes.authUserName) {
685
+ const productionAuthFields = this.connection.getAuthInfoFields();
686
+ this.logger.debug('Result from getAuthInfoFields: AuthFields %s', productionAuthFields);
687
+ // let's do headless auth via jwt (if we have privateKey) or web auth
688
+ const oauth2Options = {
689
+ loginUrl: sandboxRes.loginUrl,
690
+ instanceUrl: sandboxRes.instanceUrl,
691
+ username: sandboxRes.authUserName,
692
+ };
693
+ // If we don't have a privateKey then we assume it's web auth.
694
+ if (!productionAuthFields.privateKey) {
695
+ oauth2Options.redirectUri = `http://localhost:${await webOAuthServer_1.WebOAuthServer.determineOauthPort()}/OauthRedirect`;
696
+ oauth2Options.authCode = sandboxRes.authCode;
697
+ }
698
+ const authInfo = await authInfo_1.AuthInfo.create({
699
+ username: sandboxRes.authUserName,
700
+ oauth2Options,
701
+ parentUsername: productionAuthFields.username,
702
+ });
703
+ await authInfo.save();
704
+ const sandboxOrg = await Org.create({ aliasOrUsername: authInfo.getUsername() });
705
+ await sandboxOrg.setSandboxOrgConfigField(sandboxOrgConfig_1.SandboxOrgConfig.Fields.PROD_ORG_USERNAME,
706
+ // we couldn't get this far into the process without a production org so username will be there
707
+ productionAuthFields.username);
708
+ await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_RESULT, {
709
+ sandboxProcessObj,
710
+ sandboxRes,
711
+ });
712
+ }
713
+ else {
714
+ // no authed sandbox user, error
715
+ throw sfdxError_1.SfdxError.create('@salesforce/core', 'org', 'MissingAuthUsername', [sandboxProcessObj.SandboxName]);
716
+ }
717
+ }
718
+ /**
719
+ * Polls for the new sandbox to be created - and will write the associated auth files
720
+ *
721
+ * @private
722
+ * @param options
723
+ * sandboxProcessObj: The in-progress sandbox signup request
724
+ * retries: the number of retries to poll for every 30s
725
+ * shouldPoll: wait for polling, or just return
726
+ * pollInterval: Duration to sleep between poll events, default 30 seconds
727
+ */
728
+ async pollStatusAndAuth(options) {
729
+ const { sandboxProcessObj, retries, shouldPoll, pollInterval } = options;
730
+ this.logger.debug('PollStatusAndAuth called with SandboxProcessObject%s, retries %s', sandboxProcessObj, retries);
731
+ const lifecycle = lifecycleEvents_1.Lifecycle.getInstance();
732
+ let pollFinished = false;
733
+ let waitingOnAuth = false;
734
+ const sandboxInfo = await this.sandboxSignupComplete(sandboxProcessObj);
735
+ if (sandboxInfo) {
736
+ await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_AUTH, sandboxInfo);
737
+ try {
738
+ this.logger.debug('sandbox signup complete with %s', sandboxInfo);
739
+ await this.writeSandboxAuthFile(sandboxProcessObj, sandboxInfo);
740
+ pollFinished = true;
741
+ }
742
+ catch (err) {
743
+ this.logger.debug('Exception while calling writeSandboxAuthFile %s', err);
744
+ if ((err === null || err === void 0 ? void 0 : err.name) === 'JWTAuthError' && (err === null || err === void 0 ? void 0 : err.stack.includes("user hasn't approved"))) {
745
+ waitingOnAuth = true;
746
+ }
747
+ else {
748
+ throw sfdxError_1.SfdxError.wrap(err);
749
+ }
750
+ }
751
+ }
752
+ if (!pollFinished) {
753
+ if (retries > 0) {
754
+ // emit the signup progress of the sandbox and query the production org again after waiting the interval
755
+ await Promise.all([
756
+ await lifecycle.emit(SandboxEvents.EVENT_STATUS, {
757
+ sandboxProcessObj,
758
+ interval: pollInterval.seconds,
759
+ retries,
760
+ waitingOnAuth,
761
+ }),
762
+ await kit_1.sleep(pollInterval),
763
+ ]);
764
+ const polledSandboxProcessObj = await this.querySandboxProcess(sandboxProcessObj.SandboxInfoId);
765
+ return this.pollStatusAndAuth({
766
+ sandboxProcessObj: polledSandboxProcessObj,
767
+ retries: retries - 1,
768
+ shouldPoll,
769
+ pollInterval,
770
+ });
771
+ }
772
+ else {
773
+ if (shouldPoll) {
774
+ // timed out on retries
775
+ throw sfdxError_1.SfdxError.create('@salesforce/core', 'org', 'OrgPollingTimeout', [sandboxProcessObj.Status]);
776
+ }
777
+ else {
778
+ // The user didn't want us to poll, so simply return the status
779
+ // simply report status and exit
780
+ await lifecycle.emit(SandboxEvents.EVENT_ASYNC_RESULT, sandboxProcessObj);
781
+ }
782
+ }
783
+ }
784
+ return sandboxProcessObj;
785
+ }
786
+ /**
787
+ * query SandboxProcess via SandboxInfoId
788
+ *
789
+ * @param id SandboxInfoId to query for
790
+ * @private
791
+ */
792
+ async querySandboxProcess(id) {
793
+ const queryStr = `SELECT Id, Status, SandboxName, SandboxInfoId, LicenseType, CreatedDate, CopyProgress, SandboxOrganization, SourceId, Description, EndDate FROM SandboxProcess WHERE SandboxInfoId='${id}' AND Status != 'D'`;
794
+ return await this.connection.singleRecordQuery(queryStr, {
795
+ tooling: true,
796
+ });
797
+ }
798
+ /**
799
+ * determines if the sandbox has successfully been created
800
+ *
801
+ * @param sandboxProcessObj sandbox signup progeress
802
+ * @private
803
+ */
804
+ async sandboxSignupComplete(sandboxProcessObj) {
805
+ this.logger.debug('sandboxSignupComplete called with SandboxProcessObject %s', sandboxProcessObj);
806
+ if (!sandboxProcessObj.EndDate) {
807
+ return;
808
+ }
809
+ try {
810
+ // call server side /sandboxAuth API to auth the sandbox org user with the connected app
811
+ const authFields = this.connection.getAuthInfoFields();
812
+ const callbackUrl = `http://localhost:${await webOAuthServer_1.WebOAuthServer.determineOauthPort()}/OauthRedirect`;
813
+ const sandboxReq = {
814
+ // the sandbox signup has been completed on production, we have production clientId by this point
815
+ clientId: authFields.clientId,
816
+ sandboxName: sandboxProcessObj.SandboxName,
817
+ callbackUrl,
818
+ };
819
+ this.logger.debug('Calling sandboxAuth with SandboxUserAuthRequest %s', sandboxReq);
820
+ const url = `${this.connection.tooling._baseUrl()}/sandboxAuth`;
821
+ const params = {
822
+ method: 'POST',
823
+ url,
824
+ headers: { 'Content-Type': 'application/json' },
825
+ body: JSON.stringify(sandboxReq),
826
+ };
827
+ const result = await this.connection.tooling.request(params);
828
+ this.logger.debug('Result of calling sandboxAuth %s', result);
829
+ return result;
830
+ }
831
+ catch (err) {
832
+ // There are cases where the endDate is set before the sandbox has actually completed.
833
+ // In that case, the sandboxAuth call will throw a specific exception.
834
+ if ((err === null || err === void 0 ? void 0 : err.name) === 'INVALID_STATUS') {
835
+ this.logger.debug('Error while authenticating the user %s', err === null || err === void 0 ? void 0 : err.toString());
836
+ }
837
+ else {
838
+ // If it fails for any unexpected reason, just pass that through
839
+ throw sfdxError_1.SfdxError.wrap(err);
840
+ }
841
+ }
842
+ }
640
843
  }
641
844
  exports.Org = Org;
642
845
  (function (Org) {
package/messages/org.json CHANGED
@@ -6,5 +6,8 @@
6
6
  "InsufficientAccessToDelete": "You do not have the appropriate permissions to delete a scratch org. Please contact your Salesforce admin.",
7
7
  "ScratchOrgNotFound": "Attempting to delete an expired or deleted org",
8
8
  "SandboxDeleteFailed": "The sandbox org deletion failed with a result of %s.",
9
- "SandboxNotFound": "We can't find a SandboxProcess for the sandbox org %s."
9
+ "SandboxNotFound": "We can't find a SandboxProcess for the sandbox org %s.",
10
+ "SandboxInfoCreateFailed": "The sandbox org creation failed with a result of %s.",
11
+ "MissingAuthUsername": "The sandbox %s does not have an authorized username.",
12
+ "OrgPollingTimeout": "Sandbox status is %s; timed out waiting for completion."
10
13
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/core",
3
- "version": "2.32.0",
3
+ "version": "2.33.0",
4
4
  "description": "Core libraries to interact with SFDX projects, orgs, and APIs.",
5
5
  "main": "lib/exported",
6
6
  "types": "lib/exported.d.ts",