|
6 | 6 | import {Flags, ux} from '@oclif/core'; |
7 | 7 | import cliui from 'cliui'; |
8 | 8 | import {OdsCommand} from '@salesforce/b2c-tooling-sdk/cli'; |
9 | | -import {getApiErrorMessage, type OdsComponents} from '@salesforce/b2c-tooling-sdk'; |
| 9 | +import { |
| 10 | + getApiErrorMessage, |
| 11 | + SandboxPollingError, |
| 12 | + SandboxPollingTimeoutError, |
| 13 | + SandboxTerminalStateError, |
| 14 | + waitForSandbox, |
| 15 | + type OdsComponents, |
| 16 | +} from '@salesforce/b2c-tooling-sdk'; |
10 | 17 | import {t, withDocs} from '../../i18n/index.js'; |
11 | | -import {waitForSandboxStateCommon} from './polling.js'; |
12 | 18 |
|
13 | 19 | type SandboxModel = OdsComponents['schemas']['SandboxModel']; |
14 | 20 | type SandboxResourceProfile = OdsComponents['schemas']['SandboxResourceProfile']; |
@@ -153,7 +159,66 @@ export default class OdsCreate extends OdsCommand<typeof OdsCreate> { |
153 | 159 |
|
154 | 160 | if (wait && sandbox.id) { |
155 | 161 | this.log(t('commands.ods.create.waiting', 'Waiting for sandbox to get started..')); |
156 | | - sandbox = await this.waitForSandbox(sandbox.id, pollInterval, timeout); |
| 162 | + |
| 163 | + try { |
| 164 | + await waitForSandbox({ |
| 165 | + sandboxId: sandbox.id, |
| 166 | + targetState: 'started', |
| 167 | + pollIntervalSeconds: pollInterval, |
| 168 | + timeoutSeconds: timeout, |
| 169 | + odsClient: this.odsClient, |
| 170 | + onPoll: ({elapsedSeconds, state}) => { |
| 171 | + this.logger.info( |
| 172 | + {sandboxId: sandbox.id, elapsed: elapsedSeconds, state}, |
| 173 | + `[${elapsedSeconds}s] State: ${state}`, |
| 174 | + ); |
| 175 | + }, |
| 176 | + }); |
| 177 | + } catch (error) { |
| 178 | + if (error instanceof SandboxPollingTimeoutError) { |
| 179 | + this.error( |
| 180 | + t('commands.ods.create.timeout', 'Timeout waiting for sandbox after {{seconds}} seconds', { |
| 181 | + seconds: String(error.timeoutSeconds), |
| 182 | + }), |
| 183 | + ); |
| 184 | + } |
| 185 | + |
| 186 | + if (error instanceof SandboxTerminalStateError) { |
| 187 | + if (error.state === 'deleted') { |
| 188 | + this.error(t('commands.ods.create.deleted', 'Sandbox was deleted')); |
| 189 | + } |
| 190 | + this.error(t('commands.ods.create.failed', 'Sandbox creation failed')); |
| 191 | + } |
| 192 | + |
| 193 | + if (error instanceof SandboxPollingError) { |
| 194 | + this.error( |
| 195 | + t('commands.ods.create.pollError', 'Failed to fetch sandbox status: {{message}}', { |
| 196 | + message: error.message, |
| 197 | + }), |
| 198 | + ); |
| 199 | + } |
| 200 | + |
| 201 | + throw error; |
| 202 | + } |
| 203 | + |
| 204 | + const finalResult = await this.odsClient.GET('/sandboxes/{sandboxId}', { |
| 205 | + params: { |
| 206 | + path: {sandboxId: sandbox.id}, |
| 207 | + }, |
| 208 | + }); |
| 209 | + |
| 210 | + if (!finalResult.data?.data) { |
| 211 | + this.error( |
| 212 | + t('commands.ods.create.pollError', 'Failed to fetch sandbox status: {{message}}', { |
| 213 | + message: finalResult.response?.statusText || 'Unknown error', |
| 214 | + }), |
| 215 | + ); |
| 216 | + } |
| 217 | + |
| 218 | + sandbox = finalResult.data.data; |
| 219 | + |
| 220 | + this.log(''); |
| 221 | + this.logger.info({sandboxId: sandbox.id}, t('commands.ods.create.ready', 'Sandbox is now ready')); |
157 | 222 | } |
158 | 223 |
|
159 | 224 | if (this.jsonEnabled()) { |
@@ -223,73 +288,4 @@ export default class OdsCreate extends OdsCommand<typeof OdsCreate> { |
223 | 288 |
|
224 | 289 | ux.stdout(ui.toString()); |
225 | 290 | } |
226 | | - |
227 | | - /** |
228 | | - * Sleep for a given number of milliseconds. |
229 | | - */ |
230 | | - private async sleep(ms: number): Promise<void> { |
231 | | - await new Promise((resolve) => { |
232 | | - setTimeout(resolve, ms); |
233 | | - }); |
234 | | - } |
235 | | - |
236 | | - /** |
237 | | - * Polls for sandbox status until it reaches a terminal state. |
238 | | - * @param sandboxId - The sandbox ID to poll |
239 | | - * @param pollIntervalSeconds - Interval between polls in seconds |
240 | | - * @param timeoutSeconds - Maximum time to wait (0 for no timeout) |
241 | | - * @returns The final sandbox state |
242 | | - */ |
243 | | - private async waitForSandbox( |
244 | | - sandboxId: string, |
245 | | - pollIntervalSeconds: number, |
246 | | - timeoutSeconds: number, |
247 | | - ): Promise<SandboxModel> { |
248 | | - await waitForSandboxStateCommon({ |
249 | | - sandboxId, |
250 | | - targetState: 'started', |
251 | | - pollIntervalSeconds, |
252 | | - timeoutSeconds, |
253 | | - odsClient: this.odsClient, |
254 | | - logger: this.logger, |
255 | | - sleep: (ms) => this.sleep(ms), |
256 | | - onPollError: (message) => |
257 | | - this.error( |
258 | | - t('commands.ods.create.pollError', 'Failed to fetch sandbox status: {{message}}', { |
259 | | - message, |
260 | | - }), |
261 | | - ), |
262 | | - onTimeout: (seconds) => |
263 | | - this.error( |
264 | | - t('commands.ods.create.timeout', 'Timeout waiting for sandbox after {{seconds}} seconds', { |
265 | | - seconds: String(seconds), |
266 | | - }), |
267 | | - ), |
268 | | - onFailure: (state) => { |
269 | | - if (state === 'deleted') { |
270 | | - this.error(t('commands.ods.create.deleted', 'Sandbox was deleted')); |
271 | | - } |
272 | | - this.error(t('commands.ods.create.failed', 'Sandbox creation failed')); |
273 | | - }, |
274 | | - }); |
275 | | - |
276 | | - const finalResult = await this.odsClient.GET('/sandboxes/{sandboxId}', { |
277 | | - params: { |
278 | | - path: {sandboxId}, |
279 | | - }, |
280 | | - }); |
281 | | - |
282 | | - if (!finalResult.data?.data) { |
283 | | - this.error( |
284 | | - t('commands.ods.create.pollError', 'Failed to fetch sandbox status: {{message}}', { |
285 | | - message: finalResult.response?.statusText || 'Unknown error', |
286 | | - }), |
287 | | - ); |
288 | | - } |
289 | | - |
290 | | - this.log(''); |
291 | | - this.logger.info({sandboxId}, t('commands.ods.create.ready', 'Sandbox is now ready')); |
292 | | - |
293 | | - return finalResult.data.data; |
294 | | - } |
295 | 291 | } |
0 commit comments