Skip to content

Commit

Permalink
fix: afterSave trigger removes pointer in Parse object (#7913)
Browse files Browse the repository at this point in the history
  • Loading branch information
dblythy authored May 20, 2022
1 parent 2e750b7 commit 47d796e
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 4 deletions.
26 changes: 26 additions & 0 deletions spec/CloudCode.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1598,6 +1598,32 @@ describe('Cloud Code', () => {
expect(obj.get('count')).toBe(0);
});

it('pointer should not be cleared by triggers', async () => {
Parse.Cloud.afterSave('MyObject', () => {});
const foo = await new Parse.Object('Test', { foo: 'bar' }).save();
const obj = await new Parse.Object('MyObject', { foo }).save();
const foo2 = obj.get('foo');
expect(foo2.get('foo')).toBe('bar');
});

it('can set a pointer in triggers', async () => {
Parse.Cloud.beforeSave('MyObject', () => {});
Parse.Cloud.afterSave(
'MyObject',
async ({ object }) => {
const foo = await new Parse.Object('Test', { foo: 'bar' }).save();
object.set({ foo });
await object.save(null, { useMasterKey: true });
},
{
skipWithMasterKey: true,
}
);
const obj = await new Parse.Object('MyObject').save();
const foo2 = obj.get('foo');
expect(foo2.get('foo')).toBe('bar');
});

it('beforeSave should not sanitize database', async done => {
const { adapter } = Config.get(Parse.applicationId).database;
const spy = spyOn(adapter, 'findOneAndUpdate').and.callThrough();
Expand Down
22 changes: 22 additions & 0 deletions spec/RestQuery.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -429,4 +429,26 @@ describe('RestQuery.each', () => {
done();
});
});

it('test afterSave should not affect save response', async () => {
Parse.Cloud.beforeSave('TestObject2', ({ object }) => {
object.set('addedBeforeSave', true);
});
Parse.Cloud.afterSave('TestObject2', ({ object }) => {
object.set('addedAfterSave', true);
object.unset('initialToRemove');
});
const { response } = await rest.create(config, nobody, 'TestObject2', {
initialSave: true,
initialToRemove: true,
});
expect(Object.keys(response).sort()).toEqual([
'addedAfterSave',
'addedBeforeSave',
'createdAt',
'initialToRemove',
'objectId',
'updatedAt',
]);
});
});
13 changes: 10 additions & 3 deletions src/Controllers/SchemaController.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,15 @@ const defaultColumns: { [string]: SchemaFields } = Object.freeze({
},
});

// fields required for read or write operations on their respective classes.
const requiredColumns = Object.freeze({
_Product: ['productIdentifier', 'icon', 'order', 'title', 'subtitle'],
_Role: ['name', 'ACL'],
read: {
_User: ['username'],
},
write: {
_Product: ['productIdentifier', 'icon', 'order', 'title', 'subtitle'],
_Role: ['name', 'ACL'],
}
});

const invalidColumns = ['length'];
Expand Down Expand Up @@ -1269,7 +1275,7 @@ export default class SchemaController {

// Validates that all the properties are set for the object
validateRequiredColumns(className: string, object: any, query: any) {
const columns = requiredColumns[className];
const columns = requiredColumns.write[className];
if (!columns || columns.length == 0) {
return Promise.resolve(this);
}
Expand Down Expand Up @@ -1600,4 +1606,5 @@ export {
convertSchemaToAdapterSchema,
VolatileClassesSchemas,
SchemaController,
requiredColumns,
};
18 changes: 17 additions & 1 deletion src/RestWrite.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ var ClientSDK = require('./ClientSDK');
import RestQuery from './RestQuery';
import _ from 'lodash';
import logger from './logger';
import { requiredColumns } from './Controllers/SchemaController';

// query and data are both provided in REST API format. So data
// types are encoded by plain old objects.
Expand Down Expand Up @@ -1556,7 +1557,7 @@ RestWrite.prototype.runAfterSaveTrigger = function () {
this.response.response = result;
} else {
this.response.response = this._updateResponseWithData(
(result || updatedObject)._toFullJSON(),
(result || updatedObject).toJSON(),
this.data
);
}
Expand Down Expand Up @@ -1665,6 +1666,21 @@ RestWrite.prototype._updateResponseWithData = function (response, data) {
this.storage.fieldsChangedByTrigger.push(key);
}
}
const skipKeys = [
'objectId',
'createdAt',
'updatedAt',
...(requiredColumns.read[this.className] || []),
];
for (const key in response) {
if (skipKeys.includes(key)) {
continue;
}
const value = response[key];
if (value == null || (value.__type && value.__type === 'Pointer') || data[key] === value) {
delete response[key];
}
}
if (_.isEmpty(this.storage.fieldsChangedByTrigger)) {
return response;
}
Expand Down

0 comments on commit 47d796e

Please sign in to comment.