Skip to content

Commit 828c678

Browse files
committed
Create afterLiveQueryEvent
1 parent 97c08b2 commit 828c678

File tree

4 files changed

+333
-30
lines changed

4 files changed

+333
-30
lines changed

spec/ParseLiveQuery.spec.js

+250-2
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,262 @@ describe('ParseLiveQuery', function () {
1616
const query = new Parse.Query(TestObject);
1717
query.equalTo('objectId', object.id);
1818
const subscription = await query.subscribe();
19-
subscription.on('update', async object => {
19+
subscription.on('update', object => {
2020
expect(object.get('foo')).toBe('bar');
2121
done();
2222
});
2323
object.set({ foo: 'bar' });
2424
await object.save();
2525
});
2626

27+
it('expect afterEvent create', async done => {
28+
await reconfigureServer({
29+
liveQuery: {
30+
classNames: ['TestObject'],
31+
},
32+
startLiveQueryServer: true,
33+
verbose: false,
34+
silent: true,
35+
});
36+
Parse.Cloud.afterLiveQueryEvent('TestObject', req => {
37+
expect(req.event).toBe('Create');
38+
expect(req.user).toBeUndefined();
39+
expect(req.current.get('foo')).toBe('bar');
40+
});
41+
42+
const query = new Parse.Query(TestObject);
43+
const subscription = await query.subscribe();
44+
subscription.on('create', object => {
45+
expect(object.get('foo')).toBe('bar');
46+
done();
47+
});
48+
49+
const object = new TestObject();
50+
object.set('foo', 'bar');
51+
await object.save();
52+
});
53+
54+
it('expect afterEvent payload', async done => {
55+
await reconfigureServer({
56+
liveQuery: {
57+
classNames: ['TestObject'],
58+
},
59+
startLiveQueryServer: true,
60+
verbose: false,
61+
silent: true,
62+
});
63+
const object = new TestObject();
64+
await object.save();
65+
66+
Parse.Cloud.afterLiveQueryEvent('TestObject', req => {
67+
expect(req.event).toBe('Update');
68+
expect(req.user).toBeUndefined();
69+
expect(req.current.get('foo')).toBe('bar');
70+
expect(req.original.get('foo')).toBeUndefined();
71+
done();
72+
});
73+
74+
const query = new Parse.Query(TestObject);
75+
query.equalTo('objectId', object.id);
76+
await query.subscribe();
77+
object.set({ foo: 'bar' });
78+
await object.save();
79+
});
80+
81+
it('expect afterEvent enter', async done => {
82+
await reconfigureServer({
83+
liveQuery: {
84+
classNames: ['TestObject'],
85+
},
86+
startLiveQueryServer: true,
87+
verbose: false,
88+
silent: true,
89+
});
90+
Parse.Cloud.afterLiveQueryEvent('TestObject', req => {
91+
expect(req.event).toBe('Enter');
92+
expect(req.user).toBeUndefined();
93+
expect(req.current.get('foo')).toBe('bar');
94+
expect(req.original.get('foo')).toBeUndefined();
95+
});
96+
97+
const object = new TestObject();
98+
await object.save();
99+
100+
const query = new Parse.Query(TestObject);
101+
query.equalTo('foo', 'bar');
102+
const subscription = await query.subscribe();
103+
subscription.on('enter', object => {
104+
expect(object.get('foo')).toBe('bar');
105+
done();
106+
});
107+
108+
object.set('foo', 'bar');
109+
await object.save();
110+
});
111+
112+
it('expect afterEvent leave', async done => {
113+
await reconfigureServer({
114+
liveQuery: {
115+
classNames: ['TestObject'],
116+
},
117+
startLiveQueryServer: true,
118+
verbose: false,
119+
silent: true,
120+
});
121+
Parse.Cloud.afterLiveQueryEvent('TestObject', req => {
122+
expect(req.event).toBe('Leave');
123+
expect(req.user).toBeUndefined();
124+
expect(req.current.get('foo')).toBeUndefined();
125+
expect(req.original.get('foo')).toBe('bar');
126+
});
127+
128+
const object = new TestObject();
129+
object.set('foo', 'bar');
130+
await object.save();
131+
132+
const query = new Parse.Query(TestObject);
133+
query.equalTo('foo', 'bar');
134+
const subscription = await query.subscribe();
135+
subscription.on('leave', object => {
136+
expect(object.get('foo')).toBeUndefined();
137+
done();
138+
});
139+
140+
object.unset('foo');
141+
await object.save();
142+
});
143+
144+
it('can handle afterEvent modification', async done => {
145+
await reconfigureServer({
146+
liveQuery: {
147+
classNames: ['TestObject'],
148+
},
149+
startLiveQueryServer: true,
150+
verbose: false,
151+
silent: true,
152+
});
153+
const object = new TestObject();
154+
await object.save();
155+
156+
Parse.Cloud.afterLiveQueryEvent('TestObject', req => {
157+
const current = req.current;
158+
current.set('foo', 'yolo');
159+
160+
const original = req.original;
161+
original.set('yolo', 'foo');
162+
});
163+
164+
const query = new Parse.Query(TestObject);
165+
query.equalTo('objectId', object.id);
166+
const subscription = await query.subscribe();
167+
subscription.on('update', (object, original) => {
168+
expect(object.get('foo')).toBe('yolo');
169+
expect(original.get('yolo')).toBe('foo');
170+
done();
171+
});
172+
object.set({ foo: 'bar' });
173+
await object.save();
174+
});
175+
176+
it('can return different object in afterEvent', async done => {
177+
await reconfigureServer({
178+
liveQuery: {
179+
classNames: ['TestObject'],
180+
},
181+
startLiveQueryServer: true,
182+
verbose: false,
183+
silent: true,
184+
});
185+
const object = new TestObject();
186+
await object.save();
187+
188+
Parse.Cloud.afterLiveQueryEvent('TestObject', () => {
189+
const object = new Parse.Object('Yolo');
190+
return object;
191+
});
192+
193+
const query = new Parse.Query(TestObject);
194+
query.equalTo('objectId', object.id);
195+
const subscription = await query.subscribe();
196+
subscription.on('update', object => {
197+
expect(object.className).toBe('Yolo');
198+
done();
199+
});
200+
object.set({ foo: 'bar' });
201+
await object.save();
202+
});
203+
204+
it('can handle async afterEvent modification', async done => {
205+
await reconfigureServer({
206+
liveQuery: {
207+
classNames: ['TestObject'],
208+
},
209+
startLiveQueryServer: true,
210+
verbose: false,
211+
silent: true,
212+
});
213+
const parent = new TestObject();
214+
const child = new TestObject();
215+
child.set('bar', 'foo');
216+
await Parse.Object.saveAll([parent, child]);
217+
218+
Parse.Cloud.afterLiveQueryEvent('TestObject', async req => {
219+
const current = req.current;
220+
const pointer = current.get('child');
221+
await pointer.fetch();
222+
});
223+
224+
const query = new Parse.Query(TestObject);
225+
query.equalTo('objectId', parent.id);
226+
const subscription = await query.subscribe();
227+
subscription.on('update', object => {
228+
expect(object.get('child')).toBeDefined();
229+
expect(object.get('child').get('bar')).toBe('foo');
230+
done();
231+
});
232+
parent.set('child', child);
233+
await parent.save();
234+
});
235+
236+
it('can handle afterEvent throw', async done => {
237+
await reconfigureServer({
238+
liveQuery: {
239+
classNames: ['TestObject'],
240+
},
241+
startLiveQueryServer: true,
242+
verbose: false,
243+
silent: true,
244+
});
245+
246+
const object = new TestObject();
247+
await object.save();
248+
249+
Parse.Cloud.afterLiveQueryEvent('TestObject', req => {
250+
const current = req.current;
251+
const original = req.original;
252+
253+
setTimeout(() => {
254+
done();
255+
}, 2000);
256+
257+
if (current.get('foo') != original.get('foo')) {
258+
throw "Don't pass an update trigger, or message";
259+
}
260+
});
261+
262+
const query = new Parse.Query(TestObject);
263+
query.equalTo('objectId', object.id);
264+
const subscription = await query.subscribe();
265+
subscription.on('update', () => {
266+
fail('update should not have been called.');
267+
});
268+
subscription.on('error', () => {
269+
fail('error should not have been called.');
270+
});
271+
object.set({ foo: 'bar' });
272+
await object.save();
273+
});
274+
27275
it('can handle beforeConnect / beforeSubscribe hooks', async done => {
28276
await reconfigureServer({
29277
liveQuery: {
@@ -56,7 +304,7 @@ describe('ParseLiveQuery', function () {
56304
const query = new Parse.Query(TestObject);
57305
query.equalTo('objectId', object.id);
58306
const subscription = await query.subscribe();
59-
subscription.on('update', async object => {
307+
subscription.on('update', object => {
60308
expect(object.get('foo')).toBe('bar');
61309
done();
62310
});

src/LiveQuery/ParseLiveQueryServer.js

+53-28
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
runLiveQueryEventHandlers,
1515
maybeRunConnectTrigger,
1616
maybeRunSubscribeTrigger,
17+
maybeRunAfterEventTrigger,
1718
} from '../triggers';
1819
import { getAuthForSessionToken, Auth } from '../Auth';
1920
import { getCacheController } from '../Controllers';
@@ -193,7 +194,7 @@ class ParseLiveQueryServer {
193194
originalParseObject = message.originalParseObject.toJSON();
194195
}
195196
const classLevelPermissions = message.classLevelPermissions;
196-
const currentParseObject = message.currentParseObject.toJSON();
197+
let currentParseObject = message.currentParseObject.toJSON();
197198
const className = currentParseObject.className;
198199
logger.verbose(
199200
'ClassName: %s | ObjectId: %s',
@@ -243,6 +244,7 @@ class ParseLiveQueryServer {
243244
// Set current ParseObject ACL checking promise, if the object does not match
244245
// subscription, we do not need to check ACL
245246
let currentACLCheckingPromise;
247+
let res;
246248
if (!isCurrentSubscriptionMatched) {
247249
currentACLCheckingPromise = Promise.resolve(false);
248250
} else {
@@ -267,35 +269,58 @@ class ParseLiveQueryServer {
267269
currentACLCheckingPromise,
268270
]);
269271
})
270-
.then(
271-
([isOriginalMatched, isCurrentMatched]) => {
272-
logger.verbose(
273-
'Original %j | Current %j | Match: %s, %s, %s, %s | Query: %s',
274-
originalParseObject,
275-
currentParseObject,
276-
isOriginalSubscriptionMatched,
277-
isCurrentSubscriptionMatched,
278-
isOriginalMatched,
279-
isCurrentMatched,
280-
subscription.hash
281-
);
282-
283-
// Decide event type
284-
let type;
285-
if (isOriginalMatched && isCurrentMatched) {
286-
type = 'Update';
287-
} else if (isOriginalMatched && !isCurrentMatched) {
288-
type = 'Leave';
289-
} else if (!isOriginalMatched && isCurrentMatched) {
290-
if (originalParseObject) {
291-
type = 'Enter';
292-
} else {
293-
type = 'Create';
294-
}
272+
.then(([isOriginalMatched, isCurrentMatched]) => {
273+
logger.verbose(
274+
'Original %j | Current %j | Match: %s, %s, %s, %s | Query: %s',
275+
originalParseObject,
276+
currentParseObject,
277+
isOriginalSubscriptionMatched,
278+
isCurrentSubscriptionMatched,
279+
isOriginalMatched,
280+
isCurrentMatched,
281+
subscription.hash
282+
);
283+
284+
// Decide event type
285+
let type;
286+
if (isOriginalMatched && isCurrentMatched) {
287+
type = 'Update';
288+
} else if (isOriginalMatched && !isCurrentMatched) {
289+
type = 'Leave';
290+
} else if (!isOriginalMatched && isCurrentMatched) {
291+
if (originalParseObject) {
292+
type = 'Enter';
295293
} else {
296-
return null;
294+
type = 'Create';
295+
}
296+
} else {
297+
return null;
298+
}
299+
message.event = type;
300+
res = {
301+
event: type,
302+
sessionToken: client.sessionToken,
303+
current: currentParseObject,
304+
original: originalParseObject,
305+
};
306+
return maybeRunAfterEventTrigger('afterEvent', className, res);
307+
})
308+
.then(
309+
newObj => {
310+
if (res.current != currentParseObject) {
311+
currentParseObject = res.current.toJSON();
312+
currentParseObject.className = className;
297313
}
298-
const functionName = 'push' + type;
314+
if (res.original != originalParseObject) {
315+
originalParseObject = res.original.toJSON();
316+
originalParseObject.className = className;
317+
}
318+
if (newObj) {
319+
currentParseObject = newObj.toJSON();
320+
currentParseObject.className = newObj.className;
321+
}
322+
323+
const functionName = 'push' + message.event;
299324
client[functionName](
300325
requestId,
301326
currentParseObject,

0 commit comments

Comments
 (0)