From 2e490c4b5c39fd496009f3fc6a5297fdd9551b84 Mon Sep 17 00:00:00 2001 From: nabos440 <104625739+nabos440@users.noreply.github.com> Date: Sat, 22 Mar 2025 04:27:27 +0500 Subject: [PATCH 1/5] Update parse_array.dart This fixes the issue because the **_handleSingleResult()** function which handles the response for a single parse object in the **_ParseResponseBuilder** was calling **_notifyChildrenAboutSave();** so when **save()** was called on any object it called **_notifyChildrenAboutSaving();** first and then in the response when **_notifyChildrenAboutSave();** was called, the **_estimatedArrayBeforeSaving** had values and _savedArray was not set as empty. But in case of other queries where single object was returned like **getUpdatedUser()** or **fetch()** on any object it just called **_notifyChildrenAboutSave();** in the **_handleSingleResult()** function which resulted in **_savedArray** being set as empty since **_estimatedArrayBeforeSaving** was null. I just added the null safety and not empty check before setting the **_savedArray** to **_estimatedArrayBeforeSaving** so now the savedArray is never returned empty in the response and the issue is resolved. --- packages/dart/lib/src/objects/parse_array.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/dart/lib/src/objects/parse_array.dart b/packages/dart/lib/src/objects/parse_array.dart index f4ebf602b..415de91b2 100644 --- a/packages/dart/lib/src/objects/parse_array.dart +++ b/packages/dart/lib/src/objects/parse_array.dart @@ -68,9 +68,11 @@ class _ParseArray implements _Valuable, _ParseSaveStateAwareChild { @mustCallSuper void onSaved() { setMode = false; + if(_estimatedArrayBeforeSaving != null){ _savedArray.clear(); - _savedArray.addAll(_estimatedArrayBeforeSaving ?? []); + _savedArray.addAll(_estimatedArrayBeforeSaving!); _estimatedArrayBeforeSaving = null; + } if (_lastPreformedOperationBeforeSaving == lastPreformedOperation) { // No operations were performed during the save process From 5e28522ab86712e096c02a9b8bbf790b081e5918 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sun, 30 Nov 2025 16:38:03 +0100 Subject: [PATCH 2/5] lint --- packages/dart/lib/src/objects/parse_array.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/dart/lib/src/objects/parse_array.dart b/packages/dart/lib/src/objects/parse_array.dart index e61bc08a2..a11591337 100644 --- a/packages/dart/lib/src/objects/parse_array.dart +++ b/packages/dart/lib/src/objects/parse_array.dart @@ -66,10 +66,10 @@ class _ParseArray implements _Valuable, _ParseSaveStateAwareChild { @mustCallSuper void onSaved() { setMode = false; - if(_estimatedArrayBeforeSaving != null){ - _savedArray.clear(); - _savedArray.addAll(_estimatedArrayBeforeSaving!); - _estimatedArrayBeforeSaving = null; + if (_estimatedArrayBeforeSaving != null) { + _savedArray.clear(); + _savedArray.addAll(_estimatedArrayBeforeSaving!); + _estimatedArrayBeforeSaving = null; } if (_lastPreformedOperationBeforeSaving == lastPreformedOperation) { From baa71abfbfba0da65e67e25198f636b2b2ed38e2 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sun, 30 Nov 2025 16:41:01 +0100 Subject: [PATCH 3/5] test --- .../parse_object/parse_object_array_test.dart | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/dart/test/src/objects/parse_object/parse_object_array_test.dart b/packages/dart/test/src/objects/parse_object/parse_object_array_test.dart index 49c07f134..c9d8f8142 100644 --- a/packages/dart/test/src/objects/parse_object/parse_object_array_test.dart +++ b/packages/dart/test/src/objects/parse_object/parse_object_array_test.dart @@ -662,6 +662,27 @@ void main() { expect(listValue, orderedEquals([1, 2])); }); + test('Arrays should not be cleared when calling clearUnsavedChanges() ' + 'after fetching/querying without save', () { + // arrange - Simulate a fetch/query response (no prior save operation) + dietPlansObject.fromJson({ + keyArray: [1, 2, 3], + "objectId": "someId", + }); // assume this coming from the server via fetch/query + + // Simulate what happens in _handleSingleResult for fetch/query + // (calls _notifyChildrenAboutSave without prior _notifyChildrenAboutSaving) + // This reproduces the bug where arrays were incorrectly cleared + dietPlansObject._notifyChildrenAboutSave(); + + // act - this should NOT clear the arrays + dietPlansObject.clearUnsavedChanges(); + + // assert - arrays should still have their values + final listValue = dietPlansObject.get(keyArray); + expect(listValue, orderedEquals([1, 2, 3])); + }); + test('The list value and the value for api request should be identical ' 'before and after the save() failed to save the object', () async { // arrange From 4b88a2fc38d73ab952245939df78916ad56f29bb Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sun, 30 Nov 2025 16:48:20 +0100 Subject: [PATCH 4/5] test --- .../parse_object/parse_object_array_test.dart | 79 +++++++++++++++---- 1 file changed, 65 insertions(+), 14 deletions(-) diff --git a/packages/dart/test/src/objects/parse_object/parse_object_array_test.dart b/packages/dart/test/src/objects/parse_object/parse_object_array_test.dart index c9d8f8142..31521dc07 100644 --- a/packages/dart/test/src/objects/parse_object/parse_object_array_test.dart +++ b/packages/dart/test/src/objects/parse_object/parse_object_array_test.dart @@ -663,23 +663,74 @@ void main() { }); test('Arrays should not be cleared when calling clearUnsavedChanges() ' - 'after fetching/querying without save', () { - // arrange - Simulate a fetch/query response (no prior save operation) - dietPlansObject.fromJson({ - keyArray: [1, 2, 3], - "objectId": "someId", - }); // assume this coming from the server via fetch/query + 'after receiving response from fetch/query (issue #1038)', () async { + // arrange - First, save an object with an array to create a _ParseArray + dietPlansObject.setAdd(keyArray, 1); + dietPlansObject.setAdd(keyArray, 2); - // Simulate what happens in _handleSingleResult for fetch/query - // (calls _notifyChildrenAboutSave without prior _notifyChildrenAboutSaving) - // This reproduces the bug where arrays were incorrectly cleared - dietPlansObject._notifyChildrenAboutSave(); + when( + client.post(any, options: anyNamed("options"), data: anyNamed('data')), + ).thenAnswer( + (_) async => ParseNetworkResponse( + statusCode: 200, + data: jsonEncode({ + "objectId": "Mn1iJTkWTE", + "createdAt": "2023-03-05T00:25:31.466Z", + }), + ), + ); - // act - this should NOT clear the arrays - dietPlansObject.clearUnsavedChanges(); + await dietPlansObject + .save(); // This creates the _ParseArray and calls onSaving/onSaved + + // Now set up a mock fetch response that returns updated array data + final getPath = Uri.parse( + '$serverUrl$keyEndPointClasses${dietPlansObject.parseClassName}/${dietPlansObject.objectId}', + ).toString(); + + const resultsFromServer = { + "results": [ + { + "objectId": "Mn1iJTkWTE", + keyArray: [ + 1, + 2, + 3, + ], // Server now has an updated array with one more item + "createdAt": "2023-03-05T00:25:31.466Z", + "updatedAt": "2023-03-05T00:25:31.466Z", + }, + ], + }; - // assert - arrays should still have their values - final listValue = dietPlansObject.get(keyArray); + when( + client.get( + getPath, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + ), + ).thenAnswer( + (_) async => ParseNetworkResponse( + statusCode: 200, + data: jsonEncode(resultsFromServer), + ), + ); + + // act - Fetch the object from server to get the updated array + // This simulates the scenario from issue #1038 where after fetching/querying + // an object (e.g., via getUpdatedUser() or fetch()), calling clearUnsavedChanges() + // would incorrectly clear the arrays. + ParseObject fetchedObject = await dietPlansObject.fetch(); + + // Verify array is populated correctly from the fetch response + expect(fetchedObject.get(keyArray), orderedEquals([1, 2, 3])); + + // Now clear unsaved changes - this should NOT clear the arrays + // Before the fix (PR #1039), this would set arrays to empty + fetchedObject.clearUnsavedChanges(); + + // assert - Arrays should still have their values from the server + final listValue = fetchedObject.get(keyArray); expect(listValue, orderedEquals([1, 2, 3])); }); From 45e76e9ae76f8ec9a6b5ecc4294c8969c8e340a9 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sun, 30 Nov 2025 17:01:32 +0100 Subject: [PATCH 5/5] test --- .../parse_object/parse_object_array_test.dart | 71 +++++++++---------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/packages/dart/test/src/objects/parse_object/parse_object_array_test.dart b/packages/dart/test/src/objects/parse_object/parse_object_array_test.dart index 31521dc07..956ab8255 100644 --- a/packages/dart/test/src/objects/parse_object/parse_object_array_test.dart +++ b/packages/dart/test/src/objects/parse_object/parse_object_array_test.dart @@ -664,26 +664,29 @@ void main() { test('Arrays should not be cleared when calling clearUnsavedChanges() ' 'after receiving response from fetch/query (issue #1038)', () async { - // arrange - First, save an object with an array to create a _ParseArray - dietPlansObject.setAdd(keyArray, 1); - dietPlansObject.setAdd(keyArray, 2); - - when( - client.post(any, options: anyNamed("options"), data: anyNamed('data')), - ).thenAnswer( - (_) async => ParseNetworkResponse( - statusCode: 200, - data: jsonEncode({ - "objectId": "Mn1iJTkWTE", - "createdAt": "2023-03-05T00:25:31.466Z", - }), - ), - ); + // Steps to reproduce from issue #1038: + // 1. Query/fetch a user object with arrays from server + // 2. Call clearUnsavedChanges() - arrays should NOT become empty + // 3. Call getUpdatedUser()/fetch() again + // 4. Check that savedArray and estimatedArray both have values (not empty) + + // Step 1: Simulate getting an object from server (e.g., via query or getUpdatedUser) + // This is like getting a user from cache or initial fetch + dietPlansObject.fromJson({ + keyArray: [1, 2, 3], + "objectId": "Mn1iJTkWTE", + "createdAt": "2023-03-05T00:25:31.466Z", + }); + + // Step 2: Call clearUnsavedChanges() - this should NOT clear the arrays + // Before the fix, if savedArray was empty, this would make arrays empty + // because onClearUnsaved() does: estimatedArray = savedArray + dietPlansObject.clearUnsavedChanges(); - await dietPlansObject - .save(); // This creates the _ParseArray and calls onSaving/onSaved + // Verify arrays are NOT empty after clearUnsavedChanges + expect(dietPlansObject.get(keyArray), orderedEquals([1, 2, 3])); - // Now set up a mock fetch response that returns updated array data + // Step 3: Set up mock for getUpdatedUser()/fetch() final getPath = Uri.parse( '$serverUrl$keyEndPointClasses${dietPlansObject.parseClassName}/${dietPlansObject.objectId}', ).toString(); @@ -692,11 +695,7 @@ void main() { "results": [ { "objectId": "Mn1iJTkWTE", - keyArray: [ - 1, - 2, - 3, - ], // Server now has an updated array with one more item + keyArray: [1, 2, 3], "createdAt": "2023-03-05T00:25:31.466Z", "updatedAt": "2023-03-05T00:25:31.466Z", }, @@ -716,22 +715,20 @@ void main() { ), ); - // act - Fetch the object from server to get the updated array - // This simulates the scenario from issue #1038 where after fetching/querying - // an object (e.g., via getUpdatedUser() or fetch()), calling clearUnsavedChanges() - // would incorrectly clear the arrays. - ParseObject fetchedObject = await dietPlansObject.fetch(); - - // Verify array is populated correctly from the fetch response - expect(fetchedObject.get(keyArray), orderedEquals([1, 2, 3])); + // Fetch the object (simulating getUpdatedUser) + // NOTE: fetch() might update dietPlansObject in place OR return a new object + await dietPlansObject.fetch(); - // Now clear unsaved changes - this should NOT clear the arrays - // Before the fix (PR #1039), this would set arrays to empty - fetchedObject.clearUnsavedChanges(); + // Step 4: Verify the ORIGINAL object still has array values + // The issue reported that after fetch, arrays would be cleared + final arrayAfterFetch = dietPlansObject.get(keyArray); + expect(arrayAfterFetch, orderedEquals([1, 2, 3])); - // assert - Arrays should still have their values from the server - final listValue = fetchedObject.get(keyArray); - expect(listValue, orderedEquals([1, 2, 3])); + // Additional check: call clearUnsavedChanges() on the original object + // and verify arrays persist. This is the exact scenario from issue #1038. + dietPlansObject.clearUnsavedChanges(); + final arrayAfterSecondClear = dietPlansObject.get(keyArray); + expect(arrayAfterSecondClear, orderedEquals([1, 2, 3])); }); test('The list value and the value for api request should be identical '