Skip to content

Commit 898bbfe

Browse files
committed
feat: Add a new beforeLiveQueryEvent trigger
1 parent b537e5a commit 898bbfe

File tree

4 files changed

+188
-14
lines changed

4 files changed

+188
-14
lines changed

spec/ParseLiveQuery.spec.js

+124
Original file line numberDiff line numberDiff line change
@@ -1308,4 +1308,128 @@ describe('ParseLiveQuery', function () {
13081308
await new Promise(resolve => setTimeout(resolve, 100));
13091309
expect(createSpy).toHaveBeenCalledTimes(1);
13101310
});
1311+
1312+
it('test beforeLiveQueryEvent ran while creating an object', async function () {
1313+
await reconfigureServer({
1314+
liveQuery: {
1315+
classNames: ['TestObject'],
1316+
},
1317+
startLiveQueryServer: true,
1318+
verbose: false,
1319+
silent: true,
1320+
});
1321+
1322+
1323+
Parse.Cloud.beforeLiveQueryEvent('TestObject', req => {
1324+
expect(req.user).toBeUndefined();
1325+
expect(req.object.get('foo')).toBe('bar');
1326+
})
1327+
1328+
const query = new Parse.Query(TestObject);
1329+
const subscription = await query.subscribe();
1330+
subscription.on('create', object => {
1331+
expect(object.get('foo')).toBe('bar');
1332+
done();
1333+
});
1334+
1335+
const object = new TestObject();
1336+
object.set('foo', 'bar');
1337+
await object.save();
1338+
});
1339+
1340+
it('test beforeLiveQueryEvent ran while updating an object', async function (done) {
1341+
await reconfigureServer({
1342+
liveQuery: {
1343+
classNames: ['TestObject'],
1344+
},
1345+
startLiveQueryServer: true,
1346+
verbose: false,
1347+
silent: true,
1348+
});
1349+
const object = new TestObject();
1350+
object.set('foo', 'bar');
1351+
await object.save();
1352+
1353+
Parse.Cloud.afterSave('TestObject', async req => {
1354+
expect(req.object.get('foo')).toBe('baz');
1355+
})
1356+
Parse.Cloud.beforeLiveQueryEvent('TestObject', async req => {
1357+
expect(req.object.get('foo')).toBe('baz');
1358+
req.object.set('foo', 'rebaz');
1359+
})
1360+
Parse.Cloud.afterLiveQueryEvent('TestObject', req => {
1361+
expect(req.event).toBe('update');
1362+
expect(req.user).toBeUndefined();
1363+
expect(req.object.get('foo')).toBe('rebaz');
1364+
});
1365+
1366+
const query = new Parse.Query(TestObject);
1367+
const subscription = await query.subscribe();
1368+
subscription.on('update', object => {
1369+
expect(object.get('foo')).toBe('rebaz');
1370+
done();
1371+
});
1372+
1373+
object.set('foo', 'baz')
1374+
await object.save();
1375+
});
1376+
1377+
it('test beforeLiveQueryEvent should filter specific object creation', async function () {
1378+
await reconfigureServer({
1379+
liveQuery: {
1380+
classNames: ['TestObject'],
1381+
},
1382+
startLiveQueryServer: true,
1383+
verbose: false,
1384+
silent: true,
1385+
});
1386+
1387+
1388+
Parse.Cloud.beforeLiveQueryEvent('TestObject', req => {
1389+
expect(req.object.get('foo')).toBe('bar');
1390+
if (req.object.get('foo') === 'bar') {
1391+
req.context.preventLiveQuery === true;
1392+
}
1393+
})
1394+
1395+
const query = new Parse.Query(TestObject).equalTo('foo', 'bar');
1396+
const subscription = await query.subscribe();
1397+
subscription.on('create', () => {
1398+
fail('create should not have been called.');
1399+
});
1400+
1401+
const object = new TestObject();
1402+
object.set('foo', 'bar');
1403+
await object.save();
1404+
});
1405+
1406+
it('test beforeLiveQueryEvent should filter specific object update', async function () {
1407+
await reconfigureServer({
1408+
liveQuery: {
1409+
classNames: ['TestObject'],
1410+
},
1411+
startLiveQueryServer: true,
1412+
verbose: false,
1413+
silent: true,
1414+
});
1415+
const object = new TestObject();
1416+
object.set('foo', 'bar');
1417+
await object.save();
1418+
1419+
Parse.Cloud.beforeLiveQueryEvent('TestObject', async req => {
1420+
expect(req.object.get('foo')).toBe('baz');
1421+
if (req.object.get('foo') === 'baz') {
1422+
req.context.preventLiveQuery === true;
1423+
}
1424+
})
1425+
1426+
const query = new Parse.Query(TestObject).equalTo('foo', 'baz');
1427+
const subscription = await query.subscribe();
1428+
subscription.on('update', object => {
1429+
fail('update should not have been called.');
1430+
});
1431+
1432+
object.set('foo', 'baz')
1433+
await object.save();
1434+
});
13111435
});

src/RestWrite.js

+22-12
Original file line numberDiff line numberDiff line change
@@ -1645,7 +1645,7 @@ RestWrite.prototype.runDatabaseOperation = function () {
16451645
};
16461646

16471647
// Returns nothing - doesn't wait for the trigger.
1648-
RestWrite.prototype.runAfterSaveTrigger = function () {
1648+
RestWrite.prototype.runAfterSaveTrigger = async function () {
16491649
if (!this.response || !this.response.response || this.runOptions.many) {
16501650
return;
16511651
}
@@ -1660,21 +1660,31 @@ RestWrite.prototype.runAfterSaveTrigger = function () {
16601660
if (!hasAfterSaveHook && !hasLiveQuery) {
16611661
return Promise.resolve();
16621662
}
1663-
16641663
const { originalObject, updatedObject } = this.buildParseObjects();
16651664
updatedObject._handleSaveResponse(this.response.response, this.response.status || 200);
16661665

16671666
if (hasLiveQuery) {
1668-
this.config.database.loadSchema().then(schemaController => {
1669-
// Notify LiveQueryServer if possible
1670-
const perms = schemaController.getClassLevelPermissions(updatedObject.className);
1671-
this.config.liveQueryController.onAfterSave(
1672-
updatedObject.className,
1673-
updatedObject,
1674-
originalObject,
1675-
perms
1676-
);
1677-
});
1667+
const hasBeforeEventHook = triggers.triggerExists(
1668+
this.className,
1669+
triggers.Types.beforeEvent,
1670+
this.config.applicationId
1671+
);
1672+
const publishedObject = updatedObject.clone();
1673+
if (hasBeforeEventHook) {
1674+
await triggers.maybeRunTrigger(triggers.Types.beforeEvent, this.auth, publishedObject, originalObject, this.config, this.context);
1675+
}
1676+
if (this.context.preventLiveQuery !== true) {
1677+
this.config.database.loadSchema().then(schemaController => {
1678+
// Notify LiveQueryServer if possible
1679+
const perms = schemaController.getClassLevelPermissions(publishedObject.className);
1680+
this.config.liveQueryController.onAfterSave(
1681+
publishedObject.className,
1682+
publishedObject,
1683+
originalObject,
1684+
perms
1685+
);
1686+
});
1687+
}
16781688
}
16791689
if (!hasAfterSaveHook) {
16801690
return Promise.resolve();

src/cloud-code/Parse.Cloud.js

+37
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,43 @@ ParseCloud.beforeSubscribe = function (parseClass, handler, validationHandler) {
604604
);
605605
};
606606

607+
/**
608+
* Registers a before live query event function.
609+
*
610+
* **Available in Cloud Code only.**
611+
*
612+
* If you want to use beforeLiveQueryEvent for a predefined class in the Parse JavaScript SDK (e.g. {@link Parse.User} or {@link Parse.File}), you should pass the class itself and not the String for arg1.
613+
* ```
614+
* Parse.Cloud.beforeLiveQueryEvent('MyCustomClass', (request) => {
615+
* // code here
616+
* }, (request) => {
617+
* // validation code here
618+
* });
619+
*
620+
* Parse.Cloud.beforeLiveQueryEvent(Parse.User, (request) => {
621+
* // code here
622+
* }, { ...validationObject });
623+
*```
624+
*
625+
* @method beforeLiveQueryEvent
626+
* @name Parse.Cloud.beforeLiveQueryEvent
627+
* @param {(String|Parse.Object)} arg1 The Parse.Object subclass to register the before live query event function for. This can instead be a String that is the className of the subclass.
628+
* @param {Function} func The function to run before a live query event (publish) is made. This function can be async and should take one parameter, a {@link Parse.Cloud.TriggerRequest}.
629+
* @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.TriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}.
630+
*/
631+
ParseCloud.beforeLiveQueryEvent = function (parseClass, handler, validationHandler) {
632+
validateValidator(validationHandler);
633+
const className = triggers.getClassName(parseClass);
634+
triggers.addTrigger(
635+
triggers.Types.beforeEvent,
636+
className,
637+
handler,
638+
Parse.applicationId,
639+
validationHandler
640+
);
641+
};
642+
643+
607644
ParseCloud.onLiveQueryEvent = function (handler) {
608645
triggers.addLiveQueryEventHandler(handler, Parse.applicationId);
609646
};

src/triggers.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export const Types = {
1414
afterFind: 'afterFind',
1515
beforeConnect: 'beforeConnect',
1616
beforeSubscribe: 'beforeSubscribe',
17+
beforeEvent: 'beforeEvent',
1718
afterEvent: 'afterEvent',
1819
};
1920

@@ -278,7 +279,8 @@ export function getRequestObject(
278279
triggerType === Types.afterDelete ||
279280
triggerType === Types.beforeLogin ||
280281
triggerType === Types.afterLogin ||
281-
triggerType === Types.afterFind
282+
triggerType === Types.afterFind ||
283+
triggerType === Types.beforeEvent
282284
) {
283285
// Set a copy of the context on the request object.
284286
request.context = Object.assign({}, context);
@@ -885,7 +887,8 @@ export function maybeRunTrigger(
885887
triggerType === Types.beforeSave ||
886888
triggerType === Types.afterSave ||
887889
triggerType === Types.beforeDelete ||
888-
triggerType === Types.afterDelete
890+
triggerType === Types.afterDelete ||
891+
triggerType === Types.beforeEvent
889892
) {
890893
Object.assign(context, request.context);
891894
}

0 commit comments

Comments
 (0)