From 4adc58c113ff7b4a3f70672684656440df8fd343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kartal=20Kaan=20Bozdo=C4=9Fan?= Date: Tue, 15 Feb 2022 22:32:10 +0300 Subject: [PATCH 1/4] Added a test case demonstrating issue #1450 --- src/__tests__/ParseObject-test.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/__tests__/ParseObject-test.js b/src/__tests__/ParseObject-test.js index 06a7f3c2f..be5cfc3ff 100644 --- a/src/__tests__/ParseObject-test.js +++ b/src/__tests__/ParseObject-test.js @@ -669,6 +669,33 @@ describe('ParseObject', () => { }); }); + it('can set multiple nested fields (regression test for #1450)', () => { + const o = new ParseObject('Person'); + o._finishFetch({ + objectId: 'setNested2_1450', + objectField: { + number: 5, + letter: 'a', + }, + }); + + expect(o.attributes).toEqual({ + objectField: { number: 5, letter: 'a' }, + }); + o.set('objectField.number', 20); + o.set('objectField.letter', 'b'); + + expect(o.attributes).toEqual({ + objectField: { number: 20, letter: 'b' }, + }); + expect(o.op('objectField.number') instanceof SetOp).toBe(true); + expect(o.dirtyKeys()).toEqual(['objectField.number', 'objectField.letter', 'objectField']); + expect(o._getSaveJSON()).toEqual({ + 'objectField.number': 20, + 'objectField.letter': 'b', + }); + }); + it('can increment a nested field', () => { const o = new ParseObject('Person'); o._finishFetch({ From 4c5dbf5503977bd4873d0d44e4df72c4213d9374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kartal=20Kaan=20Bozdo=C4=9Fan?= Date: Tue, 15 Feb 2022 22:32:29 +0300 Subject: [PATCH 2/4] Fix issue 1450 by doing a deep copy only when necessary --- src/ObjectStateMutations.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ObjectStateMutations.js b/src/ObjectStateMutations.js index 271e4f6b5..10f17710f 100644 --- a/src/ObjectStateMutations.js +++ b/src/ObjectStateMutations.js @@ -122,7 +122,7 @@ export function estimateAttributes( const fields = attr.split('.'); const first = fields[0]; const last = fields[fields.length - 1]; - data[first] = { ...serverData[first] }; + if (data[first] === serverData[first]) data[first] = { ...serverData[first] }; // Do a deep copy let object = { ...data }; for (let i = 0; i < fields.length - 1; i++) { const key = fields[i]; From 3dad4fff5f30964078150f4d9387f3a66535b295 Mon Sep 17 00:00:00 2001 From: Kartal Kaan Bozdogan Date: Wed, 1 May 2024 20:43:21 +0200 Subject: [PATCH 3/4] estimateAttributes: Do deep clones even for deeply nested objects Improved the regression test for issue #1450 --- src/ObjectStateMutations.js | 6 ++--- src/__tests__/ParseObject-test.js | 38 ++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/ObjectStateMutations.js b/src/ObjectStateMutations.js index 10f17710f..dfcbcff87 100644 --- a/src/ObjectStateMutations.js +++ b/src/ObjectStateMutations.js @@ -120,14 +120,14 @@ export function estimateAttributes( if (attr.includes('.')) { // convert a.b.c into { a: { b: { c: value } } } const fields = attr.split('.'); - const first = fields[0]; const last = fields[fields.length - 1]; - if (data[first] === serverData[first]) data[first] = { ...serverData[first] }; // Do a deep copy - let object = { ...data }; + let object = data; for (let i = 0; i < fields.length - 1; i++) { const key = fields[i]; if (!(key in object)) { object[key] = {}; + } else { + object[key] = { ...object[key] }; } object = object[key]; } diff --git a/src/__tests__/ParseObject-test.js b/src/__tests__/ParseObject-test.js index be5cfc3ff..c613bb6ad 100644 --- a/src/__tests__/ParseObject-test.js +++ b/src/__tests__/ParseObject-test.js @@ -676,23 +676,55 @@ describe('ParseObject', () => { objectField: { number: 5, letter: 'a', + nested: { + number: 0, + letter: 'b', + }, }, }); expect(o.attributes).toEqual({ - objectField: { number: 5, letter: 'a' }, + objectField: { number: 5, letter: 'a', nested: { number: 0, letter: 'b' } }, }); o.set('objectField.number', 20); o.set('objectField.letter', 'b'); + o.set('objectField.nested.number', 1); + o.set('objectField.nested.letter', 'c'); expect(o.attributes).toEqual({ - objectField: { number: 20, letter: 'b' }, + objectField: { number: 20, letter: 'b', nested: { number: 1, letter: 'c' } }, }); expect(o.op('objectField.number') instanceof SetOp).toBe(true); - expect(o.dirtyKeys()).toEqual(['objectField.number', 'objectField.letter', 'objectField']); + expect(o.dirtyKeys()).toEqual([ + 'objectField.number', + 'objectField.letter', + 'objectField.nested.number', + 'objectField.nested.letter', + 'objectField', + ]); + expect(o._getSaveJSON()).toEqual({ + 'objectField.number': 20, + 'objectField.letter': 'b', + 'objectField.nested.number': 1, + 'objectField.nested.letter': 'c', + }); + + o.revert('objectField.nested.number'); + o.revert('objectField.nested.letter'); + expect(o._getSaveJSON()).toEqual({ + 'objectField.number': 20, + 'objectField.letter': 'b', + }); + expect(o.attributes).toEqual({ + objectField: { number: 20, letter: 'b', nested: { number: 0, letter: 'b' } }, + }); + + // Also test setting new root fields using the dot notation + o.set('objectField2.number', 0); expect(o._getSaveJSON()).toEqual({ 'objectField.number': 20, 'objectField.letter': 'b', + 'objectField2.number': 0, }); }); From 60bf30eb4100b010cc71adbed5a848362da157ef Mon Sep 17 00:00:00 2001 From: Kartal Kaan Bozdogan Date: Wed, 1 May 2024 21:36:30 +0200 Subject: [PATCH 4/4] Regression test for #1450: Also test that object attributes are estimated as expected --- src/__tests__/ParseObject-test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/__tests__/ParseObject-test.js b/src/__tests__/ParseObject-test.js index c613bb6ad..bd6643ff2 100644 --- a/src/__tests__/ParseObject-test.js +++ b/src/__tests__/ParseObject-test.js @@ -726,6 +726,10 @@ describe('ParseObject', () => { 'objectField.letter': 'b', 'objectField2.number': 0, }); + expect(o.attributes).toEqual({ + objectField: { number: 20, letter: 'b', nested: { number: 0, letter: 'b' } }, + objectField2: { number: 0 }, + }); }); it('can increment a nested field', () => {