From 9a91f3a4a9c5adb3530394ca05b12c11758fb41e Mon Sep 17 00:00:00 2001 From: Yogendra Singh Date: Sun, 3 May 2020 07:42:44 +0530 Subject: [PATCH 1/4] add context to Parse.Object.fetch, Parse.Object.destroy, Parse.Object.saveAll, Parse.Query.get, Parse.Query.find, Parse.Query.first, Parse.Query.each --- src/ParseObject.js | 12 ++++++ src/ParseQuery.js | 16 ++++++++ src/__tests__/ParseObject-test.js | 64 +++++++++++++++++++++++++++++++ src/__tests__/ParseQuery-test.js | 20 ++++++++-- 4 files changed, 108 insertions(+), 4 deletions(-) diff --git a/src/ParseObject.js b/src/ParseObject.js index 5f78cb191..ad6235909 100644 --- a/src/ParseObject.js +++ b/src/ParseObject.js @@ -1084,6 +1084,7 @@ class ParseObject { * behalf of a specific user. *
  • include: The name(s) of the key(s) to include. Can be a string, an array of strings, * or an array of array of strings. + *
  • context: A dictionary that is accessible in Cloud Code `beforeFind` trigger. * * @return {Promise} A promise that is fulfilled when the fetch * completes. @@ -1097,6 +1098,9 @@ class ParseObject { if (options.hasOwnProperty('sessionToken')) { fetchOptions.sessionToken = options.sessionToken; } + if (options.hasOwnProperty('context') && typeof options.context === 'object') { + fetchOptions.context = options.context; + } if (options.hasOwnProperty('include')) { fetchOptions.include = []; if (Array.isArray(options.include)) { @@ -1273,6 +1277,7 @@ class ParseObject { * be used for this request. *
  • sessionToken: A valid session token, used for making a request on * behalf of a specific user. + *
  • context: A dictionary that is accessible in Cloud Code `beforeDelete` and `afterDelete` triggers. * * @return {Promise} A promise that is fulfilled when the destroy * completes. @@ -1286,6 +1291,9 @@ class ParseObject { if (options.hasOwnProperty('sessionToken')) { destroyOptions.sessionToken = options.sessionToken; } + if (options.hasOwnProperty('context') && typeof options.context === 'object') { + destroyOptions.context = options.context; + } if (!this.id) { return Promise.resolve(); } @@ -1674,6 +1682,7 @@ class ParseObject { *
  • sessionToken: A valid session token, used for making a request on * behalf of a specific user. *
  • batchSize: Number of objects to process per request + *
  • context: A dictionary that is accessible in Cloud Code `beforeDelete` and `afterDelete` triggers. * */ static saveAll(list: Array, options: RequestOptions = {}) { @@ -1687,6 +1696,9 @@ class ParseObject { if (options.hasOwnProperty('batchSize') && typeof options.batchSize === 'number') { saveOptions.batchSize = options.batchSize; } + if (options.hasOwnProperty('context') && typeof options.context === 'object') { + saveOptions.context = options.context; + } return CoreManager.getObjectController().save( list, saveOptions diff --git a/src/ParseQuery.js b/src/ParseQuery.js index 8e89e1ddd..c7926f670 100644 --- a/src/ParseQuery.js +++ b/src/ParseQuery.js @@ -564,6 +564,7 @@ class ParseQuery { * be used for this request. *
  • sessionToken: A valid session token, used for making a request on * behalf of a specific user. + *
  • context: A dictionary that is accessible in Cloud Code `beforeFind` trigger. * * * @return {Promise} A promise that is resolved with the result when @@ -579,6 +580,9 @@ class ParseQuery { if (options && options.hasOwnProperty('sessionToken')) { firstOptions.sessionToken = options.sessionToken; } + if (options && options.hasOwnProperty('context') && typeof options.context === 'object') { + firstOptions.context = options.context; + } return this.first(firstOptions).then((response) => { if (response) { @@ -604,6 +608,7 @@ class ParseQuery { * be used for this request. *
  • sessionToken: A valid session token, used for making a request on * behalf of a specific user. + *
  • context: A dictionary that is accessible in Cloud Code `beforeFind` trigger. * * * @return {Promise} A promise that is resolved with the results when @@ -619,6 +624,9 @@ class ParseQuery { if (options.hasOwnProperty('sessionToken')) { findOptions.sessionToken = options.sessionToken; } + if (options.hasOwnProperty('context') && typeof options.context === 'object') { + findOptions.context = options.context; + } this._setRequestTask(findOptions); const controller = CoreManager.getQueryController(); @@ -799,6 +807,7 @@ class ParseQuery { * be used for this request. *
  • sessionToken: A valid session token, used for making a request on * behalf of a specific user. + *
  • context: A dictionary that is accessible in Cloud Code `beforeFind` trigger. * * * @return {Promise} A promise that is resolved with the object when @@ -814,6 +823,9 @@ class ParseQuery { if (options.hasOwnProperty('sessionToken')) { findOptions.sessionToken = options.sessionToken; } + if (options.hasOwnProperty('context') && typeof options.context === 'object') { + findOptions.context = options.context; + } this._setRequestTask(findOptions); const controller = CoreManager.getQueryController(); @@ -871,6 +883,7 @@ class ParseQuery { * be used for this request. *
  • sessionToken: A valid session token, used for making a request on * behalf of a specific user. + *
  • context: A dictionary that is accessible in Cloud Code `beforeFind` trigger. * * @return {Promise} A promise that will be fulfilled once the * iteration has completed. @@ -921,6 +934,9 @@ class ParseQuery { if (options.hasOwnProperty('sessionToken')) { findOptions.sessionToken = options.sessionToken; } + if (options.hasOwnProperty('context') && typeof options.context === 'object') { + findOptions.context = options.context; + } let finished = false; return continueWhile(() => { diff --git a/src/__tests__/ParseObject-test.js b/src/__tests__/ParseObject-test.js index e055a9d0e..889de1487 100644 --- a/src/__tests__/ParseObject-test.js +++ b/src/__tests__/ParseObject-test.js @@ -1566,6 +1566,28 @@ describe('ParseObject', () => { await result; }); + it('accepts context on saveAll', async () => { + // Mock XHR + CoreManager.getRESTController()._setXHR( + mockXHR([{ + status: 200, + response: [{}] + }]) + ); + // Spy on REST controller + const controller = CoreManager.getRESTController(); + jest.spyOn(controller, 'ajax'); + // Save object + const context = {a: "a"}; + const obj = new ParseObject('Item'); + obj.id = 'pid'; + obj.set('test', 'value'); + await ParseObject.saveAll([obj], {context}) + // Validate + const jsonBody = JSON.parse(controller.ajax.mock.calls[0][2]); + expect(jsonBody._context).toEqual(context); + }); + it('can save a chain of unsaved objects', async () => { const xhrs = []; RESTController._setXHR(function() { @@ -1734,6 +1756,27 @@ describe('ParseObject', () => { await result; }); + it('accepts context on destroy', async () => { + // Mock XHR + CoreManager.getRESTController()._setXHR( + mockXHR([{ + status: 200, + response: {} + }]) + ); + // Spy on REST controller + const controller = CoreManager.getRESTController(); + jest.spyOn(controller, 'ajax'); + // Save object + const context = {a: "a"}; + const obj = new ParseObject('Item'); + obj.id = 'pid'; + await obj.destroy({context}); + // Validate + const jsonBody = JSON.parse(controller.ajax.mock.calls[0][2]); + expect(jsonBody._context).toEqual(context); + }); + it('can save an array of objects', async (done) => { const xhr = { setRequestHeader: jest.fn(), @@ -1980,6 +2023,27 @@ describe('ObjectController', () => { jest.runAllTicks(); }); + it('accepts context on fetch', async () => { + // Mock XHR + CoreManager.getRESTController()._setXHR( + mockXHR([{ + status: 200, + response: {} + }]) + ); + // Spy on REST controller + const controller = CoreManager.getRESTController(); + jest.spyOn(controller, 'ajax'); + // Save object + const context = {a: "a"}; + const obj = new ParseObject('Item'); + obj.id = 'pid'; + await obj.fetch({context}); + // Validate + const jsonBody = JSON.parse(controller.ajax.mock.calls[0][2]); + expect(jsonBody._context).toEqual(context); + }); + it('can fetch an array of objects', (done) => { const objectController = CoreManager.getObjectController(); const objects = []; diff --git a/src/__tests__/ParseQuery-test.js b/src/__tests__/ParseQuery-test.js index 66c907785..abe148f4c 100644 --- a/src/__tests__/ParseQuery-test.js +++ b/src/__tests__/ParseQuery-test.js @@ -1295,6 +1295,7 @@ describe('ParseQuery', () => { }); it('can pass options to a get() query', (done) => { + const context = {a: "a"}; CoreManager.setQueryController({ aggregate() {}, find(className, params, options) { @@ -1307,6 +1308,7 @@ describe('ParseQuery', () => { }); expect(options.useMasterKey).toEqual(true); expect(options.sessionToken).toEqual('1234'); + expect(options.context).toEqual(context); return Promise.resolve({ results: [ { objectId: 'I27', size: 'large', name: 'Product 27' } @@ -1318,7 +1320,8 @@ describe('ParseQuery', () => { const q = new ParseQuery('Item'); q.get('I27', { useMasterKey: true, - sessionToken: '1234' + sessionToken: '1234', + context: context }).then(() => { done(); }); @@ -1437,6 +1440,7 @@ describe('ParseQuery', () => { }); it('can pass options to find()', (done) => { + const context = {a: "a"}; CoreManager.setQueryController({ aggregate() {}, find(className, params, options) { @@ -1450,6 +1454,7 @@ describe('ParseQuery', () => { }); expect(options.useMasterKey).toEqual(true); expect(options.sessionToken).toEqual('1234'); + expect(options.context).toEqual(context); return Promise.resolve({ results: [] }); @@ -1460,7 +1465,8 @@ describe('ParseQuery', () => { q.containedIn('size', ['small', 'medium']) .find({ useMasterKey: true, - sessionToken: '1234' + sessionToken: '1234', + context: context }) .then((objs) => { expect(objs).toEqual([]); @@ -1692,6 +1698,7 @@ describe('ParseQuery', () => { }); it('can pass options to each()', (done) => { + const context = {a: "a"}; CoreManager.setQueryController({ aggregate() {}, find(className, params, options) { @@ -1709,6 +1716,7 @@ describe('ParseQuery', () => { }); expect(options.useMasterKey).toEqual(true); expect(options.sessionToken).toEqual('1234'); + expect(options.context).toEqual(context); return Promise.resolve({ results: [ { objectId: 'I55', size: 'medium', name: 'Product 55' }, @@ -1729,7 +1737,8 @@ describe('ParseQuery', () => { calls++; }, { useMasterKey: true, - sessionToken: '1234' + sessionToken: '1234', + context: context }).then(() => { expect(calls).toBe(3); done(); @@ -1738,6 +1747,7 @@ describe('ParseQuery', () => { it('can pass options to each() with hint', (done) => { + const context = {a: "a"}; CoreManager.setQueryController({ aggregate() {}, find(className, params, options) { @@ -1756,6 +1766,7 @@ describe('ParseQuery', () => { }); expect(options.useMasterKey).toEqual(true); expect(options.sessionToken).toEqual('1234'); + expect(options.context).toEqual(context); return Promise.resolve({ results: [ { objectId: 'I55', size: 'medium', name: 'Product 55' }, @@ -1777,7 +1788,8 @@ describe('ParseQuery', () => { calls++; }, { useMasterKey: true, - sessionToken: '1234' + sessionToken: '1234', + context: context }).then(() => { expect(calls).toBe(3); done(); From 30ad44ca5e6dcab4c4436afc564a170804e2e4c1 Mon Sep 17 00:00:00 2001 From: Yogendra Singh Date: Sun, 3 May 2020 10:35:27 +0530 Subject: [PATCH 2/4] add context for Parse.Cloud.run --- src/Cloud.js | 3 +++ src/__tests__/Cloud-test.js | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/Cloud.js b/src/Cloud.js index 612cf02c8..0a5015e0d 100644 --- a/src/Cloud.js +++ b/src/Cloud.js @@ -57,6 +57,9 @@ export function run( if (options.sessionToken) { requestOptions.sessionToken = options.sessionToken; } + if (options.context && typeof options.context === 'object') { + requestOptions.context = options.context; + } return CoreManager.getCloudController().run(name, data, requestOptions); } diff --git a/src/__tests__/Cloud-test.js b/src/__tests__/Cloud-test.js index 2a5b9bfda..82601ec09 100644 --- a/src/__tests__/Cloud-test.js +++ b/src/__tests__/Cloud-test.js @@ -14,6 +14,7 @@ jest.dontMock('../encode'); const Cloud = require('../Cloud'); const CoreManager = require('../CoreManager'); +const mockXHR = require('./test_helpers/mockXHR'); const defaultController = CoreManager.getCloudController(); @@ -205,4 +206,21 @@ describe('CloudController', () => { expect(CoreManager.getRESTController().request.mock.calls[0]) .toEqual(['GET', 'cloud_code/jobs/data', null, { useMasterKey: true }]); }); + + it('accepts context on cloud function call', async () => { + const request = jest.fn(); + request.mockReturnValue(Promise.resolve(undefined)); + + const ajax = jest.fn(); + CoreManager.setRESTController({ request: request, ajax: ajax }); + + // Spy on REST controller + const controller = CoreManager.getRESTController(); + jest.spyOn(controller, 'request'); + // Save object + const context = {a: "a"}; + await Cloud.run('myfunction', {}, { context: context }); + // Validate + expect(controller.request.mock.calls[0][3].context).toEqual(context); + }); }); From 0a920b9d20c3e62f5470fd92f4ff08fc1f05b189 Mon Sep 17 00:00:00 2001 From: Yogendra Singh Date: Sun, 3 May 2020 10:53:39 +0530 Subject: [PATCH 3/4] build fix --- src/__tests__/Cloud-test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/__tests__/Cloud-test.js b/src/__tests__/Cloud-test.js index 82601ec09..b6f7b98ab 100644 --- a/src/__tests__/Cloud-test.js +++ b/src/__tests__/Cloud-test.js @@ -14,7 +14,6 @@ jest.dontMock('../encode'); const Cloud = require('../Cloud'); const CoreManager = require('../CoreManager'); -const mockXHR = require('./test_helpers/mockXHR'); const defaultController = CoreManager.getCloudController(); From 1e479bdc80b6928b6dc6ba6b133bc68e20506d4d Mon Sep 17 00:00:00 2001 From: Yogendra Singh Date: Sun, 3 May 2020 18:12:24 +0530 Subject: [PATCH 4/4] pass context in Parse.Object.destroyAll --- src/ParseObject.js | 6 +++++- src/__tests__/ParseObject-test.js | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/ParseObject.js b/src/ParseObject.js index ad6235909..3ae07cc78 100644 --- a/src/ParseObject.js +++ b/src/ParseObject.js @@ -1639,6 +1639,7 @@ class ParseObject { *
  • sessionToken: A valid session token, used for making a request on * behalf of a specific user. *
  • batchSize: Number of objects to process per request + *
  • context: A dictionary that is accessible in Cloud Code `beforeDelete` and `afterDelete` triggers. * * @return {Promise} A promise that is fulfilled when the destroyAll * completes. @@ -1654,6 +1655,9 @@ class ParseObject { if (options.hasOwnProperty('batchSize') && typeof options.batchSize === 'number') { destroyOptions.batchSize = options.batchSize; } + if (options.hasOwnProperty('context') && typeof options.context === 'object') { + destroyOptions.context = options.context; + } return CoreManager.getObjectController().destroy( list, destroyOptions @@ -1682,7 +1686,7 @@ class ParseObject { *
  • sessionToken: A valid session token, used for making a request on * behalf of a specific user. *
  • batchSize: Number of objects to process per request - *
  • context: A dictionary that is accessible in Cloud Code `beforeDelete` and `afterDelete` triggers. + *
  • context: A dictionary that is accessible in Cloud Code `beforeSave` and `afterSave` triggers. * */ static saveAll(list: Array, options: RequestOptions = {}) { diff --git a/src/__tests__/ParseObject-test.js b/src/__tests__/ParseObject-test.js index 889de1487..d487e9148 100644 --- a/src/__tests__/ParseObject-test.js +++ b/src/__tests__/ParseObject-test.js @@ -1588,6 +1588,27 @@ describe('ParseObject', () => { expect(jsonBody._context).toEqual(context); }); + it('accepts context on destroyAll', async () => { + // Mock XHR + CoreManager.getRESTController()._setXHR( + mockXHR([{ + status: 200, + response: [{}] + }]) + ); + // Spy on REST controller + const controller = CoreManager.getRESTController(); + jest.spyOn(controller, 'ajax'); + // Save object + const context = {a: "a"}; + const obj = new ParseObject('Item'); + obj.id = 'pid'; + await ParseObject.destroyAll([obj], { context: context }) + // Validate + const jsonBody = JSON.parse(controller.ajax.mock.calls[0][2]); + expect(jsonBody._context).toEqual(context); + }); + it('can save a chain of unsaved objects', async () => { const xhrs = []; RESTController._setXHR(function() {