Skip to content

Commit 5829b4e

Browse files
authored
Ensure LiveQuery subscribes (#878)
* Ensure LiveQuery subscribes * lint * add tests * fix tests and functionality * fix tests
1 parent 81ddc94 commit 5829b4e

File tree

7 files changed

+99
-12
lines changed

7 files changed

+99
-12
lines changed

integration/test/ParseLiveQueryTest.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,27 @@ describe('Parse LiveQuery', () => {
3333
await object.save();
3434
});
3535

36+
it('can subscribe to query with client', async (done) => {
37+
const object = new TestObject();
38+
await object.save();
39+
40+
const query = new Parse.Query(TestObject);
41+
query.equalTo('objectId', object.id);
42+
const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient();
43+
if (client.shouldOpen()) {
44+
client.open();
45+
}
46+
const subscription = client.subscribe(query);
47+
48+
subscription.on('update', object => {
49+
assert.equal(object.get('foo'), 'bar');
50+
done();
51+
});
52+
await subscription.subscribePromise;
53+
object.set({ foo: 'bar' });
54+
await object.save();
55+
});
56+
3657
it('can subscribe to multiple queries', async () => {
3758
const objectA = new TestObject();
3859
const objectB = new TestObject();

src/LiveQueryClient.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,9 @@ class LiveQueryClient extends EventEmitter {
178178
*
179179
* @param {Object} query - the ParseQuery you want to subscribe to
180180
* @param {string} sessionToken (optional)
181-
* @return {Object} subscription
181+
* @return {LiveQuerySubscription} subscription
182182
*/
183-
subscribe(query: Object, sessionToken: ?string): Object {
183+
subscribe(query: Object, sessionToken: ?string): LiveQuerySubscription {
184184
if (!query) {
185185
return;
186186
}
@@ -309,6 +309,7 @@ class LiveQueryClient extends EventEmitter {
309309
this.socket.close();
310310
// Notify each subscription about the close
311311
for (const subscription of this.subscriptions.values()) {
312+
subscription.subscribed = false;
312313
subscription.emit(SUBSCRIPTION_EMMITER_TYPES.CLOSE);
313314
}
314315
this._handleReset();
@@ -358,12 +359,15 @@ class LiveQueryClient extends EventEmitter {
358359
break;
359360
case OP_EVENTS.SUBSCRIBED:
360361
if (subscription) {
362+
subscription.subscribed = true;
363+
subscription.subscribePromise.resolve();
361364
subscription.emit(SUBSCRIPTION_EMMITER_TYPES.OPEN);
362365
}
363366
break;
364367
case OP_EVENTS.ERROR:
365368
if (data.requestId) {
366369
if (subscription) {
370+
subscription.subscribePromise.resolve();
367371
subscription.emit(SUBSCRIPTION_EMMITER_TYPES.ERROR, data.error);
368372
}
369373
} else {
@@ -435,7 +439,7 @@ class LiveQueryClient extends EventEmitter {
435439

436440
// handle case when both close/error occur at frequent rates we ensure we do not reconnect unnecessarily.
437441
// we're unable to distinguish different between close/error when we're unable to reconnect therefore
438-
// we try to reonnect in both cases
442+
// we try to reconnect in both cases
439443
// server side ws and browser WebSocket behave differently in when close/error get triggered
440444

441445
if (this.reconnectHandle) {

src/LiveQuerySubscription.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import EventEmitter from './EventEmitter';
1212
import CoreManager from './CoreManager';
13+
import { resolvingPromise } from './promiseUtils';
1314

1415
/**
1516
* Creates a new LiveQuery Subscription.
@@ -101,6 +102,8 @@ class Subscription extends EventEmitter {
101102
this.id = id;
102103
this.query = query;
103104
this.sessionToken = sessionToken;
105+
this.subscribePromise = resolvingPromise();
106+
this.subscribed = false;
104107

105108
// adding listener so process does not crash
106109
// best practice is for developer to register their own listener

src/ParseQuery.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1618,7 +1618,9 @@ class ParseQuery {
16181618
liveQueryClient.open();
16191619
}
16201620
const subscription = liveQueryClient.subscribe(this, sessionToken);
1621-
return subscription;
1621+
return subscription.subscribePromise.then(() => {
1622+
return subscription;
1623+
});
16221624
}
16231625

16241626
/**

src/__tests__/LiveQueryClient-test.js

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,18 @@ const events = require('events');
5151

5252
CoreManager.setLocalDatastore(mockLocalDatastore);
5353

54+
function resolvingPromise() {
55+
let res;
56+
let rej;
57+
const promise = new Promise((resolve, reject) => {
58+
res = resolve;
59+
rej = reject;
60+
});
61+
promise.resolve = res;
62+
promise.reject = rej;
63+
return promise;
64+
}
65+
5466
describe('LiveQueryClient', () => {
5567
beforeEach(() => {
5668
mockLocalDatastore.isEnabled = false;
@@ -152,6 +164,8 @@ describe('LiveQueryClient', () => {
152164
});
153165
// Add mock subscription
154166
const subscription = new events.EventEmitter();
167+
subscription.subscribePromise = resolvingPromise();
168+
155169
liveQueryClient.subscriptions.set(1, subscription);
156170
const data = {
157171
op: 'subscribed',
@@ -200,6 +214,39 @@ describe('LiveQueryClient', () => {
200214
expect(isChecked).toBe(true);
201215
});
202216

217+
it('can handle WebSocket error while subscribing', () => {
218+
const liveQueryClient = new LiveQueryClient({
219+
applicationId: 'applicationId',
220+
serverURL: 'ws://test',
221+
javascriptKey: 'javascriptKey',
222+
masterKey: 'masterKey',
223+
sessionToken: 'sessionToken'
224+
});
225+
const subscription = new events.EventEmitter();
226+
subscription.subscribePromise = resolvingPromise();
227+
liveQueryClient.subscriptions.set(1, subscription);
228+
229+
const data = {
230+
op: 'error',
231+
clientId: 1,
232+
requestId: 1,
233+
error: 'error thrown'
234+
};
235+
const event = {
236+
data: JSON.stringify(data)
237+
}
238+
// Register checked in advance
239+
let isChecked = false;
240+
subscription.on('error', function(error) {
241+
isChecked = true;
242+
expect(error).toEqual('error thrown');
243+
});
244+
245+
liveQueryClient._handleWebSocketMessage(event);
246+
247+
expect(isChecked).toBe(true);
248+
});
249+
203250
it('can handle WebSocket event response message', () => {
204251
const liveQueryClient = new LiveQueryClient({
205252
applicationId: 'applicationId',
@@ -457,9 +504,13 @@ describe('LiveQueryClient', () => {
457504
const query = new ParseQuery('Test');
458505
query.equalTo('key', 'value');
459506

460-
const subscription = liveQueryClient.subscribe(query);
507+
const subscribePromise = liveQueryClient.subscribe(query);
508+
const clientSub = liveQueryClient.subscriptions.get(1);
509+
clientSub.subscribePromise.resolve();
510+
511+
const subscription = await subscribePromise;
461512
liveQueryClient.connectPromise.resolve();
462-
expect(subscription).toBe(liveQueryClient.subscriptions.get(1));
513+
expect(subscription).toBe(clientSub);
463514
expect(liveQueryClient.requestId).toBe(2);
464515
await liveQueryClient.connectPromise;
465516
const messageStr = liveQueryClient.socket.send.mock.calls[0][0];

src/__tests__/ParseLiveQuery-test.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,6 @@ describe('ParseLiveQuery', () => {
157157
});
158158

159159
it('subscribes to all subscription events', (done) => {
160-
161160
CoreManager.set('UserController', {
162161
currentUserAsync() {
163162
return Promise.resolve({
@@ -235,7 +234,6 @@ describe('ParseLiveQuery', () => {
235234
}
236235
}, 1);
237236
});
238-
239237
});
240238

241239
it('should not throw on usubscribe', (done) => {

src/__tests__/ParseQuery-test.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2849,7 +2849,9 @@ describe('ParseQuery LocalDatastore', () => {
28492849
return false;
28502850
},
28512851
subscribe: function(query, sessionToken) {
2852-
return new LiveQuerySubscription('0', query, sessionToken);
2852+
const subscription = new LiveQuerySubscription('0', query, sessionToken);
2853+
subscription.subscribePromise.resolve();
2854+
return subscription;
28532855
},
28542856
};
28552857
CoreManager.set('UserController', {
@@ -2880,7 +2882,9 @@ describe('ParseQuery LocalDatastore', () => {
28802882
},
28812883
open: function() {},
28822884
subscribe: function(query, sessionToken) {
2883-
return new LiveQuerySubscription('0', query, sessionToken);
2885+
const subscription = new LiveQuerySubscription('0', query, sessionToken);
2886+
subscription.subscribePromise.resolve();
2887+
return subscription;
28842888
},
28852889
};
28862890
CoreManager.set('UserController', {
@@ -2911,7 +2915,9 @@ describe('ParseQuery LocalDatastore', () => {
29112915
},
29122916
open: function() {},
29132917
subscribe: function(query, sessionToken) {
2914-
return new LiveQuerySubscription('0', query, sessionToken);
2918+
const subscription = new LiveQuerySubscription('0', query, sessionToken);
2919+
subscription.subscribePromise.resolve();
2920+
return subscription;
29152921
},
29162922
};
29172923
CoreManager.set('UserController', {
@@ -2938,7 +2944,9 @@ describe('ParseQuery LocalDatastore', () => {
29382944
},
29392945
open: function() {},
29402946
subscribe: function(query, sessionToken) {
2941-
return new LiveQuerySubscription('0', query, sessionToken);
2947+
const subscription = new LiveQuerySubscription('0', query, sessionToken);
2948+
subscription.subscribePromise.resolve();
2949+
return subscription;
29422950
},
29432951
};
29442952
CoreManager.set('UserController', {

0 commit comments

Comments
 (0)