From 9092874a9a482a24dfdce1dce56615702999d6b8 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 26 Jan 2023 20:49:03 +1100 Subject: [PATCH] feat: Upgrade to Parse JavaScript SDK 4 (#8332) --- package-lock.json | 51 +++++----- package.json | 2 +- spec/ParseLiveQuery.spec.js | 179 ++++++++++++++---------------------- 3 files changed, 98 insertions(+), 134 deletions(-) diff --git a/package-lock.json b/package-lock.json index 685756efd2..0b1cfc3a47 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,7 +39,7 @@ "mime": "3.0.0", "mongodb": "4.10.0", "mustache": "4.2.0", - "parse": "3.4.2", + "parse": "4.0.0", "path-to-regexp": "0.1.7", "pg-monitor": "1.5.0", "pg-promise": "10.12.1", @@ -1771,9 +1771,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz", - "integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.0.tgz", + "integrity": "sha512-YMQvx/6nKEaucl0MY56mwIG483xk8SDNdlUwb2Ts6FUpr7fm85DxEmsY18LXBNhcTz6tO6JwZV8w1W06v8UKeg==", "dependencies": { "regenerator-runtime": "^0.13.4" }, @@ -16116,18 +16116,21 @@ } }, "node_modules/parse": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/parse/-/parse-3.4.2.tgz", - "integrity": "sha512-Ruehcp/S7eB3A0lDG5eAPvZHa5pABCbUR+lMJL2gUNKJLZNcD9/s3RL255PwI5jTqa+TCJ7MdPqobUplouN1pQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse/-/parse-4.0.0.tgz", + "integrity": "sha512-LKaHHqSLulEv1f76Cg3eJlDQL8FtK+mn6XehVbjY7uUGwm2B3LyzYjBH6iX0BXOFwfABcDRtq+8KCpuHXiIQ3g==", "dependencies": { - "@babel/runtime": "7.17.9", + "@babel/runtime": "7.18.0", "@babel/runtime-corejs3": "7.17.8", "idb-keyval": "6.0.3", "react-native-crypto-js": "1.0.0", "uuid": "3.4.0", - "ws": "7.5.1", + "ws": "8.6.0", "xmlhttprequest": "1.8.0" }, + "engines": { + "node": ">=14.21.0 <17 || >=18 <19" + }, "optionalDependencies": { "crypto-js": "4.1.1" } @@ -16169,11 +16172,11 @@ } }, "node_modules/parse/node_modules/ws": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.1.tgz", - "integrity": "sha512-2c6faOUH/nhoQN6abwMloF7Iyl0ZS2E9HGtsiLrWn0zOOMWlhtDmdf/uihDt6jnuCxgtwGBNy6Onsoy2s2O2Ow==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz", + "integrity": "sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==", "engines": { - "node": ">=8.3.0" + "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", @@ -21881,9 +21884,9 @@ } }, "@babel/runtime": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz", - "integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.0.tgz", + "integrity": "sha512-YMQvx/6nKEaucl0MY56mwIG483xk8SDNdlUwb2Ts6FUpr7fm85DxEmsY18LXBNhcTz6tO6JwZV8w1W06v8UKeg==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -32871,17 +32874,17 @@ } }, "parse": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/parse/-/parse-3.4.2.tgz", - "integrity": "sha512-Ruehcp/S7eB3A0lDG5eAPvZHa5pABCbUR+lMJL2gUNKJLZNcD9/s3RL255PwI5jTqa+TCJ7MdPqobUplouN1pQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse/-/parse-4.0.0.tgz", + "integrity": "sha512-LKaHHqSLulEv1f76Cg3eJlDQL8FtK+mn6XehVbjY7uUGwm2B3LyzYjBH6iX0BXOFwfABcDRtq+8KCpuHXiIQ3g==", "requires": { - "@babel/runtime": "7.17.9", + "@babel/runtime": "7.18.0", "@babel/runtime-corejs3": "7.17.8", "crypto-js": "4.1.1", "idb-keyval": "6.0.3", "react-native-crypto-js": "1.0.0", "uuid": "3.4.0", - "ws": "7.5.1", + "ws": "8.6.0", "xmlhttprequest": "1.8.0" }, "dependencies": { @@ -32891,9 +32894,9 @@ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" }, "ws": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.1.tgz", - "integrity": "sha512-2c6faOUH/nhoQN6abwMloF7Iyl0ZS2E9HGtsiLrWn0zOOMWlhtDmdf/uihDt6jnuCxgtwGBNy6Onsoy2s2O2Ow==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz", + "integrity": "sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==", "requires": {} } } diff --git a/package.json b/package.json index 9224154519..ce56872b36 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "mime": "3.0.0", "mongodb": "4.10.0", "mustache": "4.2.0", - "parse": "3.4.2", + "parse": "4.0.0", "path-to-regexp": "0.1.7", "pg-monitor": "1.5.0", "pg-promise": "10.12.1", diff --git a/spec/ParseLiveQuery.spec.js b/spec/ParseLiveQuery.spec.js index fbac779ce8..38259f50d0 100644 --- a/spec/ParseLiveQuery.spec.js +++ b/spec/ParseLiveQuery.spec.js @@ -7,6 +7,15 @@ const validatorFail = () => { }; describe('ParseLiveQuery', function () { + beforeEach(() => { + Parse.CoreManager.getLiveQueryController().setDefaultLiveQueryClient(null); + }); + afterEach(async () => { + const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); + client.close(); + // Wait for live query client to disconnect + await new Promise(resolve => setTimeout(resolve, 1000)); + }); it('access user on onLiveQueryEvent disconnect', async done => { await reconfigureServer({ liveQuery: { @@ -16,7 +25,6 @@ describe('ParseLiveQuery', function () { verbose: false, silent: true, }); - Parse.CoreManager.getLiveQueryController().setDefaultLiveQueryClient(null); const requestedUser = new Parse.User(); requestedUser.setUsername('username'); requestedUser.setPassword('password'); @@ -513,76 +521,67 @@ describe('ParseLiveQuery', function () { classNames: ['TestObject'], }, startLiveQueryServer: true, - verbose: false, - silent: true, }); const object = new TestObject(); await object.save(); - - Parse.Cloud.beforeSubscribe('TestObject', req => { - expect(req.op).toBe('subscribe'); - expect(req.requestId).toBe(1); - expect(req.query).toBeDefined(); - expect(req.user).toBeUndefined(); - }); - - Parse.Cloud.beforeConnect(req => { - expect(req.event).toBe('connect'); - expect(req.clients).toBe(0); - expect(req.subscriptions).toBe(0); - expect(req.useMasterKey).toBe(false); - expect(req.installationId).toBeDefined(); - expect(req.user).toBeUndefined(); - expect(req.client).toBeDefined(); - }); + const hooks = { + beforeSubscribe(req) { + expect(req.op).toBe('subscribe'); + expect(req.requestId).toBe(1); + expect(req.query).toBeDefined(); + expect(req.user).toBeUndefined(); + }, + beforeConnect(req) { + expect(req.event).toBe('connect'); + expect(req.clients).toBe(0); + expect(req.subscriptions).toBe(0); + expect(req.useMasterKey).toBe(false); + expect(req.installationId).toBeDefined(); + expect(req.user).toBeUndefined(); + expect(req.client).toBeDefined(); + }, + }; + spyOn(hooks, 'beforeSubscribe').and.callThrough(); + spyOn(hooks, 'beforeConnect').and.callThrough(); + Parse.Cloud.beforeSubscribe('TestObject', hooks.beforeSubscribe); + Parse.Cloud.beforeConnect(hooks.beforeConnect); const query = new Parse.Query(TestObject); query.equalTo('objectId', object.id); const subscription = await query.subscribe(); subscription.on('update', object => { expect(object.get('foo')).toBe('bar'); + expect(hooks.beforeConnect).toHaveBeenCalled(); + expect(hooks.beforeSubscribe).toHaveBeenCalled(); done(); }); object.set({ foo: 'bar' }); await object.save(); }); - it('can handle beforeConnect validation function', async done => { + it('can handle beforeConnect validation function', async () => { await reconfigureServer({ liveQuery: { classNames: ['TestObject'], }, startLiveQueryServer: true, - verbose: false, - silent: true, }); const object = new TestObject(); await object.save(); - Parse.Cloud.beforeConnect(() => {}, validatorFail); - let complete = false; - Parse.LiveQuery.on('error', error => { - Parse.LiveQuery.removeAllListeners('error'); - if (complete) { - return; - } - complete = true; - expect(error).toBe('you are not authorized'); - done(); - }); const query = new Parse.Query(TestObject); query.equalTo('objectId', object.id); - await query.subscribe(); + await expectAsync(query.subscribe()).toBeRejectedWith( + new Parse.Error(Parse.Error.VALIDATION_ERROR, 'you are not authorized') + ); }); - it('can handle beforeSubscribe validation function', async done => { + it('can handle beforeSubscribe validation function', async () => { await reconfigureServer({ liveQuery: { classNames: ['TestObject'], }, startLiveQueryServer: true, - verbose: false, - silent: true, }); const object = new TestObject(); await object.save(); @@ -590,11 +589,9 @@ describe('ParseLiveQuery', function () { Parse.Cloud.beforeSubscribe(TestObject, () => {}, validatorFail); const query = new Parse.Query(TestObject); query.equalTo('objectId', object.id); - const subscription = await query.subscribe(); - subscription.on('error', error => { - expect(error).toBe('you are not authorized'); - done(); - }); + await expectAsync(query.subscribe()).toBeRejectedWith( + new Parse.Error(Parse.Error.VALIDATION_ERROR, 'you are not authorized') + ); }); it('can handle afterEvent validation function', async done => { @@ -620,14 +617,12 @@ describe('ParseLiveQuery', function () { await object.save(); }); - it('can handle beforeConnect error', async done => { + it('can handle beforeConnect error', async () => { await reconfigureServer({ liveQuery: { classNames: ['TestObject'], }, startLiveQueryServer: true, - verbose: false, - silent: true, }); const object = new TestObject(); await object.save(); @@ -635,14 +630,9 @@ describe('ParseLiveQuery', function () { Parse.Cloud.beforeConnect(() => { throw new Error('You shall not pass!'); }); - Parse.LiveQuery.on('error', error => { - Parse.LiveQuery.removeAllListeners('error'); - expect(error).toBe('You shall not pass!'); - done(); - }); const query = new Parse.Query(TestObject); query.equalTo('objectId', object.id); - await query.subscribe(); + await expectAsync(query.subscribe()).toBeRejectedWith(new Error('You shall not pass!')); }); it('can log on beforeConnect throw', async () => { @@ -651,8 +641,6 @@ describe('ParseLiveQuery', function () { classNames: ['TestObject'], }, startLiveQueryServer: true, - verbose: false, - silent: true, }); const logger = require('../lib/logger').logger; @@ -664,22 +652,20 @@ describe('ParseLiveQuery', function () { foo.bar(); /* eslint-enable no-undef */ }); - new Parse.Query(TestObject).subscribe(); - await new Promise(resolve => Parse.LiveQuery.on('error', resolve)); - Parse.LiveQuery.removeAllListeners('error'); + await expectAsync(new Parse.Query(TestObject).subscribe()).toBeRejectedWith( + new Error('foo is not defined') + ); expect(logger.error).toHaveBeenCalledWith( `Failed running beforeConnect for session ${token} with:\n Error: {"message":"foo is not defined","code":141}` ); }); - it('can handle beforeSubscribe error', async done => { + it('can handle beforeSubscribe error', async () => { await reconfigureServer({ liveQuery: { classNames: ['TestObject'], }, startLiveQueryServer: true, - verbose: false, - silent: true, }); const object = new TestObject(); await object.save(); @@ -687,17 +673,9 @@ describe('ParseLiveQuery', function () { Parse.Cloud.beforeSubscribe(TestObject, () => { throw new Error('You shall not subscribe!'); }); - Parse.LiveQuery.on('error', error => { - expect(error).toBe('You shall not subscribe!'); - }); const query = new Parse.Query(TestObject); query.equalTo('objectId', object.id); - const subscription = await query.subscribe(); - subscription.on('error', error => { - Parse.LiveQuery.removeAllListeners('error'); - expect(error).toBe('You shall not subscribe!'); - done(); - }); + await expectAsync(query.subscribe()).toBeRejectedWith(new Error('You shall not subscribe!')); }); it('can log on beforeSubscribe error', async () => { @@ -706,8 +684,6 @@ describe('ParseLiveQuery', function () { classNames: ['TestObject'], }, startLiveQueryServer: true, - verbose: false, - silent: true, }); const logger = require('../lib/logger').logger; @@ -720,8 +696,7 @@ describe('ParseLiveQuery', function () { }); const query = new Parse.Query(TestObject); - const subscription = await query.subscribe(); - await new Promise(resolve => subscription.on('error', resolve)); + await expectAsync(query.subscribe()).toBeRejectedWith(new Error('foo is not defined')); expect(logger.error).toHaveBeenCalledWith( `Failed running beforeSubscribe on TestObject for session undefined with:\n Error: {"message":"foo is not defined","code":141}` @@ -734,29 +709,35 @@ describe('ParseLiveQuery', function () { classNames: ['TestObject'], }, startLiveQueryServer: true, - verbose: false, - silent: true, - }); - Parse.Cloud.beforeSubscribe(TestObject, request => { - const query = request.query; - query.equalTo('yolo', 'abc'); }); - + const hook = { + beforeSubscribe(request) { + request.query.equalTo('yolo', 'abc'); + }, + }; + spyOn(hook, 'beforeSubscribe').and.callThrough(); + Parse.Cloud.beforeSubscribe('TestObject', hook.beforeSubscribe); const object = new TestObject(); await object.save(); - const query = new Parse.Query(TestObject); + const query = new Parse.Query('TestObject'); query.equalTo('objectId', object.id); const subscription = await query.subscribe(); - subscription.on('update', () => { - fail(); + fail('beforeSubscribe should restrict subscription'); }); - object.set({ foo: 'bar' }); + subscription.on('enter', object => { + if (object.get('yolo') === 'abc') { + done(); + } else { + fail('beforeSubscribe should restrict queries'); + } + }); + object.set({ yolo: 'bar' }); await object.save(); - setTimeout(async () => { - done(); - }, 1000); + object.set({ yolo: 'abc' }); + await object.save(); + expect(hook.beforeSubscribe).toHaveBeenCalled(); }); it('can return a new beforeSubscribe query', async done => { @@ -954,26 +935,15 @@ describe('ParseLiveQuery', function () { await Parse.User.logIn('username', 'password'); }); - it('prevent liveQuery on Session class when not logged in', async done => { + it('prevent liveQuery on Session class when not logged in', async () => { await reconfigureServer({ liveQuery: { classNames: [Parse.Session], }, startLiveQueryServer: true, - verbose: false, - silent: true, - }); - - Parse.LiveQuery.on('error', error => { - expect(error).toBe('Invalid session token'); }); const query = new Parse.Query(Parse.Session); - const subscription = await query.subscribe(); - subscription.on('error', error => { - Parse.LiveQuery.removeAllListeners('error'); - expect(error).toBe('Invalid session token'); - done(); - }); + await expectAsync(query.subscribe()).toBeRejectedWith(new Error('Invalid session token')); }); it('handle invalid websocket payload length', async done => { @@ -1242,13 +1212,4 @@ describe('ParseLiveQuery', function () { object.set({ location: secondPoint }); await object.save(); }); - - afterEach(async function (done) { - const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); - client.close(); - // Wait for live query client to disconnect - setTimeout(() => { - done(); - }, 1000); - }); });