Skip to content

Commit

Permalink
Create afterLiveQueryEvent
Browse files Browse the repository at this point in the history
  • Loading branch information
dblythy committed Aug 12, 2020
1 parent 97c08b2 commit 828c678
Show file tree
Hide file tree
Showing 4 changed files with 333 additions and 30 deletions.
252 changes: 250 additions & 2 deletions spec/ParseLiveQuery.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,262 @@ 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.current.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.current.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.current.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.current.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('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.current;
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', () => {
const object = new Parse.Object('Yolo');
return 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 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.current;
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 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.current;
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('can handle beforeConnect / beforeSubscribe hooks', async done => {
await reconfigureServer({
liveQuery: {
Expand Down Expand Up @@ -56,7 +304,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();
});
Expand Down
81 changes: 53 additions & 28 deletions src/LiveQuery/ParseLiveQueryServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
runLiveQueryEventHandlers,
maybeRunConnectTrigger,
maybeRunSubscribeTrigger,
maybeRunAfterEventTrigger,
} from '../triggers';
import { getAuthForSessionToken, Auth } from '../Auth';
import { getCacheController } from '../Controllers';
Expand Down Expand Up @@ -193,7 +194,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',
Expand Down Expand Up @@ -243,6 +244,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 {
Expand All @@ -267,35 +269,58 @@ 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
);

// 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';
}
.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 {
return null;
type = 'Create';
}
} else {
return null;
}
message.event = type;
res = {
event: type,
sessionToken: client.sessionToken,
current: currentParseObject,
original: originalParseObject,
};
return maybeRunAfterEventTrigger('afterEvent', className, res);
})
.then(
newObj => {
if (res.current != currentParseObject) {
currentParseObject = res.current.toJSON();
currentParseObject.className = className;
}
const functionName = 'push' + type;
if (res.original != originalParseObject) {
originalParseObject = res.original.toJSON();
originalParseObject.className = className;
}
if (newObj) {
currentParseObject = newObj.toJSON();
currentParseObject.className = newObj.className;
}

const functionName = 'push' + message.event;
client[functionName](
requestId,
currentParseObject,
Expand Down
Loading

0 comments on commit 828c678

Please sign in to comment.