feat: Add ParseQuery.watch to trigger LiveQuery only on update of specific fields (#8028)
This commit is contained in:
@@ -394,6 +394,49 @@ describe('ParseLiveQuery', function () {
|
||||
await object.save();
|
||||
});
|
||||
|
||||
xit('can handle live query with fields - enable upon JS SDK support', async () => {
|
||||
await reconfigureServer({
|
||||
liveQuery: {
|
||||
classNames: ['Test'],
|
||||
},
|
||||
startLiveQueryServer: true,
|
||||
});
|
||||
const query = new Parse.Query('Test');
|
||||
query.watch('yolo');
|
||||
const subscription = await query.subscribe();
|
||||
const spy = {
|
||||
create(obj) {
|
||||
if (!obj.get('yolo')) {
|
||||
fail('create should not have been called');
|
||||
}
|
||||
},
|
||||
update(object, original) {
|
||||
if (object.get('yolo') === original.get('yolo')) {
|
||||
fail('create should not have been called');
|
||||
}
|
||||
},
|
||||
};
|
||||
const createSpy = spyOn(spy, 'create').and.callThrough();
|
||||
const updateSpy = spyOn(spy, 'update').and.callThrough();
|
||||
subscription.on('create', spy.create);
|
||||
subscription.on('update', spy.update);
|
||||
const obj = new Parse.Object('Test');
|
||||
obj.set('foo', 'bar');
|
||||
await obj.save();
|
||||
obj.set('foo', 'xyz');
|
||||
obj.set('yolo', 'xyz');
|
||||
await obj.save();
|
||||
const obj2 = new Parse.Object('Test');
|
||||
obj2.set('foo', 'bar');
|
||||
obj2.set('yolo', 'bar');
|
||||
await obj2.save();
|
||||
obj2.set('foo', 'bart');
|
||||
await obj2.save();
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
expect(createSpy).toHaveBeenCalledTimes(1);
|
||||
expect(updateSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('can handle afterEvent set pointers', async done => {
|
||||
await reconfigureServer({
|
||||
liveQuery: {
|
||||
|
||||
@@ -1087,6 +1087,61 @@ describe('ParseLiveQueryServer', function () {
|
||||
done();
|
||||
});
|
||||
|
||||
it('can handle create command with watch', async () => {
|
||||
jasmine.restoreLibrary('../lib/LiveQuery/Client', 'Client');
|
||||
const Client = require('../lib/LiveQuery/Client').Client;
|
||||
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
||||
// Make mock request message
|
||||
const message = generateMockMessage();
|
||||
|
||||
const clientId = 1;
|
||||
const parseWebSocket = {
|
||||
clientId,
|
||||
send: jasmine.createSpy('send'),
|
||||
};
|
||||
const client = new Client(clientId, parseWebSocket);
|
||||
spyOn(client, 'pushCreate').and.callThrough();
|
||||
parseLiveQueryServer.clients.set(clientId, client);
|
||||
|
||||
// Add mock subscription
|
||||
const requestId = 2;
|
||||
const query = {
|
||||
className: testClassName,
|
||||
where: {
|
||||
key: 'value',
|
||||
},
|
||||
watch: ['yolo'],
|
||||
};
|
||||
await addMockSubscription(parseLiveQueryServer, clientId, requestId, parseWebSocket, query);
|
||||
// Mock _matchesSubscription to return matching
|
||||
parseLiveQueryServer._matchesSubscription = function (parseObject) {
|
||||
if (!parseObject) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
parseLiveQueryServer._matchesACL = function () {
|
||||
return Promise.resolve(true);
|
||||
};
|
||||
|
||||
parseLiveQueryServer._onAfterSave(message);
|
||||
|
||||
// Make sure we send create command to client
|
||||
await timeout();
|
||||
|
||||
expect(client.pushCreate).not.toHaveBeenCalled();
|
||||
|
||||
message.currentParseObject.set('yolo', 'test');
|
||||
parseLiveQueryServer._onAfterSave(message);
|
||||
|
||||
await timeout();
|
||||
|
||||
const args = parseWebSocket.send.calls.mostRecent().args;
|
||||
const toSend = JSON.parse(args[0]);
|
||||
expect(toSend.object).toBeDefined();
|
||||
expect(toSend.original).toBeUndefined();
|
||||
});
|
||||
|
||||
it('can match subscription for null or undefined parse object', function () {
|
||||
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
||||
// Make mock subscription
|
||||
|
||||
@@ -22,6 +22,7 @@ import { getCacheController, getDatabaseController } from '../Controllers';
|
||||
import LRU from 'lru-cache';
|
||||
import UserRouter from '../Routers/UsersRouter';
|
||||
import DatabaseController from '../Controllers/DatabaseController';
|
||||
import { isDeepStrictEqual } from 'util';
|
||||
|
||||
class ParseLiveQueryServer {
|
||||
clients: Map;
|
||||
@@ -329,6 +330,10 @@ class ParseLiveQueryServer {
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
const watchFieldsChanged = this._checkWatchFields(client, requestId, message);
|
||||
if (!watchFieldsChanged && (type === 'update' || type === 'create')) {
|
||||
return;
|
||||
}
|
||||
res = {
|
||||
event: type,
|
||||
sessionToken: client.sessionToken,
|
||||
@@ -707,6 +712,17 @@ class ParseLiveQueryServer {
|
||||
return auth;
|
||||
}
|
||||
|
||||
_checkWatchFields(client: any, requestId: any, message: any) {
|
||||
const subscriptionInfo = client.getSubscriptionInfo(requestId);
|
||||
const watch = subscriptionInfo?.watch;
|
||||
if (!watch) {
|
||||
return true;
|
||||
}
|
||||
const object = message.currentParseObject;
|
||||
const original = message.originalParseObject;
|
||||
return watch.some(field => !isDeepStrictEqual(object.get(field), original?.get(field)));
|
||||
}
|
||||
|
||||
async _matchesACL(acl: any, client: any, requestId: number): Promise<boolean> {
|
||||
// Return true directly if ACL isn't present, ACL is public read, or client has master key
|
||||
if (!acl || acl.getPublicReadAccess() || client.hasMasterKey) {
|
||||
@@ -888,6 +904,9 @@ class ParseLiveQueryServer {
|
||||
if (request.query.fields) {
|
||||
subscriptionInfo.fields = request.query.fields;
|
||||
}
|
||||
if (request.query.watch) {
|
||||
subscriptionInfo.watch = request.query.watch;
|
||||
}
|
||||
if (request.sessionToken) {
|
||||
subscriptionInfo.sessionToken = request.sessionToken;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user