Repository: parse-community/Parse-SDK-JS
Pull request #1053 (2020-01-07 23:52:08)

Support global request batch size
Closes: https://github.com/parse-community/Parse-SDK-JS/issues/1045

Currently the /batch endpoint send multiple requests in the with the number of objects equal to batchSize (default 20). I believe batchSize was added to reduce server load during the parse.com days. Having batchSize will make result in a certain amount of overhead for your client.

This PR will allow you to decrease the number of requests which may result in saving resources and faster requests.

codecov[bot]: # [Codecov](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1053?src=pr&el=h1) Report
> Merging [#1053](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1053?src=pr&el=desc) into [master](https://codecov.io/gh/parse-community/Parse-SDK-JS/commit/6dc6e5ca7625adeac1afd3f5fe287ca7ed2f7f3e?src=pr&el=desc) will **decrease** coverage by `<.01%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1053/graphs/tree.svg?width=650&token=8nCENM0QHj&height=150&src=pr)](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1053?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1053      +/-   ##
==========================================
- Coverage   92.19%   92.19%   -0.01%     
==========================================
  Files          54       54              
  Lines        5152     5151       -1     
  Branches     1145     1145              
==========================================
- Hits         4750     4749       -1     
  Misses        402      402
```


| [Impacted Files](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1053?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/CoreManager.js](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1053/diff?src=pr&el=tree#diff-c3JjL0NvcmVNYW5hZ2VyLmpz) | `100% <ø> (ø)` | :arrow_up: |
| [src/ParseObject.js](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1053/diff?src=pr&el=tree#diff-c3JjL1BhcnNlT2JqZWN0Lmpz) | `89.43% <100%> (-0.02%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1053?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1053?src=pr&el=footer). Last update [6dc6e5c...db76067](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1053?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/src/CoreManager.js b/src/CoreManager.js
index 73e8631..3f9e409 100644
--- a/src/CoreManager.js
+++ b/src/CoreManager.js
@@ -175,6 +175,7 @@ const config: Config & { [key: string]: mixed } = {
             !!process.versions.node &&
             !process.versions.electron),
   REQUEST_ATTEMPT_LIMIT: 5,
+  REQUEST_BATCH_SIZE: 20,
   REQUEST_HEADERS: {},
   SERVER_URL: 'https://api.parse.com/1',
   SERVER_AUTH_TYPE: null,
diff --git a/src/ParseObject.js b/src/ParseObject.js
index e2d6b57..8d6d628 100644
--- a/src/ParseObject.js
+++ b/src/ParseObject.js
@@ -59,8 +59,6 @@ type SaveOptions = FullOptions & {
   cascadeSave?: boolean
 }
 
-const DEFAULT_BATCH_SIZE = 20;
-
 // Mapping of class names to constructors, so we can populate objects from the
 // server with appropriate subclasses of ParseObject
 const classMap = {};
@@ -2141,7 +2139,7 @@ const DefaultController = {
   },
 
   async destroy(target: ParseObject | Array<ParseObject>, options: RequestOptions): Promise<Array<void> | ParseObject> {
-    const batchSize = (options && options.batchSize) ? options.batchSize : DEFAULT_BATCH_SIZE;
+    const batchSize = (options && options.batchSize) ? options.batchSize : CoreManager.get('REQUEST_BATCH_SIZE');
     const localDatastore = CoreManager.getLocalDatastore();
 
     const RESTController = CoreManager.getRESTController();
@@ -2216,7 +2214,7 @@ const DefaultController = {
   },
 
   save(target: ParseObject | Array<ParseObject | ParseFile>, options: RequestOptions) {
-    const batchSize = (options && options.batchSize) ? options.batchSize : DEFAULT_BATCH_SIZE;
+    const batchSize = (options && options.batchSize) ? options.batchSize : CoreManager.get('REQUEST_BATCH_SIZE');
     const localDatastore = CoreManager.getLocalDatastore();
     const mapIdForPin = {};
 


Test patch:
diff --git a/src/__tests__/Parse-test.js b/src/__tests__/Parse-test.js
index cfdd10d..318ae4b 100644
--- a/src/__tests__/Parse-test.js
+++ b/src/__tests__/Parse-test.js
@@ -126,4 +126,11 @@ describe('Parse module', () => {
     expect(CoreManager.get('ENCRYPTED_KEY')).toBe('My Super secret key');
     expect(Parse.secret).toBe('My Super secret key');
   });
+
+  it('can set and get request batch size', () => {
+    expect(CoreManager.get('REQUEST_BATCH_SIZE')).toBe(20);
+    CoreManager.set('REQUEST_BATCH_SIZE', 4);
+    expect(CoreManager.get('REQUEST_BATCH_SIZE')).toBe(4);
+    CoreManager.set('REQUEST_BATCH_SIZE', 20);
+  });
 });
diff --git a/src/__tests__/ParseObject-test.js b/src/__tests__/ParseObject-test.js
index 2f25e23..c30002d 100644
--- a/src/__tests__/ParseObject-test.js
+++ b/src/__tests__/ParseObject-test.js
@@ -1744,6 +1744,69 @@ describe('ParseObject', () => {
     jest.runAllTicks();
   });
 
+  it('can saveAll with global batchSize', async (done) => {
+    const xhrs = [];
+    for (let i = 0; i < 2; i++) {
+      xhrs[i] = {
+        setRequestHeader: jest.fn(),
+        open: jest.fn(),
+        send: jest.fn(),
+        status: 200,
+        readyState: 4
+      };
+    }
+    let current = 0;
+    RESTController._setXHR(function() { return xhrs[current++]; });
+    const objects = [];
+    for (let i = 0; i < 22; i++) {
+      objects[i] = new ParseObject('Person');
+    }
+    ParseObject.saveAll(objects).then(() => {
+      expect(xhrs[0].open.mock.calls[0]).toEqual(
+        ['POST', 'https://api.parse.com/1/batch', true]
+      );
+      expect(xhrs[1].open.mock.calls[0]).toEqual(
+        ['POST', 'https://api.parse.com/1/batch', true]
+      );
+      done();
+    });
+    jest.runAllTicks();
+    await flushPromises();
+
+    xhrs[0].responseText = JSON.stringify([
+      { success: { objectId: 'pid0' } },
+      { success: { objectId: 'pid1' } },
+      { success: { objectId: 'pid2' } },
+      { success: { objectId: 'pid3' } },
+      { success: { objectId: 'pid4' } },
+      { success: { objectId: 'pid5' } },
+      { success: { objectId: 'pid6' } },
+      { success: { objectId: 'pid7' } },
+      { success: { objectId: 'pid8' } },
+      { success: { objectId: 'pid9' } },
+      { success: { objectId: 'pid10' } },
+      { success: { objectId: 'pid11' } },
+      { success: { objectId: 'pid12' } },
+      { success: { objectId: 'pid13' } },
+      { success: { objectId: 'pid14' } },
+      { success: { objectId: 'pid15' } },
+      { success: { objectId: 'pid16' } },
+      { success: { objectId: 'pid17' } },
+      { success: { objectId: 'pid18' } },
+      { success: { objectId: 'pid19' } },
+    ]);
+    xhrs[0].onreadystatechange();
+    jest.runAllTicks();
+    await flushPromises();
+
+    xhrs[1].responseText = JSON.stringify([
+      { success: { objectId: 'pid20' } },
+      { success: { objectId: 'pid21' } },
+    ]);
+    xhrs[1].onreadystatechange();
+    jest.runAllTicks();
+  });
+
   it('returns the first error when saving an array of objects', async (done) => {
     const xhrs = [];
     for (let i = 0; i < 2; i++) {