fix: set objects in afterFind triggers (#7311)
This commit is contained in:
@@ -154,6 +154,7 @@ ___
|
|||||||
- Refactor: uniform issue templates across repos (Manuel Trezza) [#7528](https://github.com/parse-community/parse-server/pull/7528)
|
- Refactor: uniform issue templates across repos (Manuel Trezza) [#7528](https://github.com/parse-community/parse-server/pull/7528)
|
||||||
- ci: bump ci environment (Manuel Trezza) [#7539](https://github.com/parse-community/parse-server/pull/7539)
|
- ci: bump ci environment (Manuel Trezza) [#7539](https://github.com/parse-community/parse-server/pull/7539)
|
||||||
- CI now pushes docker images to Docker Hub (Corey Baker) [#7548](https://github.com/parse-community/parse-server/pull/7548)
|
- CI now pushes docker images to Docker Hub (Corey Baker) [#7548](https://github.com/parse-community/parse-server/pull/7548)
|
||||||
|
- Allow afterFind and afterLiveQueryEvent to set unsaved pointers and keys (dblythy) [#7310](https://github.com/parse-community/parse-server/pull/7310)
|
||||||
- Allow setting descending sort to full text queries (dblythy) [#7496](https://github.com/parse-community/parse-server/pull/7496)
|
- Allow setting descending sort to full text queries (dblythy) [#7496](https://github.com/parse-community/parse-server/pull/7496)
|
||||||
- Allow cloud string for ES modules (Daniel Blyth) [#7560](https://github.com/parse-community/parse-server/pull/7560)
|
- Allow cloud string for ES modules (Daniel Blyth) [#7560](https://github.com/parse-community/parse-server/pull/7560)
|
||||||
- docs: Introduce deprecation ID for reference in comments and online search (Manuel Trezza) [#7562](https://github.com/parse-community/parse-server/pull/7562)
|
- docs: Introduce deprecation ID for reference in comments and online search (Manuel Trezza) [#7562](https://github.com/parse-community/parse-server/pull/7562)
|
||||||
|
|||||||
@@ -2391,6 +2391,53 @@ describe('afterFind hooks', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can set a pointer object in afterFind', async () => {
|
||||||
|
const obj = new Parse.Object('MyObject');
|
||||||
|
await obj.save();
|
||||||
|
Parse.Cloud.afterFind('MyObject', async ({ objects }) => {
|
||||||
|
const otherObject = new Parse.Object('Test');
|
||||||
|
otherObject.set('foo', 'bar');
|
||||||
|
await otherObject.save();
|
||||||
|
objects[0].set('Pointer', otherObject);
|
||||||
|
objects[0].set('xyz', 'yolo');
|
||||||
|
expect(objects[0].get('Pointer').get('foo')).toBe('bar');
|
||||||
|
});
|
||||||
|
const query = new Parse.Query('MyObject');
|
||||||
|
query.equalTo('objectId', obj.id);
|
||||||
|
const obj2 = await query.first();
|
||||||
|
expect(obj2.get('xyz')).toBe('yolo');
|
||||||
|
const pointer = obj2.get('Pointer');
|
||||||
|
expect(pointer.get('foo')).toBe('bar');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can set invalid object in afterFind', async () => {
|
||||||
|
const obj = new Parse.Object('MyObject');
|
||||||
|
await obj.save();
|
||||||
|
Parse.Cloud.afterFind('MyObject', () => [{}]);
|
||||||
|
const query = new Parse.Query('MyObject');
|
||||||
|
query.equalTo('objectId', obj.id);
|
||||||
|
const obj2 = await query.first();
|
||||||
|
expect(obj2).toBeDefined();
|
||||||
|
expect(obj2.toJSON()).toEqual({});
|
||||||
|
expect(obj2.id).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can return a unsaved object in afterFind', async () => {
|
||||||
|
const obj = new Parse.Object('MyObject');
|
||||||
|
await obj.save();
|
||||||
|
Parse.Cloud.afterFind('MyObject', async () => {
|
||||||
|
const otherObject = new Parse.Object('Test');
|
||||||
|
otherObject.set('foo', 'bar');
|
||||||
|
return [otherObject];
|
||||||
|
});
|
||||||
|
const query = new Parse.Query('MyObject');
|
||||||
|
const obj2 = await query.first();
|
||||||
|
expect(obj2.get('foo')).toEqual('bar');
|
||||||
|
expect(obj2.id).toBeUndefined();
|
||||||
|
await obj2.save();
|
||||||
|
expect(obj2.id).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
it('should have request headers', done => {
|
it('should have request headers', done => {
|
||||||
Parse.Cloud.afterFind('MyObject', req => {
|
Parse.Cloud.afterFind('MyObject', req => {
|
||||||
expect(req.headers).toBeDefined();
|
expect(req.headers).toBeDefined();
|
||||||
|
|||||||
@@ -358,6 +358,44 @@ describe('ParseLiveQuery', function () {
|
|||||||
await object.save();
|
await object.save();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can handle afterEvent set pointers', async done => {
|
||||||
|
await reconfigureServer({
|
||||||
|
liveQuery: {
|
||||||
|
classNames: ['TestObject'],
|
||||||
|
},
|
||||||
|
startLiveQueryServer: true,
|
||||||
|
verbose: false,
|
||||||
|
silent: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const object = new TestObject();
|
||||||
|
await object.save();
|
||||||
|
|
||||||
|
const secondObject = new Parse.Object('Test2');
|
||||||
|
secondObject.set('foo', 'bar');
|
||||||
|
await secondObject.save();
|
||||||
|
|
||||||
|
Parse.Cloud.afterLiveQueryEvent('TestObject', async ({ object }) => {
|
||||||
|
const query = new Parse.Query('Test2');
|
||||||
|
const obj = await query.first();
|
||||||
|
object.set('obj', obj);
|
||||||
|
});
|
||||||
|
|
||||||
|
const query = new Parse.Query(TestObject);
|
||||||
|
query.equalTo('objectId', object.id);
|
||||||
|
const subscription = await query.subscribe();
|
||||||
|
subscription.on('update', object => {
|
||||||
|
expect(object.get('obj')).toBeDefined();
|
||||||
|
expect(object.get('obj').get('foo')).toBe('bar');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
subscription.on('error', () => {
|
||||||
|
fail('error should not have been called.');
|
||||||
|
});
|
||||||
|
object.set({ foo: 'bar' });
|
||||||
|
await object.save();
|
||||||
|
});
|
||||||
|
|
||||||
it('can handle async afterEvent modification', async done => {
|
it('can handle async afterEvent modification', async done => {
|
||||||
await reconfigureServer({
|
await reconfigureServer({
|
||||||
liveQuery: {
|
liveQuery: {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { ParsePubSub } from './ParsePubSub';
|
|||||||
import SchemaController from '../Controllers/SchemaController';
|
import SchemaController from '../Controllers/SchemaController';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { runLiveQueryEventHandlers, getTrigger, runTrigger } from '../triggers';
|
import { runLiveQueryEventHandlers, getTrigger, runTrigger, toJSONwithObjects } from '../triggers';
|
||||||
import { getAuthForSessionToken, Auth } from '../Auth';
|
import { getAuthForSessionToken, Auth } from '../Auth';
|
||||||
import { getCacheController } from '../Controllers';
|
import { getCacheController } from '../Controllers';
|
||||||
import LRU from 'lru-cache';
|
import LRU from 'lru-cache';
|
||||||
@@ -183,8 +183,7 @@ class ParseLiveQueryServer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (res.object && typeof res.object.toJSON === 'function') {
|
if (res.object && typeof res.object.toJSON === 'function') {
|
||||||
deletedParseObject = res.object.toJSON();
|
deletedParseObject = toJSONwithObjects(res.object, res.object.className || className);
|
||||||
deletedParseObject.className = className;
|
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
(deletedParseObject.className === '_User' ||
|
(deletedParseObject.className === '_User' ||
|
||||||
@@ -337,13 +336,13 @@ class ParseLiveQueryServer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (res.object && typeof res.object.toJSON === 'function') {
|
if (res.object && typeof res.object.toJSON === 'function') {
|
||||||
currentParseObject = res.object.toJSON();
|
currentParseObject = toJSONwithObjects(res.object, res.object.className || className);
|
||||||
currentParseObject.className = res.object.className || className;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res.original && typeof res.original.toJSON === 'function') {
|
if (res.original && typeof res.original.toJSON === 'function') {
|
||||||
originalParseObject = res.original.toJSON();
|
originalParseObject = toJSONwithObjects(
|
||||||
originalParseObject.className = res.original.className || className;
|
res.original,
|
||||||
|
res.original.className || className
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
(currentParseObject.className === '_User' ||
|
(currentParseObject.className === '_User' ||
|
||||||
|
|||||||
@@ -168,6 +168,27 @@ export function _unregisterAll() {
|
|||||||
Object.keys(_triggerStore).forEach(appId => delete _triggerStore[appId]);
|
Object.keys(_triggerStore).forEach(appId => delete _triggerStore[appId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function toJSONwithObjects(object, className) {
|
||||||
|
if (!object || !object.toJSON) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const toJSON = object.toJSON();
|
||||||
|
const stateController = Parse.CoreManager.getObjectStateController();
|
||||||
|
const [pending] = stateController.getPendingOps(object._getStateIdentifier());
|
||||||
|
for (const key in pending) {
|
||||||
|
const val = object.get(key);
|
||||||
|
if (!val || !val._toFullJSON) {
|
||||||
|
toJSON[key] = val;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
toJSON[key] = val._toFullJSON();
|
||||||
|
}
|
||||||
|
if (className) {
|
||||||
|
toJSON.className = className;
|
||||||
|
}
|
||||||
|
return toJSON;
|
||||||
|
}
|
||||||
|
|
||||||
export function getTrigger(className, triggerType, applicationId) {
|
export function getTrigger(className, triggerType, applicationId) {
|
||||||
if (!applicationId) {
|
if (!applicationId) {
|
||||||
throw 'Missing ApplicationID';
|
throw 'Missing ApplicationID';
|
||||||
@@ -323,7 +344,7 @@ export function getResponseObject(request, resolve, reject) {
|
|||||||
response = request.objects;
|
response = request.objects;
|
||||||
}
|
}
|
||||||
response = response.map(object => {
|
response = response.map(object => {
|
||||||
return object.toJSON();
|
return toJSONwithObjects(object);
|
||||||
});
|
});
|
||||||
return resolve(response);
|
return resolve(response);
|
||||||
}
|
}
|
||||||
@@ -451,12 +472,6 @@ export function maybeRunAfterFindTrigger(
|
|||||||
const response = trigger(request);
|
const response = trigger(request);
|
||||||
if (response && typeof response.then === 'function') {
|
if (response && typeof response.then === 'function') {
|
||||||
return response.then(results => {
|
return response.then(results => {
|
||||||
if (!results) {
|
|
||||||
throw new Parse.Error(
|
|
||||||
Parse.Error.SCRIPT_FAILED,
|
|
||||||
'AfterFind expect results to be returned in the promise'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return results;
|
return results;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user