Fix issue with pointers getting un-hydrated when there is a beforeSave (#1884)

* Add failing test and simplify RestWrite

* simplify and add test stubs

* Fix issue
This commit is contained in:
Drew
2016-05-23 17:13:32 -07:00
parent 614e1ac8e5
commit 3a96140103
2 changed files with 59 additions and 30 deletions

View File

@@ -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();
});
});
}); });

View File

@@ -12,6 +12,7 @@ var passwordCrypto = require('./password');
var Parse = require('parse/node'); var Parse = require('parse/node');
var triggers = require('./triggers'); var triggers = require('./triggers');
import RestQuery from './RestQuery'; import RestQuery from './RestQuery';
import _ from 'lodash';
// 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.
@@ -165,8 +166,10 @@ RestWrite.prototype.runBeforeTrigger = function() {
return triggers.maybeRunTrigger(triggers.Types.beforeSave, this.auth, updatedObject, originalObject, this.config); return triggers.maybeRunTrigger(triggers.Types.beforeSave, this.auth, updatedObject, originalObject, this.config);
}).then((response) => { }).then((response) => {
if (response && response.object) { if (response && response.object) {
if (!_.isEqual(this.data, response.object)) {
this.storage.changedByTrigger = true;
}
this.data = response.object; this.data = response.object;
this.storage['changedByTrigger'] = true;
// We should delete the objectId for an update write // We should delete the objectId for an update write
if (this.query && this.query.objectId) { if (this.query && this.query.objectId) {
delete this.data.objectId delete this.data.objectId
@@ -733,19 +736,16 @@ RestWrite.prototype.runDatabaseOperation = function() {
this.data.ACL[this.query.objectId] = { read: true, write: true }; this.data.ACL[this.query.objectId] = { read: true, write: true };
} }
// Run an update // Run an update
return this.config.database.update( return this.config.database.update(this.className, this.query, this.data, this.runOptions)
this.className, this.query, this.data, this.runOptions).then((resp) => { .then(response => {
resp.updatedAt = this.updatedAt; response.updatedAt = this.updatedAt;
if (this.storage['changedByTrigger']) { if (this.storage.changedByTrigger) {
resp = Object.keys(this.data).reduce((memo, key) => { Object.keys(this.data).forEach(fieldName => {
memo[key] = resp[key] || this.data[key]; response[fieldName] = response[fieldName] || this.data[fieldName];
return memo; });
}, resp); }
} this.response = { response };
this.response = { });
response: resp
};
});
} else { } else {
// Set the default ACL for the new _User // Set the default ACL for the new _User
if (this.className === '_User') { if (this.className === '_User') {
@@ -762,23 +762,20 @@ RestWrite.prototype.runDatabaseOperation = function() {
// Run a create // Run a create
return this.config.database.create(this.className, this.data, this.runOptions) return this.config.database.create(this.className, this.data, this.runOptions)
.then((resp) => { .then(response => {
Object.assign(resp, { response.objectId = this.data.objectId;
objectId: this.data.objectId, response.createdAt = this.data.createdAt;
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) => { this.response = {
memo[key] = resp[key] || this.data[key]; status: 201,
return memo; response,
}, resp); location: this.location()
} };
this.response = { });
status: 201,
response: resp,
location: this.location()
};
});
} }
}; };