From cbd466b869651c3fcd6bffb9a71ba98b8da36829 Mon Sep 17 00:00:00 2001 From: exaby73 Date: Fri, 4 Aug 2023 13:49:47 +0530 Subject: [PATCH 1/2] fix: before and after are deeply merged --- src/firebase_functions/db_fn.py | 2 +- src/firebase_functions/private/util.py | 10 ++++++++++ tests/test_util.py | 26 +++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/firebase_functions/db_fn.py b/src/firebase_functions/db_fn.py index 410d5f8..aa7d33b 100644 --- a/src/firebase_functions/db_fn.py +++ b/src/firebase_functions/db_fn.py @@ -91,7 +91,7 @@ def _db_endpoint_handler( after = event_data["delta"] # Merge delta into data to generate an 'after' view of the data. if isinstance(before, dict) and isinstance(after, dict): - after = _util.prune_nones({**before, **after}) + after = _util.prune_nones(_util.deep_merge(before, after)) database_event_data = Change( before=before, after=after, diff --git a/src/firebase_functions/private/util.py b/src/firebase_functions/private/util.py index 9c521e9..cae4fcb 100644 --- a/src/firebase_functions/private/util.py +++ b/src/firebase_functions/private/util.py @@ -76,6 +76,16 @@ def prune_nones(obj: dict) -> dict: prune_nones(obj[key]) return obj +def deep_merge(dict1, dict2): + result = dict1.copy() + for key, value in dict2.items(): + if isinstance(value, dict): + node = result.get(key, {}) + result[key] = deep_merge(node, value) + else: + result[key] = value + return result + def valid_on_call_request(request: _Request) -> bool: """Validate request""" diff --git a/tests/test_util.py b/tests/test_util.py index 355d0c9..0f41328 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -15,7 +15,7 @@ Internal utils tests. """ from os import environ, path -from firebase_functions.private.util import firebase_config, microsecond_timestamp_conversion, nanoseconds_timestamp_conversion, is_precision_timestamp, normalize_path +from firebase_functions.private.util import firebase_config, microsecond_timestamp_conversion, nanoseconds_timestamp_conversion, is_precision_timestamp, normalize_path, deep_merge import datetime as _dt test_bucket = "python-functions-testing.appspot.com" @@ -121,3 +121,27 @@ def test_normalize_document_path(): test_path2 = "test/document" assert normalize_path(test_path2) == "test/document", ( "Failure, path should not be changed if it is already normalized.") + + +def test_toplevel_keys(): + dict1 = {'baz': {'answer': 42, 'qux': 'quux'}, 'foo': 'bar'} + dict2 = {'baz': {'answer': 33}} + result = deep_merge(dict1, dict2) + assert 'foo' in result + assert 'baz' in result + + +def test_nested_merge(): + dict1 = {'baz': {'answer': 42, 'qux': 'quux'}, 'foo': 'bar'} + dict2 = {'baz': {'answer': 33}} + result = deep_merge(dict1, dict2) + assert result['baz']['answer'] == 33 + assert result['baz']['qux'] == 'quux' + + +def test_does_not_modify_originals(): + dict1 = {'baz': {'answer': 42, 'qux': 'quux'}, 'foo': 'bar'} + dict2 = {'baz': {'answer': 33}} + deep_merge(dict1, dict2) + assert dict1['baz']['answer'] == 42 + assert dict2['baz']['answer'] == 33 From 583ed9fc4569c061d4741a4a208493f2173df0dc Mon Sep 17 00:00:00 2001 From: exaby73 Date: Fri, 4 Aug 2023 13:59:06 +0530 Subject: [PATCH 2/2] fix: linting and formatting --- src/firebase_functions/private/util.py | 1 + tests/test_util.py | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/firebase_functions/private/util.py b/src/firebase_functions/private/util.py index cae4fcb..0dd2eaa 100644 --- a/src/firebase_functions/private/util.py +++ b/src/firebase_functions/private/util.py @@ -76,6 +76,7 @@ def prune_nones(obj: dict) -> dict: prune_nones(obj[key]) return obj + def deep_merge(dict1, dict2): result = dict1.copy() for key, value in dict2.items(): diff --git a/tests/test_util.py b/tests/test_util.py index 0f41328..e524033 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -124,24 +124,24 @@ def test_normalize_document_path(): def test_toplevel_keys(): - dict1 = {'baz': {'answer': 42, 'qux': 'quux'}, 'foo': 'bar'} - dict2 = {'baz': {'answer': 33}} + dict1 = {"baz": {"answer": 42, "qux": "quux"}, "foo": "bar"} + dict2 = {"baz": {"answer": 33}} result = deep_merge(dict1, dict2) - assert 'foo' in result - assert 'baz' in result + assert "foo" in result + assert "baz" in result def test_nested_merge(): - dict1 = {'baz': {'answer': 42, 'qux': 'quux'}, 'foo': 'bar'} - dict2 = {'baz': {'answer': 33}} + dict1 = {"baz": {"answer": 42, "qux": "quux"}, "foo": "bar"} + dict2 = {"baz": {"answer": 33}} result = deep_merge(dict1, dict2) - assert result['baz']['answer'] == 33 - assert result['baz']['qux'] == 'quux' + assert result["baz"]["answer"] == 33 + assert result["baz"]["qux"] == "quux" def test_does_not_modify_originals(): - dict1 = {'baz': {'answer': 42, 'qux': 'quux'}, 'foo': 'bar'} - dict2 = {'baz': {'answer': 33}} + dict1 = {"baz": {"answer": 42, "qux": "quux"}, "foo": "bar"} + dict2 = {"baz": {"answer": 33}} deep_merge(dict1, dict2) - assert dict1['baz']['answer'] == 42 - assert dict2['baz']['answer'] == 33 + assert dict1["baz"]["answer"] == 42 + assert dict2["baz"]["answer"] == 33