diff --git a/spec/CloudCode.spec.js b/spec/CloudCode.spec.js index 8c2802d3e5..c6bc8b25c8 100644 --- a/spec/CloudCode.spec.js +++ b/spec/CloudCode.spec.js @@ -557,4 +557,36 @@ describe('Cloud Code', () => { }); }); }); + + it('trivial beforeSave should not affect fetched pointers (regression test for #1238)', done => { + Parse.Cloud.beforeSave('BeforeSaveUnchanged', (req, res) => { + res.success(); + }); + + var TestObject = Parse.Object.extend("TestObject"); + var NoBeforeSaveObject = Parse.Object.extend("NoBeforeSave"); + var BeforeSaveObject = Parse.Object.extend("BeforeSaveUnchanged"); + + var aTestObject = new TestObject(); + aTestObject.set("foo", "bar"); + aTestObject.save() + .then(aTestObject => { + var aNoBeforeSaveObj = new NoBeforeSaveObject(); + aNoBeforeSaveObj.set("aTestObject", aTestObject); + expect(aNoBeforeSaveObj.get("aTestObject").get("foo")).toEqual("bar"); + return aNoBeforeSaveObj.save(); + }) + .then(aNoBeforeSaveObj => { + expect(aNoBeforeSaveObj.get("aTestObject").get("foo")).toEqual("bar"); + + var aBeforeSaveObj = new BeforeSaveObject(); + aBeforeSaveObj.set("aTestObject", aTestObject); + expect(aBeforeSaveObj.get("aTestObject").get("foo")).toEqual("bar"); + return aBeforeSaveObj.save(); + }) + .then(aBeforeSaveObj => { + expect(aBeforeSaveObj.get("aTestObject").get("foo")).toEqual("bar"); + done(); + }); + }); }); diff --git a/src/RestWrite.js b/src/RestWrite.js index be460d4c48..fc40a71031 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -12,6 +12,7 @@ var passwordCrypto = require('./password'); var Parse = require('parse/node'); var triggers = require('./triggers'); import RestQuery from './RestQuery'; +import _ from 'lodash'; // query and data are both provided in REST API format. So data // types are encoded by plain old objects. @@ -165,8 +166,10 @@ RestWrite.prototype.runBeforeTrigger = function() { return triggers.maybeRunTrigger(triggers.Types.beforeSave, this.auth, updatedObject, originalObject, this.config); }).then((response) => { if (response && response.object) { + if (!_.isEqual(this.data, response.object)) { + this.storage.changedByTrigger = true; + } this.data = response.object; - this.storage['changedByTrigger'] = true; // We should delete the objectId for an update write if (this.query && this.query.objectId) { delete this.data.objectId @@ -733,19 +736,16 @@ RestWrite.prototype.runDatabaseOperation = function() { this.data.ACL[this.query.objectId] = { read: true, write: true }; } // Run an update - return this.config.database.update( - this.className, this.query, this.data, this.runOptions).then((resp) => { - resp.updatedAt = this.updatedAt; - if (this.storage['changedByTrigger']) { - resp = Object.keys(this.data).reduce((memo, key) => { - memo[key] = resp[key] || this.data[key]; - return memo; - }, resp); - } - this.response = { - response: resp - }; - }); + return this.config.database.update(this.className, this.query, this.data, this.runOptions) + .then(response => { + response.updatedAt = this.updatedAt; + if (this.storage.changedByTrigger) { + Object.keys(this.data).forEach(fieldName => { + response[fieldName] = response[fieldName] || this.data[fieldName]; + }); + } + this.response = { response }; + }); } else { // Set the default ACL for the new _User if (this.className === '_User') { @@ -762,23 +762,20 @@ RestWrite.prototype.runDatabaseOperation = function() { // Run a create return this.config.database.create(this.className, this.data, this.runOptions) - .then((resp) => { - Object.assign(resp, { - objectId: this.data.objectId, - createdAt: this.data.createdAt + .then(response => { + response.objectId = this.data.objectId; + response.createdAt = this.data.createdAt; + if (this.storage.changedByTrigger) { + Object.keys(this.data).forEach(fieldName => { + response[fieldName] = response[fieldName] || this.data[fieldName]; }); - if (this.storage['changedByTrigger']) { - resp = Object.keys(this.data).reduce((memo, key) => { - memo[key] = resp[key] || this.data[key]; - return memo; - }, resp); - } - this.response = { - status: 201, - response: resp, - location: this.location() - }; - }); + } + this.response = { + status: 201, + response, + location: this.location() + }; + }); } };