diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b6e132ee3..9558992d66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -131,6 +131,7 @@ ___ - Add building Docker image as CI check (Manuel Trezza) [#7332](https://github.com/parse-community/parse-server/pull/7332) - Add NPM package-lock version check to CI (Manuel Trezza) [#7333](https://github.com/parse-community/parse-server/pull/7333) - Fix incorrect LiveQuery events triggered for multiple subscriptions on the same class with different events [#7341](https://github.com/parse-community/parse-server/pull/7341) +- Updates on deeply nested documents keys are now correctly passed to afterSave triggers (Kartal Kaan Bozdogan - Ocell) [#7385](https://github.com/parse-community/parse-server/pull/7385) ___ ## 4.5.0 [Full Changelog](https://github.com/parse-community/parse-server/compare/4.4.0...4.5.0) diff --git a/package-lock.json b/package-lock.json index 2a81292068..4bab03734e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6957,9 +6957,9 @@ } }, "idb-keyval": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-5.0.4.tgz", - "integrity": "sha512-qS0kplHuadZujoE90ze0NUkhW0/Fbfib7d+mYNMXNEn45NSh2NWY3fBewoX4GZUsKkGHBgc8JiAwMx0zrfL3LQ==" + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-5.0.5.tgz", + "integrity": "sha512-cqi65rrjhgPExI9vmSU7VcYEbHCUfIBY+9YUWxyr0PyGizptFgGFnvZQ0w+tqOXk1lUcGCZGVLfabf7QnR2S0g==" }, "ieee754": { "version": "1.2.1", @@ -10040,24 +10040,31 @@ } }, "parse": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/parse/-/parse-3.2.0.tgz", - "integrity": "sha512-yncA9l0LneOzzBFACVjdPNpWuNGQ/LhXGEO/qj6vYAyJpwWabNf7Eq2ucMAWzALb13KDRoAiTlb2cH3Nt/S9gw==", + "version": "github:Ocell-io/Parse-SDK-JS#1ed03b3dca8af9c648606f95a6cb473b36b33a04", + "from": "github:Ocell-io/Parse-SDK-JS#1ed03b3dca8af9c648606f95a6cb473b36b33a04", "requires": { - "@babel/runtime": "7.13.10", - "@babel/runtime-corejs3": "7.13.10", + "@babel/runtime": "7.13.17", + "@babel/runtime-corejs3": "7.13.17", "crypto-js": "4.0.0", - "idb-keyval": "5.0.4", + "idb-keyval": "5.0.5", "react-native-crypto-js": "1.0.0", "uuid": "3.4.0", - "ws": "7.4.4", + "ws": "7.4.5", "xmlhttprequest": "1.8.0" }, "dependencies": { + "@babel/runtime": { + "version": "7.13.17", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.17.tgz", + "integrity": "sha512-NCdgJEelPTSh+FEFylhnP1ylq848l1z9t9N0j1Lfbcw0+KXGjsTvUmkxy+voLLXB5SOKMbLLx4jxYliGrYQseA==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, "@babel/runtime-corejs3": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.13.10.tgz", - "integrity": "sha512-x/XYVQ1h684pp1mJwOV4CyvqZXqbc8CMsMGUnAbuc82ZCdv1U63w5RSUzgDSXQHG5Rps/kiksH6g2D5BuaKyXg==", + "version": "7.13.17", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.13.17.tgz", + "integrity": "sha512-RGXINY1YvduBlGrP+vHjJqd/nK7JVpfM4rmZLGMx77WoL3sMrhheA0qxii9VNn1VHnxJLEyxmvCB+Wqc+x/FMw==", "requires": { "core-js-pure": "^3.0.0", "regenerator-runtime": "^0.13.4" diff --git a/package.json b/package.json index 5395916522..01b4d145e3 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "mime": "2.5.2", "mongodb": "3.6.6", "mustache": "4.2.0", - "parse": "3.2.0", + "parse": "github:Ocell-io/Parse-SDK-JS#1ed03b3dca8af9c648606f95a6cb473b36b33a04", "pg-monitor": "1.4.1", "pg-promise": "10.10.1", "pluralize": "8.0.0", diff --git a/spec/ParseAPI.spec.js b/spec/ParseAPI.spec.js index 76143e0580..87306eefd2 100644 --- a/spec/ParseAPI.spec.js +++ b/spec/ParseAPI.spec.js @@ -691,6 +691,57 @@ describe('miscellaneous', function () { ); }); + 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); + }); + it('test afterSave get original object on update', function (done) { let triggerTime = 0; // Register a mock beforeSave hook diff --git a/spec/ParseUser.spec.js b/spec/ParseUser.spec.js index 91aeb4920a..f5e191799a 100644 --- a/spec/ParseUser.spec.js +++ b/spec/ParseUser.spec.js @@ -793,7 +793,7 @@ describe('Parse.User testing', () => { user.set('username', 'test'); await user.save(); - equal(Object.keys(user.attributes).length, 6); + equal(Object.keys(user.attributes).length, 5); ok(user.attributes['username']); ok(user.attributes['email']); await user.destroy(); diff --git a/src/RestWrite.js b/src/RestWrite.js index c55436983a..ecfa671e8d 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -1589,19 +1589,7 @@ RestWrite.prototype.buildUpdatedObject = function (extraData) { 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') { - 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;