From ab5be44238f81ad436aa01f8242a8582f802b184 Mon Sep 17 00:00:00 2001 From: dblythy Date: Sun, 5 Jun 2022 20:05:41 +1000 Subject: [PATCH 1/3] fix: prevent LiveQuery override with query.select --- src/LiveQueryClient.js | 5 +++- src/__tests__/LiveQueryClient-test.js | 37 +++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/LiveQueryClient.js b/src/LiveQueryClient.js index 3066ac1c6..2210dee4d 100644 --- a/src/LiveQueryClient.js +++ b/src/LiveQueryClient.js @@ -419,7 +419,10 @@ class LiveQueryClient extends EventEmitter { data.original = ParseObject.fromJSON(data.original, false); } delete data.object.__type; - const parseObject = ParseObject.fromJSON(data.object, override); + const parseObject = ParseObject.fromJSON( + data.object, + !(subscription.query && subscription.query._select) ? override : false + ); if (data.original) { subscription.emit(data.op, parseObject, data.original, response); diff --git a/src/__tests__/LiveQueryClient-test.js b/src/__tests__/LiveQueryClient-test.js index 812ebb978..4329bcd01 100644 --- a/src/__tests__/LiveQueryClient-test.js +++ b/src/__tests__/LiveQueryClient-test.js @@ -522,6 +522,43 @@ describe('LiveQueryClient', () => { spy.mockRestore(); }); + it('can handle select in websocket payload', () => { + const liveQueryClient = new LiveQueryClient({ + applicationId: 'applicationId', + serverURL: 'ws://test', + javascriptKey: 'javascriptKey', + masterKey: 'masterKey', + sessionToken: 'sessionToken', + }); + // Add mock subscription + const subscription = new events.EventEmitter(); + subscription.query = new ParseQuery('Test').select('foo'); + liveQueryClient.subscriptions.set(1, subscription); + const object = new ParseObject('Test'); + const original = new ParseObject('Test'); + object.set('key', 'value'); + original.set('key', 'old'); + const data = { + op: 'update', + clientId: 1, + requestId: 1, + object: object._toFullJSON(), + original: original._toFullJSON(), + }; + const event = { + data: JSON.stringify(data), + }; + + const spy = jest + .spyOn(ParseObject, 'fromJSON') + .mockImplementationOnce(() => original) + .mockImplementationOnce(() => object); + + liveQueryClient._handleWebSocketMessage(event); + expect(ParseObject.fromJSON.mock.calls[1][1]).toEqual(false); + spy.mockRestore(); + }); + it('can handle WebSocket response unset field', async () => { const liveQueryClient = new LiveQueryClient({ applicationId: 'applicationId', From 3073b777396441e36ef69b113fa0f82854bffe3e Mon Sep 17 00:00:00 2001 From: dblythy Date: Sun, 5 Jun 2022 21:31:10 +1000 Subject: [PATCH 2/3] fix: allow `Parse.Cloud.beforeSubscribe` rejections to be caught by query.subscribe --- integration/test/ParseLiveQueryTest.js | 27 ++++++++++++++++++++++++ integration/test/cloud/before_connect.js | 3 +++ integration/test/cloud/main.js | 4 ++++ src/LiveQueryClient.js | 16 ++++++++++++-- 4 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 integration/test/cloud/before_connect.js diff --git a/integration/test/ParseLiveQueryTest.js b/integration/test/ParseLiveQueryTest.js index d3089f691..56d78a735 100644 --- a/integration/test/ParseLiveQueryTest.js +++ b/integration/test/ParseLiveQueryTest.js @@ -256,4 +256,31 @@ describe('Parse LiveQuery', () => { object.set({ foo: 'bar' }); await object.save(); }); + + it('live query can handle beforeConnect and beforeSubscribe errors', async () => { + const client = new Parse.LiveQueryClient({ + applicationId: 'integration', + serverURL: 'ws://localhost:1337', + javascriptKey: null, + masterKey: null, + sessionToken: null, + installationId: null, + }); + client.open(); + const query = new Parse.Query('TestError'); + let subscription = client.subscribe(query); + await expectAsync(subscription.subscribePromise).toBeRejectedWith( + new Parse.Error(141, 'not allowed to subscribe') + ); + client.close(); + await reconfigureServer({ + cloud: `${__dirname}/cloud/before_connect.js`, + }); + client.open(); + subscription = client.subscribe(query); + await expectAsync(subscription.subscribePromise).toBeRejectedWith( + new Parse.Error(141, 'not allowed to connect') + ); + client.close(); + }); }); diff --git a/integration/test/cloud/before_connect.js b/integration/test/cloud/before_connect.js new file mode 100644 index 000000000..e334bdc53 --- /dev/null +++ b/integration/test/cloud/before_connect.js @@ -0,0 +1,3 @@ +Parse.Cloud.beforeConnect(() => { + throw 'not allowed to connect'; +}); diff --git a/integration/test/cloud/main.js b/integration/test/cloud/main.js index 920948d83..063986120 100644 --- a/integration/test/cloud/main.js +++ b/integration/test/cloud/main.js @@ -48,3 +48,7 @@ Parse.Cloud.job('CloudJob2', function () { Parse.Cloud.job('CloudJobFailing', function () { throw 'cloud job failed'; }); + +Parse.Cloud.beforeSubscribe('TestError', () => { + throw 'not allowed to subscribe'; +}); diff --git a/src/LiveQueryClient.js b/src/LiveQueryClient.js index 2210dee4d..198c2d3b0 100644 --- a/src/LiveQueryClient.js +++ b/src/LiveQueryClient.js @@ -14,6 +14,7 @@ import EventEmitter from './EventEmitter'; import ParseObject from './ParseObject'; import LiveQuerySubscription from './LiveQuerySubscription'; import { resolvingPromise } from './promiseUtils'; +import ParseError from './ParseError'; // The LiveQuery client inner state const CLIENT_STATE = { @@ -218,6 +219,10 @@ class LiveQueryClient extends EventEmitter { this.subscriptions.set(this.requestId, subscription); this.requestId += 1; this.connectPromise.then(() => { + if (this.connectError) { + subscription.subscribePromise.reject(this.connectError); + return; + } this.socket.send(JSON.stringify(subscribeRequest)); }); @@ -382,10 +387,16 @@ class LiveQueryClient extends EventEmitter { setTimeout(() => subscription.emit(SUBSCRIPTION_EMMITER_TYPES.OPEN, response), 200); } break; - case OP_EVENTS.ERROR: + case OP_EVENTS.ERROR: { + const parseError = new ParseError(data.code, data.error); + if (!this.id) { + this.connectError = parseError; + this.connectPromise.resolve(); + this.state = CLIENT_STATE.DISCONNECTED; + } if (data.requestId) { if (subscription) { - subscription.subscribePromise.resolve(); + subscription.subscribePromise.reject(parseError); setTimeout(() => subscription.emit(SUBSCRIPTION_EMMITER_TYPES.ERROR, data.error), 200); } } else { @@ -398,6 +409,7 @@ class LiveQueryClient extends EventEmitter { this._handleReconnect(); } break; + } case OP_EVENTS.UNSUBSCRIBED: // We have already deleted subscription in unsubscribe(), do nothing here break; From 498d226636bca77d291adfac9e5c12c4dcc6e38a Mon Sep 17 00:00:00 2001 From: dblythy Date: Sun, 5 Jun 2022 21:37:11 +1000 Subject: [PATCH 3/3] Revert "fix: allow `Parse.Cloud.beforeSubscribe` rejections to be caught by query.subscribe" This reverts commit 3073b777396441e36ef69b113fa0f82854bffe3e. --- integration/test/ParseLiveQueryTest.js | 27 ------------------------ integration/test/cloud/before_connect.js | 3 --- integration/test/cloud/main.js | 4 ---- src/LiveQueryClient.js | 16 ++------------ 4 files changed, 2 insertions(+), 48 deletions(-) delete mode 100644 integration/test/cloud/before_connect.js diff --git a/integration/test/ParseLiveQueryTest.js b/integration/test/ParseLiveQueryTest.js index 56d78a735..d3089f691 100644 --- a/integration/test/ParseLiveQueryTest.js +++ b/integration/test/ParseLiveQueryTest.js @@ -256,31 +256,4 @@ describe('Parse LiveQuery', () => { object.set({ foo: 'bar' }); await object.save(); }); - - it('live query can handle beforeConnect and beforeSubscribe errors', async () => { - const client = new Parse.LiveQueryClient({ - applicationId: 'integration', - serverURL: 'ws://localhost:1337', - javascriptKey: null, - masterKey: null, - sessionToken: null, - installationId: null, - }); - client.open(); - const query = new Parse.Query('TestError'); - let subscription = client.subscribe(query); - await expectAsync(subscription.subscribePromise).toBeRejectedWith( - new Parse.Error(141, 'not allowed to subscribe') - ); - client.close(); - await reconfigureServer({ - cloud: `${__dirname}/cloud/before_connect.js`, - }); - client.open(); - subscription = client.subscribe(query); - await expectAsync(subscription.subscribePromise).toBeRejectedWith( - new Parse.Error(141, 'not allowed to connect') - ); - client.close(); - }); }); diff --git a/integration/test/cloud/before_connect.js b/integration/test/cloud/before_connect.js deleted file mode 100644 index e334bdc53..000000000 --- a/integration/test/cloud/before_connect.js +++ /dev/null @@ -1,3 +0,0 @@ -Parse.Cloud.beforeConnect(() => { - throw 'not allowed to connect'; -}); diff --git a/integration/test/cloud/main.js b/integration/test/cloud/main.js index 063986120..920948d83 100644 --- a/integration/test/cloud/main.js +++ b/integration/test/cloud/main.js @@ -48,7 +48,3 @@ Parse.Cloud.job('CloudJob2', function () { Parse.Cloud.job('CloudJobFailing', function () { throw 'cloud job failed'; }); - -Parse.Cloud.beforeSubscribe('TestError', () => { - throw 'not allowed to subscribe'; -}); diff --git a/src/LiveQueryClient.js b/src/LiveQueryClient.js index 198c2d3b0..2210dee4d 100644 --- a/src/LiveQueryClient.js +++ b/src/LiveQueryClient.js @@ -14,7 +14,6 @@ import EventEmitter from './EventEmitter'; import ParseObject from './ParseObject'; import LiveQuerySubscription from './LiveQuerySubscription'; import { resolvingPromise } from './promiseUtils'; -import ParseError from './ParseError'; // The LiveQuery client inner state const CLIENT_STATE = { @@ -219,10 +218,6 @@ class LiveQueryClient extends EventEmitter { this.subscriptions.set(this.requestId, subscription); this.requestId += 1; this.connectPromise.then(() => { - if (this.connectError) { - subscription.subscribePromise.reject(this.connectError); - return; - } this.socket.send(JSON.stringify(subscribeRequest)); }); @@ -387,16 +382,10 @@ class LiveQueryClient extends EventEmitter { setTimeout(() => subscription.emit(SUBSCRIPTION_EMMITER_TYPES.OPEN, response), 200); } break; - case OP_EVENTS.ERROR: { - const parseError = new ParseError(data.code, data.error); - if (!this.id) { - this.connectError = parseError; - this.connectPromise.resolve(); - this.state = CLIENT_STATE.DISCONNECTED; - } + case OP_EVENTS.ERROR: if (data.requestId) { if (subscription) { - subscription.subscribePromise.reject(parseError); + subscription.subscribePromise.resolve(); setTimeout(() => subscription.emit(SUBSCRIPTION_EMMITER_TYPES.ERROR, data.error), 200); } } else { @@ -409,7 +398,6 @@ class LiveQueryClient extends EventEmitter { this._handleReconnect(); } break; - } case OP_EVENTS.UNSUBSCRIBED: // We have already deleted subscription in unsubscribe(), do nothing here break;