diff --git a/src/MutationBatch.js b/src/MutationBatch.js index 2a625bf..150c898 100644 --- a/src/MutationBatch.js +++ b/src/MutationBatch.js @@ -30,14 +30,16 @@ import type { ParseRequestOptions } from './MutationExecutor'; class MutationBatch { static maxBatchSize: number; + _aborted: boolean; + _dispatched: boolean; _requests: Array; _promises: Array; - addRequest: (options: ParseRequestOptions) => Parse.Promise; constructor() { + this._aborted = false; + this._dispatched = false; this._requests = []; this._promises = []; - this.addRequest = this.addRequest.bind(this); } getNumberOfRequests(): number { @@ -45,6 +47,9 @@ class MutationBatch { } addRequest(options: ParseRequestOptions): Parse.Promise { + if (this._aborted || this._dispatched) { + throw new Error('Cannot add a request to aborted or dispatched batch.'); + } if (this.getNumberOfRequests() === MutationBatch.maxBatchSize) { throw new Error('Cannot batch more than ' + MutationBatch.maxBatchSize + ' requests at a time.'); @@ -56,6 +61,10 @@ class MutationBatch { } dispatch(): Parse.Promise { + if (this._aborted || this._dispatched) { + throw new Error('Cannot dispatch an already dispatched or aborted batch.'); + } + this._dispatched = true; var requests = this._requests.map((req) => { var path = '/1/' + req.route; if (req.className) { @@ -90,6 +99,12 @@ class MutationBatch { return Parse.Promise.error(error); }); } + + abort() { + this._aborted = true; + var error = new Error('Batch was aborted.'); + this._promises.forEach((promise) => promise.reject(error)); + } } MutationBatch.maxBatchSize = 50; diff --git a/src/MutationExecutor.js b/src/MutationExecutor.js index 90d9bfc..a9d6ab3 100644 --- a/src/MutationExecutor.js +++ b/src/MutationExecutor.js @@ -121,7 +121,7 @@ function execute( var className = (typeof target === 'string') ? target : target.className; var objectId = (typeof target === 'string') ? '' : target.objectId; var payload; - var request = batch ? batch.addRequest : sendRequest; + var request = batch ? batch.addRequest.bind(batch) : sendRequest; switch (mutation.action) { case 'CREATE': return request({ diff --git a/src/__tests__/MutationBatch-test.js b/src/__tests__/MutationBatch-test.js index c8482fd..1b2c968 100644 --- a/src/__tests__/MutationBatch-test.js +++ b/src/__tests__/MutationBatch-test.js @@ -142,9 +142,13 @@ describe('MutationBatch', function() { expect(thirdPromiseResult).toBe(null); expect(thirdPromiseError).toEqual(expectedThirdError); expect(batchPromiseResolved).toBe(true); + + // Once dispatched, the batch cannot be dispatched or aborted anymore. + expect(function() { batch.dispatch(); }).toThrow(); + expect(function() { batch.abort(); }).toThrow(); }); - it('resolves promises even when request fails', function() { + it('rejects promises when the entire request fails', function() { Parse.XMLHttpRequest = testXHR( function() {}, function() {}, @@ -189,6 +193,49 @@ describe('MutationBatch', function() { expect(firstPromiseError).toBe(secondPromiseError); expect(batchPromiseError).toBe(firstPromiseError); expect(batchPromiseSuccess).toBe(false); + + // Once dispatched, the batch cannot be dispatched or aborted anymore. + expect(function() { batch.dispatch(); }).toThrow(); + expect(function() { batch.abort(); }).toThrow(); + }); + + it('rejects promises when the mutation is aborted', function() { + var batch = new MutationBatch(); + var firstPromise = batch.addRequest({ + method: 'DELETE', + route: 'classes', + className: 'MadeUpClassName', + objectId: 'randomId', + }); + var secondPromise = batch.addRequest({ + method: 'CREATE', + route: 'classes', + className: 'MadeUpClassName', + data: {some: 'fields', and: 'stuff'}, + }); + + var firstPromiseResult = null; + var firstPromiseError = null; + var secondPromiseResult = null; + var secondPromiseError = null; + firstPromise.then( + function(result) { firstPromiseResult = result; }, + function(error) { firstPromiseError = error; } + ); + secondPromise.then( + function(result) { secondPromiseResult = result; }, + function(error) { secondPromiseError = error; } + ); + batch.abort() + + expect(firstPromiseResult).toBe(null); + expect(secondPromiseResult).toBe(null); + expect(firstPromiseError instanceof Error).toBe(true); + expect(secondPromiseError instanceof Error).toBe(true); + + // Once aborted, the batch cannot be dispatched or aborted anymore. + expect(function() { batch.dispatch(); }).toThrow(); + expect(function() { batch.abort(); }).toThrow(); }); });