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);
|
const query = new Parse.Query(TestObject);
|
||||||
query.equalTo('objectId', object.id);
|
query.equalTo('objectId', object.id);
|
||||||
const subscription = await query.subscribe();
|
const subscription = await query.subscribe();
|
||||||
subscription.on('update', async object => {
|
subscription.on('update', object => {
|
||||||
expect(object.get('foo')).toBe('bar');
|
expect(object.get('foo')).toBe('bar');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
object.set({ foo: 'bar' });
|
object.set({ foo: 'bar' });
|
||||||
await object.save();
|
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 => {
|
it('can handle beforeConnect / beforeSubscribe hooks', async done => {
|
||||||
await reconfigureServer({
|
await reconfigureServer({
|
||||||
@@ -58,7 +513,7 @@ describe('ParseLiveQuery', function () {
|
|||||||
const query = new Parse.Query(TestObject);
|
const query = new Parse.Query(TestObject);
|
||||||
query.equalTo('objectId', object.id);
|
query.equalTo('objectId', object.id);
|
||||||
const subscription = await query.subscribe();
|
const subscription = await query.subscribe();
|
||||||
subscription.on('update', async object => {
|
subscription.on('update', object => {
|
||||||
expect(object.get('foo')).toBe('bar');
|
expect(object.get('foo')).toBe('bar');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
runLiveQueryEventHandlers,
|
runLiveQueryEventHandlers,
|
||||||
maybeRunConnectTrigger,
|
maybeRunConnectTrigger,
|
||||||
maybeRunSubscribeTrigger,
|
maybeRunSubscribeTrigger,
|
||||||
|
maybeRunAfterEventTrigger,
|
||||||
} from '../triggers';
|
} from '../triggers';
|
||||||
import { getAuthForSessionToken, Auth } from '../Auth';
|
import { getAuthForSessionToken, Auth } from '../Auth';
|
||||||
import { getCacheController } from '../Controllers';
|
import { getCacheController } from '../Controllers';
|
||||||
@@ -124,7 +125,7 @@ class ParseLiveQueryServer {
|
|||||||
_onAfterDelete(message: any): void {
|
_onAfterDelete(message: any): void {
|
||||||
logger.verbose(Parse.applicationId + 'afterDelete is triggered');
|
logger.verbose(Parse.applicationId + 'afterDelete is triggered');
|
||||||
|
|
||||||
const deletedParseObject = message.currentParseObject.toJSON();
|
let deletedParseObject = message.currentParseObject.toJSON();
|
||||||
const classLevelPermissions = message.classLevelPermissions;
|
const classLevelPermissions = message.classLevelPermissions;
|
||||||
const className = deletedParseObject.className;
|
const className = deletedParseObject.className;
|
||||||
logger.verbose(
|
logger.verbose(
|
||||||
@@ -158,6 +159,7 @@ class ParseLiveQueryServer {
|
|||||||
const acl = message.currentParseObject.getACL();
|
const acl = message.currentParseObject.getACL();
|
||||||
// Check CLP
|
// Check CLP
|
||||||
const op = this._getCLPOperation(subscription.query);
|
const op = this._getCLPOperation(subscription.query);
|
||||||
|
let res = {};
|
||||||
this._matchesCLP(
|
this._matchesCLP(
|
||||||
classLevelPermissions,
|
classLevelPermissions,
|
||||||
message.currentParseObject,
|
message.currentParseObject,
|
||||||
@@ -173,6 +175,22 @@ class ParseLiveQueryServer {
|
|||||||
if (!isMatched) {
|
if (!isMatched) {
|
||||||
return null;
|
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);
|
client.pushDelete(requestId, deletedParseObject);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@@ -193,7 +211,7 @@ class ParseLiveQueryServer {
|
|||||||
originalParseObject = message.originalParseObject.toJSON();
|
originalParseObject = message.originalParseObject.toJSON();
|
||||||
}
|
}
|
||||||
const classLevelPermissions = message.classLevelPermissions;
|
const classLevelPermissions = message.classLevelPermissions;
|
||||||
const currentParseObject = message.currentParseObject.toJSON();
|
let currentParseObject = message.currentParseObject.toJSON();
|
||||||
const className = currentParseObject.className;
|
const className = currentParseObject.className;
|
||||||
logger.verbose(
|
logger.verbose(
|
||||||
'ClassName: %s | ObjectId: %s',
|
'ClassName: %s | ObjectId: %s',
|
||||||
@@ -243,6 +261,7 @@ class ParseLiveQueryServer {
|
|||||||
// Set current ParseObject ACL checking promise, if the object does not match
|
// Set current ParseObject ACL checking promise, if the object does not match
|
||||||
// subscription, we do not need to check ACL
|
// subscription, we do not need to check ACL
|
||||||
let currentACLCheckingPromise;
|
let currentACLCheckingPromise;
|
||||||
|
let res = {};
|
||||||
if (!isCurrentSubscriptionMatched) {
|
if (!isCurrentSubscriptionMatched) {
|
||||||
currentACLCheckingPromise = Promise.resolve(false);
|
currentACLCheckingPromise = Promise.resolve(false);
|
||||||
} else {
|
} else {
|
||||||
@@ -267,40 +286,67 @@ class ParseLiveQueryServer {
|
|||||||
currentACLCheckingPromise,
|
currentACLCheckingPromise,
|
||||||
]);
|
]);
|
||||||
})
|
})
|
||||||
.then(
|
.then(([isOriginalMatched, isCurrentMatched]) => {
|
||||||
([isOriginalMatched, isCurrentMatched]) => {
|
logger.verbose(
|
||||||
logger.verbose(
|
'Original %j | Current %j | Match: %s, %s, %s, %s | Query: %s',
|
||||||
'Original %j | Current %j | Match: %s, %s, %s, %s | Query: %s',
|
originalParseObject,
|
||||||
originalParseObject,
|
currentParseObject,
|
||||||
currentParseObject,
|
isOriginalSubscriptionMatched,
|
||||||
isOriginalSubscriptionMatched,
|
isCurrentSubscriptionMatched,
|
||||||
isCurrentSubscriptionMatched,
|
isOriginalMatched,
|
||||||
isOriginalMatched,
|
isCurrentMatched,
|
||||||
isCurrentMatched,
|
subscription.hash
|
||||||
subscription.hash
|
);
|
||||||
);
|
|
||||||
|
|
||||||
// Decide event type
|
// Decide event type
|
||||||
let type;
|
let type;
|
||||||
if (isOriginalMatched && isCurrentMatched) {
|
if (isOriginalMatched && isCurrentMatched) {
|
||||||
type = 'Update';
|
type = 'Update';
|
||||||
} else if (isOriginalMatched && !isCurrentMatched) {
|
} else if (isOriginalMatched && !isCurrentMatched) {
|
||||||
type = 'Leave';
|
type = 'Leave';
|
||||||
} else if (!isOriginalMatched && isCurrentMatched) {
|
} else if (!isOriginalMatched && isCurrentMatched) {
|
||||||
if (originalParseObject) {
|
if (originalParseObject) {
|
||||||
type = 'Enter';
|
type = 'Enter';
|
||||||
} else {
|
|
||||||
type = 'Create';
|
|
||||||
}
|
|
||||||
} else {
|
} 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 => {
|
error => {
|
||||||
logger.error('Matching ACL error : ', error);
|
logger.error('Matching ACL error : ', error);
|
||||||
|
|||||||
@@ -511,6 +511,32 @@ ParseCloud.onLiveQueryEvent = function (handler) {
|
|||||||
triggers.addLiveQueryEventHandler(handler, Parse.applicationId);
|
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 = () => {
|
ParseCloud._removeAllHooks = () => {
|
||||||
triggers._unregisterAll();
|
triggers._unregisterAll();
|
||||||
};
|
};
|
||||||
@@ -563,6 +589,19 @@ module.exports = ParseCloud;
|
|||||||
* @property {String} sessionToken If set, the session of the user that made the request.
|
* @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
|
* @interface Parse.Cloud.BeforeFindRequest
|
||||||
* @property {String} installationId If set, the installationId triggering the request.
|
* @property {String} installationId If set, the installationId triggering the request.
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export const Types = {
|
|||||||
afterDeleteFile: 'afterDeleteFile',
|
afterDeleteFile: 'afterDeleteFile',
|
||||||
beforeConnect: 'beforeConnect',
|
beforeConnect: 'beforeConnect',
|
||||||
beforeSubscribe: 'beforeSubscribe',
|
beforeSubscribe: 'beforeSubscribe',
|
||||||
|
afterEvent: 'afterEvent',
|
||||||
};
|
};
|
||||||
|
|
||||||
const FileClassName = '@File';
|
const FileClassName = '@File';
|
||||||
@@ -802,6 +803,25 @@ export async function maybeRunSubscribeTrigger(
|
|||||||
request.query = query;
|
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) {
|
async function userForSessionToken(sessionToken) {
|
||||||
if (!sessionToken) {
|
if (!sessionToken) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user