From 41daddbcb6b7374ff79132aeccce192dd9217226 Mon Sep 17 00:00:00 2001 From: Samuel Denis-D'Ortun Date: Sat, 5 Jun 2021 21:50:13 -0400 Subject: [PATCH 1/7] Added beforeUnsubscribe Trigger --- spec/ParseLiveQuery.spec.js | 32 +++++++++++++++++++++ src/LiveQuery/ParseLiveQueryServer.js | 20 ++++++++++++-- src/cloud-code/Parse.Cloud.js | 40 +++++++++++++++++++++++++++ src/triggers.js | 1 + 4 files changed, 90 insertions(+), 3 deletions(-) diff --git a/spec/ParseLiveQuery.spec.js b/spec/ParseLiveQuery.spec.js index 38259f50d0..8469223db1 100644 --- a/spec/ParseLiveQuery.spec.js +++ b/spec/ParseLiveQuery.spec.js @@ -798,6 +798,38 @@ describe('ParseLiveQuery', function () { await object.save(); }); + it('can handle select beforeUnsubscribe trigger', async done => { + await reconfigureServer({ + liveQuery: { + classNames: ['TestObject'], + }, + startLiveQueryServer: true, + verbose: false, + silent: true, + }); + + Parse.Cloud.beforeSubscribe(TestObject, request => { + expect(request.requestId).toBe(1); + }); + + Parse.Cloud.beforeUnsubscribe(TestObject, request => { + expect(request.requestId).toBe(1); + done(); + }); + + const object = new TestObject(); + await object.save(); + + const query = new Parse.Query(TestObject); + query.equalTo('objectId', object.id); + const subscription = await query.subscribe(); + + object.set({ foo: 'bar', yolo: 'abc' }); + await object.save(); + + await subscription.unsubscribe(); + }); + it('LiveQuery with ACL', async () => { await reconfigureServer({ liveQuery: { diff --git a/src/LiveQuery/ParseLiveQueryServer.js b/src/LiveQuery/ParseLiveQueryServer.js index 0b71265f33..08ab9916cd 100644 --- a/src/LiveQuery/ParseLiveQueryServer.js +++ b/src/LiveQuery/ParseLiveQueryServer.js @@ -953,7 +953,7 @@ class ParseLiveQueryServer { this._handleSubscribe(parseWebsocket, request); } - _handleUnsubscribe(parseWebsocket: any, request: any, notifyClient: boolean = true): any { + async _handleUnsubscribe(parseWebsocket: any, request: any, notifyClient: boolean = true): any { // If we can not find this client, return error to client if (!Object.prototype.hasOwnProperty.call(parseWebsocket, 'clientId')) { Client.pushError( @@ -1000,11 +1000,25 @@ class ParseLiveQueryServer { return; } + const subscription = subscriptionInfo.subscription; + const className = subscription.className; + const trigger = getTrigger(className, 'beforeUnsubscribe', Parse.applicationId); + if (trigger) { + const auth = await this.getAuthFromClient(client, request.requestId, request.sessionToken); + if (auth && auth.user) { + request.user = auth.user; + } + + request.sessionToken = subscriptionInfo.sessionToken; + request.useMasterKey = client.hasMasterKey; + request.installationId = client.installationId; + + await runTrigger(trigger, `beforeUnsubscribe.${className}`, request, auth); + } + // Remove subscription from client client.deleteSubscriptionInfo(requestId); // Remove client from subscription - const subscription = subscriptionInfo.subscription; - const className = subscription.className; subscription.deleteClientSubscription(parseWebsocket.clientId, requestId); // If there is no client which is subscribing this subscription, remove it from subscriptions const classSubscriptions = this.subscriptions.get(className); diff --git a/src/cloud-code/Parse.Cloud.js b/src/cloud-code/Parse.Cloud.js index 5540e8d719..ed142e8ad5 100644 --- a/src/cloud-code/Parse.Cloud.js +++ b/src/cloud-code/Parse.Cloud.js @@ -729,6 +729,46 @@ ParseCloud.onLiveQueryEvent = function (handler) { triggers.addLiveQueryEventHandler(handler, Parse.applicationId); }; +/** + * Registers a before live query subscription function. + * + * **Available in Cloud Code only.** + * + * If you want to use beforeUnsubscribe for a predefined class in the Parse JavaScript SDK (e.g. {@link Parse.User}), you should pass the class itself and not the String for arg1. + * ``` + * Parse.Cloud.beforeUnsubscribe('MyCustomClass', (request) => { + * // code here + * }, (request) => { + * // validation code here + * }); + * + * Parse.Cloud.beforeUnsubscribe(Parse.User, (request) => { + * // code here + * }, { ...validationObject }); + *``` + * + * @method beforeUnsubscribe + * @name Parse.Cloud.beforeUnsubscribe + * @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the before subscription function for. This can instead be a String that is the className of the subclass. + * @param {Function} func The function to run before a subscription. This function can be async and should take one parameter, a {@link Parse.Cloud.TriggerRequest}. + * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.TriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. + */ +ParseCloud.beforeUnsubscribe = function (parseClass, handler, validationHandler) { + validateValidator(validationHandler); + var className = getClassName(parseClass); + triggers.addTrigger( + triggers.Types.beforeUnsubscribe, + className, + handler, + Parse.applicationId, + validationHandler + ); +}; + +ParseCloud.onLiveQueryEvent = function (handler) { + triggers.addLiveQueryEventHandler(handler, Parse.applicationId); +}; + /** * Registers an after live query server event function. * diff --git a/src/triggers.js b/src/triggers.js index b5f11435df..9796b09b10 100644 --- a/src/triggers.js +++ b/src/triggers.js @@ -14,6 +14,7 @@ export const Types = { afterFind: 'afterFind', beforeConnect: 'beforeConnect', beforeSubscribe: 'beforeSubscribe', + beforeUnsubscribe: 'beforeUnsubscribe', afterEvent: 'afterEvent', }; From 98d48de23c0ad59570b5005a3aa429380ebdcfa0 Mon Sep 17 00:00:00 2001 From: Samuel Denis-D'Ortun Date: Mon, 7 Jun 2021 13:13:09 -0400 Subject: [PATCH 2/7] Remove duplicate onLiveQueryEvent --- src/cloud-code/Parse.Cloud.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/cloud-code/Parse.Cloud.js b/src/cloud-code/Parse.Cloud.js index ed142e8ad5..f7191c0c31 100644 --- a/src/cloud-code/Parse.Cloud.js +++ b/src/cloud-code/Parse.Cloud.js @@ -765,10 +765,6 @@ ParseCloud.beforeUnsubscribe = function (parseClass, handler, validationHandler) ); }; -ParseCloud.onLiveQueryEvent = function (handler) { - triggers.addLiveQueryEventHandler(handler, Parse.applicationId); -}; - /** * Registers an after live query server event function. * From 7bb0d2d8c7a74755703112bc62d8fd7d3baf8954 Mon Sep 17 00:00:00 2001 From: Samuel Denis-D'Ortun Date: Wed, 11 Aug 2021 13:55:09 -0400 Subject: [PATCH 3/7] Add logged-in user to add missing coverage --- spec/ParseLiveQuery.spec.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/spec/ParseLiveQuery.spec.js b/spec/ParseLiveQuery.spec.js index 8469223db1..ff9befba9c 100644 --- a/spec/ParseLiveQuery.spec.js +++ b/spec/ParseLiveQuery.spec.js @@ -807,13 +807,19 @@ describe('ParseLiveQuery', function () { verbose: false, silent: true, }); + const user = new Parse.User(); + user.setUsername('username'); + user.setPassword('password'); + await user.signUp(); - Parse.Cloud.beforeSubscribe(TestObject, request => { - expect(request.requestId).toBe(1); + Parse.Cloud.beforeSubscribe(TestObject, req => { + expect(req.requestId).toBe(1); + expect(req.user).toBeDefined(); }); - Parse.Cloud.beforeUnsubscribe(TestObject, request => { - expect(request.requestId).toBe(1); + Parse.Cloud.beforeUnsubscribe(TestObject, req => { + expect(req.requestId).toBe(1); + expect(req.user).toBeDefined(); done(); }); From 157f4522741054b4b80de4e60a3c6f2ee86df6dc Mon Sep 17 00:00:00 2001 From: Samuel Denis-D'Ortun Date: Mon, 16 Aug 2021 11:51:15 -0400 Subject: [PATCH 4/7] Updated CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51ff85f1af..46b659bfc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ Details: - Branch: [beta][branch_beta] - Purpose: feature maturation - Suitable environment: development +- Added Cloud Trigger for LiveQuery event beforeUnsubscribe (sadortun) [#7419](https://github.com/parse-community/parse-server/pull/7419) ## 🔥 [Alpha Releases][log_alpha] From 927ca635f7fb90cb585b6af07418f46d50e55a0a Mon Sep 17 00:00:00 2001 From: Samuel Denis-D'Ortun Date: Fri, 3 Sep 2021 12:33:31 -0400 Subject: [PATCH 5/7] Add username check and try/catch block --- spec/ParseLiveQuery.spec.js | 1 + src/LiveQuery/ParseLiveQueryServer.js | 10 +++++++++- src/cloud-code/Parse.Cloud.js | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/spec/ParseLiveQuery.spec.js b/spec/ParseLiveQuery.spec.js index ff9befba9c..c5828511e4 100644 --- a/spec/ParseLiveQuery.spec.js +++ b/spec/ParseLiveQuery.spec.js @@ -820,6 +820,7 @@ describe('ParseLiveQuery', function () { Parse.Cloud.beforeUnsubscribe(TestObject, req => { expect(req.requestId).toBe(1); expect(req.user).toBeDefined(); + expect(req.user.get('username')).toBe('username'); done(); }); diff --git a/src/LiveQuery/ParseLiveQueryServer.js b/src/LiveQuery/ParseLiveQueryServer.js index 08ab9916cd..8937a55d05 100644 --- a/src/LiveQuery/ParseLiveQueryServer.js +++ b/src/LiveQuery/ParseLiveQueryServer.js @@ -1013,7 +1013,15 @@ class ParseLiveQueryServer { request.useMasterKey = client.hasMasterKey; request.installationId = client.installationId; - await runTrigger(trigger, `beforeUnsubscribe.${className}`, request, auth); + try { + await runTrigger(trigger, `beforeUnsubscribe.${className}`, request, auth); + } catch (error) { + Client.pushError(parseWebsocket, error.code || 141, error.message || error, false); + logger.error( + `Failed running beforeUnsubscribe for session ${request.sessionToken} with:\n Error: ` + + JSON.stringify(error) + ); + } } // Remove subscription from client diff --git a/src/cloud-code/Parse.Cloud.js b/src/cloud-code/Parse.Cloud.js index f7191c0c31..85f684a9ad 100644 --- a/src/cloud-code/Parse.Cloud.js +++ b/src/cloud-code/Parse.Cloud.js @@ -755,7 +755,7 @@ ParseCloud.onLiveQueryEvent = function (handler) { */ ParseCloud.beforeUnsubscribe = function (parseClass, handler, validationHandler) { validateValidator(validationHandler); - var className = getClassName(parseClass); + const className = getClassName(parseClass); triggers.addTrigger( triggers.Types.beforeUnsubscribe, className, From 55be1036f4db4b486744551d9b7be07c9312a1b2 Mon Sep 17 00:00:00 2001 From: Samuel Denis-D'Ortun Date: Fri, 3 Sep 2021 18:13:01 -0400 Subject: [PATCH 6/7] Update ParseLiveQueryServer.js --- src/LiveQuery/ParseLiveQueryServer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LiveQuery/ParseLiveQueryServer.js b/src/LiveQuery/ParseLiveQueryServer.js index 8937a55d05..2d8daf4908 100644 --- a/src/LiveQuery/ParseLiveQueryServer.js +++ b/src/LiveQuery/ParseLiveQueryServer.js @@ -1016,7 +1016,7 @@ class ParseLiveQueryServer { try { await runTrigger(trigger, `beforeUnsubscribe.${className}`, request, auth); } catch (error) { - Client.pushError(parseWebsocket, error.code || 141, error.message || error, false); + Client.pushError(parseWebsocket, error.code || Parse.Error.SCRIPT_FAILED, error.message || error, false); logger.error( `Failed running beforeUnsubscribe for session ${request.sessionToken} with:\n Error: ` + JSON.stringify(error) From d2c37b035ed4d74929ab8b4e0f69968d008854d2 Mon Sep 17 00:00:00 2001 From: Samuel Denis-D'Ortun Date: Sun, 13 Aug 2023 09:52:55 -0400 Subject: [PATCH 7/7] Clean up after rebase --- CHANGELOG.md | 1 - src/cloud-code/Parse.Cloud.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46b659bfc1..51ff85f1af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,6 @@ Details: - Branch: [beta][branch_beta] - Purpose: feature maturation - Suitable environment: development -- Added Cloud Trigger for LiveQuery event beforeUnsubscribe (sadortun) [#7419](https://github.com/parse-community/parse-server/pull/7419) ## 🔥 [Alpha Releases][log_alpha] diff --git a/src/cloud-code/Parse.Cloud.js b/src/cloud-code/Parse.Cloud.js index 85f684a9ad..d43179a057 100644 --- a/src/cloud-code/Parse.Cloud.js +++ b/src/cloud-code/Parse.Cloud.js @@ -755,7 +755,7 @@ ParseCloud.onLiveQueryEvent = function (handler) { */ ParseCloud.beforeUnsubscribe = function (parseClass, handler, validationHandler) { validateValidator(validationHandler); - const className = getClassName(parseClass); + const className = triggers.getClassName(parseClass); triggers.addTrigger( triggers.Types.beforeUnsubscribe, className,