Files
kami-parse-server/spec/ParseLiveQuery.spec.js
dblythy bf39cd68ef 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>
2020-10-19 10:38:55 -05:00

766 lines
21 KiB
JavaScript

'use strict';
const UserController = require('../lib/Controllers/UserController')
.UserController;
const Config = require('../lib/Config');
describe('ParseLiveQuery', function () {
it('can subscribe to query', async done => {
await reconfigureServer({
liveQuery: {
classNames: ['TestObject'],
},
startLiveQueryServer: true,
verbose: false,
silent: true,
});
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('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({
liveQuery: {
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.sessionToken).toBeUndefined();
expect(req.client).toBeDefined();
});
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');
done();
});
object.set({ foo: 'bar' });
await object.save();
});
it('can handle beforeConnect error', async done => {
await reconfigureServer({
liveQuery: {
classNames: ['TestObject'],
},
startLiveQueryServer: true,
verbose: false,
silent: true,
});
const object = new TestObject();
await object.save();
Parse.Cloud.beforeConnect(() => {
throw new Error('You shall not pass!');
});
Parse.LiveQuery.on('error', error => {
expect(error).toBe('You shall not pass!');
done();
});
const query = new Parse.Query(TestObject);
query.equalTo('objectId', object.id);
await query.subscribe();
});
it('can handle beforeSubscribe error', async done => {
await reconfigureServer({
liveQuery: {
classNames: ['TestObject'],
},
startLiveQueryServer: true,
verbose: false,
silent: true,
});
const object = new TestObject();
await object.save();
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 => {
expect(error).toBe('You shall not subscribe!');
done();
});
});
it('can handle mutate beforeSubscribe query', async done => {
await reconfigureServer({
liveQuery: {
classNames: ['TestObject'],
},
startLiveQueryServer: true,
verbose: false,
silent: true,
});
Parse.Cloud.beforeSubscribe(TestObject, request => {
const query = request.query;
query.equalTo('yolo', 'abc');
});
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('update', () => {
fail();
});
object.set({ foo: 'bar' });
await object.save();
setTimeout(async () => {
done();
}, 1000);
});
it('can return a new beforeSubscribe query', async done => {
await reconfigureServer({
liveQuery: {
classNames: ['TestObject'],
},
startLiveQueryServer: true,
verbose: false,
silent: true,
});
Parse.Cloud.beforeSubscribe(TestObject, request => {
const query = new Parse.Query(TestObject);
query.equalTo('foo', 'yolo');
request.query = query;
});
const query = new Parse.Query(TestObject);
query.equalTo('foo', 'bar');
const subscription = await query.subscribe();
subscription.on('create', object => {
expect(object.get('foo')).toBe('yolo');
done();
});
const object = new TestObject();
object.set({ foo: 'yolo' });
await object.save();
});
it('can handle select beforeSubscribe query', async done => {
await reconfigureServer({
liveQuery: {
classNames: ['TestObject'],
},
startLiveQueryServer: true,
verbose: false,
silent: true,
});
Parse.Cloud.beforeSubscribe(TestObject, request => {
const query = request.query;
query.select('yolo');
});
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('update', object => {
expect(object.get('foo')).toBeUndefined();
expect(object.get('yolo')).toBe('abc');
done();
});
object.set({ foo: 'bar', yolo: 'abc' });
await object.save();
});
it('handle invalid websocket payload length', async done => {
await reconfigureServer({
liveQuery: {
classNames: ['TestObject'],
},
startLiveQueryServer: true,
verbose: false,
silent: true,
websocketTimeout: 100,
});
const object = new TestObject();
await object.save();
const query = new Parse.Query(TestObject);
query.equalTo('objectId', object.id);
const subscription = await query.subscribe();
// All control frames must have a payload length of 125 bytes or less.
// https://tools.ietf.org/html/rfc6455#section-5.5
//
// 0x89 = 10001001 = ping
// 0xfe = 11111110 = first bit is masking the remaining 7 are 1111110 or 126 the payload length
// https://tools.ietf.org/html/rfc6455#section-5.2
const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient();
client.socket._socket.write(Buffer.from([0x89, 0xfe]));
subscription.on('update', async object => {
expect(object.get('foo')).toBe('bar');
done();
});
// Wait for Websocket timeout to reconnect
setTimeout(async () => {
object.set({ foo: 'bar' });
await object.save();
}, 1000);
});
it('should execute live query update on email validation', async done => {
const emailAdapter = {
sendVerificationEmail: () => {},
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => {},
};
await reconfigureServer({
liveQuery: {
classNames: ['_User'],
},
startLiveQueryServer: true,
verbose: false,
silent: true,
websocketTimeout: 100,
appName: 'liveQueryEmailValidation',
verifyUserEmails: true,
emailAdapter: emailAdapter,
emailVerifyTokenValidityDuration: 20, // 0.5 second
publicServerURL: 'http://localhost:8378/1',
}).then(() => {
const user = new Parse.User();
user.set('password', 'asdf');
user.set('email', 'asdf@example.com');
user.set('username', 'zxcv');
user
.signUp()
.then(() => {
const config = Config.get('test');
return config.database.find('_User', {
username: 'zxcv',
});
})
.then(async results => {
const foundUser = results[0];
const query = new Parse.Query('_User');
query.equalTo('objectId', foundUser.objectId);
const subscription = await query.subscribe();
subscription.on('update', async object => {
expect(object).toBeDefined();
expect(object.get('emailVerified')).toBe(true);
done();
});
const userController = new UserController(emailAdapter, 'test', {
verifyUserEmails: true,
});
userController.verifyEmail(
foundUser.username,
foundUser._email_verify_token
);
});
});
});
afterEach(async function (done) {
const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient();
client.close();
// Wait for live query client to disconnect
setTimeout(() => {
done();
}, 1000);
});
});