@@ -111,6 +111,19 @@ export type SandboxProcessObject = {
111
111
ApexClassId ?: string ;
112
112
EndDate ?: string ;
113
113
} ;
114
+ const sandboxProcessFields = [
115
+ 'Id' ,
116
+ 'Status' ,
117
+ 'SandboxName' ,
118
+ 'SandboxInfoId' ,
119
+ 'LicenseType' ,
120
+ 'CreatedDate' ,
121
+ 'CopyProgress' ,
122
+ 'SandboxOrganization' ,
123
+ 'SourceId' ,
124
+ 'Description' ,
125
+ 'EndDate' ,
126
+ ] ;
114
127
115
128
export type SandboxRequest = {
116
129
SandboxName : string ;
@@ -124,6 +137,27 @@ export type ResumeSandboxRequest = {
124
137
SandboxProcessObjId ?: string ;
125
138
} ;
126
139
140
+ // https://developer.salesforce.com/docs/atlas.en-us.api_tooling.meta/api_tooling/tooling_api_objects_sandboxinfo.htm
141
+ export type SandboxInfo = {
142
+ Id : string ; // 0GQB0000000TVobOAG
143
+ IsDeleted : boolean ;
144
+ CreatedDate : string ; // 2023-06-16T18:35:47.000+0000
145
+ CreatedById : string ; // 005B0000004TiUpIAK
146
+ LastModifiedDate : string ; // 2023-09-27T20:50:26.000+0000
147
+ LastModifiedById : string ; // 005B0000004TiUpIAK
148
+ SandboxName : string ; // must be 10 or less alphanumeric chars
149
+ LicenseType : 'DEVELOPER' | 'DEVELOPER PRO' | 'PARTIAL' | 'FULL' ;
150
+ TemplateId ?: string ; // reference to PartitionLevelScheme
151
+ HistoryDays : - 1 | 0 | 10 | 20 | 30 | 60 | 90 | 120 | 150 | 180 ; // full sandboxes only
152
+ CopyChatter : boolean ;
153
+ AutoActivate : boolean ; // only editable for an update/refresh
154
+ ApexClassId ?: string ; // apex class ID. Only editable on create.
155
+ Description ?: string ;
156
+ SourceId ?: string ; // SandboxInfoId as the source org used for a clone
157
+ // 'ActivationUserGroupId', // Support might be added back in API v61.0 (Summer '24)
158
+ CopyArchivedActivities ?: boolean ; // only for full sandboxes; depends if a license was purchased
159
+ } ;
160
+
127
161
export type ScratchOrgRequest = Omit < ScratchOrgCreateOptions , 'hubOrg' > ;
128
162
129
163
export type SandboxFields = {
@@ -227,6 +261,62 @@ export class Org extends AsyncOptionalCreatable<Org.Options> {
227
261
} ) ;
228
262
}
229
263
264
+ /**
265
+ * Refresh (update) a sandbox from a production org.
266
+ * 'this' needs to be a production org with sandbox licenses available
267
+ *
268
+ * @param sandboxInfo SandboxInfo to update the sandbox with
269
+ * @param options Wait: The amount of time to wait before timing out, Interval: The time interval between polling
270
+ */
271
+ public async refreshSandbox (
272
+ sandboxInfo : SandboxInfo ,
273
+ options : { wait ?: Duration ; interval ?: Duration ; async ?: boolean } = {
274
+ wait : Duration . minutes ( 6 ) ,
275
+ async : false ,
276
+ interval : Duration . seconds ( 30 ) ,
277
+ }
278
+ ) : Promise < SandboxProcessObject > {
279
+ this . logger . debug ( sandboxInfo , 'RefreshSandbox called with SandboxInfo' ) ;
280
+ const refreshResult = await this . connection . tooling . update ( 'SandboxInfo' , sandboxInfo ) ;
281
+ this . logger . debug ( refreshResult , 'Return from calling tooling.update' ) ;
282
+
283
+ if ( ! refreshResult . success ) {
284
+ throw messages . createError ( 'sandboxInfoRefreshFailed' , [ JSON . stringify ( refreshResult ) ] ) ;
285
+ }
286
+
287
+ const soql = `SELECT ${ sandboxProcessFields . join ( ',' ) } FROM SandboxProcess WHERE SandboxName='${
288
+ sandboxInfo . SandboxName
289
+ } ' ORDER BY CreatedDate DESC`;
290
+ const sbxProcessObjects = ( await this . connection . tooling . query < SandboxProcessObject > ( soql ) ) . records . filter (
291
+ ( item ) => ! item . Status . startsWith ( 'Del' )
292
+ ) ;
293
+ this . logger . debug ( sbxProcessObjects , `SandboxProcesses for ${ sandboxInfo . SandboxName } ` ) ;
294
+
295
+ // throw if none found
296
+ if ( sbxProcessObjects ?. length === 0 ) {
297
+ throw new Error ( `No SandboxProcesses found for: ${ sandboxInfo . SandboxName } ` ) ;
298
+ }
299
+ const sandboxRefreshProgress = sbxProcessObjects [ 0 ] ;
300
+
301
+ const isAsync = ! ! options . async ;
302
+
303
+ if ( isAsync ) {
304
+ // The user didn't want us to poll, so simply return the status
305
+ await Lifecycle . getInstance ( ) . emit ( SandboxEvents . EVENT_ASYNC_RESULT , sandboxRefreshProgress ) ;
306
+ return sandboxRefreshProgress ;
307
+ }
308
+ const [ wait , pollInterval ] = this . validateWaitOptions ( options ) ;
309
+ this . logger . debug (
310
+ sandboxRefreshProgress ,
311
+ `refresh - pollStatusAndAuth sandboxProcessObj, max wait time of ${ wait . minutes } minutes`
312
+ ) ;
313
+ return this . pollStatusAndAuth ( {
314
+ sandboxProcessObj : sandboxRefreshProgress ,
315
+ wait,
316
+ pollInterval,
317
+ } ) ;
318
+ }
319
+
230
320
/**
231
321
*
232
322
* @param sandboxReq SandboxRequest options to create the sandbox with
@@ -245,10 +335,10 @@ export class Org extends AsyncOptionalCreatable<Org.Options> {
245
335
}
246
336
247
337
/**
248
- * Resume a sandbox creation from a production org.
338
+ * Resume a sandbox create or refresh from a production org.
249
339
* `this` needs to be a production org with sandbox licenses available.
250
340
*
251
- * @param resumeSandboxRequest SandboxRequest options to create the sandbox with
341
+ * @param resumeSandboxRequest SandboxRequest options to create/refresh the sandbox with
252
342
* @param options Wait: The amount of time to wait (default: 0 minutes) before timing out,
253
343
* Interval: The time interval (default: 30 seconds) between polling
254
344
*/
@@ -1293,7 +1383,6 @@ export class Org extends AsyncOptionalCreatable<Org.Options> {
1293
1383
1294
1384
const authInfo = await AuthInfo . create ( {
1295
1385
username : sandboxRes . authUserName ,
1296
- oauth2Options,
1297
1386
parentUsername : productionAuthFields . username ,
1298
1387
} ) ;
1299
1388
@@ -1305,8 +1394,12 @@ export class Org extends AsyncOptionalCreatable<Org.Options> {
1305
1394
} ,
1306
1395
'Creating AuthInfo for sandbox'
1307
1396
) ;
1308
- // save auth info for new sandbox
1309
- await authInfo . save ( ) ;
1397
+ // save auth info for sandbox
1398
+ await authInfo . save ( {
1399
+ ...oauth2Options ,
1400
+ isScratch : false ,
1401
+ isSandbox : true ,
1402
+ } ) ;
1310
1403
1311
1404
const sandboxOrgId = authInfo . getFields ( ) . orgId ;
1312
1405
@@ -1390,7 +1483,9 @@ export class Org extends AsyncOptionalCreatable<Org.Options> {
1390
1483
* @private
1391
1484
*/
1392
1485
private async querySandboxProcess ( where : string ) : Promise < SandboxProcessObject > {
1393
- const soql = `SELECT Id, Status, SandboxName, SandboxInfoId, LicenseType, CreatedDate, CopyProgress, SandboxOrganization, SourceId, Description, EndDate FROM SandboxProcess WHERE ${ where } ORDER BY CreatedDate DESC` ;
1486
+ const soql = `SELECT ${ sandboxProcessFields . join (
1487
+ ','
1488
+ ) } FROM SandboxProcess WHERE ${ where } ORDER BY CreatedDate DESC`;
1394
1489
const result = ( await this . connection . tooling . query < SandboxProcessObject > ( soql ) ) . records . filter (
1395
1490
( item ) => ! item . Status . startsWith ( 'Del' )
1396
1491
) ;
0 commit comments