Skip to content

Commit

Permalink
feat: Add ParseQuery.watch to trigger LiveQuery only on update of s…
Browse files Browse the repository at this point in the history
…pecific fields (#1839)
  • Loading branch information
dplewis committed Apr 2, 2023
1 parent a59ce65 commit 7479343
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 11 deletions.
42 changes: 40 additions & 2 deletions integration/test/ParseLiveQueryTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ describe('Parse LiveQuery', () => {
query.equalTo('objectId', object.id);
const subscription = await client.subscribe(query);
const promise = resolvingPromise();
subscription.on('update', async (object) => {
subscription.on('update', async object => {
assert.equal(object.get('foo'), 'bar');
await client.close();
promise.resolve();
Expand Down Expand Up @@ -206,7 +206,7 @@ describe('Parse LiveQuery', () => {
subscription.on('update', async object => {
assert.equal(object.get('foo'), 'bar');
await Parse.User.logOut();
promise.resolve()
promise.resolve();
});
await object.save({ foo: 'bar' });
await promise;
Expand Down Expand Up @@ -276,6 +276,44 @@ describe('Parse LiveQuery', () => {
await promise;
});

it('can subscribe to query with watch', async () => {
const query = new Parse.Query(TestObject);
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 TestObject();
obj.set('foo', 'bar');
await obj.save();
obj.set('foo', 'xyz');
obj.set('yolo', 'xyz');
await obj.save();
const obj2 = new TestObject();
obj2.set('foo', 'bar');
obj2.set('yolo', 'bar');
await obj2.save();
obj2.set('foo', 'bart');
await obj2.save();
await sleep(1000);
await subscription.unsubscribe();
expect(createSpy).toHaveBeenCalledTimes(1);
expect(updateSpy).toHaveBeenCalledTimes(1);
});

it('live query can handle beforeConnect and beforeSubscribe errors', async () => {
await reconfigureServer({
cloud({ Cloud }) {
Expand Down
4 changes: 3 additions & 1 deletion src/LiveQueryClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,14 +191,16 @@ class LiveQueryClient extends EventEmitter {
const className = query.className;
const queryJSON = query.toJSON();
const where = queryJSON.where;
const fields = queryJSON.keys ? queryJSON.keys.split(',') : undefined;
const fields = queryJSON.keys?.split(',');
const watch = queryJSON.watch?.split(',');
const subscribeRequest = {
op: OP_TYPES.SUBSCRIBE,
requestId: this.requestId,
query: {
className,
where,
fields,
watch,
},
};

Expand Down
29 changes: 29 additions & 0 deletions src/ParseQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export type WhereClause = {

export type QueryJSON = {
where: WhereClause,
watch?: string,
include?: string,
excludeKeys?: string,
keys?: string,
Expand Down Expand Up @@ -224,6 +225,7 @@ class ParseQuery {
*/
className: string;
_where: any;
_watch: Array<string>;
_include: Array<string>;
_exclude: Array<string>;
_select: Array<string>;
Expand Down Expand Up @@ -265,6 +267,7 @@ class ParseQuery {
}

this._where = {};
this._watch = [];
this._include = [];
this._exclude = [];
this._count = false;
Expand Down Expand Up @@ -426,6 +429,9 @@ class ParseQuery {
where: this._where,
};

if (this._watch.length) {
params.watch = this._watch.join(',');
}
if (this._include.length) {
params.include = this._include.join(',');
}
Expand Down Expand Up @@ -495,6 +501,10 @@ class ParseQuery {
this._where = json.where;
}

if (json.watch) {
this._watch = json.watch.split(',');
}

if (json.include) {
this._include = json.include.split(',');
}
Expand Down Expand Up @@ -1921,6 +1931,25 @@ class ParseQuery {
return this;
}

/**
* Restricts live query to trigger only for watched fields.
*
* Requires Parse Server 6.0.0+
*
* @param {...string|Array<string>} keys The name(s) of the key(s) to watch.
* @returns {Parse.Query} Returns the query, so you can chain this call.
*/
watch(...keys: Array<string | Array<string>>): ParseQuery {
keys.forEach(key => {
if (Array.isArray(key)) {
this._watch = this._watch.concat(key);
} else {
this._watch.push(key);
}
});
return this;
}

/**
* Changes the read preference that the backend will use when performing the query to the database.
*
Expand Down
38 changes: 30 additions & 8 deletions src/__tests__/ParseQuery-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,32 @@ describe('ParseQuery', () => {
expect(q2._exclude).toEqual(['foo', 'bar']);
});

it('can watch keys', () => {
const q = new ParseQuery('Item');
q.watch('foo');
const json = q.toJSON();
expect(json).toEqual({
where: {},
watch: 'foo',
});
const q2 = new ParseQuery('Item');
q2.withJSON(json);
expect(q2._watch).toEqual(['foo']);
});

it('can watch multiple keys', () => {
const q = new ParseQuery('Item');
q.watch(['foo', 'bar']);
const json = q.toJSON();
expect(json).toEqual({
where: {},
watch: 'foo,bar',
});
const q2 = new ParseQuery('Item');
q2.withJSON(json);
expect(q2._watch).toEqual(['foo', 'bar']);
});

it('can use extraOptions', () => {
const q = new ParseQuery('Item');
q._extraOptions.randomOption = 'test';
Expand Down Expand Up @@ -2334,8 +2360,7 @@ describe('ParseQuery', () => {

const q = new ParseQuery('Thing');
let testObject;
q
.find()
q.find()
.then(results => {
testObject = results[0];

Expand Down Expand Up @@ -2460,8 +2485,7 @@ describe('ParseQuery', () => {

const q = new ParseQuery('Thing');
let testObject;
q
.first()
q.first()
.then(result => {
testObject = result;

Expand Down Expand Up @@ -2875,8 +2899,7 @@ describe('ParseQuery', () => {
const q = new ParseQuery('Thing');
q.select('other', 'tbd', 'subObject.key1');
let testObject;
q
.find()
q.find()
.then(results => {
testObject = results[0];

Expand Down Expand Up @@ -2926,8 +2949,7 @@ describe('ParseQuery', () => {

const q = new ParseQuery('Thing');
let testObject;
q
.find()
q.find()
.then(results => {
testObject = results[0];

Expand Down

0 comments on commit 7479343

Please sign in to comment.