55 * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66 */
77import os from 'node:os' ;
8-
98import { SfCommand } from '@salesforce/sf-plugins-core' ;
109import { Config } from '@oclif/core' ;
1110import {
@@ -21,8 +20,7 @@ import {
2120 SandboxUserAuthResponse ,
2221 StatusEvent ,
2322} from '@salesforce/core' ;
24- import { SandboxProgress } from './sandboxProgress.js' ;
25- import { State } from './stagedProgress.js' ;
23+ import { SandboxStages } from './sandboxStages.js' ;
2624
2725Messages . importMessagesDirectoryFromMetaUrl ( import . meta. url ) ;
2826const messages = Messages . loadMessages ( '@salesforce/plugin-org' , 'sandboxbase' ) ;
@@ -32,7 +30,7 @@ export type SandboxCommandResponse = SandboxProcessObject & {
3230} ;
3331
3432export abstract class SandboxCommandBase < T > extends SfCommand < T > {
35- protected sandboxProgress : SandboxProgress ;
33+ protected stages ! : SandboxStages ;
3634 protected latestSandboxProgressObj ?: SandboxProcessObject ;
3735 protected sandboxAuth ?: SandboxUserAuthResponse ;
3836 protected prodOrg ?: Org ;
@@ -47,10 +45,9 @@ export abstract class SandboxCommandBase<T> extends SfCommand<T> {
4745 this . action =
4846 this . constructor . name === 'RefreshSandbox'
4947 ? 'Refresh'
50- : this . constructor . name === 'CreateSandbox'
48+ : [ 'CreateSandbox' , 'ResumeSandbox' ] . includes ( this . constructor . name )
5149 ? 'Create'
5250 : 'Create/Refresh' ;
53- this . sandboxProgress = new SandboxProgress ( { action : this . action } ) ;
5451 }
5552 protected async getSandboxRequestConfig ( ) : Promise < SandboxRequestCache > {
5653 if ( ! this . sandboxRequestConfig ) {
@@ -85,72 +82,81 @@ export abstract class SandboxCommandBase<T> extends SfCommand<T> {
8582 return true ;
8683 }
8784
88- protected registerLifecycleListeners (
85+ protected registerLifecycleListenersAndMSO (
8986 lifecycle : Lifecycle ,
90- options : { isAsync : boolean ; alias ?: string ; setDefault ?: boolean ; prodOrg ?: Org ; tracksSource ?: boolean }
87+ options : {
88+ mso : { title : string ; refresh ?: boolean } ;
89+ isAsync : boolean ;
90+ alias ?: string ;
91+ setDefault ?: boolean ;
92+ prodOrg ?: Org ;
93+ tracksSource ?: boolean ;
94+ }
9195 ) : void {
96+ this . stages = new SandboxStages ( {
97+ refresh : options . mso . refresh ?? false ,
98+ jsonEnabled : this . jsonEnabled ( ) ,
99+ title : options . isAsync ? `${ options . mso . title } (async)` : options . mso . title ,
100+ } ) ;
101+
102+ this . stages . start ( ) ;
103+
92104 lifecycle . on ( 'POLLING_TIME_OUT' , async ( ) => {
93105 this . pollingTimeOut = true ;
106+ this . stages . stop ( ) ;
94107 return Promise . resolve ( this . updateSandboxRequestData ( ) ) ;
95108 } ) ;
96109
97110 lifecycle . on ( SandboxEvents . EVENT_RESUME , async ( results : SandboxProcessObject ) => {
111+ this . stages . start ( ) ;
98112 this . latestSandboxProgressObj = results ;
99- this . sandboxProgress . markPreviousStagesAsCompleted (
100- results . Status !== 'Completed' ? results . Status : 'Authenticating'
101- ) ;
113+ this . stages . update ( this . latestSandboxProgressObj ) ;
114+
102115 return Promise . resolve ( this . updateSandboxRequestData ( ) ) ;
103116 } ) ;
104117
105- lifecycle . on ( SandboxEvents . EVENT_ASYNC_RESULT , async ( results ?: SandboxProcessObject ) => {
106- this . latestSandboxProgressObj = results ?? this . latestSandboxProgressObj ;
107- this . updateSandboxRequestData ( ) ;
108- if ( ! options . isAsync ) {
109- this . spinner . stop ( ) ;
110- }
111- // things that require data on latestSandboxProgressObj
112- if ( this . latestSandboxProgressObj ) {
113- const progress = this . sandboxProgress . getSandboxProgress ( {
114- sandboxProcessObj : this . latestSandboxProgressObj ,
115- sandboxRes : undefined ,
116- } ) ;
117- const currentStage = progress . status ;
118- this . sandboxProgress . markPreviousStagesAsCompleted ( currentStage ) ;
119- this . updateStage ( currentStage , 'inProgress' ) ;
120- this . updateProgress (
121- { sandboxProcessObj : this . latestSandboxProgressObj , sandboxRes : undefined } ,
122- options . isAsync
123- ) ;
118+ lifecycle . on ( SandboxEvents . EVENT_ASYNC_RESULT , async ( results : SandboxProcessObject | undefined ) => {
119+ // this event is fired by commands on poll timeout without any payload,
120+ // we want to make sure to only update state if there's payload (event from sfdx-core).
121+ if ( results ) {
122+ this . latestSandboxProgressObj = results ;
123+ this . stages . update ( this . latestSandboxProgressObj ) ;
124+ this . updateSandboxRequestData ( ) ;
124125 }
126+
127+ this . stages . stop ( 'async' ) ;
125128 if ( this . pollingTimeOut ) {
126129 this . warn ( messages . getMessage ( 'warning.ClientTimeoutWaitingForSandboxProcess' , [ this . action . toLowerCase ( ) ] ) ) ;
127130 }
128- this . log ( this . sandboxProgress . formatProgressStatus ( false ) ) ;
129131 return Promise . resolve ( this . info ( messages . getMessage ( 'checkSandboxStatus' , this . getCheckSandboxStatusParams ( ) ) ) ) ;
130132 } ) ;
131133
132134 lifecycle . on ( SandboxEvents . EVENT_STATUS , async ( results : StatusEvent ) => {
135+ // this starts MSO for:
136+ // * org create/create sandbox
137+ this . stages . start ( ) ;
133138 this . latestSandboxProgressObj = results . sandboxProcessObj ;
134139 this . updateSandboxRequestData ( ) ;
135- const progress = this . sandboxProgress . getSandboxProgress ( results ) ;
136- const currentStage = progress . status ;
137- this . updateStage ( currentStage , 'inProgress' ) ;
138- return Promise . resolve ( this . updateProgress ( results , options . isAsync ) ) ;
140+
141+ this . stages . update ( this . latestSandboxProgressObj ) ;
142+
143+ return Promise . resolve ( ) ;
139144 } ) ;
140145
141146 lifecycle . on ( SandboxEvents . EVENT_AUTH , async ( results : SandboxUserAuthResponse ) => {
147+ this . sandboxUsername = results . authUserName ;
148+ this . stages . auth ( ) ;
142149 this . sandboxAuth = results ;
143150 return Promise . resolve ( ) ;
144151 } ) ;
145152
146153 lifecycle . on ( SandboxEvents . EVENT_RESULT , async ( results : ResultEvent ) => {
147154 this . latestSandboxProgressObj = results . sandboxProcessObj ;
155+ this . sandboxUsername = results . sandboxRes . authUserName ;
148156 this . updateSandboxRequestData ( ) ;
149- this . sandboxProgress . markPreviousStagesAsCompleted ( ) ;
150- this . updateProgress ( results , options . isAsync ) ;
151- if ( ! options . isAsync ) {
152- this . progress . stop ( ) ;
153- }
157+
158+ this . stages . update ( results . sandboxProcessObj ) ;
159+
154160 if ( results . sandboxRes ?. authUserName ) {
155161 const authInfo = await AuthInfo . create ( { username : results . sandboxRes ?. authUserName } ) ;
156162 await authInfo . handleAliasAndDefaultSettings ( {
@@ -160,8 +166,9 @@ export abstract class SandboxCommandBase<T> extends SfCommand<T> {
160166 setTracksSource : await this . calculateTrackingSetting ( options . tracksSource ) ,
161167 } ) ;
162168 }
169+ this . stages . stop ( ) ;
170+
163171 this . removeSandboxProgressConfig ( ) ;
164- this . updateProgress ( results , options . isAsync ) ;
165172 this . reportResults ( results ) ;
166173 } ) ;
167174
@@ -185,43 +192,14 @@ export abstract class SandboxCommandBase<T> extends SfCommand<T> {
185192 }
186193
187194 protected reportResults ( results : ResultEvent ) : void {
188- this . log ( ) ;
189- this . styledHeader ( `Sandbox Org ${ this . action } Status` ) ;
190- this . log ( this . sandboxProgress . formatProgressStatus ( false ) ) ;
191195 this . logSuccess (
192196 [
193- messages . getMessage ( 'sandboxSuccess' , [ this . action . toLowerCase ( ) ] ) ,
194- messages . getMessages ( 'sandboxSuccess.actions' , [
195- results . sandboxRes ?. authUserName ,
196- this . config . bin ,
197- results . sandboxRes ?. authUserName ,
198- ] ) ,
197+ messages . getMessage ( 'sandboxSuccess' ) ,
198+ messages . getMessages ( 'sandboxSuccess.actions' , [ this . config . bin , results . sandboxRes ?. authUserName ] ) ,
199199 ] . join ( os . EOL )
200200 ) ;
201201 }
202202
203- protected updateProgress (
204- event : StatusEvent | ( Omit < ResultEvent , 'sandboxRes' > & { sandboxRes ?: ResultEvent [ 'sandboxRes' ] } ) ,
205- isAsync : boolean
206- ) : void {
207- const sandboxProgress = this . sandboxProgress . getSandboxProgress ( event ) ;
208- this . sandboxUsername = ( event as ResultEvent ) . sandboxRes ?. authUserName ;
209- this . sandboxProgress . statusData = {
210- sandboxUsername : this . sandboxUsername ,
211- sandboxProgress,
212- sandboxProcessObj : event . sandboxProcessObj ,
213- } ;
214- if ( ! isAsync ) {
215- this . spinner . status = this . sandboxProgress . formatProgressStatus ( ) ;
216- }
217- }
218-
219- protected updateStage ( stage : string | undefined , state : State ) : void {
220- if ( stage ) {
221- this . sandboxProgress . transitionStages ( stage , state ) ;
222- }
223- }
224-
225203 protected updateSandboxRequestData ( ) : void {
226204 if ( this . sandboxRequestData && this . latestSandboxProgressObj ) {
227205 this . sandboxRequestData . sandboxProcessObject = this . latestSandboxProgressObj ;
@@ -262,6 +240,13 @@ export abstract class SandboxCommandBase<T> extends SfCommand<T> {
262240 return { ...( this . latestSandboxProgressObj as SandboxProcessObject ) , SandboxUsername : sbxUsername } ;
263241 }
264242
243+ protected catch ( error : Error ) : Promise < never > {
244+ if ( this . stages ) {
245+ this . stages . stop ( 'failed' ) ;
246+ }
247+
248+ return super . catch ( error ) ;
249+ }
265250 // eslint-disable-next-line @typescript-eslint/no-explicit-any
266251 protected async finally ( _ : Error | undefined ) : Promise < any > {
267252 const lifecycle = Lifecycle . getInstance ( ) ;
0 commit comments