@@ -47,6 +47,7 @@ export type SaveOptions = FullOptions & {
47
47
cascadeSave ?: boolean ;
48
48
context ?: AttributeMap ;
49
49
batchSize ?: number ;
50
+ transaction ?: boolean ;
50
51
} ;
51
52
52
53
type FetchOptions = {
@@ -1354,6 +1355,29 @@ class ParseObject {
1354
1355
}
1355
1356
const controller = CoreManager . getObjectController ( ) ;
1356
1357
const unsaved = options . cascadeSave !== false ? unsavedChildren ( this ) : null ;
1358
+ if (
1359
+ unsaved &&
1360
+ unsaved . length &&
1361
+ options . transaction === true &&
1362
+ unsaved . some ( el => el instanceof ParseObject )
1363
+ ) {
1364
+ saveOptions . transaction = options . transaction ;
1365
+ const unsavedFiles : ParseFile [ ] = [ ] ;
1366
+ const unsavedObjects : ParseObject [ ] = [ ] ;
1367
+ unsaved . forEach ( el => {
1368
+ if ( el instanceof ParseFile ) unsavedFiles . push ( el ) ;
1369
+ else unsavedObjects . push ( el ) ;
1370
+ } ) ;
1371
+ unsavedObjects . push ( this ) ;
1372
+
1373
+ const filePromise = unsavedFiles . length
1374
+ ? controller . save ( unsavedFiles , saveOptions )
1375
+ : Promise . resolve ( ) ;
1376
+
1377
+ return filePromise
1378
+ . then ( ( ) => controller . save ( unsavedObjects , saveOptions ) )
1379
+ . then ( ( savedOjbects : this[ ] ) => savedOjbects . pop ( ) ) ;
1380
+ }
1357
1381
return controller . save ( unsaved , saveOptions ) . then ( ( ) => {
1358
1382
return controller . save ( this , saveOptions ) ;
1359
1383
} ) as Promise < ParseObject > as Promise < this> ;
@@ -1770,6 +1794,9 @@ class ParseObject {
1770
1794
if ( options . hasOwnProperty ( 'sessionToken' ) ) {
1771
1795
destroyOptions . sessionToken = options . sessionToken ;
1772
1796
}
1797
+ if ( options . hasOwnProperty ( 'transaction' ) && typeof options . transaction === 'boolean' ) {
1798
+ destroyOptions . transaction = options . transaction ;
1799
+ }
1773
1800
if ( options . hasOwnProperty ( 'batchSize' ) && typeof options . batchSize === 'number' ) {
1774
1801
destroyOptions . batchSize = options . batchSize ;
1775
1802
}
@@ -1805,6 +1832,9 @@ class ParseObject {
1805
1832
if ( options . hasOwnProperty ( 'sessionToken' ) ) {
1806
1833
saveOptions . sessionToken = options . sessionToken ;
1807
1834
}
1835
+ if ( options . hasOwnProperty ( 'transaction' ) && typeof options . transaction === 'boolean' ) {
1836
+ saveOptions . transaction = options . transaction ;
1837
+ }
1808
1838
if ( options . hasOwnProperty ( 'batchSize' ) && typeof options . batchSize === 'number' ) {
1809
1839
saveOptions . batchSize = options . batchSize ;
1810
1840
}
@@ -2322,12 +2352,20 @@ const DefaultController = {
2322
2352
target : ParseObject | Array < ParseObject > ,
2323
2353
options : RequestOptions
2324
2354
) : Promise < ParseObject | Array < ParseObject > > {
2325
- const batchSize =
2355
+ if ( options && options . batchSize && options . transaction )
2356
+ throw new ParseError (
2357
+ ParseError . OTHER_CAUSE ,
2358
+ 'You cannot use both transaction and batchSize options simultaneously.'
2359
+ ) ;
2360
+
2361
+ let batchSize =
2326
2362
options && options . batchSize ? options . batchSize : CoreManager . get ( 'REQUEST_BATCH_SIZE' ) ;
2327
2363
const localDatastore = CoreManager . getLocalDatastore ( ) ;
2328
2364
2329
2365
const RESTController = CoreManager . getRESTController ( ) ;
2330
2366
if ( Array . isArray ( target ) ) {
2367
+ if ( options && options . transaction && target . length > 1 ) batchSize = target . length ;
2368
+
2331
2369
if ( target . length < 1 ) {
2332
2370
return Promise . resolve ( [ ] ) ;
2333
2371
}
@@ -2348,21 +2386,20 @@ const DefaultController = {
2348
2386
let deleteCompleted = Promise . resolve ( ) ;
2349
2387
const errors = [ ] ;
2350
2388
batches . forEach ( batch => {
2389
+ const requests = batch . map ( obj => {
2390
+ return {
2391
+ method : 'DELETE' ,
2392
+ path : getServerUrlPath ( ) + 'classes/' + obj . className + '/' + obj . _getId ( ) ,
2393
+ body : { } ,
2394
+ } ;
2395
+ } ) ;
2396
+ const body =
2397
+ options && options . transaction && requests . length > 1
2398
+ ? { requests, transaction : true }
2399
+ : { requests } ;
2400
+
2351
2401
deleteCompleted = deleteCompleted . then ( ( ) => {
2352
- return RESTController . request (
2353
- 'POST' ,
2354
- 'batch' ,
2355
- {
2356
- requests : batch . map ( obj => {
2357
- return {
2358
- method : 'DELETE' ,
2359
- path : getServerUrlPath ( ) + 'classes/' + obj . className + '/' + obj . _getId ( ) ,
2360
- body : { } ,
2361
- } ;
2362
- } ) ,
2363
- } ,
2364
- options
2365
- ) . then ( results => {
2402
+ return RESTController . request ( 'POST' , 'batch' , body , options ) . then ( results => {
2366
2403
for ( let i = 0 ; i < results . length ; i ++ ) {
2367
2404
if ( results [ i ] && results [ i ] . hasOwnProperty ( 'error' ) ) {
2368
2405
const err = new ParseError ( results [ i ] . error . code , results [ i ] . error . error ) ;
@@ -2402,8 +2439,17 @@ const DefaultController = {
2402
2439
target : ParseObject | null | Array < ParseObject | ParseFile > ,
2403
2440
options : RequestOptions
2404
2441
) : Promise < ParseObject | Array < ParseObject > | ParseFile | undefined > {
2405
- const batchSize =
2442
+ if ( options && options . batchSize && options . transaction )
2443
+ return Promise . reject (
2444
+ new ParseError (
2445
+ ParseError . OTHER_CAUSE ,
2446
+ 'You cannot use both transaction and batchSize options simultaneously.'
2447
+ )
2448
+ ) ;
2449
+
2450
+ let batchSize =
2406
2451
options && options . batchSize ? options . batchSize : CoreManager . get ( 'REQUEST_BATCH_SIZE' ) ;
2452
+
2407
2453
const localDatastore = CoreManager . getLocalDatastore ( ) ;
2408
2454
const mapIdForPin = { } ;
2409
2455
@@ -2437,6 +2483,17 @@ const DefaultController = {
2437
2483
}
2438
2484
} ) ;
2439
2485
2486
+ if ( options && options . transaction && pending . length > 1 ) {
2487
+ if ( pending . some ( el => ! canBeSerialized ( el ) ) )
2488
+ return Promise . reject (
2489
+ new ParseError (
2490
+ ParseError . OTHER_CAUSE ,
2491
+ 'Tried to save a transactional batch containing an object with unserializable attributes.'
2492
+ )
2493
+ ) ;
2494
+ batchSize = pending . length ;
2495
+ }
2496
+
2440
2497
return Promise . all ( filesSaved ) . then ( ( ) => {
2441
2498
let objectError = null ;
2442
2499
return continueWhile (
@@ -2504,18 +2561,16 @@ const DefaultController = {
2504
2561
when ( batchReady )
2505
2562
. then ( ( ) => {
2506
2563
// Kick off the batch request
2507
- return RESTController . request (
2508
- 'POST' ,
2509
- 'batch' ,
2510
- {
2511
- requests : batch . map ( obj => {
2512
- const params = obj . _getSaveParams ( ) ;
2513
- params . path = getServerUrlPath ( ) + params . path ;
2514
- return params ;
2515
- } ) ,
2516
- } ,
2517
- options
2518
- ) ;
2564
+ const requests = batch . map ( obj => {
2565
+ const params = obj . _getSaveParams ( ) ;
2566
+ params . path = getServerUrlPath ( ) + params . path ;
2567
+ return params ;
2568
+ } ) ;
2569
+ const body =
2570
+ options && options . transaction && requests . length > 1
2571
+ ? { requests, transaction : true }
2572
+ : { requests } ;
2573
+ return RESTController . request ( 'POST' , 'batch' , body , options ) ;
2519
2574
} )
2520
2575
. then ( batchReturned . resolve , error => {
2521
2576
batchReturned . reject ( new ParseError ( ParseError . INCORRECT_TYPE , error . message ) ) ;
0 commit comments