From bc3193fa9b64368144ccb684700931051a826c3e Mon Sep 17 00:00:00 2001
From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com>
Date: Sun, 21 Apr 2024 10:34:20 +0100
Subject: [PATCH] mergeCollection handle null values
---
API-INTERNAL.md | 4 +-
lib/Onyx.ts | 8 +--
lib/OnyxUtils.ts | 12 +++--
tests/unit/onyxMultiMergeWebStorageTest.js | 62 ++++++++++++++++++++++
4 files changed, 77 insertions(+), 9 deletions(-)
diff --git a/API-INTERNAL.md b/API-INTERNAL.md
index aae82ccc7..0ff7cf5b6 100644
--- a/API-INTERNAL.md
+++ b/API-INTERNAL.md
@@ -120,7 +120,7 @@ Otherwise removes all nested null values in objects and returns the object
prepareKeyValuePairsForStorage() ⇒
Storage expects array like: [["@MyApp_user", value_1], ["@MyApp_key", value_2]]
This method transforms an object like {'@MyApp_user': myUserValue, '@MyApp_key': myKeyValue}
-to an array of key-value pairs in the above format and removes key-value pairs that are being set to null
+to an array of key-value pairs in the above format
applyMerge(changes)
Merges an array of changes with an existing value
@@ -376,7 +376,7 @@ Otherwise removes all nested null values in objects and returns the object
## prepareKeyValuePairsForStorage() ⇒
Storage expects array like: [["@MyApp_user", value_1], ["@MyApp_key", value_2]]
This method transforms an object like {'@MyApp_user': myUserValue, '@MyApp_key': myKeyValue}
-to an array of key-value pairs in the above format and removes key-value pairs that are being set to null
+to an array of key-value pairs in the above format
**Kind**: global function
**Returns**: an array of key - value pairs <[key, value]>
diff --git a/lib/Onyx.ts b/lib/Onyx.ts
index ee039a918..0091da8ed 100644
--- a/lib/Onyx.ts
+++ b/lib/Onyx.ts
@@ -245,7 +245,7 @@ function set(key: TKey, value: OnyxEntry): Promise {
- const keyValuePairs = OnyxUtils.prepareKeyValuePairsForStorage(data);
+ const keyValuePairs = OnyxUtils.prepareKeyValuePairsForStorage(data, true);
const updatePromises = keyValuePairs.map(([key, value]) => {
const prevValue = cache.getValue(key, false);
@@ -419,8 +419,10 @@ function mergeCollection(collectionKey: TK
obj[key] = mergedCollection[key];
return obj;
}, {});
- const keyValuePairsForExistingCollection = OnyxUtils.prepareKeyValuePairsForStorage(existingKeyCollection);
- const keyValuePairsForNewCollection = OnyxUtils.prepareKeyValuePairsForStorage(newCollection);
+
+ // We don't want to remove null values because the provider's merge method uses them to remove their respective keys
+ const keyValuePairsForExistingCollection = OnyxUtils.prepareKeyValuePairsForStorage(existingKeyCollection, false);
+ const keyValuePairsForNewCollection = OnyxUtils.prepareKeyValuePairsForStorage(newCollection, true);
const promises = [];
diff --git a/lib/OnyxUtils.ts b/lib/OnyxUtils.ts
index 67c654c74..311c65fa3 100644
--- a/lib/OnyxUtils.ts
+++ b/lib/OnyxUtils.ts
@@ -982,11 +982,15 @@ function removeNullValues(key: OnyxKey, value: OnyxValue): RemoveNullVa
/**
* Storage expects array like: [["@MyApp_user", value_1], ["@MyApp_key", value_2]]
* This method transforms an object like {'@MyApp_user': myUserValue, '@MyApp_key': myKeyValue}
- * to an array of key-value pairs in the above format and removes key-value pairs that are being set to null
-
-* @return an array of key - value pairs <[key, value]>
+ * to an array of key-value pairs in the above format
+ *
+ * @return an array of key - value pairs <[key, value]>
*/
-function prepareKeyValuePairsForStorage(data: Record>): Array<[OnyxKey, OnyxValue]> {
+function prepareKeyValuePairsForStorage(data: Record>, shouldRemoveNullObjectValues: boolean): Array<[OnyxKey, OnyxValue]> {
+ if (!shouldRemoveNullObjectValues) {
+ return Object.entries(data);
+ }
+
const keyValuePairs: Array<[OnyxKey, OnyxValue]> = [];
Object.entries(data).forEach(([key, value]) => {
diff --git a/tests/unit/onyxMultiMergeWebStorageTest.js b/tests/unit/onyxMultiMergeWebStorageTest.js
index f1091bc6b..7e7e5efc6 100644
--- a/tests/unit/onyxMultiMergeWebStorageTest.js
+++ b/tests/unit/onyxMultiMergeWebStorageTest.js
@@ -143,6 +143,68 @@ describe('Onyx.mergeCollection() and WebStorage', () => {
});
});
+ it('merge with null values', () => {
+ // Given empty storage
+ expect(StorageMock.getMockStore().test_1).toBeFalsy();
+ expect(StorageMock.getMockStore().test_2).toBeFalsy();
+ expect(StorageMock.getMockStore().test_3).toBeFalsy();
+
+ // And an empty cache values for the collection keys
+ expect(OnyxCache.getValue('test_1')).toBeFalsy();
+ expect(OnyxCache.getValue('test_2')).toBeFalsy();
+ expect(OnyxCache.getValue('test_3')).toBeFalsy();
+
+ // When we merge additional data and wait for the change
+ const data = {a: 'a', b: 'b'};
+ Onyx.mergeCollection(ONYX_KEYS.COLLECTION.TEST_KEY, {
+ test_1: data,
+ test_2: data,
+ test_3: data,
+ });
+
+ return waitForPromisesToResolve()
+ .then(() => {
+ // Then the cache and storage should match
+ expect(OnyxCache.getValue('test_1')).toEqual(data);
+ expect(OnyxCache.getValue('test_2')).toEqual(data);
+ expect(OnyxCache.getValue('test_3')).toEqual(data);
+ expect(StorageMock.getMockStore().test_1).toEqual(data);
+ expect(StorageMock.getMockStore().test_2).toEqual(data);
+ expect(StorageMock.getMockStore().test_3).toEqual(data);
+
+ // When we merge additional data containing null values and wait for the change
+ const additionalData = {b: null, c: 'c'};
+ Onyx.mergeCollection(ONYX_KEYS.COLLECTION.TEST_KEY, {
+ test_1: additionalData,
+ test_2: additionalData,
+ test_3: additionalData,
+ });
+
+ return waitForPromisesToResolve();
+ })
+ .then(() => {
+ const finalObjectCache = {
+ a: 'a',
+ b: null,
+ c: 'c',
+ };
+ const finalObject = {
+ a: 'a',
+ c: 'c',
+ };
+
+ // Then our new data should merge with the existing data in the cache
+ expect(OnyxCache.getValue('test_1')).toEqual(finalObjectCache);
+ expect(OnyxCache.getValue('test_2')).toEqual(finalObjectCache);
+ expect(OnyxCache.getValue('test_3')).toEqual(finalObjectCache);
+
+ // And the storage should reflect the same state but with nulled key-values removed
+ expect(StorageMock.getMockStore().test_1).toEqual(finalObject);
+ expect(StorageMock.getMockStore().test_2).toEqual(finalObject);
+ expect(StorageMock.getMockStore().test_3).toEqual(finalObject);
+ });
+ });
+
it('setItem() and multiMerge()', () => {
// Onyx should be empty after clear() is called
expect(StorageMock.getMockStore()).toEqual({});