@salesforce/core 4.0.0-v3.0 → 4.0.1
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/LICENSE.txt +1 -1
- package/README.md +93 -44
- package/lib/config/aliasesConfig.d.ts +12 -0
- package/lib/config/aliasesConfig.js +28 -0
- package/lib/config/authInfoConfig.d.ts +19 -0
- package/lib/config/authInfoConfig.js +35 -0
- package/lib/config/config.d.ts +181 -48
- package/lib/config/config.js +382 -159
- package/lib/config/configAggregator.d.ts +59 -42
- package/lib/config/configAggregator.js +135 -82
- package/lib/config/configFile.d.ts +2 -2
- package/lib/config/configFile.js +40 -31
- package/lib/config/configGroup.d.ts +9 -9
- package/lib/config/configGroup.js +13 -11
- package/lib/config/configStore.d.ts +9 -9
- package/lib/config/configStore.js +29 -26
- package/lib/config/envVars.d.ts +102 -0
- package/lib/config/envVars.js +457 -0
- package/lib/config/orgUsersConfig.d.ts +8 -0
- package/lib/config/orgUsersConfig.js +12 -0
- package/lib/config/sandboxOrgConfig.d.ts +8 -0
- package/lib/config/sandboxOrgConfig.js +12 -0
- package/lib/config/sandboxProcessCache.d.ts +16 -0
- package/lib/config/sandboxProcessCache.js +38 -0
- package/lib/config/tokensConfig.d.ts +10 -0
- package/lib/config/tokensConfig.js +29 -0
- package/lib/config/ttlConfig.d.ts +34 -0
- package/lib/config/ttlConfig.js +50 -0
- package/lib/crypto/crypto.js +29 -16
- package/lib/crypto/keyChain.js +4 -4
- package/lib/crypto/keyChainImpl.d.ts +5 -3
- package/lib/crypto/keyChainImpl.js +65 -66
- package/lib/crypto/secureBuffer.d.ts +1 -1
- package/lib/crypto/secureBuffer.js +1 -1
- package/lib/deviceOauthService.d.ts +5 -5
- package/lib/deviceOauthService.js +37 -33
- package/lib/exported.d.ts +22 -15
- package/lib/exported.js +49 -25
- package/lib/global.d.ts +17 -3
- package/lib/global.js +46 -13
- package/lib/lifecycleEvents.d.ts +39 -2
- package/lib/lifecycleEvents.js +77 -3
- package/lib/logger.d.ts +21 -11
- package/lib/logger.js +121 -105
- package/lib/messages.d.ts +53 -36
- package/lib/messages.js +89 -97
- package/lib/org/authInfo.d.ts +84 -72
- package/lib/org/authInfo.js +326 -320
- package/lib/org/authRemover.d.ts +16 -23
- package/lib/org/authRemover.js +62 -60
- package/lib/org/connection.d.ts +33 -59
- package/lib/org/connection.js +129 -190
- package/lib/org/index.js +6 -2
- package/lib/org/org.d.ts +263 -38
- package/lib/org/org.js +734 -149
- package/lib/org/orgConfigProperties.d.ts +69 -0
- package/lib/org/orgConfigProperties.js +121 -0
- package/lib/org/permissionSetAssignment.js +6 -15
- package/lib/org/scratchOrgCache.d.ts +20 -0
- package/lib/org/scratchOrgCache.js +33 -0
- package/lib/org/scratchOrgCreate.d.ts +54 -0
- package/lib/org/scratchOrgCreate.js +214 -0
- package/lib/org/scratchOrgErrorCodes.d.ts +10 -0
- package/lib/org/scratchOrgErrorCodes.js +79 -0
- package/lib/org/scratchOrgFeatureDeprecation.d.ts +26 -0
- package/lib/org/scratchOrgFeatureDeprecation.js +105 -0
- package/lib/org/scratchOrgInfoApi.d.ts +68 -0
- package/lib/org/scratchOrgInfoApi.js +416 -0
- package/lib/org/scratchOrgInfoGenerator.d.ts +64 -0
- package/lib/org/scratchOrgInfoGenerator.js +237 -0
- package/lib/org/scratchOrgLifecycleEvents.d.ts +10 -0
- package/lib/org/scratchOrgLifecycleEvents.js +41 -0
- package/lib/org/scratchOrgSettingsGenerator.d.ts +79 -0
- package/lib/org/scratchOrgSettingsGenerator.js +277 -0
- package/lib/org/scratchOrgTypes.d.ts +43 -0
- package/lib/{status/client.js → org/scratchOrgTypes.js} +1 -1
- package/lib/org/user.d.ts +7 -2
- package/lib/org/user.js +78 -54
- package/lib/schema/printer.d.ts +6 -0
- package/lib/schema/printer.js +49 -46
- package/lib/schema/validator.d.ts +12 -10
- package/lib/schema/validator.js +56 -76
- package/lib/{sfdxError.d.ts → sfError.d.ts} +12 -15
- package/lib/{sfdxError.js → sfError.js} +42 -24
- package/lib/{sfdxProject.d.ts → sfProject.d.ts} +75 -35
- package/lib/sfProject.js +651 -0
- package/lib/stateAggregator/accessors/aliasAccessor.d.ts +98 -0
- package/lib/stateAggregator/accessors/aliasAccessor.js +146 -0
- package/lib/stateAggregator/accessors/orgAccessor.d.ts +101 -0
- package/lib/stateAggregator/accessors/orgAccessor.js +240 -0
- package/lib/stateAggregator/accessors/sandboxAccessor.d.ts +8 -0
- package/lib/stateAggregator/accessors/sandboxAccessor.js +28 -0
- package/lib/stateAggregator/accessors/tokenAccessor.d.ts +63 -0
- package/lib/stateAggregator/accessors/tokenAccessor.js +80 -0
- package/lib/stateAggregator/index.d.ts +4 -0
- package/lib/stateAggregator/index.js +27 -0
- package/lib/stateAggregator/stateAggregator.d.ts +25 -0
- package/lib/stateAggregator/stateAggregator.js +46 -0
- package/lib/status/myDomainResolver.d.ts +1 -1
- package/lib/status/myDomainResolver.js +10 -10
- package/lib/status/pollingClient.d.ts +2 -6
- package/lib/status/pollingClient.js +38 -64
- package/lib/status/streamingClient.d.ts +5 -80
- package/lib/status/streamingClient.js +74 -94
- package/lib/status/types.d.ts +89 -0
- package/lib/status/types.js +18 -0
- package/lib/testSetup.d.ts +212 -79
- package/lib/testSetup.js +478 -182
- package/lib/util/cache.d.ts +11 -0
- package/lib/util/cache.js +70 -0
- package/lib/util/checkLightningDomain.d.ts +1 -0
- package/lib/util/checkLightningDomain.js +29 -0
- package/lib/util/directoryWriter.d.ts +12 -0
- package/lib/util/directoryWriter.js +54 -0
- package/lib/util/getJwtAudienceUrl.d.ts +4 -0
- package/lib/util/getJwtAudienceUrl.js +19 -0
- package/lib/util/internal.d.ts +28 -2
- package/lib/util/internal.js +65 -8
- package/lib/util/jsonXmlTools.d.ts +14 -0
- package/lib/util/jsonXmlTools.js +39 -0
- package/lib/util/mapKeys.d.ts +14 -0
- package/lib/util/mapKeys.js +52 -0
- package/lib/util/sfdc.d.ts +51 -63
- package/lib/util/sfdc.js +75 -127
- package/lib/util/sfdcUrl.d.ts +64 -0
- package/lib/util/sfdcUrl.js +197 -0
- package/lib/util/structuredWriter.d.ts +9 -0
- package/lib/util/structuredWriter.js +3 -0
- package/lib/util/zipWriter.d.ts +16 -0
- package/lib/util/zipWriter.js +68 -0
- package/lib/webOAuthServer.d.ts +20 -7
- package/lib/webOAuthServer.js +107 -60
- package/messageTransformer/messageTransformer.ts +93 -0
- package/messages/auth.md +11 -3
- package/messages/config.md +94 -6
- package/messages/connection.md +8 -0
- package/messages/core.json +3 -3
- package/messages/core.md +11 -1
- package/messages/envVars.md +313 -0
- package/messages/org.md +64 -0
- package/messages/scratchOrgCreate.md +23 -0
- package/messages/scratchOrgErrorCodes.md +115 -0
- package/messages/scratchOrgFeatureDeprecation.md +11 -0
- package/messages/scratchOrgInfoApi.md +20 -0
- package/messages/scratchOrgInfoGenerator.md +27 -0
- package/messages/user.md +12 -0
- package/package.json +138 -66
- package/CHANGELOG.md +0 -699
- package/lib/config/aliases.d.ts +0 -56
- package/lib/config/aliases.js +0 -96
- package/lib/config/globalInfoConfig.d.ts +0 -74
- package/lib/config/globalInfoConfig.js +0 -144
- package/lib/config/keychainConfig.d.ts +0 -19
- package/lib/config/keychainConfig.js +0 -43
- package/lib/config/sfdxDataHandler.d.ts +0 -36
- package/lib/config/sfdxDataHandler.js +0 -165
- package/lib/sfdxProject.js +0 -546
- package/lib/status/client.d.ts +0 -15
- package/lib/util/fs.d.ts +0 -198
- package/lib/util/fs.js +0 -374
package/lib/org/org.js
CHANGED
|
@@ -5,27 +5,48 @@
|
|
|
5
5
|
* Licensed under the BSD 3-Clause license.
|
|
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
|
+
/* eslint-disable class-methods-use-this */
|
|
8
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.Org = void 0;
|
|
10
|
+
exports.Org = exports.sandboxIsResumable = exports.SandboxEvents = exports.OrgTypes = void 0;
|
|
10
11
|
const path_1 = require("path");
|
|
12
|
+
const fs = require("fs");
|
|
11
13
|
const kit_1 = require("@salesforce/kit");
|
|
12
14
|
const ts_types_1 = require("@salesforce/ts-types");
|
|
13
|
-
const aliases_1 = require("../config/aliases");
|
|
14
15
|
const config_1 = require("../config/config");
|
|
15
16
|
const configAggregator_1 = require("../config/configAggregator");
|
|
16
17
|
const orgUsersConfig_1 = require("../config/orgUsersConfig");
|
|
17
|
-
const sandboxOrgConfig_1 = require("../config/sandboxOrgConfig");
|
|
18
18
|
const global_1 = require("../global");
|
|
19
|
+
const lifecycleEvents_1 = require("../lifecycleEvents");
|
|
19
20
|
const logger_1 = require("../logger");
|
|
20
|
-
const
|
|
21
|
-
const fs_1 = require("../util/fs");
|
|
21
|
+
const sfError_1 = require("../sfError");
|
|
22
22
|
const sfdc_1 = require("../util/sfdc");
|
|
23
|
-
const
|
|
23
|
+
const webOAuthServer_1 = require("../webOAuthServer");
|
|
24
24
|
const messages_1 = require("../messages");
|
|
25
|
+
const stateAggregator_1 = require("../stateAggregator");
|
|
26
|
+
const pollingClient_1 = require("../status/pollingClient");
|
|
25
27
|
const connection_1 = require("./connection");
|
|
26
28
|
const authInfo_1 = require("./authInfo");
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
+
const scratchOrgCreate_1 = require("./scratchOrgCreate");
|
|
30
|
+
const orgConfigProperties_1 = require("./orgConfigProperties");
|
|
31
|
+
const messages = new messages_1.Messages('@salesforce/core', 'org', new Map([["notADevHub", "The provided dev hub username %s is not a valid dev hub."], ["noUsernameFound", "No username found."], ["noDevHubFound", "Unable to associate this scratch org with a DevHub."], ["deleteOrgHubError", "The Dev Hub org cannot be deleted."], ["insufficientAccessToDelete", "You do not have the appropriate permissions to delete a scratch org. Please contact your Salesforce admin."], ["scratchOrgNotFound", "Attempting to delete an expired or deleted org"], ["sandboxDeleteFailed", "The sandbox org deletion failed with a result of %s."], ["sandboxNotFound", "We can't find a SandboxProcess for the sandbox %s."], ["sandboxInfoCreateFailed", "The sandbox org creation failed with a result of %s."], ["missingAuthUsername", "The sandbox %s does not have an authorized username."], ["orgPollingTimeout", "Sandbox status is %s; timed out waiting for completion."], ["NotFoundOnDevHub", "The scratch org does not belong to the dev hub username %s."], ["AuthInfoOrgIdUndefined", "AuthInfo orgId is undefined."], ["sandboxCreateNotComplete", "The sandbox creation has not completed."], ["SandboxProcessNotFoundBySandboxName", "We can't find a SandboxProcess with the SandboxName %s."], ["MultiSandboxProcessNotFoundBySandboxName", "We found more than one SandboxProcess with the SandboxName %s."], ["sandboxNotResumable", "The sandbox %s cannot resume with status of %s."]]));
|
|
32
|
+
var OrgTypes;
|
|
33
|
+
(function (OrgTypes) {
|
|
34
|
+
OrgTypes["Scratch"] = "scratch";
|
|
35
|
+
OrgTypes["Sandbox"] = "sandbox";
|
|
36
|
+
})(OrgTypes = exports.OrgTypes || (exports.OrgTypes = {}));
|
|
37
|
+
var SandboxEvents;
|
|
38
|
+
(function (SandboxEvents) {
|
|
39
|
+
SandboxEvents["EVENT_STATUS"] = "status";
|
|
40
|
+
SandboxEvents["EVENT_ASYNC_RESULT"] = "asyncResult";
|
|
41
|
+
SandboxEvents["EVENT_RESULT"] = "result";
|
|
42
|
+
SandboxEvents["EVENT_AUTH"] = "auth";
|
|
43
|
+
SandboxEvents["EVENT_RESUME"] = "resume";
|
|
44
|
+
})(SandboxEvents = exports.SandboxEvents || (exports.SandboxEvents = {}));
|
|
45
|
+
const resumableSandboxStatus = ['Activating', 'Pending', 'Pending Activation', 'Processing', 'Sampling', 'Completed'];
|
|
46
|
+
function sandboxIsResumable(value) {
|
|
47
|
+
return resumableSandboxStatus.includes(value);
|
|
48
|
+
}
|
|
49
|
+
exports.sandboxIsResumable = sandboxIsResumable;
|
|
29
50
|
/**
|
|
30
51
|
* Provides a way to manage a locally authenticated Org.
|
|
31
52
|
*
|
|
@@ -40,7 +61,7 @@ const messages = messages_1.Messages.load('@salesforce/core', 'org', ['notADevHu
|
|
|
40
61
|
* ```
|
|
41
62
|
* // Email username
|
|
42
63
|
* const org1: Org = await Org.create({ aliasOrUsername: 'foo@example.com' });
|
|
43
|
-
* // The
|
|
64
|
+
* // The target-org config property
|
|
44
65
|
* const org2: Org = await Org.create();
|
|
45
66
|
* // Full Connection
|
|
46
67
|
* const org3: Org = await Org.create({
|
|
@@ -59,7 +80,136 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
59
80
|
constructor(options) {
|
|
60
81
|
super(options);
|
|
61
82
|
this.status = Org.Status.UNKNOWN;
|
|
62
|
-
this.options = options
|
|
83
|
+
this.options = options ?? {};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* create a sandbox from a production org
|
|
87
|
+
* 'this' needs to be a production org with sandbox licenses available
|
|
88
|
+
*
|
|
89
|
+
* @param sandboxReq SandboxRequest options to create the sandbox with
|
|
90
|
+
* @param options Wait: The amount of time to wait before timing out, Interval: The time interval between polling
|
|
91
|
+
*/
|
|
92
|
+
async createSandbox(sandboxReq, options = {
|
|
93
|
+
wait: kit_1.Duration.minutes(6),
|
|
94
|
+
async: false,
|
|
95
|
+
interval: kit_1.Duration.seconds(30),
|
|
96
|
+
}) {
|
|
97
|
+
this.logger.debug(`CreateSandbox called with SandboxRequest: ${JSON.stringify(sandboxReq, undefined, 2)}`);
|
|
98
|
+
const createResult = await this.connection.tooling.create('SandboxInfo', sandboxReq);
|
|
99
|
+
this.logger.debug(`Return from calling tooling.create: ${JSON.stringify(createResult, undefined, 2)}`);
|
|
100
|
+
if (Array.isArray(createResult) || !createResult.success) {
|
|
101
|
+
throw messages.createError('sandboxInfoCreateFailed', [JSON.stringify(createResult)]);
|
|
102
|
+
}
|
|
103
|
+
const sandboxCreationProgress = await this.querySandboxProcessBySandboxInfoId(createResult.id);
|
|
104
|
+
this.logger.debug(`Return from calling singleRecordQuery with tooling: ${JSON.stringify(sandboxCreationProgress, undefined, 2)}`);
|
|
105
|
+
const isAsync = !!options.async;
|
|
106
|
+
if (isAsync) {
|
|
107
|
+
// The user didn't want us to poll, so simply return the status
|
|
108
|
+
await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_ASYNC_RESULT, sandboxCreationProgress);
|
|
109
|
+
return sandboxCreationProgress;
|
|
110
|
+
}
|
|
111
|
+
const [wait, pollInterval] = this.validateWaitOptions(options);
|
|
112
|
+
this.logger.debug(`create - pollStatusAndAuth sandboxProcessObj ${JSON.stringify(sandboxCreationProgress, undefined, 2)}, max wait time of ${wait.minutes} minutes`);
|
|
113
|
+
return this.pollStatusAndAuth({
|
|
114
|
+
sandboxProcessObj: sandboxCreationProgress,
|
|
115
|
+
wait,
|
|
116
|
+
pollInterval,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
*
|
|
121
|
+
* @param sandboxReq SandboxRequest options to create the sandbox with
|
|
122
|
+
* @param sourceSandboxName the name of the sandbox that your new sandbox will be based on
|
|
123
|
+
* @param options Wait: The amount of time to wait before timing out, defaults to 0, Interval: The time interval between polling defaults to 30 seconds
|
|
124
|
+
* @returns {SandboxProcessObject} the newly created sandbox process object
|
|
125
|
+
*/
|
|
126
|
+
async cloneSandbox(sandboxReq, sourceSandboxName, options) {
|
|
127
|
+
sandboxReq.SourceId = (await this.querySandboxProcessBySandboxName(sourceSandboxName)).SandboxInfoId;
|
|
128
|
+
this.logger.debug('Clone sandbox sourceId %s', sandboxReq.SourceId);
|
|
129
|
+
return this.createSandbox(sandboxReq, options);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* resume a sandbox creation from a production org
|
|
133
|
+
* 'this' needs to be a production org with sandbox licenses available
|
|
134
|
+
*
|
|
135
|
+
* @param resumeSandboxRequest SandboxRequest options to create the sandbox with
|
|
136
|
+
* @param options Wait: The amount of time to wait (default: 30 minutes) before timing out,
|
|
137
|
+
* Interval: The time interval (default: 30 seconds) between polling
|
|
138
|
+
*/
|
|
139
|
+
async resumeSandbox(resumeSandboxRequest, options = {
|
|
140
|
+
wait: kit_1.Duration.minutes(0),
|
|
141
|
+
async: false,
|
|
142
|
+
interval: kit_1.Duration.seconds(30),
|
|
143
|
+
}) {
|
|
144
|
+
this.logger.debug(`ResumeSandbox called with ResumeSandboxRequest: ${JSON.stringify(resumeSandboxRequest, undefined, 2)}`);
|
|
145
|
+
let sandboxCreationProgress;
|
|
146
|
+
// seed the sandboxCreationProgress via the resumeSandboxRequest options
|
|
147
|
+
if (resumeSandboxRequest.SandboxName) {
|
|
148
|
+
sandboxCreationProgress = await this.querySandboxProcessBySandboxName(resumeSandboxRequest.SandboxName);
|
|
149
|
+
}
|
|
150
|
+
else if (resumeSandboxRequest.SandboxProcessObjId) {
|
|
151
|
+
sandboxCreationProgress = await this.querySandboxProcessById(resumeSandboxRequest.SandboxProcessObjId);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
throw messages.createError('sandboxNotFound', [
|
|
155
|
+
resumeSandboxRequest.SandboxName ?? resumeSandboxRequest.SandboxProcessObjId,
|
|
156
|
+
]);
|
|
157
|
+
}
|
|
158
|
+
this.logger.debug(`Return from calling singleRecordQuery with tooling: ${JSON.stringify(sandboxCreationProgress, undefined, 2)}`);
|
|
159
|
+
if (!sandboxIsResumable(sandboxCreationProgress.Status)) {
|
|
160
|
+
throw messages.createError('sandboxNotResumable', [
|
|
161
|
+
sandboxCreationProgress.SandboxName,
|
|
162
|
+
sandboxCreationProgress.Status,
|
|
163
|
+
]);
|
|
164
|
+
}
|
|
165
|
+
await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_RESUME, sandboxCreationProgress);
|
|
166
|
+
const [wait, pollInterval] = this.validateWaitOptions(options);
|
|
167
|
+
// if wait is 0, return the sandboxCreationProgress immediately
|
|
168
|
+
if (wait.seconds === 0) {
|
|
169
|
+
if (sandboxCreationProgress.Status === 'Completed') {
|
|
170
|
+
// check to see if sandbox can authenticate via sandboxAuth endpoint
|
|
171
|
+
const sandboxInfo = await this.sandboxSignupComplete(sandboxCreationProgress);
|
|
172
|
+
if (sandboxInfo) {
|
|
173
|
+
await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_AUTH, sandboxInfo);
|
|
174
|
+
try {
|
|
175
|
+
this.logger.debug(`sandbox signup complete with ${JSON.stringify(sandboxInfo, undefined, 2)}`);
|
|
176
|
+
await this.writeSandboxAuthFile(sandboxCreationProgress, sandboxInfo);
|
|
177
|
+
return sandboxCreationProgress;
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
// eat the error, we don't want to throw an error if we can't write the file
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_ASYNC_RESULT, sandboxCreationProgress);
|
|
185
|
+
throw messages.createError('sandboxCreateNotComplete');
|
|
186
|
+
}
|
|
187
|
+
this.logger.debug(`resume - pollStatusAndAuth sandboxProcessObj ${JSON.stringify(sandboxCreationProgress, undefined, 2)}, max wait time of ${wait.minutes} minutes`);
|
|
188
|
+
return this.pollStatusAndAuth({
|
|
189
|
+
sandboxProcessObj: sandboxCreationProgress,
|
|
190
|
+
wait,
|
|
191
|
+
pollInterval,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Creates a scratchOrg
|
|
196
|
+
* 'this' needs to be a valid dev-hub
|
|
197
|
+
*
|
|
198
|
+
* @param {options} ScratchOrgCreateOptions
|
|
199
|
+
* @returns {ScratchOrgCreateResult}
|
|
200
|
+
*/
|
|
201
|
+
async scratchOrgCreate(options) {
|
|
202
|
+
return (0, scratchOrgCreate_1.scratchOrgCreate)({ ...options, hubOrg: this });
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Reports sandbox org creation status. If the org is ready, authenticates to the org.
|
|
206
|
+
*
|
|
207
|
+
* @param {sandboxname} string the sandbox name
|
|
208
|
+
* @param options Wait: The amount of time to wait before timing out, Interval: The time interval between polling
|
|
209
|
+
* @returns {SandboxProcessObject} the sandbox process object
|
|
210
|
+
*/
|
|
211
|
+
async sandboxStatus(sandboxname, options) {
|
|
212
|
+
return this.authWithRetriesByName(sandboxname, options);
|
|
63
213
|
}
|
|
64
214
|
/**
|
|
65
215
|
* Clean all data files in the org's data path. Usually <workspace>/.sfdx/orgs/<username>.
|
|
@@ -70,12 +220,11 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
70
220
|
async cleanLocalOrgData(orgDataPath, throwWhenRemoveFails = false) {
|
|
71
221
|
let dataPath;
|
|
72
222
|
try {
|
|
73
|
-
|
|
74
|
-
dataPath = path_1.join(rootFolder, global_1.Global.SFDX_STATE_FOLDER, orgDataPath ? orgDataPath : 'orgs');
|
|
223
|
+
dataPath = await this.getLocalDataDir(orgDataPath);
|
|
75
224
|
this.logger.debug(`cleaning data for path: ${dataPath}`);
|
|
76
225
|
}
|
|
77
226
|
catch (err) {
|
|
78
|
-
if (err.name === 'InvalidProjectWorkspaceError') {
|
|
227
|
+
if (err instanceof Error && err.name === 'InvalidProjectWorkspaceError') {
|
|
79
228
|
// If we aren't in a project dir, we can't clean up data files.
|
|
80
229
|
// If the user unlink this org outside of the workspace they used it in,
|
|
81
230
|
// data files will be left over.
|
|
@@ -83,18 +232,16 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
83
232
|
}
|
|
84
233
|
throw err;
|
|
85
234
|
}
|
|
86
|
-
return this.manageDelete(async () =>
|
|
235
|
+
return this.manageDelete(async () => fs.promises.rmdir(dataPath), dataPath, throwWhenRemoveFails);
|
|
87
236
|
}
|
|
88
237
|
/**
|
|
89
238
|
* @ignore
|
|
90
239
|
*/
|
|
91
240
|
async retrieveOrgUsersConfig() {
|
|
92
|
-
return
|
|
241
|
+
return orgUsersConfig_1.OrgUsersConfig.create(orgUsersConfig_1.OrgUsersConfig.getOptions(this.getOrgId()));
|
|
93
242
|
}
|
|
94
243
|
/**
|
|
95
|
-
*
|
|
96
|
-
* files, all user auth files for the org, matching default config settings, and any
|
|
97
|
-
* matching aliases.
|
|
244
|
+
* Cleans up all org related artifacts including users, sandbox config (if a sandbox), source tracking files, and auth file.
|
|
98
245
|
*
|
|
99
246
|
* @param throwWhenRemoveFails Determines if the call should throw an error or fail silently.
|
|
100
247
|
*/
|
|
@@ -104,7 +251,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
104
251
|
if (this.getConnection().isUsingAccessToken()) {
|
|
105
252
|
return Promise.resolve();
|
|
106
253
|
}
|
|
107
|
-
await this.removeSandboxConfig(
|
|
254
|
+
await this.removeSandboxConfig();
|
|
108
255
|
await this.removeUsers(throwWhenRemoveFails);
|
|
109
256
|
await this.removeUsersConfig();
|
|
110
257
|
// An attempt to remove this org's auth file occurs in this.removeUsersConfig. That's because this org's usersname is also
|
|
@@ -112,38 +259,45 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
112
259
|
//
|
|
113
260
|
// So, just in case no users are added to this org we will try the remove again.
|
|
114
261
|
await this.removeAuth();
|
|
262
|
+
await this.removeSourceTrackingFiles();
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Check if org is a sandbox org by checking its SandboxOrgConfig.
|
|
266
|
+
*
|
|
267
|
+
*/
|
|
268
|
+
async isSandbox() {
|
|
269
|
+
return (await stateAggregator_1.StateAggregator.getInstance()).sandboxes.hasFile(this.getOrgId());
|
|
115
270
|
}
|
|
116
271
|
/**
|
|
117
272
|
* Check that this org is a scratch org by asking the dev hub if it knows about it.
|
|
118
273
|
*
|
|
119
|
-
* **Throws** *{@link
|
|
274
|
+
* **Throws** *{@link SfError}{ name: 'NotADevHubError' }* Not a Dev Hub.
|
|
120
275
|
*
|
|
121
|
-
* **Throws** *{@link
|
|
276
|
+
* **Throws** *{@link SfError}{ name: 'NoResultsError' }* No results.
|
|
122
277
|
*
|
|
123
278
|
* @param devHubUsernameOrAlias The username or alias of the dev hub org.
|
|
124
279
|
*/
|
|
125
280
|
async checkScratchOrg(devHubUsernameOrAlias) {
|
|
126
281
|
let aliasOrUsername = devHubUsernameOrAlias;
|
|
127
282
|
if (!aliasOrUsername) {
|
|
128
|
-
aliasOrUsername =
|
|
283
|
+
aliasOrUsername = this.configAggregator.getPropertyValue(orgConfigProperties_1.OrgConfigProperties.TARGET_DEV_HUB);
|
|
129
284
|
}
|
|
130
285
|
const devHubConnection = (await Org.create({ aliasOrUsername })).getConnection();
|
|
131
286
|
const thisOrgAuthConfig = this.getConnection().getAuthInfoFields();
|
|
132
|
-
const trimmedId = sfdc_1.
|
|
287
|
+
const trimmedId = (0, sfdc_1.trimTo15)(thisOrgAuthConfig.orgId);
|
|
133
288
|
const DEV_HUB_SOQL = `SELECT CreatedDate,Edition,ExpirationDate FROM ActiveScratchOrg WHERE ScratchOrg='${trimmedId}'`;
|
|
134
|
-
let results;
|
|
135
289
|
try {
|
|
136
|
-
results = await devHubConnection.query(DEV_HUB_SOQL);
|
|
290
|
+
const results = await devHubConnection.query(DEV_HUB_SOQL);
|
|
291
|
+
if (results.records.length !== 1) {
|
|
292
|
+
throw new sfError_1.SfError('No results', 'NoResultsError');
|
|
293
|
+
}
|
|
137
294
|
}
|
|
138
295
|
catch (err) {
|
|
139
|
-
if (err.name === 'INVALID_TYPE') {
|
|
296
|
+
if (err instanceof Error && err.name === 'INVALID_TYPE') {
|
|
140
297
|
throw messages.createError('notADevHub', [devHubConnection.getUsername()]);
|
|
141
298
|
}
|
|
142
299
|
throw err;
|
|
143
300
|
}
|
|
144
|
-
if (ts_types_1.getNumber(results, 'records.length') !== 1) {
|
|
145
|
-
throw new sfdxError_1.SfdxError('No results', 'NoResultsError');
|
|
146
|
-
}
|
|
147
301
|
return thisOrgAuthConfig;
|
|
148
302
|
}
|
|
149
303
|
/**
|
|
@@ -154,7 +308,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
154
308
|
return this;
|
|
155
309
|
}
|
|
156
310
|
else if (this.getField(Org.Fields.DEV_HUB_USERNAME)) {
|
|
157
|
-
const devHubUsername = ts_types_1.ensureString(this.getField(Org.Fields.DEV_HUB_USERNAME));
|
|
311
|
+
const devHubUsername = (0, ts_types_1.ensureString)(this.getField(Org.Fields.DEV_HUB_USERNAME));
|
|
158
312
|
return Org.create({
|
|
159
313
|
connection: await connection_1.Connection.create({
|
|
160
314
|
authInfo: await authInfo_1.AuthInfo.create({ username: devHubUsername }),
|
|
@@ -171,13 +325,43 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
171
325
|
*/
|
|
172
326
|
isDevHubOrg() {
|
|
173
327
|
const isDevHub = this.getField(Org.Fields.IS_DEV_HUB);
|
|
174
|
-
if (ts_types_1.isBoolean(isDevHub)) {
|
|
328
|
+
if ((0, ts_types_1.isBoolean)(isDevHub)) {
|
|
175
329
|
return isDevHub;
|
|
176
330
|
}
|
|
177
331
|
else {
|
|
178
332
|
return false;
|
|
179
333
|
}
|
|
180
334
|
}
|
|
335
|
+
/**
|
|
336
|
+
* Will delete 'this' instance remotely and any files locally
|
|
337
|
+
*
|
|
338
|
+
* @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
|
|
339
|
+
*/
|
|
340
|
+
async deleteFrom(controllingOrg) {
|
|
341
|
+
if (typeof controllingOrg === 'string') {
|
|
342
|
+
controllingOrg = await Org.create({
|
|
343
|
+
aggregator: this.configAggregator,
|
|
344
|
+
aliasOrUsername: controllingOrg,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
if (await this.isSandbox()) {
|
|
348
|
+
await this.deleteSandbox(controllingOrg);
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
await this.deleteScratchOrg(controllingOrg);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Will delete 'this' instance remotely and any files locally
|
|
356
|
+
*/
|
|
357
|
+
async delete() {
|
|
358
|
+
if (await this.isSandbox()) {
|
|
359
|
+
await this.deleteSandbox();
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
await this.deleteScratchOrg();
|
|
363
|
+
}
|
|
364
|
+
}
|
|
181
365
|
/**
|
|
182
366
|
* Returns `true` if the org is a Dev Hub.
|
|
183
367
|
*
|
|
@@ -190,7 +374,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
190
374
|
*/
|
|
191
375
|
async determineIfDevHubOrg(forceServerCheck = false) {
|
|
192
376
|
const cachedIsDevHub = this.getField(Org.Fields.IS_DEV_HUB);
|
|
193
|
-
if (!forceServerCheck && ts_types_1.isBoolean(cachedIsDevHub)) {
|
|
377
|
+
if (!forceServerCheck && (0, ts_types_1.isBoolean)(cachedIsDevHub)) {
|
|
194
378
|
return cachedIsDevHub;
|
|
195
379
|
}
|
|
196
380
|
if (this.isDevHubOrg()) {
|
|
@@ -206,7 +390,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
206
390
|
catch (err) {
|
|
207
391
|
/* Not a dev hub */
|
|
208
392
|
}
|
|
209
|
-
const username = ts_types_1.ensure(this.getUsername());
|
|
393
|
+
const username = (0, ts_types_1.ensure)(this.getUsername());
|
|
210
394
|
const authInfo = await authInfo_1.AuthInfo.create({ username });
|
|
211
395
|
await authInfo.save({ isDevHub });
|
|
212
396
|
// Reset the connection with the updated auth file
|
|
@@ -222,13 +406,52 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
222
406
|
*/
|
|
223
407
|
isScratch() {
|
|
224
408
|
const isScratch = this.getField(Org.Fields.IS_SCRATCH);
|
|
225
|
-
if (ts_types_1.isBoolean(isScratch)) {
|
|
409
|
+
if ((0, ts_types_1.isBoolean)(isScratch)) {
|
|
226
410
|
return isScratch;
|
|
227
411
|
}
|
|
228
412
|
else {
|
|
229
413
|
return false;
|
|
230
414
|
}
|
|
231
415
|
}
|
|
416
|
+
/**
|
|
417
|
+
* Returns `true` if the org uses source tracking.
|
|
418
|
+
* Side effect: updates files where the property doesn't currently exist
|
|
419
|
+
*/
|
|
420
|
+
async tracksSource() {
|
|
421
|
+
// use the property if it exists
|
|
422
|
+
const tracksSource = this.getField(Org.Fields.TRACKS_SOURCE);
|
|
423
|
+
if ((0, ts_types_1.isBoolean)(tracksSource)) {
|
|
424
|
+
return tracksSource;
|
|
425
|
+
}
|
|
426
|
+
// scratch orgs with no property use tracking by default
|
|
427
|
+
if (await this.determineIfScratch()) {
|
|
428
|
+
// save true for next time to avoid checking again
|
|
429
|
+
await this.setTracksSource(true);
|
|
430
|
+
return true;
|
|
431
|
+
}
|
|
432
|
+
if (await this.determineIfSandbox()) {
|
|
433
|
+
// does the sandbox know about the SourceMember object?
|
|
434
|
+
const supportsSourceMembers = await this.supportsSourceTracking();
|
|
435
|
+
await this.setTracksSource(supportsSourceMembers);
|
|
436
|
+
return supportsSourceMembers;
|
|
437
|
+
}
|
|
438
|
+
// any other non-sandbox, non-scratch orgs won't use tracking
|
|
439
|
+
await this.setTracksSource(false);
|
|
440
|
+
return false;
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Set the tracking property on the org's auth file
|
|
444
|
+
*
|
|
445
|
+
* @param value true or false (whether the org should use source tracking or not)
|
|
446
|
+
*/
|
|
447
|
+
async setTracksSource(value) {
|
|
448
|
+
const originalAuth = await authInfo_1.AuthInfo.create({ username: this.getUsername() });
|
|
449
|
+
return originalAuth.handleAliasAndDefaultSettings({
|
|
450
|
+
setDefault: false,
|
|
451
|
+
setDefaultDevHub: false,
|
|
452
|
+
setTracksSource: value,
|
|
453
|
+
});
|
|
454
|
+
}
|
|
232
455
|
/**
|
|
233
456
|
* Returns `true` if the org is a scratch org.
|
|
234
457
|
*
|
|
@@ -239,36 +462,11 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
239
462
|
async determineIfScratch() {
|
|
240
463
|
let cache = this.getField(Org.Fields.IS_SCRATCH);
|
|
241
464
|
if (!cache) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
// const isSandbox = organization.IsSandbox && organization.TrialExpirationDate;
|
|
245
|
-
// this.getConnection().getAuthInfo().update({
|
|
246
|
-
// [Org.Fields.NAME]: organization.Name,
|
|
247
|
-
// [Org.Fields.NAME]: organization.InstanceName,
|
|
248
|
-
// [Org.Fields.NAME]: organization.NamespacePrefix,
|
|
249
|
-
// [Org.Fields.NAME]: organization.IsSandbox,
|
|
250
|
-
// [Org.Fields.NAME]: organization.TrialExpirationDate,
|
|
251
|
-
// });
|
|
252
|
-
cache = isScratch;
|
|
465
|
+
await this.updateLocalInformation();
|
|
466
|
+
cache = this.getField(Org.Fields.IS_SCRATCH);
|
|
253
467
|
}
|
|
254
468
|
return cache;
|
|
255
469
|
}
|
|
256
|
-
/**
|
|
257
|
-
* Returns `true` if the org is a sandbox.
|
|
258
|
-
*
|
|
259
|
-
* **Note** This relies on a cached value in the auth file. If that property
|
|
260
|
-
* is not cached, this method will **always return false even if the org is a
|
|
261
|
-
* sandbox**. If you need accuracy, use the {@link Org.determineIfDevHubOrg} method.
|
|
262
|
-
*/
|
|
263
|
-
isSandbox() {
|
|
264
|
-
const isSandbox = this.getField(Org.Fields.IS_SANDBOX);
|
|
265
|
-
if (ts_types_1.isBoolean(isSandbox)) {
|
|
266
|
-
return isSandbox;
|
|
267
|
-
}
|
|
268
|
-
else {
|
|
269
|
-
return false;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
470
|
/**
|
|
273
471
|
* Returns `true` if the org is a sandbox.
|
|
274
472
|
*
|
|
@@ -301,10 +499,10 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
301
499
|
const username = this.getUsername();
|
|
302
500
|
if (username) {
|
|
303
501
|
const organization = await this.retrieveOrganizationInformation();
|
|
304
|
-
const isScratch = organization.IsSandbox && organization.TrialExpirationDate;
|
|
502
|
+
const isScratch = organization.IsSandbox && Boolean(organization.TrialExpirationDate);
|
|
305
503
|
const isSandbox = organization.IsSandbox && !organization.TrialExpirationDate;
|
|
306
|
-
const
|
|
307
|
-
|
|
504
|
+
const stateAggregator = await stateAggregator_1.StateAggregator.getInstance();
|
|
505
|
+
stateAggregator.orgs.update(username, {
|
|
308
506
|
[Org.Fields.NAME]: organization.Name,
|
|
309
507
|
[Org.Fields.INSTANCE_NAME]: organization.InstanceName,
|
|
310
508
|
[Org.Fields.NAMESPACE_PREFIX]: organization.NamespacePrefix,
|
|
@@ -312,7 +510,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
312
510
|
[Org.Fields.IS_SCRATCH]: isScratch,
|
|
313
511
|
[Org.Fields.TRIAL_EXPIRATION_DATE]: organization.TrialExpirationDate,
|
|
314
512
|
});
|
|
315
|
-
await
|
|
513
|
+
await stateAggregator.orgs.write(username);
|
|
316
514
|
}
|
|
317
515
|
}
|
|
318
516
|
/**
|
|
@@ -333,8 +531,8 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
333
531
|
async readUserAuthFiles() {
|
|
334
532
|
const config = await this.retrieveOrgUsersConfig();
|
|
335
533
|
const contents = await config.read();
|
|
336
|
-
const thisUsername = ts_types_1.ensure(this.getUsername());
|
|
337
|
-
const usernames = ts_types_1.ensureJsonArray(contents.usernames
|
|
534
|
+
const thisUsername = (0, ts_types_1.ensure)(this.getUsername());
|
|
535
|
+
const usernames = (0, ts_types_1.ensureJsonArray)(contents.usernames ?? [thisUsername]);
|
|
338
536
|
return Promise.all(usernames.map((username) => {
|
|
339
537
|
if (username === thisUsername) {
|
|
340
538
|
return authInfo_1.AuthInfo.create({
|
|
@@ -342,7 +540,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
342
540
|
});
|
|
343
541
|
}
|
|
344
542
|
else {
|
|
345
|
-
return authInfo_1.AuthInfo.create({ username: ts_types_1.ensureString(username) });
|
|
543
|
+
return authInfo_1.AuthInfo.create({ username: (0, ts_types_1.ensureString)(username) });
|
|
346
544
|
}
|
|
347
545
|
}));
|
|
348
546
|
}
|
|
@@ -365,20 +563,20 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
365
563
|
*/
|
|
366
564
|
async addUsername(auth) {
|
|
367
565
|
if (!auth) {
|
|
368
|
-
throw new
|
|
566
|
+
throw new sfError_1.SfError('Missing auth info', 'MissingAuthInfo');
|
|
369
567
|
}
|
|
370
|
-
const authInfo = ts_types_1.isString(auth) ? await authInfo_1.AuthInfo.create({ username: auth }) : auth;
|
|
568
|
+
const authInfo = (0, ts_types_1.isString)(auth) ? await authInfo_1.AuthInfo.create({ username: auth }) : auth;
|
|
371
569
|
this.logger.debug(`adding username ${authInfo.getFields().username}`);
|
|
372
570
|
const orgConfig = await this.retrieveOrgUsersConfig();
|
|
373
571
|
const contents = await orgConfig.read();
|
|
374
572
|
// TODO: This is kind of screwy because contents values can be `AnyJson | object`...
|
|
375
573
|
// needs config refactoring to improve
|
|
376
|
-
const usernames = contents.usernames
|
|
377
|
-
if (!ts_types_1.isArray(usernames)) {
|
|
378
|
-
throw new
|
|
574
|
+
const usernames = contents.usernames ?? [];
|
|
575
|
+
if (!(0, ts_types_1.isArray)(usernames)) {
|
|
576
|
+
throw new sfError_1.SfError('Usernames is not an array', 'UnexpectedDataFormat');
|
|
379
577
|
}
|
|
380
578
|
let shouldUpdate = false;
|
|
381
|
-
const thisUsername = ts_types_1.ensure(this.getUsername());
|
|
579
|
+
const thisUsername = (0, ts_types_1.ensure)(this.getUsername());
|
|
382
580
|
if (!usernames.includes(thisUsername)) {
|
|
383
581
|
usernames.push(thisUsername);
|
|
384
582
|
shouldUpdate = true;
|
|
@@ -397,44 +595,41 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
397
595
|
/**
|
|
398
596
|
* Removes a username from the user config for this object. For convenience `this` object is returned.
|
|
399
597
|
*
|
|
400
|
-
* **Throws** *{@link
|
|
598
|
+
* **Throws** *{@link SfError}{ name: 'MissingAuthInfoError' }* Auth info is missing.
|
|
401
599
|
*
|
|
402
600
|
* @param {AuthInfo | string} auth The AuthInfo containing the username to remove.
|
|
403
601
|
*/
|
|
404
602
|
async removeUsername(auth) {
|
|
405
603
|
if (!auth) {
|
|
406
|
-
throw new
|
|
604
|
+
throw new sfError_1.SfError('Missing auth info', 'MissingAuthInfoError');
|
|
407
605
|
}
|
|
408
|
-
const authInfo = ts_types_1.isString(auth) ? await authInfo_1.AuthInfo.create({ username: auth }) : auth;
|
|
606
|
+
const authInfo = (0, ts_types_1.isString)(auth) ? await authInfo_1.AuthInfo.create({ username: auth }) : auth;
|
|
409
607
|
this.logger.debug(`removing username ${authInfo.getFields().username}`);
|
|
410
608
|
const orgConfig = await this.retrieveOrgUsersConfig();
|
|
411
609
|
const contents = await orgConfig.read();
|
|
412
610
|
const targetUser = authInfo.getFields().username;
|
|
413
|
-
const usernames = (contents.usernames
|
|
611
|
+
const usernames = (contents.usernames ?? []);
|
|
414
612
|
contents.usernames = usernames.filter((username) => username !== targetUser);
|
|
415
613
|
await orgConfig.write();
|
|
416
614
|
return this;
|
|
417
615
|
}
|
|
418
616
|
/**
|
|
419
|
-
*
|
|
617
|
+
* set the sandbox config related to this given org
|
|
420
618
|
*
|
|
421
|
-
*
|
|
422
|
-
* @param {
|
|
423
|
-
* @param {value} The value to save
|
|
619
|
+
* @param orgId {string} orgId of the sandbox
|
|
620
|
+
* @param config {SandboxFields} config of the sandbox
|
|
424
621
|
*/
|
|
425
|
-
async
|
|
426
|
-
|
|
427
|
-
sandboxOrgConfig.set(field, value);
|
|
428
|
-
await sandboxOrgConfig.write();
|
|
622
|
+
async setSandboxConfig(orgId, config) {
|
|
623
|
+
(await stateAggregator_1.StateAggregator.getInstance()).sandboxes.set(orgId, config);
|
|
429
624
|
return this;
|
|
430
625
|
}
|
|
431
626
|
/**
|
|
432
|
-
*
|
|
627
|
+
* get the sandbox config for the given orgId
|
|
628
|
+
*
|
|
629
|
+
* @param orgId {string} orgId of the sandbox
|
|
433
630
|
*/
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
const sandboxOrgConfig = await this.retrieveSandboxOrgConfig();
|
|
437
|
-
return sandboxOrgConfig.get(field);
|
|
631
|
+
async getSandboxConfig(orgId) {
|
|
632
|
+
return (await stateAggregator_1.StateAggregator.getInstance()).sandboxes.read(orgId);
|
|
438
633
|
}
|
|
439
634
|
/**
|
|
440
635
|
* Retrieves the highest api version that is supported by the target server instance. If the apiVersion configured for
|
|
@@ -442,7 +637,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
442
637
|
* results in a warning.
|
|
443
638
|
*/
|
|
444
639
|
async retrieveMaxApiVersion() {
|
|
445
|
-
return
|
|
640
|
+
return this.getConnection().retrieveMaxApiVersion();
|
|
446
641
|
}
|
|
447
642
|
/**
|
|
448
643
|
* Returns the admin username used to create the org.
|
|
@@ -454,7 +649,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
454
649
|
* Returns the orgId for this org.
|
|
455
650
|
*/
|
|
456
651
|
getOrgId() {
|
|
457
|
-
return this.orgId
|
|
652
|
+
return this.orgId ?? this.getField(Org.Fields.ORG_ID);
|
|
458
653
|
}
|
|
459
654
|
/**
|
|
460
655
|
* Returns for the config aggregator.
|
|
@@ -466,9 +661,14 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
466
661
|
* Returns an org field. Returns undefined if the field is not set or invalid.
|
|
467
662
|
*/
|
|
468
663
|
getField(key) {
|
|
469
|
-
|
|
470
|
-
// @ts-ignore
|
|
471
|
-
|
|
664
|
+
/* eslint-disable @typescript-eslint/ban-ts-comment, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment */
|
|
665
|
+
// @ts-ignore Legacy. We really shouldn't be doing this.
|
|
666
|
+
const ownProp = this[key];
|
|
667
|
+
if (ownProp && typeof ownProp !== 'function')
|
|
668
|
+
return ownProp;
|
|
669
|
+
// @ts-ignore
|
|
670
|
+
return this.getConnection().getAuthInfoFields()[key];
|
|
671
|
+
/* eslint-enable @typescript-eslint/ban-ts-comment, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment */
|
|
472
672
|
}
|
|
473
673
|
/**
|
|
474
674
|
* Returns a map of requested fields.
|
|
@@ -482,30 +682,86 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
482
682
|
}
|
|
483
683
|
/**
|
|
484
684
|
* Returns the JSForce connection for the org.
|
|
685
|
+
* side effect: If you pass it an apiVersion, it will set it on the Org
|
|
686
|
+
* so that future calls to getConnection() will also use that version.
|
|
687
|
+
*
|
|
688
|
+
* @param apiVersion The API version to use for the connection.
|
|
485
689
|
*/
|
|
486
|
-
getConnection() {
|
|
690
|
+
getConnection(apiVersion) {
|
|
691
|
+
if (apiVersion) {
|
|
692
|
+
if (this.connection.getApiVersion() === apiVersion) {
|
|
693
|
+
this.logger.warn(`Default API version is already ${apiVersion}`);
|
|
694
|
+
}
|
|
695
|
+
else {
|
|
696
|
+
this.connection.setApiVersion(apiVersion);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
487
699
|
return this.connection;
|
|
488
700
|
}
|
|
701
|
+
async supportsSourceTracking() {
|
|
702
|
+
if (this.isScratch()) {
|
|
703
|
+
return true;
|
|
704
|
+
}
|
|
705
|
+
try {
|
|
706
|
+
await this.getConnection().tooling.sobject('SourceMember').describe();
|
|
707
|
+
return true;
|
|
708
|
+
}
|
|
709
|
+
catch (err) {
|
|
710
|
+
if (err.message.includes('The requested resource does not exist')) {
|
|
711
|
+
return false;
|
|
712
|
+
}
|
|
713
|
+
throw err;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* query SandboxProcess via sandbox name
|
|
718
|
+
*
|
|
719
|
+
* @param name SandboxName to query for
|
|
720
|
+
* @private
|
|
721
|
+
*/
|
|
722
|
+
async querySandboxProcessBySandboxName(name) {
|
|
723
|
+
return this.querySandboxProcess(`SandboxName='${name}'`);
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* query SandboxProcess via SandboxInfoId
|
|
727
|
+
*
|
|
728
|
+
* @param id SandboxInfoId to query for
|
|
729
|
+
* @private
|
|
730
|
+
*/
|
|
731
|
+
async querySandboxProcessBySandboxInfoId(id) {
|
|
732
|
+
return this.querySandboxProcess(`SandboxInfoId='${id}'`);
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* query SandboxProcess via Id
|
|
736
|
+
*
|
|
737
|
+
* @param id SandboxProcessId to query for
|
|
738
|
+
* @private
|
|
739
|
+
*/
|
|
740
|
+
async querySandboxProcessById(id) {
|
|
741
|
+
return this.querySandboxProcess(`Id='${id}'`);
|
|
742
|
+
}
|
|
489
743
|
/**
|
|
490
744
|
* Initialize async components.
|
|
491
745
|
*/
|
|
492
746
|
async init() {
|
|
747
|
+
const stateAggregator = await stateAggregator_1.StateAggregator.getInstance();
|
|
493
748
|
this.logger = await logger_1.Logger.child('Org');
|
|
494
749
|
this.configAggregator = this.options.aggregator ? this.options.aggregator : await configAggregator_1.ConfigAggregator.create();
|
|
495
750
|
if (!this.options.connection) {
|
|
496
751
|
if (this.options.aliasOrUsername == null) {
|
|
497
752
|
this.configAggregator = this.getConfigAggregator();
|
|
498
753
|
const aliasOrUsername = this.options.isDevHub
|
|
499
|
-
?
|
|
500
|
-
:
|
|
501
|
-
this.options.aliasOrUsername = aliasOrUsername
|
|
754
|
+
? this.configAggregator.getPropertyValue(orgConfigProperties_1.OrgConfigProperties.TARGET_DEV_HUB)
|
|
755
|
+
: this.configAggregator.getPropertyValue(orgConfigProperties_1.OrgConfigProperties.TARGET_ORG);
|
|
756
|
+
this.options.aliasOrUsername = aliasOrUsername ?? undefined;
|
|
757
|
+
}
|
|
758
|
+
const username = stateAggregator.aliases.resolveUsername(this.options.aliasOrUsername);
|
|
759
|
+
if (!username) {
|
|
760
|
+
throw messages.createError('noUsernameFound');
|
|
502
761
|
}
|
|
503
|
-
const username = this.options.aliasOrUsername;
|
|
504
762
|
this.connection = await connection_1.Connection.create({
|
|
505
|
-
// If no username is provided or resolvable from an alias, AuthInfo will throw an
|
|
506
|
-
authInfo: await authInfo_1.AuthInfo.create({
|
|
507
|
-
username: (username != null && (await aliases_1.Aliases.fetch(username))) || username,
|
|
508
|
-
}),
|
|
763
|
+
// If no username is provided or resolvable from an alias, AuthInfo will throw an SfError.
|
|
764
|
+
authInfo: await authInfo_1.AuthInfo.create({ username, isDevHub: this.options.isDevHub }),
|
|
509
765
|
});
|
|
510
766
|
}
|
|
511
767
|
else {
|
|
@@ -514,26 +770,187 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
514
770
|
this.orgId = this.getField(Org.Fields.ORG_ID);
|
|
515
771
|
}
|
|
516
772
|
/**
|
|
517
|
-
* **Throws** *{@link
|
|
773
|
+
* **Throws** *{@link SfError}{ name: 'NotSupportedError' }* Throws an unsupported error.
|
|
518
774
|
*/
|
|
775
|
+
// eslint-disable-next-line class-methods-use-this
|
|
519
776
|
getDefaultOptions() {
|
|
520
|
-
throw new
|
|
777
|
+
throw new sfError_1.SfError('Not Supported', 'NotSupportedError');
|
|
778
|
+
}
|
|
779
|
+
async getLocalDataDir(orgDataPath) {
|
|
780
|
+
const rootFolder = await config_1.Config.resolveRootFolder(false);
|
|
781
|
+
return (0, path_1.join)(rootFolder, global_1.Global.SFDX_STATE_FOLDER, orgDataPath ? orgDataPath : 'orgs');
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Gets the sandboxProcessObject and then polls for it to complete.
|
|
785
|
+
*
|
|
786
|
+
* @param sandboxProcessName sanbox process name
|
|
787
|
+
* @param options { wait?: Duration; interval?: Duration }
|
|
788
|
+
* @returns {SandboxProcessObject} The SandboxProcessObject for the sandbox
|
|
789
|
+
*/
|
|
790
|
+
async authWithRetriesByName(sandboxProcessName, options) {
|
|
791
|
+
return this.authWithRetries(await this.queryLatestSandboxProcessBySandboxName(sandboxProcessName), options);
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Polls the sandbox org for the sandboxProcessObject.
|
|
795
|
+
*
|
|
796
|
+
* @param sandboxProcessObj: The in-progress sandbox signup request
|
|
797
|
+
* @param options { wait?: Duration; interval?: Duration }
|
|
798
|
+
* @returns {SandboxProcessObject}
|
|
799
|
+
*/
|
|
800
|
+
async authWithRetries(sandboxProcessObj, options = {
|
|
801
|
+
wait: kit_1.Duration.minutes(0),
|
|
802
|
+
interval: kit_1.Duration.seconds(30),
|
|
803
|
+
}) {
|
|
804
|
+
const [wait, pollInterval] = this.validateWaitOptions(options);
|
|
805
|
+
this.logger.debug(`AuthWithRetries sandboxProcessObj ${JSON.stringify(sandboxProcessObj, undefined, 2)}, max wait time of ${wait.minutes} minutes`);
|
|
806
|
+
return this.pollStatusAndAuth({
|
|
807
|
+
sandboxProcessObj,
|
|
808
|
+
wait,
|
|
809
|
+
pollInterval,
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* Query the sandbox for the SandboxProcessObject by sandbox name
|
|
814
|
+
*
|
|
815
|
+
* @param sandboxName The name of the sandbox to query
|
|
816
|
+
* @returns {SandboxProcessObject} The SandboxProcessObject for the sandbox
|
|
817
|
+
*/
|
|
818
|
+
async queryLatestSandboxProcessBySandboxName(sandboxNameIn) {
|
|
819
|
+
const { tooling } = this.getConnection();
|
|
820
|
+
this.logger.debug('QueryLatestSandboxProcessBySandboxName called with SandboxName: %s ', sandboxNameIn);
|
|
821
|
+
const queryStr = `SELECT Id, Status, SandboxName, SandboxInfoId, LicenseType, CreatedDate, CopyProgress, SandboxOrganization, SourceId, Description, EndDate FROM SandboxProcess WHERE SandboxName='${sandboxNameIn}' AND Status != 'D' ORDER BY CreatedDate DESC LIMIT 1`;
|
|
822
|
+
const queryResult = await tooling.query(queryStr);
|
|
823
|
+
this.logger.debug('Return from calling queryToolingApi: %s ', queryResult);
|
|
824
|
+
if (queryResult?.records?.length === 1) {
|
|
825
|
+
return queryResult.records[0];
|
|
826
|
+
}
|
|
827
|
+
else if (queryResult.records && queryResult.records.length > 1) {
|
|
828
|
+
throw messages.createError('MultiSandboxProcessNotFoundBySandboxName', [sandboxNameIn]);
|
|
829
|
+
}
|
|
830
|
+
else {
|
|
831
|
+
throw messages.createError('SandboxProcessNotFoundBySandboxName', [sandboxNameIn]);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
// eslint-disable-next-line class-methods-use-this
|
|
835
|
+
async queryProduction(org, field, value) {
|
|
836
|
+
return org.connection.singleRecordQuery(`SELECT SandboxInfoId FROM SandboxProcess WHERE ${field} ='${value}' AND Status NOT IN ('D', 'E')`, { tooling: true });
|
|
837
|
+
}
|
|
838
|
+
async destroySandbox(org, id) {
|
|
839
|
+
return org.getConnection().tooling.delete('SandboxInfo', id);
|
|
840
|
+
}
|
|
841
|
+
async destroyScratchOrg(org, id) {
|
|
842
|
+
return org.getConnection().delete('ActiveScratchOrg', id);
|
|
843
|
+
}
|
|
844
|
+
/**
|
|
845
|
+
* this method will delete the sandbox org from the production org and clean up any local files
|
|
846
|
+
*
|
|
847
|
+
* @param prodOrg - Production org associated with this sandbox
|
|
848
|
+
* @private
|
|
849
|
+
*/
|
|
850
|
+
async deleteSandbox(prodOrg) {
|
|
851
|
+
const sandbox = await this.getSandboxConfig(this.getOrgId());
|
|
852
|
+
prodOrg ??= await Org.create({
|
|
853
|
+
aggregator: this.configAggregator,
|
|
854
|
+
aliasOrUsername: sandbox?.prodOrgUsername,
|
|
855
|
+
});
|
|
856
|
+
let sandboxInfoId = sandbox?.sandboxInfoId;
|
|
857
|
+
if (!sandboxInfoId) {
|
|
858
|
+
let result;
|
|
859
|
+
try {
|
|
860
|
+
// grab sandboxName from config or try to calculate from the sandbox username
|
|
861
|
+
const sandboxName = sandbox?.sandboxName ?? (this.getUsername() ?? '').split(`${prodOrg.getUsername()}.`)[1];
|
|
862
|
+
if (!sandboxName) {
|
|
863
|
+
this.logger.debug('Sandbox name is not available');
|
|
864
|
+
// jump to query by orgId
|
|
865
|
+
throw new Error();
|
|
866
|
+
}
|
|
867
|
+
this.logger.debug(`attempting to locate sandbox with sandbox ${sandboxName}`);
|
|
868
|
+
try {
|
|
869
|
+
result = await this.queryProduction(prodOrg, 'SandboxName', sandboxName);
|
|
870
|
+
}
|
|
871
|
+
catch (err) {
|
|
872
|
+
this.logger.debug(`Failed to find sandbox with sandbox name: ${sandboxName}`);
|
|
873
|
+
// jump to query by orgId
|
|
874
|
+
throw err;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
catch {
|
|
878
|
+
// if an error is thrown, don't panic yet. we'll try querying by orgId
|
|
879
|
+
const trimmedId = (0, sfdc_1.trimTo15)(this.getOrgId());
|
|
880
|
+
this.logger.debug(`defaulting to trimming id from ${this.getOrgId()} to ${trimmedId}`);
|
|
881
|
+
try {
|
|
882
|
+
result = await this.queryProduction(prodOrg, 'SandboxOrganization', trimmedId);
|
|
883
|
+
sandboxInfoId = result.SandboxInfoId;
|
|
884
|
+
}
|
|
885
|
+
catch {
|
|
886
|
+
// eating exceptions when trying to find sandbox process record by orgId
|
|
887
|
+
// allows idempotent cleanup of sandbox orgs
|
|
888
|
+
this.logger.debug(`Failed find a SandboxProcess for the sandbox org: ${this.getOrgId()}`);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
if (sandboxInfoId) {
|
|
893
|
+
const deleteResult = await this.destroySandbox(prodOrg, sandboxInfoId);
|
|
894
|
+
this.logger.debug('Return from calling tooling.delete: ', deleteResult);
|
|
895
|
+
}
|
|
896
|
+
// cleanup remaining artifacts
|
|
897
|
+
await this.remove();
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* If this Org is a scratch org, calling this method will delete the scratch org from the DevHub and clean up any local files
|
|
901
|
+
*
|
|
902
|
+
* @param devHub - optional DevHub Org of the to-be-deleted scratch org
|
|
903
|
+
* @private
|
|
904
|
+
*/
|
|
905
|
+
async deleteScratchOrg(devHub) {
|
|
906
|
+
// if we didn't get a devHub, we'll get it from the this org
|
|
907
|
+
devHub ??= await this.getDevHubOrg();
|
|
908
|
+
if (!devHub) {
|
|
909
|
+
throw messages.createError('noDevHubFound');
|
|
910
|
+
}
|
|
911
|
+
if (devHub.getOrgId() === this.getOrgId()) {
|
|
912
|
+
// we're attempting to delete a DevHub
|
|
913
|
+
throw messages.createError('deleteOrgHubError');
|
|
914
|
+
}
|
|
915
|
+
try {
|
|
916
|
+
const devHubConn = devHub.getConnection();
|
|
917
|
+
const username = this.getUsername();
|
|
918
|
+
const activeScratchOrgRecordId = (await devHubConn.singleRecordQuery(`SELECT Id FROM ActiveScratchOrg WHERE SignupUsername='${username}'`)).Id;
|
|
919
|
+
this.logger.trace(`found matching ActiveScratchOrg with SignupUsername: ${username}. Deleting...`);
|
|
920
|
+
await this.destroyScratchOrg(devHub, activeScratchOrgRecordId);
|
|
921
|
+
await this.remove();
|
|
922
|
+
}
|
|
923
|
+
catch (err) {
|
|
924
|
+
this.logger.info(err instanceof Error ? err.message : err);
|
|
925
|
+
if (err instanceof Error && (err.name === 'INVALID_TYPE' || err.name === 'INSUFFICIENT_ACCESS_OR_READONLY')) {
|
|
926
|
+
// most likely from devHubConn.delete
|
|
927
|
+
this.logger.info('Insufficient privilege to access ActiveScratchOrgs.');
|
|
928
|
+
throw messages.createError('insufficientAccessToDelete');
|
|
929
|
+
}
|
|
930
|
+
if (err instanceof Error && err.name === connection_1.SingleRecordQueryErrors.NoRecords) {
|
|
931
|
+
// most likely from singleRecordQuery
|
|
932
|
+
this.logger.info('The above error can be the result of deleting an expired or already deleted org.');
|
|
933
|
+
this.logger.info('attempting to cleanup the auth file');
|
|
934
|
+
await this.removeAuth();
|
|
935
|
+
throw messages.createError('scratchOrgNotFound');
|
|
936
|
+
}
|
|
937
|
+
throw err;
|
|
938
|
+
}
|
|
521
939
|
}
|
|
522
940
|
/**
|
|
523
|
-
*
|
|
524
|
-
* this Org
|
|
941
|
+
* Delete an auth info file from the local file system and any related cache information for
|
|
942
|
+
* this Org. You don't want to call this method directly. Instead consider calling Org.remove()
|
|
525
943
|
*/
|
|
526
944
|
async removeAuth() {
|
|
527
|
-
const
|
|
945
|
+
const stateAggregator = await stateAggregator_1.StateAggregator.getInstance();
|
|
528
946
|
const username = this.getUsername();
|
|
529
947
|
// If there is no username, it has already been removed from the globalInfo.
|
|
530
948
|
// We can skip the unset and just ensure that globalInfo is updated.
|
|
531
949
|
if (username) {
|
|
532
950
|
this.logger.debug(`Removing auth for user: ${username}`);
|
|
533
951
|
this.logger.debug(`Clearing auth cache for user: ${username}`);
|
|
534
|
-
|
|
952
|
+
await stateAggregator.orgs.remove(username);
|
|
535
953
|
}
|
|
536
|
-
await config.write();
|
|
537
954
|
}
|
|
538
955
|
/**
|
|
539
956
|
* Deletes the users config file
|
|
@@ -545,13 +962,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
545
962
|
await config.unlink();
|
|
546
963
|
}
|
|
547
964
|
}
|
|
548
|
-
|
|
549
|
-
* @ignore
|
|
550
|
-
*/
|
|
551
|
-
async retrieveSandboxOrgConfig() {
|
|
552
|
-
return await sandboxOrgConfig_1.SandboxOrgConfig.create(sandboxOrgConfig_1.SandboxOrgConfig.getOptions(this.getOrgId()));
|
|
553
|
-
}
|
|
554
|
-
manageDelete(cb, dirPath, throwWhenRemoveFails) {
|
|
965
|
+
async manageDelete(cb, dirPath, throwWhenRemoveFails) {
|
|
555
966
|
return cb().catch((e) => {
|
|
556
967
|
if (throwWhenRemoveFails) {
|
|
557
968
|
throw e;
|
|
@@ -569,46 +980,215 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
569
980
|
*/
|
|
570
981
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
571
982
|
async removeUsers(throwWhenRemoveFails) {
|
|
983
|
+
const stateAggregator = await stateAggregator_1.StateAggregator.getInstance();
|
|
572
984
|
this.logger.debug(`Removing users associate with org: ${this.getOrgId()}`);
|
|
573
985
|
const config = await this.retrieveOrgUsersConfig();
|
|
574
986
|
this.logger.debug(`using path for org users: ${config.getPath()}`);
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
987
|
+
const authInfos = await this.readUserAuthFiles();
|
|
988
|
+
await Promise.all(authInfos
|
|
989
|
+
.map((auth) => auth.getFields().username)
|
|
990
|
+
.map(async (username) => {
|
|
991
|
+
const aliasKeys = (username && stateAggregator.aliases.getAll(username)) ?? [];
|
|
992
|
+
stateAggregator.aliases.unsetAll(username);
|
|
993
|
+
const orgForUser = username === this.getUsername()
|
|
994
|
+
? this
|
|
995
|
+
: await Org.create({
|
|
996
|
+
connection: await connection_1.Connection.create({ authInfo: await authInfo_1.AuthInfo.create({ username }) }),
|
|
997
|
+
});
|
|
998
|
+
const orgType = this.isDevHubOrg() ? orgConfigProperties_1.OrgConfigProperties.TARGET_DEV_HUB : orgConfigProperties_1.OrgConfigProperties.TARGET_ORG;
|
|
999
|
+
const configInfo = orgForUser.configAggregator.getInfo(orgType);
|
|
1000
|
+
const needsConfigUpdate = (configInfo.isGlobal() || configInfo.isLocal()) &&
|
|
1001
|
+
(configInfo.value === username || aliasKeys.includes(configInfo.value));
|
|
1002
|
+
return [
|
|
1003
|
+
orgForUser.removeAuth(),
|
|
1004
|
+
needsConfigUpdate ? config_1.Config.update(configInfo.isGlobal(), orgType, undefined) : undefined,
|
|
1005
|
+
].filter(Boolean);
|
|
1006
|
+
}));
|
|
1007
|
+
await stateAggregator.aliases.write();
|
|
1008
|
+
}
|
|
1009
|
+
async removeSandboxConfig() {
|
|
1010
|
+
const stateAggregator = await stateAggregator_1.StateAggregator.getInstance();
|
|
1011
|
+
await stateAggregator.sandboxes.remove(this.getOrgId());
|
|
1012
|
+
}
|
|
1013
|
+
async writeSandboxAuthFile(sandboxProcessObj, sandboxRes) {
|
|
1014
|
+
this.logger.debug(`writeSandboxAuthFile sandboxProcessObj: ${JSON.stringify(sandboxProcessObj)}, sandboxRes: ${JSON.stringify(sandboxRes)}`);
|
|
1015
|
+
if (sandboxRes.authUserName) {
|
|
1016
|
+
const productionAuthFields = this.connection.getAuthInfoFields();
|
|
1017
|
+
this.logger.debug('Result from getAuthInfoFields: AuthFields', productionAuthFields);
|
|
1018
|
+
// let's do headless auth via jwt (if we have privateKey) or web auth
|
|
1019
|
+
const oauth2Options = {
|
|
1020
|
+
loginUrl: sandboxRes.loginUrl,
|
|
1021
|
+
instanceUrl: sandboxRes.instanceUrl,
|
|
1022
|
+
username: sandboxRes.authUserName,
|
|
1023
|
+
};
|
|
1024
|
+
// If we don't have a privateKey then we assume it's web auth.
|
|
1025
|
+
if (!productionAuthFields.privateKey) {
|
|
1026
|
+
oauth2Options.redirectUri = `http://localhost:${await webOAuthServer_1.WebOAuthServer.determineOauthPort()}/OauthRedirect`;
|
|
1027
|
+
oauth2Options.authCode = sandboxRes.authCode;
|
|
1028
|
+
}
|
|
1029
|
+
else {
|
|
1030
|
+
oauth2Options.privateKey = productionAuthFields.privateKey;
|
|
1031
|
+
oauth2Options.clientId = productionAuthFields.clientId;
|
|
1032
|
+
}
|
|
1033
|
+
const authInfo = await authInfo_1.AuthInfo.create({
|
|
1034
|
+
username: sandboxRes.authUserName,
|
|
1035
|
+
oauth2Options,
|
|
1036
|
+
parentUsername: productionAuthFields.username,
|
|
1037
|
+
});
|
|
1038
|
+
this.logger.debug('Creating AuthInfo for sandbox', sandboxRes.authUserName, productionAuthFields.username, oauth2Options);
|
|
1039
|
+
// save auth info for new sandbox
|
|
1040
|
+
await authInfo.save();
|
|
1041
|
+
const sandboxOrgId = authInfo.getFields().orgId;
|
|
1042
|
+
if (!sandboxOrgId) {
|
|
1043
|
+
throw messages.createError('AuthInfoOrgIdUndefined');
|
|
1044
|
+
}
|
|
1045
|
+
// set the sandbox config value
|
|
1046
|
+
const sfSandbox = {
|
|
1047
|
+
sandboxUsername: sandboxRes.authUserName,
|
|
1048
|
+
sandboxOrgId,
|
|
1049
|
+
prodOrgUsername: this.getUsername(),
|
|
1050
|
+
sandboxName: sandboxProcessObj.SandboxName,
|
|
1051
|
+
sandboxProcessId: sandboxProcessObj.Id,
|
|
1052
|
+
sandboxInfoId: sandboxProcessObj.SandboxInfoId,
|
|
1053
|
+
timestamp: new Date().toISOString(),
|
|
1054
|
+
};
|
|
1055
|
+
await this.setSandboxConfig(sandboxOrgId, sfSandbox);
|
|
1056
|
+
await (await stateAggregator_1.StateAggregator.getInstance()).sandboxes.write(sandboxOrgId);
|
|
1057
|
+
await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_RESULT, {
|
|
1058
|
+
sandboxProcessObj,
|
|
1059
|
+
sandboxRes,
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
else {
|
|
1063
|
+
// no authed sandbox user, error
|
|
1064
|
+
throw messages.createError('missingAuthUsername', [sandboxProcessObj.SandboxName]);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
async pollStatusAndAuth(options) {
|
|
1068
|
+
this.logger.debug('PollStatusAndAuth called with SandboxProcessObject', options.sandboxProcessObj, options.wait.minutes, options.pollInterval.seconds);
|
|
1069
|
+
let remainingWait = options.wait;
|
|
1070
|
+
let waitingOnAuth = false;
|
|
1071
|
+
const pollingClient = await pollingClient_1.PollingClient.create({
|
|
1072
|
+
poll: async () => {
|
|
1073
|
+
const sandboxProcessObj = await this.querySandboxProcessBySandboxInfoId(options.sandboxProcessObj.SandboxInfoId);
|
|
1074
|
+
// check to see if sandbox can authenticate via sandboxAuth endpoint
|
|
1075
|
+
const sandboxInfo = await this.sandboxSignupComplete(sandboxProcessObj);
|
|
1076
|
+
if (sandboxInfo) {
|
|
1077
|
+
await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_AUTH, sandboxInfo);
|
|
1078
|
+
try {
|
|
1079
|
+
this.logger.debug('sandbox signup complete with', sandboxInfo);
|
|
1080
|
+
await this.writeSandboxAuthFile(sandboxProcessObj, sandboxInfo);
|
|
1081
|
+
return { completed: true, payload: sandboxProcessObj };
|
|
1082
|
+
}
|
|
1083
|
+
catch (err) {
|
|
1084
|
+
const error = err;
|
|
1085
|
+
this.logger.debug('Exception while calling writeSandboxAuthFile', err);
|
|
1086
|
+
if (error?.name === 'JwtAuthError' && error?.stack?.includes("user hasn't approved")) {
|
|
1087
|
+
waitingOnAuth = true;
|
|
1088
|
+
}
|
|
1089
|
+
else {
|
|
1090
|
+
throw sfError_1.SfError.wrap(error);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
597
1093
|
}
|
|
598
|
-
await
|
|
1094
|
+
await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_STATUS, {
|
|
1095
|
+
sandboxProcessObj,
|
|
1096
|
+
remainingWait: remainingWait.seconds,
|
|
1097
|
+
interval: options.pollInterval.seconds,
|
|
1098
|
+
waitingOnAuth,
|
|
1099
|
+
});
|
|
1100
|
+
remainingWait = kit_1.Duration.seconds(remainingWait.seconds - options.pollInterval.seconds);
|
|
1101
|
+
return { completed: false, payload: sandboxProcessObj };
|
|
1102
|
+
},
|
|
1103
|
+
frequency: options.pollInterval,
|
|
1104
|
+
timeout: options.wait,
|
|
1105
|
+
});
|
|
1106
|
+
return pollingClient.subscribe();
|
|
1107
|
+
}
|
|
1108
|
+
/**
|
|
1109
|
+
* query SandboxProcess using supplied where clause
|
|
1110
|
+
*
|
|
1111
|
+
* @param where clause to query for
|
|
1112
|
+
* @private
|
|
1113
|
+
*/
|
|
1114
|
+
async querySandboxProcess(where) {
|
|
1115
|
+
const queryStr = `SELECT Id, Status, SandboxName, SandboxInfoId, LicenseType, CreatedDate, CopyProgress, SandboxOrganization, SourceId, Description, EndDate FROM SandboxProcess WHERE ${where} AND Status != 'D'`;
|
|
1116
|
+
return this.connection.singleRecordQuery(queryStr, {
|
|
1117
|
+
tooling: true,
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
/**
|
|
1121
|
+
* determines if the sandbox has successfully been created
|
|
1122
|
+
*
|
|
1123
|
+
* @param sandboxProcessObj sandbox signup progress
|
|
1124
|
+
* @private
|
|
1125
|
+
*/
|
|
1126
|
+
async sandboxSignupComplete(sandboxProcessObj) {
|
|
1127
|
+
this.logger.debug('sandboxSignupComplete called with SandboxProcessObject', sandboxProcessObj);
|
|
1128
|
+
if (!sandboxProcessObj.EndDate) {
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
try {
|
|
1132
|
+
// call server side /sandboxAuth API to auth the sandbox org user with the connected app
|
|
1133
|
+
const authFields = this.connection.getAuthInfoFields();
|
|
1134
|
+
const callbackUrl = `http://localhost:${await webOAuthServer_1.WebOAuthServer.determineOauthPort()}/OauthRedirect`;
|
|
1135
|
+
const sandboxReq = {
|
|
1136
|
+
// the sandbox signup has been completed on production, we have production clientId by this point
|
|
1137
|
+
clientId: authFields.clientId,
|
|
1138
|
+
sandboxName: sandboxProcessObj.SandboxName,
|
|
1139
|
+
callbackUrl,
|
|
1140
|
+
};
|
|
1141
|
+
this.logger.debug('Calling sandboxAuth with SandboxUserAuthRequest', sandboxReq);
|
|
1142
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
1143
|
+
const url = `${this.connection.tooling._baseUrl()}/sandboxAuth`;
|
|
1144
|
+
const params = {
|
|
1145
|
+
method: 'POST',
|
|
1146
|
+
url,
|
|
1147
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1148
|
+
body: JSON.stringify(sandboxReq),
|
|
1149
|
+
};
|
|
1150
|
+
const result = await this.connection.tooling.request(params);
|
|
1151
|
+
this.logger.debug('Result of calling sandboxAuth', result);
|
|
1152
|
+
return result;
|
|
1153
|
+
}
|
|
1154
|
+
catch (err) {
|
|
1155
|
+
const error = err;
|
|
1156
|
+
// There are cases where the endDate is set before the sandbox has actually completed.
|
|
1157
|
+
// In that case, the sandboxAuth call will throw a specific exception.
|
|
1158
|
+
if (error?.name === 'INVALID_STATUS') {
|
|
1159
|
+
this.logger.debug('Error while authenticating the user', error?.toString());
|
|
1160
|
+
}
|
|
1161
|
+
else {
|
|
1162
|
+
// If it fails for any unexpected reason, just pass that through
|
|
1163
|
+
throw sfError_1.SfError.wrap(error);
|
|
599
1164
|
}
|
|
600
|
-
await aliases.write();
|
|
601
1165
|
}
|
|
602
1166
|
}
|
|
1167
|
+
validateWaitOptions(options) {
|
|
1168
|
+
const wait = options.wait ?? kit_1.Duration.minutes(30);
|
|
1169
|
+
const interval = options.interval ?? kit_1.Duration.seconds(30);
|
|
1170
|
+
let pollInterval = options.async ? kit_1.Duration.seconds(0) : interval;
|
|
1171
|
+
// pollInterval cannot be > wait.
|
|
1172
|
+
pollInterval = pollInterval.seconds > wait.seconds ? wait : pollInterval;
|
|
1173
|
+
return [wait, pollInterval];
|
|
1174
|
+
}
|
|
603
1175
|
/**
|
|
604
|
-
*
|
|
1176
|
+
* removes source tracking files hosted in the project/.sf/orgs/<org id>/
|
|
605
1177
|
*
|
|
606
|
-
* @
|
|
1178
|
+
* @private
|
|
607
1179
|
*/
|
|
608
|
-
async
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
await
|
|
1180
|
+
async removeSourceTrackingFiles() {
|
|
1181
|
+
try {
|
|
1182
|
+
const rootFolder = await config_1.Config.resolveRootFolder(false);
|
|
1183
|
+
await fs.promises.rm((0, path_1.join)(rootFolder, global_1.Global.SF_STATE_FOLDER, 'orgs', this.getOrgId()), {
|
|
1184
|
+
recursive: true,
|
|
1185
|
+
force: true,
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
catch (e) {
|
|
1189
|
+
const err = sfError_1.SfError.wrap(e);
|
|
1190
|
+
// consume the error in case something went wrong
|
|
1191
|
+
this.logger.debug(`error deleting source tracking information for ${this.getOrgId()} error: ${err.message}`);
|
|
612
1192
|
}
|
|
613
1193
|
}
|
|
614
1194
|
}
|
|
@@ -694,6 +1274,11 @@ exports.Org = Org;
|
|
|
694
1274
|
* The snapshot used to create the scratch org.
|
|
695
1275
|
*/
|
|
696
1276
|
Fields["SNAPSHOT"] = "snapshot";
|
|
1277
|
+
/**
|
|
1278
|
+
* true: the org supports and wants source tracking
|
|
1279
|
+
* false: the org opted out of tracking or can't support it
|
|
1280
|
+
*/
|
|
1281
|
+
Fields["TRACKS_SOURCE"] = "tracksSource";
|
|
697
1282
|
// Should it be on org? Leave it off for now, as it might
|
|
698
1283
|
// be confusing to the consumer what this actually is.
|
|
699
1284
|
// USERNAMES = 'usernames',
|