Skip to content

Commit e4c2e65

Browse files
sud80flovilmart
authored andcommitted
Non trivial before save pointer clobber (#2406)
* test case to check beforeSave changes clobbers fetched pointer fields Basically if beforeSave makes any changes to the object it is trying to save, the fetched pointer fields on the client gets clobbered to only pointer. * propogate only changed fields to response. Earlier we were returning all fields even if any changes happened in beforeSave. This causes the fetched pointer fields on the client to get clobbered to only pointers. This fix returns only the changed fields thus avoiding pointer clobber. * The goal of this comparision seems to be checking that the all returns the user correctly. Also it is consistent with the hosted parse that user.username not returned from PUT request.
1 parent eb1b2eb commit e4c2e65

File tree

3 files changed

+52
-12
lines changed

3 files changed

+52
-12
lines changed

spec/CloudCode.spec.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,44 @@ describe('Cloud Code', () => {
684684
});
685685
});
686686

687+
it('beforeSave should not affect fetched pointers', done => {
688+
Parse.Cloud.beforeSave('BeforeSaveUnchanged', (req, res) => {
689+
res.success();
690+
});
691+
692+
Parse.Cloud.beforeSave('BeforeSaveChanged', function(req, res) {
693+
req.object.set('foo', 'baz');
694+
res.success();
695+
});
696+
697+
var TestObject = Parse.Object.extend("TestObject");
698+
var BeforeSaveUnchangedObject = Parse.Object.extend("BeforeSaveUnchanged");
699+
var BeforeSaveChangedObject = Parse.Object.extend("BeforeSaveChanged");
700+
701+
var aTestObject = new TestObject();
702+
aTestObject.set("foo", "bar");
703+
aTestObject.save()
704+
.then(aTestObject => {
705+
var aBeforeSaveUnchangedObject = new BeforeSaveUnchangedObject();
706+
aBeforeSaveUnchangedObject.set("aTestObject", aTestObject);
707+
expect(aBeforeSaveUnchangedObject.get("aTestObject").get("foo")).toEqual("bar");
708+
return aBeforeSaveUnchangedObject.save();
709+
})
710+
.then(aBeforeSaveUnchangedObject => {
711+
expect(aBeforeSaveUnchangedObject.get("aTestObject").get("foo")).toEqual("bar");
712+
713+
var aBeforeSaveChangedObject = new BeforeSaveChangedObject();
714+
aBeforeSaveChangedObject.set("aTestObject", aTestObject);
715+
expect(aBeforeSaveChangedObject.get("aTestObject").get("foo")).toEqual("bar");
716+
return aBeforeSaveChangedObject.save();
717+
})
718+
.then(aBeforeSaveChangedObject => {
719+
expect(aBeforeSaveChangedObject.get("aTestObject").get("foo")).toEqual("bar");
720+
expect(aBeforeSaveChangedObject.get("foo")).toEqual("baz");
721+
done();
722+
});
723+
});
724+
687725
it_exclude_dbs(['postgres'])('should fully delete objects when using `unset` with beforeSave (regression test for #1840)', done => {
688726
var TestObject = Parse.Object.extend('TestObject');
689727
var NoBeforeSaveObject = Parse.Object.extend('NoBeforeSave');

spec/ParseUser.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2374,7 +2374,7 @@ describe('Parse.User testing', () => {
23742374
},
23752375
json: true
23762376
}, (err, res, body) => {
2377-
expect(body.username).toEqual(user.username);
2377+
expect(body.username).toEqual('user');
23782378
expect(body.objectId).toEqual(originalUserId);
23792379
if (err) {
23802380
reject(err);

src/RestWrite.js

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,12 @@ RestWrite.prototype.runBeforeTrigger = function() {
165165
return triggers.maybeRunTrigger(triggers.Types.beforeSave, this.auth, updatedObject, originalObject, this.config);
166166
}).then((response) => {
167167
if (response && response.object) {
168-
if (!_.isEqual(this.data, response.object)) {
169-
this.storage.changedByTrigger = true;
170-
}
168+
this.storage.fieldsChangedByTrigger = _.reduce(response.object, (result, value, key) => {
169+
if (!_.isEqual(this.data[key], value)) {
170+
result.push(key);
171+
}
172+
return result;
173+
}, []);
171174
this.data = response.object;
172175
// We should delete the objectId for an update write
173176
if (this.query && this.query.objectId) {
@@ -792,9 +795,7 @@ RestWrite.prototype.runDatabaseOperation = function() {
792795
return this.config.database.update(this.className, this.query, this.data, this.runOptions)
793796
.then(response => {
794797
response.updatedAt = this.updatedAt;
795-
if (this.storage.changedByTrigger) {
796-
this.updateResponseWithData(response, this.data);
797-
}
798+
this._updateResponseWithData(response, this.data);
798799
this.response = { response };
799800
});
800801
} else {
@@ -850,9 +851,7 @@ RestWrite.prototype.runDatabaseOperation = function() {
850851
if (this.responseShouldHaveUsername) {
851852
response.username = this.data.username;
852853
}
853-
if (this.storage.changedByTrigger) {
854-
this.updateResponseWithData(response, this.data);
855-
}
854+
this._updateResponseWithData(response, this.data);
856855
this.response = {
857856
status: 201,
858857
response,
@@ -940,9 +939,12 @@ RestWrite.prototype.cleanUserAuthData = function() {
940939
}
941940
};
942941

943-
RestWrite.prototype.updateResponseWithData = function(response, data) {
942+
RestWrite.prototype._updateResponseWithData = function(response, data) {
943+
if (_.isEmpty(this.storage.fieldsChangedByTrigger)) {
944+
return response;
945+
}
944946
let clientSupportsDelete = ClientSDK.supportsForwardDelete(this.clientSDK);
945-
Object.keys(data).forEach(fieldName => {
947+
this.storage.fieldsChangedByTrigger.forEach(fieldName => {
946948
let dataValue = data[fieldName];
947949
let responseValue = response[fieldName];
948950

0 commit comments

Comments
 (0)