diff --git a/spec/ParseAPI.spec.js b/spec/ParseAPI.spec.js index 6edfa79109..cb9fc168c3 100644 --- a/spec/ParseAPI.spec.js +++ b/spec/ParseAPI.spec.js @@ -1845,6 +1845,58 @@ describe('miscellaneous', () => { } ); }); + + it('test afterSave with deeply nested keys (#7384)', async () => { + let triggerTime = 0; + Parse.Cloud.afterSave('GameScore', function (req) { + const object = req.object; + expect(object instanceof Parse.Object).toBeTruthy(); + triggerTime++; + if (triggerTime == 1) { + // Create + expect(object.get('a')).toEqual({ b: 0, c: { d: 1 } }); + expect(object.get('e')).toEqual(2); + } else if (triggerTime == 2) { + // Update, increment + expect(object.get('a')).toEqual({ b: 10, c: { d: 12 } }); + expect(object.get('e')).toEqual(14); + } else if (triggerTime == 3) { + // Update, set + expect(object.get('a')).toEqual({ b: 100, c: { d: 200 } }); + expect(object.get('e')).toEqual(300); + } else if (triggerTime == 4) { + // Update, unset on a.c.d + expect(object.get('a')).toEqual({ b: 100, c: {} }); + expect(object.get('e')).toEqual(300); + } else if (triggerTime == 5) { + // Update, unset on a.b + expect(object.get('a')).toEqual({ c: {} }); + expect(object.get('e')).toEqual(300); + } else { + throw new Error(); + } + + }); + + const obj = new Parse.Object('GameScore'); + obj.set('a', { b: 0, c: { d: 1 } }); + obj.set('e', 2); + await obj.save(); + obj.increment('a.b', 10); + obj.increment('a.c.d', 11); + obj.increment('e', 12); + await obj.save(); + obj.set('a.b', 100); + obj.set('a.c.d', 200); + obj.set('e', 300); + await obj.save(); + obj.unset('a.c.d'); + await obj.save(); + obj.unset('a.b'); + await obj.save(); + // Make sure the checking has been triggered + expect(triggerTime).toBe(5); + }); }); describe_only_db('mongo')('legacy _acl', () => { diff --git a/src/RestWrite.js b/src/RestWrite.js index 78dd8c8878..51d7125703 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -1751,21 +1751,7 @@ RestWrite.prototype.buildParseObjects = function () { const updatedObject = triggers.inflate(extraData, this.originalData); Object.keys(this.data).reduce(function (data, key) { if (key.indexOf('.') > 0) { - if (typeof data[key].__op === 'string') { - if (!readOnlyAttributes.includes(key)) { - updatedObject.set(key, data[key]); - } - } else { - // subdocument key with dot notation { 'x.y': v } => { 'x': { 'y' : v } }) - const splittedKey = key.split('.'); - const parentProp = splittedKey[0]; - let parentVal = updatedObject.get(parentProp); - if (typeof parentVal !== 'object') { - parentVal = {}; - } - parentVal[splittedKey[1]] = data[key]; - updatedObject.set(parentProp, parentVal); - } + updatedObject.set(key, data[key]); delete data[key]; } return data;