UNPKG

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