fix: afterSave trigger removes pointer in Parse object (#7913)
This commit is contained in:
@@ -1598,6 +1598,32 @@ describe('Cloud Code', () => {
|
|||||||
expect(obj.get('count')).toBe(0);
|
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 => {
|
it('beforeSave should not sanitize database', async done => {
|
||||||
const { adapter } = Config.get(Parse.applicationId).database;
|
const { adapter } = Config.get(Parse.applicationId).database;
|
||||||
const spy = spyOn(adapter, 'findOneAndUpdate').and.callThrough();
|
const spy = spyOn(adapter, 'findOneAndUpdate').and.callThrough();
|
||||||
|
|||||||
@@ -429,4 +429,26 @@ describe('RestQuery.each', () => {
|
|||||||
done();
|
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',
|
||||||
|
]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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({
|
const requiredColumns = Object.freeze({
|
||||||
_Product: ['productIdentifier', 'icon', 'order', 'title', 'subtitle'],
|
read: {
|
||||||
_Role: ['name', 'ACL'],
|
_User: ['username'],
|
||||||
|
},
|
||||||
|
write: {
|
||||||
|
_Product: ['productIdentifier', 'icon', 'order', 'title', 'subtitle'],
|
||||||
|
_Role: ['name', 'ACL'],
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const invalidColumns = ['length'];
|
const invalidColumns = ['length'];
|
||||||
@@ -1269,7 +1275,7 @@ export default class SchemaController {
|
|||||||
|
|
||||||
// Validates that all the properties are set for the object
|
// Validates that all the properties are set for the object
|
||||||
validateRequiredColumns(className: string, object: any, query: any) {
|
validateRequiredColumns(className: string, object: any, query: any) {
|
||||||
const columns = requiredColumns[className];
|
const columns = requiredColumns.write[className];
|
||||||
if (!columns || columns.length == 0) {
|
if (!columns || columns.length == 0) {
|
||||||
return Promise.resolve(this);
|
return Promise.resolve(this);
|
||||||
}
|
}
|
||||||
@@ -1600,4 +1606,5 @@ export {
|
|||||||
convertSchemaToAdapterSchema,
|
convertSchemaToAdapterSchema,
|
||||||
VolatileClassesSchemas,
|
VolatileClassesSchemas,
|
||||||
SchemaController,
|
SchemaController,
|
||||||
|
requiredColumns,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ var ClientSDK = require('./ClientSDK');
|
|||||||
import RestQuery from './RestQuery';
|
import RestQuery from './RestQuery';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import logger from './logger';
|
import logger from './logger';
|
||||||
|
import { requiredColumns } from './Controllers/SchemaController';
|
||||||
|
|
||||||
// query and data are both provided in REST API format. So data
|
// query and data are both provided in REST API format. So data
|
||||||
// types are encoded by plain old objects.
|
// types are encoded by plain old objects.
|
||||||
@@ -1556,7 +1557,7 @@ RestWrite.prototype.runAfterSaveTrigger = function () {
|
|||||||
this.response.response = result;
|
this.response.response = result;
|
||||||
} else {
|
} else {
|
||||||
this.response.response = this._updateResponseWithData(
|
this.response.response = this._updateResponseWithData(
|
||||||
(result || updatedObject)._toFullJSON(),
|
(result || updatedObject).toJSON(),
|
||||||
this.data
|
this.data
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1665,6 +1666,21 @@ RestWrite.prototype._updateResponseWithData = function (response, data) {
|
|||||||
this.storage.fieldsChangedByTrigger.push(key);
|
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)) {
|
if (_.isEmpty(this.storage.fieldsChangedByTrigger)) {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user