From 6ba2d10c8935d9b372184a42ea0c89cefcf16d5a Mon Sep 17 00:00:00 2001 From: Lena Garber <114949949+lgarber-akamai@users.noreply.github.com> Date: Thu, 18 Apr 2024 13:43:35 -0400 Subject: [PATCH 1/3] fix: Always load object values when converting MappedObjects to dict (#400) * Always load object values when converting MappedObjects to dict * Prevent overriding identifier attributes during construction * make format * Update unit test to cover parent_id issue --- linode_api4/objects/base.py | 27 ++++++++++++++++++++++++--- linode_api4/objects/dbase.py | 4 ++-- linode_api4/objects/object_storage.py | 4 ++-- test/unit/objects/linode_test.py | 8 ++++++++ 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/linode_api4/objects/base.py b/linode_api4/objects/base.py index 9d86b8808..45d18e4aa 100644 --- a/linode_api4/objects/base.py +++ b/linode_api4/objects/base.py @@ -1,5 +1,6 @@ import time from datetime import datetime, timedelta +from typing import Any, Dict, Optional from linode_api4.objects.serializable import JSONObject @@ -99,6 +100,18 @@ def _expand_vals(self, target, **vals): def __repr__(self): return "Mapping containing {}".format(vars(self).keys()) + @staticmethod + def _flatten_base_subclass(obj: "Base") -> Optional[Dict[str, Any]]: + if obj is None: + return None + + # If the object hasn't already been lazy-loaded, + # manually refresh it + if not getattr(obj, "_populated", False): + obj._api_get() + + return obj._raw_json + @property def dict(self): result = vars(self).copy() @@ -112,12 +125,17 @@ def dict(self): ( item.dict if isinstance(item, cls) - else item._raw_json if isinstance(item, Base) else item + else ( + self._flatten_base_subclass(item) + if isinstance(item, Base) + else item + ) ) for item in v ] elif isinstance(v, Base): - result[k] = v._raw_json + result[k] = self._flatten_base_subclass(v) + return result @@ -140,7 +158,10 @@ def __init__(self, client: object, id: object, json: object = {}) -> object: #: be updated on access. self._set("_raw_json", None) - for k in type(self).properties: + for k, v in type(self).properties.items(): + if v.identifier: + continue + self._set(k, None) self._set("id", id) diff --git a/linode_api4/objects/dbase.py b/linode_api4/objects/dbase.py index 3bd5bb7df..b6e288769 100644 --- a/linode_api4/objects/dbase.py +++ b/linode_api4/objects/dbase.py @@ -12,10 +12,10 @@ class DerivedBase(Base): parent_id_name = "parent_id" # override in child classes def __init__(self, client, id, parent_id, json={}): - Base.__init__(self, client, id, json=json) - self._set(type(self).parent_id_name, parent_id) + Base.__init__(self, client, id, json=json) + @classmethod def _api_get_derived(cls, parent, client): base_url = "{}/{}".format( diff --git a/linode_api4/objects/object_storage.py b/linode_api4/objects/object_storage.py index f1f040677..d9eb32433 100644 --- a/linode_api4/objects/object_storage.py +++ b/linode_api4/objects/object_storage.py @@ -31,10 +31,10 @@ class ObjectStorageBucket(DerivedBase): id_attribute = "label" properties = { - "cluster": Property(), + "cluster": Property(identifier=True), "created": Property(is_datetime=True), "hostname": Property(), - "label": Property(), + "label": Property(identifier=True), "objects": Property(), "size": Property(), } diff --git a/test/unit/objects/linode_test.py b/test/unit/objects/linode_test.py index c68dcfef6..9759bba41 100644 --- a/test/unit/objects/linode_test.py +++ b/test/unit/objects/linode_test.py @@ -554,6 +554,14 @@ def test_interface_ipv4(self): self.assertEqual(ipv4.vpc, "10.0.0.1") self.assertEqual(ipv4.nat_1_1, "any") + def test_config_devices_unwrap(self): + """ + Tests that config devices can be successfully converted to a dict. + """ + + inst = Instance(self.client, 123) + assert inst.configs[0].devices.dict.get("sda").get("id") == 12345 + class StackScriptTest(ClientBaseCase): """ From 9d4a6ee06c5a228d1bfd16d81845595cd5fcf684 Mon Sep 17 00:00:00 2001 From: Youjung Kim <126618609+ykim-1@users.noreply.github.com> Date: Thu, 18 Apr 2024 10:52:30 -0700 Subject: [PATCH 2/3] CI: Fix cross testing workflow (#401) * fix cross testing workflow * Update name --- .github/workflows/release-cross-repo-test.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release-cross-repo-test.yml b/.github/workflows/release-cross-repo-test.yml index 0f484d9af..0850ed9ea 100644 --- a/.github/workflows/release-cross-repo-test.yml +++ b/.github/workflows/release-cross-repo-test.yml @@ -26,9 +26,6 @@ jobs: with: python-version: '3.10' - - name: Install linode_api4 - run: make install - - name: checkout repo uses: actions/checkout@v3 with: @@ -48,12 +45,15 @@ jobs: cd .ansible/collections/ansible_collections/linode/cloud make install + - name: Install linode_api4 # Need to install from source after all ansible dependencies have been installed + run: make install + - name: replace existing keys run: | cd .ansible/collections/ansible_collections/linode/cloud rm -rf ~/.ansible/test && mkdir -p ~/.ansible/test && ssh-keygen -m PEM -q -t rsa -N '' -f ~/.ansible/test/id_rsa - - name: run tests + - name: Run Ansible Tests run: | cd .ansible/collections/ansible_collections/linode/cloud make testall From b0920fd7dcbf01a0239cf26ca8b3336996c14174 Mon Sep 17 00:00:00 2001 From: Zhiwei Liang <121905282+zliang-akamai@users.noreply.github.com> Date: Thu, 18 Apr 2024 15:29:10 -0400 Subject: [PATCH 3/3] Serialize `JSONObject` in `MappedObject` (#399) --- linode_api4/objects/base.py | 4 +++- test/unit/objects/mapped_object_test.py | 24 ++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/linode_api4/objects/base.py b/linode_api4/objects/base.py index 45d18e4aa..abee4cdaa 100644 --- a/linode_api4/objects/base.py +++ b/linode_api4/objects/base.py @@ -124,7 +124,7 @@ def dict(self): result[k] = [ ( item.dict - if isinstance(item, cls) + if isinstance(item, (cls, JSONObject)) else ( self._flatten_base_subclass(item) if isinstance(item, Base) @@ -135,6 +135,8 @@ def dict(self): ] elif isinstance(v, Base): result[k] = self._flatten_base_subclass(v) + elif isinstance(v, JSONObject): + result[k] = v.dict return result diff --git a/test/unit/objects/mapped_object_test.py b/test/unit/objects/mapped_object_test.py index 2d83008ae..ac2448a4a 100644 --- a/test/unit/objects/mapped_object_test.py +++ b/test/unit/objects/mapped_object_test.py @@ -1,6 +1,7 @@ +from dataclasses import dataclass from test.unit.base import ClientBaseCase -from linode_api4.objects import Base, MappedObject, Property +from linode_api4.objects import Base, JSONObject, MappedObject, Property class MappedObjectCase(ClientBaseCase): @@ -20,7 +21,7 @@ def test_mapped_object_dict(self): mapped_obj = MappedObject(**test_dict) self.assertEqual(mapped_obj.dict, test_dict) - def test_mapped_object_dict(self): + def test_serialize_base_objects(self): test_property_name = "bar" test_property_value = "bar" @@ -42,3 +43,22 @@ class Foo(Base): mapped_obj = MappedObject(foo=foo) self.assertEqual(mapped_obj.dict, expected_dict) + + def test_serialize_json_objects(self): + test_property_name = "bar" + test_property_value = "bar" + + @dataclass + class Foo(JSONObject): + bar: str = "" + + foo = Foo.from_json({test_property_name: test_property_value}) + + expected_dict = { + "foo": { + test_property_name: test_property_value, + } + } + + mapped_obj = MappedObject(foo=foo) + self.assertEqual(mapped_obj.dict, expected_dict)