Repository: parse-community/Parse-SDK-JS
Pull request #1302 (2021-02-22 17:22:22)

Support PushStatus on Parse.Push
Similar to how jobStatus is handled.

* Parse.Push.send now returns the pushStatusId
* Add useMasterKey to Parse.Push.send
* Add new Parse.Push.getPushStatus
* Add new Push Integration tests.

```
const pushStatusId = await Parse.Push.send(payload);
const pushStatus = await Parse.Push.getPushStatus(pushStatusId);
```
This is currently a breaking change. Parse.Push.send returned `{ result: true }` before.

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/1954465646).
<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/1302?src=pr&el=h1) Report
> Merging [#1302](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1302?src=pr&el=desc) (8359b10) into [master](https://codecov.io/gh/parse-community/Parse-SDK-JS/commit/10002deeaa083f4c45459bf897d3e0fd8d8e3734?el=desc) (10002de) will **not change** coverage.
> The diff coverage is `100.00%`.

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

```diff
@@            Coverage Diff            @@
##            master     #1302   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           60        60           
  Lines         5823      5834   +11     
  Branches      1306      1311    +5     
=========================================
+ Hits          5823      5834   +11     
```


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

------

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


Patch:
diff --git a/src/Push.js b/src/Push.js
index dcfec33..4ef1006 100644
--- a/src/Push.js
+++ b/src/Push.js
@@ -13,6 +13,7 @@ import CoreManager from './CoreManager';
 import ParseQuery from './ParseQuery';
 
 import type { WhereClause } from './ParseQuery';
+import type { FullOptions } from './RESTController';
 
 export type PushData = {
   where?: WhereClause | ParseQuery,
@@ -37,7 +38,7 @@ export type PushData = {
  *
  * @function send
  * @name Parse.Push.send
- * @param {object} data -  The data of the push notification.  Valid fields
+ * @param {object} data -  The data of the push notification. Valid fields
  * are:
  *   <ol>
  *     <li>channels - An Array of channels to push to.</li>
@@ -49,10 +50,15 @@ export type PushData = {
  *         a set of installations to push to.</li>
  *     <li>data - The data to send as part of the push.</li>
  *   <ol>
+ * @param {object} options Valid options
+ * are:<ul>
+ *   <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
+ *     be used for this request.
+ * </ul>
  * @returns {Promise} A promise that is fulfilled when the push request
  *     completes.
  */
-export function send(data: PushData): Promise {
+export function send(data: PushData, options?: FullOptions = {}): Promise {
   if (data.where && data.where instanceof ParseQuery) {
     data.where = data.where.toJSON().where;
   }
@@ -69,14 +75,39 @@ export function send(data: PushData): Promise {
     throw new Error('expiration_time and expiration_interval cannot both be set.');
   }
 
-  return CoreManager.getPushController().send(data);
+  const pushOptions = { useMasterKey: true };
+  if (options.hasOwnProperty('useMasterKey')) {
+    pushOptions.useMasterKey = options.useMasterKey;
+  }
+
+  return CoreManager.getPushController().send(data, pushOptions);
+}
+
+/**
+ * Gets push status by Id
+ *
+ * @function getPushStatus
+ * @name Parse.Push.getPushStatus
+ * @param {string} pushStatusId The Id of Push Status.
+ * @param {object} options Valid options
+ * are:<ul>
+ *   <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
+ *     be used for this request.
+ * </ul>
+ * @returns {Parse.Object} Status of Push.
+ */
+export function getPushStatus(pushStatusId: string, options?: FullOptions = {}): Promise<string> {
+  const pushOptions = { useMasterKey: true };
+  if (options.hasOwnProperty('useMasterKey')) {
+    pushOptions.useMasterKey = options.useMasterKey;
+  }
+  const query = new ParseQuery('_PushStatus');
+  return query.get(pushStatusId, pushOptions);
 }
 
 const DefaultController = {
-  send(data: PushData) {
-    return CoreManager.getRESTController().request('POST', 'push', data, {
-      useMasterKey: true,
-    });
+  send(data: PushData, options?: FullOptions) {
+    return CoreManager.getRESTController().request('POST', 'push', data, options);
   },
 };
 
diff --git a/src/RESTController.js b/src/RESTController.js
index 292cf90..85e18fc 100644
--- a/src/RESTController.js
+++ b/src/RESTController.js
@@ -122,6 +122,9 @@ const RESTController = {
               if ((xhr.getAllResponseHeaders() || '').includes('x-parse-job-status-id: ')) {
                 response = xhr.getResponseHeader('x-parse-job-status-id');
               }
+              if ((xhr.getAllResponseHeaders() || '').includes('x-parse-push-status-id: ')) {
+                response = xhr.getResponseHeader('x-parse-push-status-id');
+              }
             }
           } catch (e) {
             promise.reject(e.toString());


Test patch:
diff --git a/integration/test/ParsePushTest.js b/integration/test/ParsePushTest.js
new file mode 100644
index 0000000..e34913c
--- /dev/null
+++ b/integration/test/ParsePushTest.js
@@ -0,0 +1,15 @@
+'use strict';
+
+const Parse = require('../../node');
+
+describe('Parse Push', () => {
+  it('can get pushStatusId', async () => {
+    const payload = {
+      data: { alert: 'We return status!' },
+      where: { deviceType: { $eq: 'random' } },
+    };
+    const pushStatusId = await Parse.Push.send(payload, { useMasterKey: true });
+    const pushStatus = await Parse.Push.getPushStatus(pushStatusId, { useMasterKey: true });
+    expect(pushStatus.id).toBe(pushStatusId);
+  });
+});
diff --git a/integration/test/helper.js b/integration/test/helper.js
index 87786d8..3517b75 100644
--- a/integration/test/helper.js
+++ b/integration/test/helper.js
@@ -36,6 +36,12 @@ const defaultConfiguration = {
   },
   verbose: false,
   silent: true,
+  push: {
+    android: {
+      senderId: 'yolo',
+      apiKey: 'yolo',
+    },
+  },
   idempotencyOptions: {
     paths: ['functions/CloudFunctionIdempotency', 'jobs/CloudJob1', 'classes/IdempotentTest'],
     ttl: 120,
diff --git a/src/__tests__/Cloud-test.js b/src/__tests__/Cloud-test.js
index 370e639..b65bff2 100644
--- a/src/__tests__/Cloud-test.js
+++ b/src/__tests__/Cloud-test.js
@@ -14,9 +14,11 @@ jest.dontMock('../encode');
 jest.dontMock('../ParseError');
 jest.dontMock('../ParseObject');
 jest.dontMock('../ParseQuery');
+jest.dontMock('../Push');
 
 const Cloud = require('../Cloud');
 const CoreManager = require('../CoreManager');
+const Push = require('../Push');
 
 const defaultController = CoreManager.getCloudController();
 
@@ -304,4 +306,48 @@ describe('CloudController', () => {
     });
     expect(options.useMasterKey).toBe(true);
   });
+
+  it('can get push status', async () => {
+    const request = jest.fn();
+    request.mockReturnValue(
+      Promise.resolve({
+        results: [{ className: '_PushStatus', objectId: 'pushId1234' }],
+      })
+    );
+    CoreManager.setRESTController({ request: request, ajax: jest.fn() });
+
+    await Push.getPushStatus('pushId1234');
+    const [method, path, data, options] = request.mock.calls[0];
+    expect(method).toBe('GET');
+    expect(path).toBe('classes/_PushStatus');
+    expect(data).toEqual({
+      limit: 1,
+      where: {
+        objectId: 'pushId1234',
+      },
+    });
+    expect(options.useMasterKey).toBe(true);
+  });
+
+  it('can get push status with masterKey', async () => {
+    const request = jest.fn();
+    request.mockReturnValue(
+      Promise.resolve({
+        results: [{ className: '_PushStatus', objectId: 'pushId1234' }],
+      })
+    );
+    CoreManager.setRESTController({ request: request, ajax: jest.fn() });
+
+    await Push.getPushStatus('pushId1234', { useMasterKey: false });
+    const [method, path, data, options] = request.mock.calls[0];
+    expect(method).toBe('GET');
+    expect(path).toBe('classes/_PushStatus');
+    expect(data).toEqual({
+      limit: 1,
+      where: {
+        objectId: 'pushId1234',
+      },
+    });
+    expect(options.useMasterKey).toBe(false);
+  });
 });
diff --git a/src/__tests__/RESTController-test.js b/src/__tests__/RESTController-test.js
index cc6ffa8..950605f 100644
--- a/src/__tests__/RESTController-test.js
+++ b/src/__tests__/RESTController-test.js
@@ -242,6 +242,29 @@ describe('RESTController', () => {
     expect(response).toBe(1234);
   });
 
+  it('handles x-parse-push-status-id header', async () => {
+    const XHR = function () {};
+    XHR.prototype = {
+      open: function () {},
+      setRequestHeader: function () {},
+      getResponseHeader: function () {
+        return 1234;
+      },
+      send: function () {
+        this.status = 200;
+        this.responseText = '{}';
+        this.readyState = 4;
+        this.onreadystatechange();
+      },
+      getAllResponseHeaders: function () {
+        return 'x-parse-push-status-id: 1234';
+      },
+    };
+    RESTController._setXHR(XHR);
+    const response = await RESTController.request('POST', 'push', {}, {});
+    expect(response).toBe(1234);
+  });
+
   it('handles invalid header', async () => {
     const XHR = function () {};
     XHR.prototype = {