Create Cloud function afterLiveQueryEvent (#6859)
* Before Connect + Before Subscribe #1 * Cleanup and Documentation * Add E2E tests * Bump parse to 2.15.0 * Create afterLiveQueryEvent * Revert "Create afterLiveQueryEvent" This reverts commit 828c678a6995216b843a75f5b3c864aec063ba43. * afterLiveQueryEvent * Add delete event * Fix failing tests * Fix lint * Update ParseLiveQueryServer.js * Remove Facebook AccountKit auth (#6870) * Remove Facebook AccountKit auth Account Kit services are no longer available. https://developers.facebook.com/blog/post/2019/09/09/account-kit-services-no-longer-available-starting-march/ https://www.sinch.com/blog/facebook-account-kit-is-closing-down-are-your-apps-covered/ * remove flaky test * fix: upgrade uuid from 8.2.0 to 8.3.0 (#6865) Snyk has created this PR to upgrade uuid from 8.2.0 to 8.3.0. See this package in npm: https://www.npmjs.com/package/uuid See this project in Snyk: https://app.snyk.io/org/acinader/project/8c1a9edb-c8f5-4dc1-b221-4d6030a323eb?utm_source=github&utm_medium=upgrade-pr Co-authored-by: Diamond Lewis <findlewis@gmail.com> * fix: package.json & package-lock.json to reduce vulnerabilities (#6864) The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JS-LODASH-590103 Co-authored-by: Diamond Lewis <findlewis@gmail.com> * fix: upgrade ldapjs from 2.0.0 to 2.1.0 (#6857) Snyk has created this PR to upgrade ldapjs from 2.0.0 to 2.1.0. See this package in npm: https://www.npmjs.com/package/ldapjs See this project in Snyk: https://app.snyk.io/org/acinader/project/8c1a9edb-c8f5-4dc1-b221-4d6030a323eb?utm_source=github&utm_medium=upgrade-pr Co-authored-by: Diamond Lewis <findlewis@gmail.com> * fix: upgrade apollo-server-express from 2.15.1 to 2.16.0 (#6851) Snyk has created this PR to upgrade apollo-server-express from 2.15.1 to 2.16.0. See this package in npm: https://www.npmjs.com/package/apollo-server-express See this project in Snyk: https://app.snyk.io/org/acinader/project/8c1a9edb-c8f5-4dc1-b221-4d6030a323eb?utm_source=github&utm_medium=upgrade-pr Co-authored-by: Diamond Lewis <findlewis@gmail.com> * fix: upgrade @graphql-tools/stitch from 6.0.12 to 6.0.13 (#6845) Snyk has created this PR to upgrade @graphql-tools/stitch from 6.0.12 to 6.0.13. See this package in npm: https://www.npmjs.com/package/@graphql-tools/stitch See this project in Snyk: https://app.snyk.io/org/acinader/project/8c1a9edb-c8f5-4dc1-b221-4d6030a323eb?utm_source=github&utm_medium=upgrade-pr Co-authored-by: Diamond Lewis <findlewis@gmail.com> * fix: upgrade @graphql-tools/utils from 6.0.12 to 6.0.13 (#6846) Snyk has created this PR to upgrade @graphql-tools/utils from 6.0.12 to 6.0.13. See this package in npm: https://www.npmjs.com/package/@graphql-tools/utils See this project in Snyk: https://app.snyk.io/org/acinader/project/8c1a9edb-c8f5-4dc1-b221-4d6030a323eb?utm_source=github&utm_medium=upgrade-pr Co-authored-by: Diamond Lewis <findlewis@gmail.com> * [Snyk] Upgrade winston from 3.2.1 to 3.3.2 (#6799) * fix: upgrade winston from 3.2.1 to 3.3.2 Snyk has created this PR to upgrade winston from 3.2.1 to 3.3.2. See this package in NPM: https://www.npmjs.com/package/winston See this project in Snyk: https://app.snyk.io/org/acinader/project/8c1a9edb-c8f5-4dc1-b221-4d6030a323eb?utm_source=github&utm_medium=upgrade-pr * fix tests Co-authored-by: Diamond Lewis <findlewis@gmail.com> * afterLiveQueryEvent * Add delete event * Fix failing tests * Before Connect + Before Subscribe #1 * Cleanup and Documentation * Create afterLiveQueryEvent * Revert "Create afterLiveQueryEvent" This reverts commit 828c678a6995216b843a75f5b3c864aec063ba43. * Update ParseLiveQueryServer.js * Rebase * Remove return value / deduplicate tests * Add docs * Add additional data to trigger Co-authored-by: Diamond Lewis <findlewis@gmail.com> Co-authored-by: Snyk bot <snyk-bot@snyk.io>
This commit is contained in:
@@ -18,13 +18,468 @@ describe('ParseLiveQuery', function () {
|
||||
const query = new Parse.Query(TestObject);
|
||||
query.equalTo('objectId', object.id);
|
||||
const subscription = await query.subscribe();
|
||||
subscription.on('update', async object => {
|
||||
subscription.on('update', object => {
|
||||
expect(object.get('foo')).toBe('bar');
|
||||
done();
|
||||
});
|
||||
object.set({ foo: 'bar' });
|
||||
await object.save();
|
||||
});
|
||||
it('expect afterEvent create', async done => {
|
||||
await reconfigureServer({
|
||||
liveQuery: {
|
||||
classNames: ['TestObject'],
|
||||
},
|
||||
startLiveQueryServer: true,
|
||||
verbose: false,
|
||||
silent: true,
|
||||
});
|
||||
Parse.Cloud.afterLiveQueryEvent('TestObject', req => {
|
||||
expect(req.event).toBe('Create');
|
||||
expect(req.user).toBeUndefined();
|
||||
expect(req.object.get('foo')).toBe('bar');
|
||||
});
|
||||
|
||||
const query = new Parse.Query(TestObject);
|
||||
const subscription = await query.subscribe();
|
||||
subscription.on('create', object => {
|
||||
expect(object.get('foo')).toBe('bar');
|
||||
done();
|
||||
});
|
||||
|
||||
const object = new TestObject();
|
||||
object.set('foo', 'bar');
|
||||
await object.save();
|
||||
});
|
||||
|
||||
it('expect afterEvent payload', async done => {
|
||||
await reconfigureServer({
|
||||
liveQuery: {
|
||||
classNames: ['TestObject'],
|
||||
},
|
||||
startLiveQueryServer: true,
|
||||
verbose: false,
|
||||
silent: true,
|
||||
});
|
||||
const object = new TestObject();
|
||||
await object.save();
|
||||
|
||||
Parse.Cloud.afterLiveQueryEvent('TestObject', req => {
|
||||
expect(req.event).toBe('Update');
|
||||
expect(req.user).toBeUndefined();
|
||||
expect(req.object.get('foo')).toBe('bar');
|
||||
expect(req.original.get('foo')).toBeUndefined();
|
||||
done();
|
||||
});
|
||||
|
||||
const query = new Parse.Query(TestObject);
|
||||
query.equalTo('objectId', object.id);
|
||||
await query.subscribe();
|
||||
object.set({ foo: 'bar' });
|
||||
await object.save();
|
||||
});
|
||||
|
||||
it('expect afterEvent enter', async done => {
|
||||
await reconfigureServer({
|
||||
liveQuery: {
|
||||
classNames: ['TestObject'],
|
||||
},
|
||||
startLiveQueryServer: true,
|
||||
verbose: false,
|
||||
silent: true,
|
||||
});
|
||||
Parse.Cloud.afterLiveQueryEvent('TestObject', req => {
|
||||
expect(req.event).toBe('Enter');
|
||||
expect(req.user).toBeUndefined();
|
||||
expect(req.object.get('foo')).toBe('bar');
|
||||
expect(req.original.get('foo')).toBeUndefined();
|
||||
});
|
||||
|
||||
const object = new TestObject();
|
||||
await object.save();
|
||||
|
||||
const query = new Parse.Query(TestObject);
|
||||
query.equalTo('foo', 'bar');
|
||||
const subscription = await query.subscribe();
|
||||
subscription.on('enter', object => {
|
||||
expect(object.get('foo')).toBe('bar');
|
||||
done();
|
||||
});
|
||||
|
||||
object.set('foo', 'bar');
|
||||
await object.save();
|
||||
});
|
||||
|
||||
it('expect afterEvent leave', async done => {
|
||||
await reconfigureServer({
|
||||
liveQuery: {
|
||||
classNames: ['TestObject'],
|
||||
},
|
||||
startLiveQueryServer: true,
|
||||
verbose: false,
|
||||
silent: true,
|
||||
});
|
||||
Parse.Cloud.afterLiveQueryEvent('TestObject', req => {
|
||||
expect(req.event).toBe('Leave');
|
||||
expect(req.user).toBeUndefined();
|
||||
expect(req.object.get('foo')).toBeUndefined();
|
||||
expect(req.original.get('foo')).toBe('bar');
|
||||
});
|
||||
|
||||
const object = new TestObject();
|
||||
object.set('foo', 'bar');
|
||||
await object.save();
|
||||
|
||||
const query = new Parse.Query(TestObject);
|
||||
query.equalTo('foo', 'bar');
|
||||
const subscription = await query.subscribe();
|
||||
subscription.on('leave', object => {
|
||||
expect(object.get('foo')).toBeUndefined();
|
||||
done();
|
||||
});
|
||||
|
||||
object.unset('foo');
|
||||
await object.save();
|
||||
});
|
||||
|
||||
it('expect afterEvent delete', async done => {
|
||||
await reconfigureServer({
|
||||
liveQuery: {
|
||||
classNames: ['TestObject'],
|
||||
},
|
||||
startLiveQueryServer: true,
|
||||
verbose: false,
|
||||
silent: true,
|
||||
});
|
||||
Parse.Cloud.afterLiveQueryEvent('TestObject', req => {
|
||||
expect(req.event).toBe('Delete');
|
||||
expect(req.user).toBeUndefined();
|
||||
req.object.set('foo', 'bar');
|
||||
});
|
||||
|
||||
const object = new TestObject();
|
||||
await object.save();
|
||||
|
||||
const query = new Parse.Query(TestObject);
|
||||
query.equalTo('objectId', object.id);
|
||||
|
||||
const subscription = await query.subscribe();
|
||||
subscription.on('delete', object => {
|
||||
expect(object.get('foo')).toBe('bar');
|
||||
done();
|
||||
});
|
||||
|
||||
await object.destroy();
|
||||
});
|
||||
|
||||
it('can handle afterEvent modification', async done => {
|
||||
await reconfigureServer({
|
||||
liveQuery: {
|
||||
classNames: ['TestObject'],
|
||||
},
|
||||
startLiveQueryServer: true,
|
||||
verbose: false,
|
||||
silent: true,
|
||||
});
|
||||
const object = new TestObject();
|
||||
await object.save();
|
||||
|
||||
Parse.Cloud.afterLiveQueryEvent('TestObject', req => {
|
||||
const current = req.object;
|
||||
current.set('foo', 'yolo');
|
||||
|
||||
const original = req.original;
|
||||
original.set('yolo', 'foo');
|
||||
});
|
||||
|
||||
const query = new Parse.Query(TestObject);
|
||||
query.equalTo('objectId', object.id);
|
||||
const subscription = await query.subscribe();
|
||||
subscription.on('update', (object, original) => {
|
||||
expect(object.get('foo')).toBe('yolo');
|
||||
expect(original.get('yolo')).toBe('foo');
|
||||
done();
|
||||
});
|
||||
object.set({ foo: 'bar' });
|
||||
await object.save();
|
||||
});
|
||||
|
||||
it('can return different object in afterEvent', async done => {
|
||||
await reconfigureServer({
|
||||
liveQuery: {
|
||||
classNames: ['TestObject'],
|
||||
},
|
||||
startLiveQueryServer: true,
|
||||
verbose: false,
|
||||
silent: true,
|
||||
});
|
||||
const object = new TestObject();
|
||||
await object.save();
|
||||
|
||||
Parse.Cloud.afterLiveQueryEvent('TestObject', req => {
|
||||
const object = new Parse.Object('Yolo');
|
||||
req.object = object;
|
||||
});
|
||||
|
||||
const query = new Parse.Query(TestObject);
|
||||
query.equalTo('objectId', object.id);
|
||||
const subscription = await query.subscribe();
|
||||
subscription.on('update', object => {
|
||||
expect(object.className).toBe('Yolo');
|
||||
done();
|
||||
});
|
||||
object.set({ foo: 'bar' });
|
||||
await object.save();
|
||||
});
|
||||
|
||||
it('can handle afterEvent throw', async done => {
|
||||
await reconfigureServer({
|
||||
liveQuery: {
|
||||
classNames: ['TestObject'],
|
||||
},
|
||||
startLiveQueryServer: true,
|
||||
verbose: false,
|
||||
silent: true,
|
||||
});
|
||||
|
||||
const object = new TestObject();
|
||||
await object.save();
|
||||
|
||||
Parse.Cloud.afterLiveQueryEvent('TestObject', req => {
|
||||
const current = req.object;
|
||||
const original = req.original;
|
||||
|
||||
setTimeout(() => {
|
||||
done();
|
||||
}, 2000);
|
||||
|
||||
if (current.get('foo') != original.get('foo')) {
|
||||
throw "Don't pass an update trigger, or message";
|
||||
}
|
||||
});
|
||||
|
||||
const query = new Parse.Query(TestObject);
|
||||
query.equalTo('objectId', object.id);
|
||||
const subscription = await query.subscribe();
|
||||
subscription.on('update', () => {
|
||||
fail('update should not have been called.');
|
||||
});
|
||||
subscription.on('error', () => {
|
||||
fail('error should not have been called.');
|
||||
});
|
||||
object.set({ foo: 'bar' });
|
||||
await object.save();
|
||||
});
|
||||
it('expect afterEvent create', async done => {
|
||||
await reconfigureServer({
|
||||
liveQuery: {
|
||||
classNames: ['TestObject'],
|
||||
},
|
||||
startLiveQueryServer: true,
|
||||
verbose: false,
|
||||
silent: true,
|
||||
});
|
||||
Parse.Cloud.afterLiveQueryEvent('TestObject', req => {
|
||||
expect(req.event).toBe('Create');
|
||||
expect(req.user).toBeUndefined();
|
||||
expect(req.object.get('foo')).toBe('bar');
|
||||
});
|
||||
|
||||
const query = new Parse.Query(TestObject);
|
||||
const subscription = await query.subscribe();
|
||||
subscription.on('create', object => {
|
||||
expect(object.get('foo')).toBe('bar');
|
||||
done();
|
||||
});
|
||||
|
||||
const object = new TestObject();
|
||||
object.set('foo', 'bar');
|
||||
await object.save();
|
||||
});
|
||||
|
||||
it('expect afterEvent payload', async done => {
|
||||
await reconfigureServer({
|
||||
liveQuery: {
|
||||
classNames: ['TestObject'],
|
||||
},
|
||||
startLiveQueryServer: true,
|
||||
verbose: false,
|
||||
silent: true,
|
||||
});
|
||||
const object = new TestObject();
|
||||
await object.save();
|
||||
|
||||
Parse.Cloud.afterLiveQueryEvent('TestObject', req => {
|
||||
expect(req.event).toBe('Update');
|
||||
expect(req.user).toBeUndefined();
|
||||
expect(req.object.get('foo')).toBe('bar');
|
||||
expect(req.original.get('foo')).toBeUndefined();
|
||||
done();
|
||||
});
|
||||
|
||||
const query = new Parse.Query(TestObject);
|
||||
query.equalTo('objectId', object.id);
|
||||
await query.subscribe();
|
||||
object.set({ foo: 'bar' });
|
||||
await object.save();
|
||||
});
|
||||
|
||||
it('expect afterEvent enter', async done => {
|
||||
await reconfigureServer({
|
||||
liveQuery: {
|
||||
classNames: ['TestObject'],
|
||||
},
|
||||
startLiveQueryServer: true,
|
||||
verbose: false,
|
||||
silent: true,
|
||||
});
|
||||
Parse.Cloud.afterLiveQueryEvent('TestObject', req => {
|
||||
expect(req.event).toBe('Enter');
|
||||
expect(req.user).toBeUndefined();
|
||||
expect(req.object.get('foo')).toBe('bar');
|
||||
expect(req.original.get('foo')).toBeUndefined();
|
||||
});
|
||||
|
||||
const object = new TestObject();
|
||||
await object.save();
|
||||
|
||||
const query = new Parse.Query(TestObject);
|
||||
query.equalTo('foo', 'bar');
|
||||
const subscription = await query.subscribe();
|
||||
subscription.on('enter', object => {
|
||||
expect(object.get('foo')).toBe('bar');
|
||||
done();
|
||||
});
|
||||
|
||||
object.set('foo', 'bar');
|
||||
await object.save();
|
||||
});
|
||||
|
||||
it('expect afterEvent leave', async done => {
|
||||
await reconfigureServer({
|
||||
liveQuery: {
|
||||
classNames: ['TestObject'],
|
||||
},
|
||||
startLiveQueryServer: true,
|
||||
verbose: false,
|
||||
silent: true,
|
||||
});
|
||||
Parse.Cloud.afterLiveQueryEvent('TestObject', req => {
|
||||
expect(req.event).toBe('Leave');
|
||||
expect(req.user).toBeUndefined();
|
||||
expect(req.object.get('foo')).toBeUndefined();
|
||||
expect(req.original.get('foo')).toBe('bar');
|
||||
});
|
||||
|
||||
const object = new TestObject();
|
||||
object.set('foo', 'bar');
|
||||
await object.save();
|
||||
|
||||
const query = new Parse.Query(TestObject);
|
||||
query.equalTo('foo', 'bar');
|
||||
const subscription = await query.subscribe();
|
||||
subscription.on('leave', object => {
|
||||
expect(object.get('foo')).toBeUndefined();
|
||||
done();
|
||||
});
|
||||
|
||||
object.unset('foo');
|
||||
await object.save();
|
||||
});
|
||||
|
||||
it('expect afterEvent delete', async done => {
|
||||
await reconfigureServer({
|
||||
liveQuery: {
|
||||
classNames: ['TestObject'],
|
||||
},
|
||||
startLiveQueryServer: true,
|
||||
verbose: false,
|
||||
silent: true,
|
||||
});
|
||||
Parse.Cloud.afterLiveQueryEvent('TestObject', req => {
|
||||
expect(req.event).toBe('Delete');
|
||||
expect(req.user).toBeUndefined();
|
||||
req.object.set('foo', 'bar');
|
||||
});
|
||||
|
||||
const object = new TestObject();
|
||||
await object.save();
|
||||
|
||||
const query = new Parse.Query(TestObject);
|
||||
query.equalTo('objectId', object.id);
|
||||
|
||||
const subscription = await query.subscribe();
|
||||
subscription.on('delete', object => {
|
||||
expect(object.get('foo')).toBe('bar');
|
||||
done();
|
||||
});
|
||||
|
||||
await object.destroy();
|
||||
});
|
||||
|
||||
it('can handle afterEvent modification', async done => {
|
||||
await reconfigureServer({
|
||||
liveQuery: {
|
||||
classNames: ['TestObject'],
|
||||
},
|
||||
startLiveQueryServer: true,
|
||||
verbose: false,
|
||||
silent: true,
|
||||
});
|
||||
const object = new TestObject();
|
||||
await object.save();
|
||||
|
||||
Parse.Cloud.afterLiveQueryEvent('TestObject', req => {
|
||||
const current = req.object;
|
||||
current.set('foo', 'yolo');
|
||||
|
||||
const original = req.original;
|
||||
original.set('yolo', 'foo');
|
||||
});
|
||||
|
||||
const query = new Parse.Query(TestObject);
|
||||
query.equalTo('objectId', object.id);
|
||||
const subscription = await query.subscribe();
|
||||
subscription.on('update', (object, original) => {
|
||||
expect(object.get('foo')).toBe('yolo');
|
||||
expect(original.get('yolo')).toBe('foo');
|
||||
done();
|
||||
});
|
||||
object.set({ foo: 'bar' });
|
||||
await object.save();
|
||||
});
|
||||
|
||||
it('can handle async afterEvent modification', async done => {
|
||||
await reconfigureServer({
|
||||
liveQuery: {
|
||||
classNames: ['TestObject'],
|
||||
},
|
||||
startLiveQueryServer: true,
|
||||
verbose: false,
|
||||
silent: true,
|
||||
});
|
||||
const parent = new TestObject();
|
||||
const child = new TestObject();
|
||||
child.set('bar', 'foo');
|
||||
await Parse.Object.saveAll([parent, child]);
|
||||
|
||||
Parse.Cloud.afterLiveQueryEvent('TestObject', async req => {
|
||||
const current = req.object;
|
||||
const pointer = current.get('child');
|
||||
await pointer.fetch();
|
||||
});
|
||||
|
||||
const query = new Parse.Query(TestObject);
|
||||
query.equalTo('objectId', parent.id);
|
||||
const subscription = await query.subscribe();
|
||||
subscription.on('update', object => {
|
||||
expect(object.get('child')).toBeDefined();
|
||||
expect(object.get('child').get('bar')).toBe('foo');
|
||||
done();
|
||||
});
|
||||
parent.set('child', child);
|
||||
await parent.save();
|
||||
});
|
||||
|
||||
it('can handle beforeConnect / beforeSubscribe hooks', async done => {
|
||||
await reconfigureServer({
|
||||
@@ -58,7 +513,7 @@ describe('ParseLiveQuery', function () {
|
||||
const query = new Parse.Query(TestObject);
|
||||
query.equalTo('objectId', object.id);
|
||||
const subscription = await query.subscribe();
|
||||
subscription.on('update', async object => {
|
||||
subscription.on('update', object => {
|
||||
expect(object.get('foo')).toBe('bar');
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
runLiveQueryEventHandlers,
|
||||
maybeRunConnectTrigger,
|
||||
maybeRunSubscribeTrigger,
|
||||
maybeRunAfterEventTrigger,
|
||||
} from '../triggers';
|
||||
import { getAuthForSessionToken, Auth } from '../Auth';
|
||||
import { getCacheController } from '../Controllers';
|
||||
@@ -124,7 +125,7 @@ class ParseLiveQueryServer {
|
||||
_onAfterDelete(message: any): void {
|
||||
logger.verbose(Parse.applicationId + 'afterDelete is triggered');
|
||||
|
||||
const deletedParseObject = message.currentParseObject.toJSON();
|
||||
let deletedParseObject = message.currentParseObject.toJSON();
|
||||
const classLevelPermissions = message.classLevelPermissions;
|
||||
const className = deletedParseObject.className;
|
||||
logger.verbose(
|
||||
@@ -158,6 +159,7 @@ class ParseLiveQueryServer {
|
||||
const acl = message.currentParseObject.getACL();
|
||||
// Check CLP
|
||||
const op = this._getCLPOperation(subscription.query);
|
||||
let res = {};
|
||||
this._matchesCLP(
|
||||
classLevelPermissions,
|
||||
message.currentParseObject,
|
||||
@@ -173,6 +175,22 @@ class ParseLiveQueryServer {
|
||||
if (!isMatched) {
|
||||
return null;
|
||||
}
|
||||
res = {
|
||||
event: 'Delete',
|
||||
sessionToken: client.sessionToken,
|
||||
object: deletedParseObject,
|
||||
clients: this.clients.size,
|
||||
subscriptions: this.subscriptions.size,
|
||||
useMasterKey: client.hasMasterKey,
|
||||
installationId: client.installationId
|
||||
};
|
||||
return maybeRunAfterEventTrigger('afterEvent', className, res);
|
||||
})
|
||||
.then(() => {
|
||||
if (res.object && typeof res.object.toJSON === 'function') {
|
||||
deletedParseObject = res.object.toJSON();
|
||||
deletedParseObject.className = className;
|
||||
}
|
||||
client.pushDelete(requestId, deletedParseObject);
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -193,7 +211,7 @@ class ParseLiveQueryServer {
|
||||
originalParseObject = message.originalParseObject.toJSON();
|
||||
}
|
||||
const classLevelPermissions = message.classLevelPermissions;
|
||||
const currentParseObject = message.currentParseObject.toJSON();
|
||||
let currentParseObject = message.currentParseObject.toJSON();
|
||||
const className = currentParseObject.className;
|
||||
logger.verbose(
|
||||
'ClassName: %s | ObjectId: %s',
|
||||
@@ -243,6 +261,7 @@ class ParseLiveQueryServer {
|
||||
// Set current ParseObject ACL checking promise, if the object does not match
|
||||
// subscription, we do not need to check ACL
|
||||
let currentACLCheckingPromise;
|
||||
let res = {};
|
||||
if (!isCurrentSubscriptionMatched) {
|
||||
currentACLCheckingPromise = Promise.resolve(false);
|
||||
} else {
|
||||
@@ -267,40 +286,67 @@ class ParseLiveQueryServer {
|
||||
currentACLCheckingPromise,
|
||||
]);
|
||||
})
|
||||
.then(
|
||||
([isOriginalMatched, isCurrentMatched]) => {
|
||||
logger.verbose(
|
||||
'Original %j | Current %j | Match: %s, %s, %s, %s | Query: %s',
|
||||
originalParseObject,
|
||||
currentParseObject,
|
||||
isOriginalSubscriptionMatched,
|
||||
isCurrentSubscriptionMatched,
|
||||
isOriginalMatched,
|
||||
isCurrentMatched,
|
||||
subscription.hash
|
||||
);
|
||||
.then(([isOriginalMatched, isCurrentMatched]) => {
|
||||
logger.verbose(
|
||||
'Original %j | Current %j | Match: %s, %s, %s, %s | Query: %s',
|
||||
originalParseObject,
|
||||
currentParseObject,
|
||||
isOriginalSubscriptionMatched,
|
||||
isCurrentSubscriptionMatched,
|
||||
isOriginalMatched,
|
||||
isCurrentMatched,
|
||||
subscription.hash
|
||||
);
|
||||
|
||||
// Decide event type
|
||||
let type;
|
||||
if (isOriginalMatched && isCurrentMatched) {
|
||||
type = 'Update';
|
||||
} else if (isOriginalMatched && !isCurrentMatched) {
|
||||
type = 'Leave';
|
||||
} else if (!isOriginalMatched && isCurrentMatched) {
|
||||
if (originalParseObject) {
|
||||
type = 'Enter';
|
||||
} else {
|
||||
type = 'Create';
|
||||
}
|
||||
// Decide event type
|
||||
let type;
|
||||
if (isOriginalMatched && isCurrentMatched) {
|
||||
type = 'Update';
|
||||
} else if (isOriginalMatched && !isCurrentMatched) {
|
||||
type = 'Leave';
|
||||
} else if (!isOriginalMatched && isCurrentMatched) {
|
||||
if (originalParseObject) {
|
||||
type = 'Enter';
|
||||
} else {
|
||||
return null;
|
||||
type = 'Create';
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
message.event = type;
|
||||
res = {
|
||||
event: type,
|
||||
sessionToken: client.sessionToken,
|
||||
object: currentParseObject,
|
||||
original: originalParseObject,
|
||||
clients: this.clients.size,
|
||||
subscriptions: this.subscriptions.size,
|
||||
useMasterKey: client.hasMasterKey,
|
||||
installationId: client.installationId
|
||||
};
|
||||
return maybeRunAfterEventTrigger('afterEvent', className, res);
|
||||
})
|
||||
.then(
|
||||
() => {
|
||||
if (res.object && typeof res.object.toJSON === 'function') {
|
||||
currentParseObject = res.object.toJSON();
|
||||
currentParseObject.className =
|
||||
res.object.className || className;
|
||||
}
|
||||
|
||||
if (res.original && typeof res.original.toJSON === 'function') {
|
||||
originalParseObject = res.original.toJSON();
|
||||
originalParseObject.className =
|
||||
res.original.className || className;
|
||||
}
|
||||
const functionName = 'push' + message.event;
|
||||
if (client[functionName]) {
|
||||
client[functionName](
|
||||
requestId,
|
||||
currentParseObject,
|
||||
originalParseObject
|
||||
);
|
||||
}
|
||||
const functionName = 'push' + type;
|
||||
client[functionName](
|
||||
requestId,
|
||||
currentParseObject,
|
||||
originalParseObject
|
||||
);
|
||||
},
|
||||
error => {
|
||||
logger.error('Matching ACL error : ', error);
|
||||
|
||||
@@ -511,6 +511,32 @@ ParseCloud.onLiveQueryEvent = function (handler) {
|
||||
triggers.addLiveQueryEventHandler(handler, Parse.applicationId);
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers an after live query server event function.
|
||||
*
|
||||
* **Available in Cloud Code only.**
|
||||
*
|
||||
* ```
|
||||
* Parse.Cloud.afterLiveQueryEvent('MyCustomClass', (request) => {
|
||||
* // code here
|
||||
* })
|
||||
*```
|
||||
*
|
||||
* @method afterLiveQueryEvent
|
||||
* @name Parse.Cloud.afterLiveQueryEvent
|
||||
* @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the after live query event function for. This can instead be a String that is the className of the subclass.
|
||||
* @param {Function} func The function to run after a live query event. This function can be async and should take one parameter, a {@link Parse.Cloud.LiveQueryEventTrigger}.
|
||||
*/
|
||||
ParseCloud.afterLiveQueryEvent = function (parseClass, handler) {
|
||||
const className = getClassName(parseClass);
|
||||
triggers.addTrigger(
|
||||
triggers.Types.afterEvent,
|
||||
className,
|
||||
handler,
|
||||
Parse.applicationId
|
||||
);
|
||||
};
|
||||
|
||||
ParseCloud._removeAllHooks = () => {
|
||||
triggers._unregisterAll();
|
||||
};
|
||||
@@ -563,6 +589,19 @@ module.exports = ParseCloud;
|
||||
* @property {String} sessionToken If set, the session of the user that made the request.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @interface Parse.Cloud.LiveQueryEventTrigger
|
||||
* @property {String} installationId If set, the installationId triggering the request.
|
||||
* @property {Boolean} useMasterKey If true, means the master key was used.
|
||||
* @property {Parse.User} user If set, the user that made the request.
|
||||
* @property {String} sessionToken If set, the session of the user that made the request.
|
||||
* @property {String} event The live query event that triggered the request.
|
||||
* @property {Parse.Object} object The object triggering the hook.
|
||||
* @property {Parse.Object} original If set, the object, as currently stored.
|
||||
* @property {Integer} clients The number of clients connected.
|
||||
* @property {Integer} subscriptions The number of subscriptions connected.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @interface Parse.Cloud.BeforeFindRequest
|
||||
* @property {String} installationId If set, the installationId triggering the request.
|
||||
|
||||
@@ -18,6 +18,7 @@ export const Types = {
|
||||
afterDeleteFile: 'afterDeleteFile',
|
||||
beforeConnect: 'beforeConnect',
|
||||
beforeSubscribe: 'beforeSubscribe',
|
||||
afterEvent: 'afterEvent',
|
||||
};
|
||||
|
||||
const FileClassName = '@File';
|
||||
@@ -802,6 +803,25 @@ export async function maybeRunSubscribeTrigger(
|
||||
request.query = query;
|
||||
}
|
||||
|
||||
export async function maybeRunAfterEventTrigger(
|
||||
triggerType,
|
||||
className,
|
||||
request
|
||||
) {
|
||||
const trigger = getTrigger(className, triggerType, Parse.applicationId);
|
||||
if (!trigger) {
|
||||
return;
|
||||
}
|
||||
if (request.object) {
|
||||
request.object = Parse.Object.fromJSON(request.object);
|
||||
}
|
||||
if (request.original) {
|
||||
request.original = Parse.Object.fromJSON(request.original);
|
||||
}
|
||||
request.user = await userForSessionToken(request.sessionToken);
|
||||
return trigger(request);
|
||||
}
|
||||
|
||||
async function userForSessionToken(sessionToken) {
|
||||
if (!sessionToken) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user