Repository: parse-community/Parse-SDK-JS
Pull request #1305 (2021-02-23 17:29:35)

fix(EventuallyQueue): Poll against /health endpoint
The `/health` endpoint has been supported on the server since 2.2.5.
This fixes an issue where a `403` would let us know that server was online.

peril-parse-community[bot]: 
<!--
  0 failure: 
  0 warning: 
  
  1 markdown notices
  DangerID: danger-id-Peril;
-->



Danger run resulted in 1 warning; to find out more, see the [checks page](https://github.com/parse-community/Parse-SDK-JS/runs/1963418094).
<p align="right">
  Generated by :no_entry_sign: <a href="https://danger.systems/js">dangerJS</a>
</p>

codecov[bot]: # [Codecov](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1305?src=pr&el=h1) Report
> Merging [#1305](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1305?src=pr&el=desc) (d478078) into [master](https://codecov.io/gh/parse-community/Parse-SDK-JS/commit/1abb8415a885be7c9e968526c1533097a3c83886?el=desc) (1abb841) will **not change** coverage.
> The diff coverage is `100.00%`.

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

```diff
@@            Coverage Diff            @@
##            master     #1305   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           60        60           
  Lines         5837      5838    +1     
  Branches      1312      1311    -1     
=========================================
+ Hits          5837      5838    +1     
```


| [Impacted Files](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1305?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/EventuallyQueue.js](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1305/diff?src=pr&el=tree#diff-c3JjL0V2ZW50dWFsbHlRdWV1ZS5qcw==) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1305?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/1305?src=pr&el=footer). Last update [1abb841...606acda](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1305?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

dplewis: What do you think about adding a health check in the SDK like the PHP SDK

https://github.com/parse-community/parse-php-sdk/pull/366

Patch:
diff --git a/src/EventuallyQueue.js b/src/EventuallyQueue.js
index b84fe9c..8925fda 100644
--- a/src/EventuallyQueue.js
+++ b/src/EventuallyQueue.js
@@ -308,12 +308,14 @@ const EventuallyQueue = {
     }
     polling = setInterval(() => {
       const RESTController = CoreManager.getRESTController();
-      RESTController.ajax('GET', CoreManager.get('SERVER_URL')).catch(error => {
-        if (error !== 'Unable to connect to the Parse API') {
-          this.stopPoll();
-          return this.sendQueue();
-        }
-      });
+      RESTController.request('GET', 'health')
+        .then(({ status }) => {
+          if (status === 'ok') {
+            this.stopPoll();
+            return this.sendQueue();
+          }
+        })
+        .catch(e => e);
     }, ms);
   },
 
diff --git a/src/Parse.js b/src/Parse.js
index 41f9c87..3d8af96 100644
--- a/src/Parse.js
+++ b/src/Parse.js
@@ -80,6 +80,16 @@ const Parse = {
     CoreManager.setLocalDatastoreController(controller);
   },
 
+  /**
+   * Returns information regarding the current server's health
+   *
+   * @returns {Promise}
+   * @static
+   */
+  getServerHealth() {
+    return CoreManager.getRESTController().request('GET', 'health');
+  },
+
   /**
    * @member {string} Parse.applicationId
    * @static
diff --git a/src/ParseQuery.js b/src/ParseQuery.js
index 4f3c358..57228e5 100644
--- a/src/ParseQuery.js
+++ b/src/ParseQuery.js
@@ -1573,13 +1573,14 @@ class ParseQuery {
    *
    * @param {string} key The key that the string to match is stored in.
    * @param {string} prefix The substring that the value must start with.
+   * @param {string} modifiers The regular expression mode.
    * @returns {Parse.Query} Returns the query, so you can chain this call.
    */
-  startsWith(key: string, prefix: string): ParseQuery {
+  startsWith(key: string, prefix: string, modifiers: string): ParseQuery {
     if (typeof prefix !== 'string') {
       throw new Error('The value being searched for must be a string.');
     }
-    return this._addCondition(key, '$regex', this._regexStartWith(prefix));
+    return this.matches(key, this._regexStartWith(prefix), modifiers);
   }
 
   /**
@@ -1588,13 +1589,14 @@ class ParseQuery {
    *
    * @param {string} key The key that the string to match is stored in.
    * @param {string} suffix The substring that the value must end with.
+   * @param {string} modifiers The regular expression mode.
    * @returns {Parse.Query} Returns the query, so you can chain this call.
    */
-  endsWith(key: string, suffix: string): ParseQuery {
+  endsWith(key: string, suffix: string, modifiers: string): ParseQuery {
     if (typeof suffix !== 'string') {
       throw new Error('The value being searched for must be a string.');
     }
-    return this._addCondition(key, '$regex', quote(suffix) + '$');
+    return this.matches(key, quote(suffix) + '$', modifiers);
   }
 
   /**


Test patch:
diff --git a/integration/test/ParseQueryTest.js b/integration/test/ParseQueryTest.js
index 4815585..90f8292 100644
--- a/integration/test/ParseQueryTest.js
+++ b/integration/test/ParseQueryTest.js
@@ -1158,7 +1158,7 @@ describe('Parse Query', () => {
       });
   });
 
-  it('can test case insensitive regex', done => {
+  it('can test case insensitive matches', done => {
     const obj = new TestObject();
     obj.set('myString', 'hockey');
     obj
@@ -1175,6 +1175,32 @@ describe('Parse Query', () => {
       });
   });
 
+  it('can test case insensitive startsWith', async () => {
+    const obj = new TestObject();
+    obj.set('myString', 'basketball');
+    await obj.save();
+
+    const query = new Parse.Query(TestObject);
+    query.startsWith('myString', 'baSKet', 'i');
+    const results = await query.find();
+
+    assert.strictEqual(results.length, 1);
+    assert.strictEqual(results[0].get('myString'), 'basketball');
+  });
+
+  it('can test case insensitive endsWith', async () => {
+    const obj = new TestObject();
+    obj.set('myString', 'basketball');
+    await obj.save();
+
+    const query = new Parse.Query(TestObject);
+    query.endsWith('myString', 'tBAll', 'i');
+    const results = await query.find();
+
+    assert.strictEqual(results.length, 1);
+    assert.strictEqual(results[0].get('myString'), 'basketball');
+  });
+
   it('fails for invalid regex options', done => {
     const query = new Parse.Query(TestObject);
     query.matches('myString', 'football', 'some invalid thing');
diff --git a/src/__tests__/EventuallyQueue-test.js b/src/__tests__/EventuallyQueue-test.js
index c5460d2..d514367 100644
--- a/src/__tests__/EventuallyQueue-test.js
+++ b/src/__tests__/EventuallyQueue-test.js
@@ -52,6 +52,12 @@ const RESTController = require('../RESTController');
 const Storage = require('../Storage');
 const mockXHR = require('./test_helpers/mockXHR');
 
+CoreManager.setInstallationController({
+  currentInstallationId() {
+    return Promise.resolve('iid');
+  },
+});
+
 function flushPromises() {
   return new Promise(resolve => setImmediate(resolve));
 }
@@ -400,7 +406,7 @@ describe('EventuallyQueue', () => {
 
   it('can poll server', async () => {
     jest.spyOn(EventuallyQueue, 'sendQueue').mockImplementationOnce(() => {});
-    RESTController._setXHR(mockXHR([{ status: 107, response: { error: 'ok' } }]));
+    RESTController._setXHR(mockXHR([{ status: 200, response: { status: 'ok' } }]));
     EventuallyQueue.poll();
     expect(EventuallyQueue.isPolling()).toBe(true);
     jest.runOnlyPendingTimers();
diff --git a/src/__tests__/Parse-test.js b/src/__tests__/Parse-test.js
index 9e6d548..afe8ebd 100644
--- a/src/__tests__/Parse-test.js
+++ b/src/__tests__/Parse-test.js
@@ -161,6 +161,18 @@ describe('Parse module', () => {
     CoreManager.set('REQUEST_BATCH_SIZE', 20);
   });
 
+  it('getServerHealth', () => {
+    const controller = {
+      request: jest.fn(),
+      ajax: jest.fn(),
+    };
+    CoreManager.setRESTController(controller);
+    Parse.getServerHealth();
+    const [method, path] = controller.request.mock.calls[0];
+    expect(method).toBe('GET');
+    expect(path).toBe('health');
+  });
+
   it('_request', () => {
     const controller = {
       request: jest.fn(),