Skip to content

Commit 7da04e2

Browse files
authored
LiveQuery override data on update (#718)
* livequery override serverdata * improve doc * lint * isPinned integration tests
1 parent 2ec5a6f commit 7da04e2

File tree

5 files changed

+137
-2
lines changed

5 files changed

+137
-2
lines changed

integration/test/ParseLocalDatastoreTest.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,23 @@ function runTest(controller) {
8181
assert.equal(cachedObject.field, 'new info');
8282
});
8383

84+
it(`${controller.name} can check if pinned`, async () => {
85+
const object = new TestObject();
86+
object.set('field', 'test');
87+
await object.save();
88+
89+
let isPinned = await object.isPinned();
90+
assert.equal(isPinned, false);
91+
92+
await object.pin();
93+
isPinned = await object.isPinned();
94+
assert.equal(isPinned, true);
95+
96+
await object.unPin();
97+
isPinned = await object.isPinned();
98+
assert.equal(isPinned, false);
99+
});
100+
84101
it(`${controller.name} can pin (recursive)`, async () => {
85102
const parent = new TestObject();
86103
const child = new Item();

src/LiveQueryClient.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
*/
1010
/* global WebSocket */
1111

12+
import CoreManager from './CoreManager';
1213
import EventEmitter from './EventEmitter';
1314
import ParseObject from './ParseObject';
1415
import LiveQuerySubscription from './LiveQuerySubscription';
@@ -392,7 +393,9 @@ class LiveQueryClient extends EventEmitter {
392393
if (!subscription) {
393394
break;
394395
}
396+
let override = false;
395397
if (data.original) {
398+
override = true;
396399
delete data.original.__type;
397400
// Check for removed fields
398401
for (const field in data.original) {
@@ -403,9 +406,14 @@ class LiveQueryClient extends EventEmitter {
403406
data.original = ParseObject.fromJSON(data.original, false);
404407
}
405408
delete data.object.__type;
406-
const parseObject = ParseObject.fromJSON(data.object, false);
409+
const parseObject = ParseObject.fromJSON(data.object, override);
407410

408411
subscription.emit(data.op, parseObject, data.original);
412+
413+
const localDatastore = CoreManager.getLocalDatastore();
414+
if (override && localDatastore.isEnabled) {
415+
localDatastore._updateObjectIfPinned(parseObject).then(() => {});
416+
}
409417
}
410418
}
411419
}

src/ParseObject.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,6 +1228,25 @@ class ParseObject {
12281228
return ParseObject.unPinAllWithName(localDatastore.DEFAULT_PIN, [this]);
12291229
}
12301230

1231+
/**
1232+
* Asynchronously returns if the object is pinned
1233+
*
1234+
* <pre>
1235+
* const isPinned = await object.isPinned();
1236+
* </pre>
1237+
*/
1238+
async isPinned(): Promise {
1239+
const localDatastore = CoreManager.getLocalDatastore();
1240+
if (localDatastore.checkIfEnabled()) {
1241+
const objectKey = localDatastore.getKeyForObject(this);
1242+
const pin = await localDatastore.fromPinWithName(objectKey);
1243+
if (pin) {
1244+
return Promise.resolve(true);
1245+
}
1246+
}
1247+
return Promise.resolve(false);
1248+
}
1249+
12311250
/**
12321251
* Asynchronously stores the objects and every object they point to in the local datastore, recursively.
12331252
*
@@ -1253,6 +1272,8 @@ class ParseObject {
12531272
* <pre>
12541273
* await object.unPinWithName(name);
12551274
* </pre>
1275+
*
1276+
* @param {String} name Name of Pin.
12561277
*/
12571278
unPinWithName(name: string): Promise {
12581279
return ParseObject.unPinAllWithName(name, [this]);

src/__tests__/LiveQueryClient-test.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,29 @@ jest.dontMock('../unsavedChildren');
3333
jest.dontMock('../ParseACL');
3434
jest.dontMock('../ParseQuery');
3535
jest.dontMock('../LiveQuerySubscription');
36+
jest.dontMock('../LocalDatastore');
37+
3638
jest.useFakeTimers();
3739

40+
const mockLocalDatastore = {
41+
isEnabled: false,
42+
_updateObjectIfPinned: jest.fn(),
43+
};
44+
jest.setMock('../LocalDatastore', mockLocalDatastore);
45+
46+
const CoreManager = require('../CoreManager');
3847
const LiveQueryClient = require('../LiveQueryClient').default;
3948
const ParseObject = require('../ParseObject').default;
4049
const ParseQuery = require('../ParseQuery').default;
4150
const events = require('events');
4251

52+
CoreManager.setLocalDatastore(mockLocalDatastore);
53+
4354
describe('LiveQueryClient', () => {
55+
beforeEach(() => {
56+
mockLocalDatastore.isEnabled = false;
57+
});
58+
4459
it('can connect to server', () => {
4560
const liveQueryClient = new LiveQueryClient({
4661
applicationId: 'applicationId',
@@ -264,6 +279,63 @@ describe('LiveQueryClient', () => {
264279
expect(isChecked).toBe(true);
265280
});
266281

282+
it('can handle WebSocket response override data on update', () => {
283+
const liveQueryClient = new LiveQueryClient({
284+
applicationId: 'applicationId',
285+
serverURL: 'ws://test',
286+
javascriptKey: 'javascriptKey',
287+
masterKey: 'masterKey',
288+
sessionToken: 'sessionToken'
289+
});
290+
// Add mock subscription
291+
const subscription = new events.EventEmitter();
292+
liveQueryClient.subscriptions.set(1, subscription);
293+
const object = new ParseObject('Test');
294+
const original = new ParseObject('Test');
295+
object.set('key', 'value');
296+
original.set('key', 'old');
297+
const data = {
298+
op: 'update',
299+
clientId: 1,
300+
requestId: 1,
301+
object: object._toFullJSON(),
302+
original: original._toFullJSON(),
303+
};
304+
const event = {
305+
data: JSON.stringify(data)
306+
}
307+
308+
jest.spyOn(
309+
mockLocalDatastore,
310+
'_updateObjectIfPinned'
311+
)
312+
.mockImplementationOnce(() => Promise.resolve());
313+
314+
const spy = jest.spyOn(
315+
ParseObject,
316+
'fromJSON'
317+
)
318+
.mockImplementationOnce(() => original)
319+
.mockImplementationOnce(() => object);
320+
321+
322+
mockLocalDatastore.isEnabled = true;
323+
324+
let isChecked = false;
325+
subscription.on('update', () => {
326+
isChecked = true;
327+
});
328+
329+
liveQueryClient._handleWebSocketMessage(event);
330+
const override = true;
331+
332+
expect(ParseObject.fromJSON.mock.calls[1][1]).toEqual(override);
333+
expect(mockLocalDatastore._updateObjectIfPinned).toHaveBeenCalledTimes(1);
334+
335+
expect(isChecked).toBe(true);
336+
spy.mockRestore();
337+
});
338+
267339
it('can handle WebSocket response unset field', async () => {
268340
const liveQueryClient = new LiveQueryClient({
269341
applicationId: 'applicationId',

src/__tests__/ParseObject-test.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2639,6 +2639,22 @@ describe('ParseObject pin', () => {
26392639
expect(mockLocalDatastore._handleUnPinWithName).toHaveBeenCalledWith('test_pin', object);
26402640
});
26412641

2642+
it('can check if pinned', async () => {
2643+
const object = new ParseObject('Item');
2644+
object.id = '1234';
2645+
mockLocalDatastore
2646+
.fromPinWithName
2647+
.mockImplementationOnce(() => {
2648+
return { 'Item_1234': object._toFullJSON() }
2649+
})
2650+
.mockImplementationOnce(() => null);
2651+
2652+
let isPinned = await object.isPinned();
2653+
expect(isPinned).toEqual(true);
2654+
isPinned = await object.isPinned();
2655+
expect(isPinned).toEqual(false);
2656+
});
2657+
26422658
it('can fetchFromLocalDatastore', async () => {
26432659
const object = new ParseObject('Item');
26442660
object.id = '123';
@@ -2704,6 +2720,7 @@ describe('ParseObject pin', () => {
27042720
const obj = new ParseObject('Item');
27052721
await obj.pin();
27062722
await obj.unPin();
2723+
await obj.isPinned();
27072724
await obj.pinWithName(name);
27082725
await obj.unPinWithName(name);
27092726
await obj.fetchFromLocalDatastore();
@@ -2715,7 +2732,7 @@ describe('ParseObject pin', () => {
27152732
await ParseObject.unPinAllObjects();
27162733
await ParseObject.unPinAllObjectsWithName(name);
27172734

2718-
expect(spy).toHaveBeenCalledTimes(11);
2735+
expect(spy).toHaveBeenCalledTimes(12);
27192736
expect(spy).toHaveBeenCalledWith('Parse.enableLocalDatastore() must be called first');
27202737
spy.mockRestore();
27212738
});

0 commit comments

Comments
 (0)