diff --git a/starmap_client/utils.py b/starmap_client/utils.py index a6f575a..62dffe9 100644 --- a/starmap_client/utils.py +++ b/starmap_client/utils.py @@ -20,4 +20,16 @@ def dict_merge(a: Dict[str, Any], b: Dict[str, Any]) -> Dict[str, Any]: """ for x in [a, b]: assert_is_dict(x) + + # Process the inner values before merging + for k, v in a.items(): + # Merge two inner dictionaries + if b.get(k) and all([isinstance(x, dict) for x in [v, b.get(k)]]): + b[k] = dict_merge(v, b[k]) + + # Merge left inner dictionary + elif isinstance(v, dict) and not b.get(k): + b[k] = dict_merge(v, {}) + + # Default merge of dictionaries return a | b diff --git a/tests/data/query_v2/mapping_response_obj/valid_mro4.json b/tests/data/query_v2/mapping_response_obj/valid_mro4.json new file mode 100644 index 0000000..b47a73f --- /dev/null +++ b/tests/data/query_v2/mapping_response_obj/valid_mro4.json @@ -0,0 +1,28 @@ +{ + "meta": { + "mapping": "key", + "combined": { + "mapping": "mapping" + }, + "merged": { + "name": "mapping" + } + }, + "destinations": [ + { + "architecture": "x86_64", + "destination": "fake_destination", + "overwrite": false, + "restrict_version": true, + "meta": { + "destination": "key", + "combined": { + "destination": "destination" + }, + "merged": { + "name": "destination" + } + } + } + ] +} diff --git a/tests/data/query_v2/mapping_response_obj/valid_mro4_meta.json b/tests/data/query_v2/mapping_response_obj/valid_mro4_meta.json new file mode 100644 index 0000000..5c07c9e --- /dev/null +++ b/tests/data/query_v2/mapping_response_obj/valid_mro4_meta.json @@ -0,0 +1,11 @@ +{ + "destination": "key", + "combined": { + "mapping": "mapping", + "destination": "destination" + }, + "mapping": "key", + "merged": { + "name": "destination" + } +} \ No newline at end of file diff --git a/tests/data/query_v2/query_response_entity/valid_qre5.json b/tests/data/query_v2/query_response_entity/valid_qre5.json new file mode 100644 index 0000000..5ce4fc1 --- /dev/null +++ b/tests/data/query_v2/query_response_entity/valid_qre5.json @@ -0,0 +1,109 @@ +{ + "cloud": "test", + "meta": { + "global": "key", + "combined": { + "package": "package" + }, + "merged": { + "name": "package" + } + }, + "mappings": { + "test-marketplace": { + "destinations": [ + { + "destination": "test-destination/foo/bar", + "overwrite": false, + "restrict_version": false, + "meta": { + "destination": "key", + "combined": { + "destination": "destination" + }, + "merged": { + "name": "destination" + } + } + }, + { + "destination": "second-test-destination/foo/bar", + "overwrite": true, + "restrict_version": false, + "meta": { + "destination": "key", + "combined": { + "destination": "destination" + }, + "merged": { + "name": "destination" + } + } + } + ], + "provider": null, + "meta": { + "mapping": "key", + "combined": { + "mapping": "mapping" + }, + "merged": { + "name": "mapping" + } + } + }, + "another-marketplace": { + "destinations": [ + { + "destination": "aaaaaaaaaaaaaaa", + "overwrite": false, + "restrict_version": false, + "meta": { + "destination": "key", + "combined": { + "destination": "destination" + }, + "merged": { + "name": "destination" + }, + "last": { + "test": true + } + } + }, + { + "destination": "bbbbbbbbbbbbb", + "overwrite": true, + "restrict_version": false, + "meta": { + "destination": "key", + "combined": { + "destination": "destination" + }, + "merged": { + "name": "destination" + }, + "last": { + "test": true + } + } + } + ], + "provider": null, + "meta": { + "mapping": "key", + "combined": { + "mapping": "mapping" + }, + "merged": { + "name": "mapping" + }, + "another": { + "foo": "bar" + } + } + } + }, + "workflow": "stratosphere", + "name": "sample-product" +} diff --git a/tests/data/query_v2/query_response_entity/valid_qre5_meta.json b/tests/data/query_v2/query_response_entity/valid_qre5_meta.json new file mode 100644 index 0000000..5a40312 --- /dev/null +++ b/tests/data/query_v2/query_response_entity/valid_qre5_meta.json @@ -0,0 +1,34 @@ +{ + "test-marketplace": { + "destination": "key", + "combined": { + "package": "package", + "mapping": "mapping", + "destination": "destination" + }, + "global": "key", + "mapping": "key", + "merged": { + "name": "destination" + } + }, + "another-marketplace": { + "destination": "key", + "combined": { + "package": "package", + "mapping": "mapping", + "destination": "destination" + }, + "global": "key", + "mapping": "key", + "merged": { + "name": "destination" + }, + "another": { + "foo": "bar" + }, + "last": { + "test": true + } + } +} \ No newline at end of file diff --git a/tests/test_models.py b/tests/test_models.py index 782cb88..0afbfee 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -262,6 +262,11 @@ class TestV2MappingResponseObject: "tests/data/query_v2/mapping_response_obj/valid_mro3_meta.json", None, ), + ( + "tests/data/query_v2/mapping_response_obj/valid_mro4.json", + "tests/data/query_v2/mapping_response_obj/valid_mro4_meta.json", + None, + ), ], ) def test_valid_mapping_response_obj(self, json_file, meta, provider) -> None: @@ -310,6 +315,10 @@ class TestV2QueryResponseEntity: "tests/data/query_v2/query_response_entity/valid_qre4.json", "tests/data/query_v2/query_response_entity/valid_qre4_meta.json", ), + ( + "tests/data/query_v2/query_response_entity/valid_qre5.json", + "tests/data/query_v2/query_response_entity/valid_qre5_meta.json", + ), ], ) def test_valid_query_response_entity(self, json_file, meta) -> None: @@ -318,9 +327,13 @@ def test_valid_query_response_entity(self, json_file, meta) -> None: expected_meta_dict = load_json(meta) q = QueryResponseEntity.from_json(data) + + # Test the merged meta attributes on destinations for account_name in q.account_names: - assert q.mappings[account_name].meta == expected_meta_dict[account_name] + for dest in q.mappings[account_name].destinations: + assert dest.meta == expected_meta_dict[account_name] + # Test the billing_code_config if q.billing_code_config: bc_asdict = {k: asdict(v) for k, v in q.billing_code_config.items()} assert bc_asdict == d["billing-code-config"] diff --git a/tests/test_utils.py b/tests/test_utils.py index 81bf496..f59921d 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -24,6 +24,16 @@ def test_assert_is_dict() -> None: ({"1": 1, "3": 3}, {"2": 2}, {"1": 1, "2": 2, "3": 3}), ({"1": 1, "3": 3}, {"2": 2, "3": 4}, {"1": 1, "2": 2, "3": 4}), ({"A": True, "B": True, "C": True}, {"B": False}, {"A": True, "B": False, "C": True}), + ( + {"dic1": {"foo": "bar"}, "dic2": {"key": "value"}}, + {"dic1": {"bar": "foo"}}, + {"dic1": {"foo": "bar", "bar": "foo"}, "dic2": {"key": "value"}}, + ), + ( + {"dic1": {"foo": "bar"}}, + {"dic1": {"bar": "foo"}, "dic2": {"key": "value"}}, + {"dic1": {"foo": "bar", "bar": "foo"}, "dic2": {"key": "value"}}, + ), ], ) def test_dict_merge(a: Dict[str, Any], b: Dict[str, Any], expected: Dict[str, Any]) -> None: