diff --git a/src/commands/org/create/sandbox.ts b/src/commands/org/create/sandbox.ts index daa6dd29..a49b2040 100644 --- a/src/commands/org/create/sandbox.ts +++ b/src/commands/org/create/sandbox.ts @@ -124,6 +124,7 @@ export default class CreateSandbox extends SandboxCommandBase { const lifecycle = Lifecycle.getInstance(); - this.prodOrg = this.flags['target-org']; + const sandboxReq = await this.createSandboxRequest(); + await this.confirmSandboxReq({ + ...sandboxReq, + }); + this.initSandboxProcessData(sandboxReq); + this.registerLifecycleListeners(lifecycle, { isAsync: this.flags.async, setDefault: this.flags['set-default'], @@ -197,15 +203,6 @@ export default class CreateSandbox extends SandboxCommandBase 0) { - this.spinner.start(`Resume ${this.sandboxRequestData.action ?? 'Create/Refresh'}`); - } - this.debug('Calling resume with ResumeSandboxRequest: %s ', sandboxReq); try { diff --git a/src/shared/sandboxCommandBase.ts b/src/shared/sandboxCommandBase.ts index f4575129..a58f5bf3 100644 --- a/src/shared/sandboxCommandBase.ts +++ b/src/shared/sandboxCommandBase.ts @@ -5,7 +5,7 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ import os from 'node:os'; - +import { MultiStageOutput } from '@oclif/multi-stage-output'; import { SfCommand } from '@salesforce/sf-plugins-core'; import { Config } from '@oclif/core'; import { @@ -24,6 +24,13 @@ import { import { SandboxProgress } from './sandboxProgress.js'; import { State } from './stagedProgress.js'; +type SandboxData = { + sandboxStatus: StatusEvent; + status: string; + id: string; + copyProgress: number; +}; + Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); const messages = Messages.loadMessages('@salesforce/plugin-org', 'sandboxbase'); @@ -50,6 +57,7 @@ export abstract class SandboxCommandBase extends SfCommand { : this.constructor.name === 'CreateSandbox' ? 'Create' : 'Create/Refresh'; + this.sandboxProgress = new SandboxProgress({ action: this.action }); } protected async getSandboxRequestConfig(): Promise { @@ -89,25 +97,57 @@ export abstract class SandboxCommandBase extends SfCommand { lifecycle: Lifecycle, options: { isAsync: boolean; alias?: string; setDefault?: boolean; prodOrg?: Org; tracksSource?: boolean } ): void { + const mso = new MultiStageOutput({ + stages: ['Creating new sandbox', 'Authenticating', 'Done'], + title: 'Sandbox Process', + jsonEnabled: false, + postStagesBlock: [ + { + label: 'Status', + get: (data) => data?.status, + type: 'dynamic-key-value', + bold: true, + }, + { + label: 'Sandbox ID', + get: (data) => data?.id, + type: 'static-key-value', + }, + { + label: 'Copy Progress', + get: (data) => `${data?.copyProgress ?? 0}%`, + type: 'dynamic-key-value', + }, + ], + }); lifecycle.on('POLLING_TIME_OUT', async () => { this.pollingTimeOut = true; + mso.updateData({ status: 'Polling Timeout' }); return Promise.resolve(this.updateSandboxRequestData()); }); lifecycle.on(SandboxEvents.EVENT_RESUME, async (results: SandboxProcessObject) => { this.latestSandboxProgressObj = results; - this.sandboxProgress.markPreviousStagesAsCompleted( - results.Status !== 'Completed' ? results.Status : 'Authenticating' - ); + + if (results.Status === 'Activating') { + mso.goto('Authenticating'); + mso.updateData({ status: results.Status, id: results.Id, copyProgress: results.CopyProgress }); + } else if (results.Status === 'Completed') { + mso.goto('Done'); + mso.updateData({ status: results.Status, id: results.Id, copyProgress: results.CopyProgress }); + mso.stop(); + } return Promise.resolve(this.updateSandboxRequestData()); }); lifecycle.on(SandboxEvents.EVENT_ASYNC_RESULT, async (results?: SandboxProcessObject) => { this.latestSandboxProgressObj = results ?? this.latestSandboxProgressObj; this.updateSandboxRequestData(); + if (!options.isAsync) { - this.spinner.stop(); + mso.stop(); } + // things that require data on latestSandboxProgressObj if (this.latestSandboxProgressObj) { const progress = this.sandboxProgress.getSandboxProgress({ @@ -117,11 +157,15 @@ export abstract class SandboxCommandBase extends SfCommand { const currentStage = progress.status; this.sandboxProgress.markPreviousStagesAsCompleted(currentStage); this.updateStage(currentStage, 'inProgress'); - this.updateProgress( - { sandboxProcessObj: this.latestSandboxProgressObj, sandboxRes: undefined }, - options.isAsync - ); + mso.goto('Creating new sandbox'); + mso.updateData({ + status: currentStage, + id: this.latestSandboxProgressObj?.Id, + copyProgress: this.latestSandboxProgressObj?.CopyProgress, + }); + mso.goto(progress.status); } + if (this.pollingTimeOut) { this.warn(messages.getMessage('warning.ClientTimeoutWaitingForSandboxProcess', [this.action.toLowerCase()])); } @@ -135,7 +179,14 @@ export abstract class SandboxCommandBase extends SfCommand { const progress = this.sandboxProgress.getSandboxProgress(results); const currentStage = progress.status; this.updateStage(currentStage, 'inProgress'); - return Promise.resolve(this.updateProgress(results, options.isAsync)); + + mso.goto('Creating new sandbox'); + mso.updateData({ + status: currentStage, + id: results.sandboxProcessObj.Id, + copyProgress: results.sandboxProcessObj.CopyProgress, + }); + return Promise.resolve(this.updateProgress(results)); }); lifecycle.on(SandboxEvents.EVENT_AUTH, async (results: SandboxUserAuthResponse) => { @@ -147,10 +198,27 @@ export abstract class SandboxCommandBase extends SfCommand { this.latestSandboxProgressObj = results.sandboxProcessObj; this.updateSandboxRequestData(); this.sandboxProgress.markPreviousStagesAsCompleted(); - this.updateProgress(results, options.isAsync); + this.updateProgress(results); + if (!options.isAsync) { - this.progress.stop(); + mso.stop(); } + + if (results.sandboxProcessObj.Status === 'Authenticating') { + mso.goto('Authenticating'); + mso.updateData({ + status: results.sandboxProcessObj.Status, + id: results.sandboxProcessObj.Id, + copyProgress: results.sandboxProcessObj.CopyProgress, + }); + } + mso.goto('Done'); + mso.updateData({ + status: 'Completed', + id: results.sandboxProcessObj.Id, + copyProgress: results.sandboxProcessObj.CopyProgress, + }); + mso.stop(); if (results.sandboxRes?.authUserName) { const authInfo = await AuthInfo.create({ username: results.sandboxRes?.authUserName }); await authInfo.handleAliasAndDefaultSettings({ @@ -161,7 +229,7 @@ export abstract class SandboxCommandBase extends SfCommand { }); } this.removeSandboxProgressConfig(); - this.updateProgress(results, options.isAsync); + this.updateProgress(results); this.reportResults(results); }); @@ -201,8 +269,7 @@ export abstract class SandboxCommandBase extends SfCommand { } protected updateProgress( - event: StatusEvent | (Omit & { sandboxRes?: ResultEvent['sandboxRes'] }), - isAsync: boolean + event: StatusEvent | (Omit & { sandboxRes?: ResultEvent['sandboxRes'] }) ): void { const sandboxProgress = this.sandboxProgress.getSandboxProgress(event); this.sandboxUsername = (event as ResultEvent).sandboxRes?.authUserName; @@ -211,9 +278,6 @@ export abstract class SandboxCommandBase extends SfCommand { sandboxProgress, sandboxProcessObj: event.sandboxProcessObj, }; - if (!isAsync) { - this.spinner.status = this.sandboxProgress.formatProgressStatus(); - } } protected updateStage(stage: string | undefined, state: State): void { diff --git a/src/shared/sandboxProgress.ts b/src/shared/sandboxProgress.ts index 6b6a97c2..59bb9905 100644 --- a/src/shared/sandboxProgress.ts +++ b/src/shared/sandboxProgress.ts @@ -66,17 +66,10 @@ export class SandboxProgress extends StagedProgress { } public formatProgressStatus(withClock = true): string { - const table = getSandboxTableAsText(undefined, this.statusData?.sandboxProcessObj); return [ withClock && this.statusData - ? `${getClockForSeconds(this.statusData.sandboxProgress.remainingWaitTime)} until timeout. ${ - this.statusData.sandboxProgress.percentComplete ?? 0 - }%` + ? `${getClockForSeconds(this.statusData.sandboxProgress.remainingWaitTime)} until timeout.` : undefined, - table, - '---------------------', - `Sandbox ${this.action ?? ''} Stages`, - this.formatStages(), ] .filter(isDefined) .join(os.EOL);