From ffc523f9cd0fa2dd3fcc9754d5f15c3c1c78314f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kartal=20Kaan=20Bozdo=C4=9Fan?= Date: Fri, 14 May 2021 18:14:03 +0300 Subject: [PATCH] Improve support for nested keys (#1364) * Better support for nested keys * Added a test for nested fields Bugfix on ObjectStateMutations patch * Move the new test under integration/ * Modified an existing test ParseUser.get('password') used to return undefined, now password is not an attribute * Use curly braces --- integration/test/ParseObjectTest.js | 14 ++++++++++++++ integration/test/ParseUserTest.js | 2 +- src/ObjectStateMutations.js | 15 ++++++++++++++- src/ParseObject.js | 2 +- 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/integration/test/ParseObjectTest.js b/integration/test/ParseObjectTest.js index b5c738a75..64e4a42e0 100644 --- a/integration/test/ParseObjectTest.js +++ b/integration/test/ParseObjectTest.js @@ -401,6 +401,20 @@ describe('Parse Object', () => { assert.equal(result.get('objectField').number, 20); }); + it('can set non existing nested fields to objects', async () => { + const o = new Parse.Object('Person'); + expect(o.attributes).toEqual({}); + o.set('data', {}); + await o.save(); + expect(o.get('data')).toEqual({}); + o.set('data.a', {}); + await o.save(); + expect(o.get('data')).toEqual({ a: {} }); + o.set('data.a.b', {}); + await o.save(); + expect(o.get('data')).toEqual({ a: { b: {} } }); + }); + it('can set non existing fields', async () => { const obj = new TestObject(); obj.set('objectField', { number: 5 }); diff --git a/integration/test/ParseUserTest.js b/integration/test/ParseUserTest.js index 721104b6b..89af62a36 100644 --- a/integration/test/ParseUserTest.js +++ b/integration/test/ParseUserTest.js @@ -505,7 +505,7 @@ describe('Parse User', () => { return user.save(); }) .then(() => { - assert.equal(Object.keys(user.attributes).length, 6); + assert.equal(Object.keys(user.attributes).length, 5); assert(user.attributes.hasOwnProperty('username')); assert(user.attributes.hasOwnProperty('email')); return user.destroy(); diff --git a/src/ObjectStateMutations.js b/src/ObjectStateMutations.js index bcac048a0..7603c6a10 100644 --- a/src/ObjectStateMutations.js +++ b/src/ObjectStateMutations.js @@ -154,6 +154,19 @@ export function estimateAttributes( return data; } +function nestedSet(obj, key, value) { + const path = key.split('.'); + for (let i = 0; i < path.length - 1; i++) { + if (!(path[i] in obj)) obj[path[i]] = {}; + obj = obj[path[i]]; + } + if (typeof value === 'undefined') { + delete obj[path[path.length - 1]]; + } else { + obj[path[path.length - 1]] = value; + } +} + export function commitServerChanges( serverData: AttributeMap, objectCache: ObjectCache, @@ -161,7 +174,7 @@ export function commitServerChanges( ) { for (const attr in changes) { const val = changes[attr]; - serverData[attr] = val; + nestedSet(serverData, attr, val); if ( val && typeof val === 'object' && diff --git a/src/ParseObject.js b/src/ParseObject.js index 8f4198cfd..87738d56f 100644 --- a/src/ParseObject.js +++ b/src/ParseObject.js @@ -409,7 +409,7 @@ class ParseObject { for (attr in pending) { if (pending[attr] instanceof RelationOp) { changes[attr] = pending[attr].applyTo(undefined, this, attr); - } else if (!(attr in response) && !attr.includes('.')) { + } else if (!(attr in response)) { // Only SetOps and UnsetOps should not come back with results changes[attr] = pending[attr].applyTo(undefined); }