From c0acc1f67a5db17ce019edc3ebc673f9ad038418 Mon Sep 17 00:00:00 2001 From: tdstein Date: Wed, 9 Oct 2024 14:39:05 -0400 Subject: [PATCH] feat: add vanities --- .../tests/posit/connect/test_vanities.py | 75 +++++++++++++++++++ src/posit/connect/vanities.py | 35 +++++---- tests/posit/connect/test_vanities.py | 26 ++----- 3 files changed, 101 insertions(+), 35 deletions(-) create mode 100644 integration/tests/posit/connect/test_vanities.py diff --git a/integration/tests/posit/connect/test_vanities.py b/integration/tests/posit/connect/test_vanities.py new file mode 100644 index 00000000..b6dcc927 --- /dev/null +++ b/integration/tests/posit/connect/test_vanities.py @@ -0,0 +1,75 @@ +from posit import connect + + +class TestVanities: + + @classmethod + def setup_class(cls): + cls.client = connect.Client() + + @classmethod + def teardown_class(cls): + assert cls.client.content.count() == 0 + + def test_all(self): + content = self.client.content.create(name="example") + + # None by default + vanities = self.client.vanities.all() + assert len(vanities) == 0 + + # Set + content.vanity = "example" + + # Get + vanities = self.client.vanities.all() + assert len(vanities) == 1 + + # Cleanup + content.delete() + + vanities = self.client.vanities.all() + assert len(vanities) == 0 + + def test_property(self): + content = self.client.content.create(name="example") + + # None by default + assert content.vanity is None + + # Set + content.vanity = "example" + + # Get + vanity = content.vanity + assert vanity + assert vanity["path"] == "/example/" + + # Delete + del content.vanity + assert content.vanity is None + + # Cleanup + content.delete() + + + def test_destroy(self): + content = self.client.content.create(name="example") + + # None by default + assert content.vanity is None + + # Set + content.vanity = "example" + + # Get + vanity = content.vanity + assert vanity + assert vanity["path"] == "/example/" + + # Delete + vanity.destroy() + assert content.vanity is None + + # Cleanup + content.delete() diff --git a/src/posit/connect/vanities.py b/src/posit/connect/vanities.py index 6d3fa56f..af5a8040 100644 --- a/src/posit/connect/vanities.py +++ b/src/posit/connect/vanities.py @@ -1,5 +1,7 @@ from typing import Callable, Optional, Union, overload +from posit.connect.errors import ClientError + from .resources import Resource, ResourceParameters, Resources AfterDestroyCallback = Callable[[], None] @@ -21,12 +23,10 @@ def __init__( def destroy(self) -> None: """Destroy the vanity resource.""" - content_guid = self.get("content_guid") - if content_guid is None: - raise ValueError( - "The 'content_guid' is missing. Unable to perform the destroy operation." - ) - endpoint = self.params.url + f"v1/content/{content_guid}/vanity" + fuid = self.get("content_guid") + if fuid is None: + raise ValueError("Missing value for required field: 'content_guid'.") + endpoint = self.params.url + f"v1/content/{fuid}/vanity" self.params.session.delete(endpoint) self._after_destroy() @@ -50,21 +50,24 @@ def __init__(self, /, params: ResourceParameters, **kwargs): self._vanity: Optional[Vanity] = None @property - def vanity(self) -> Vanity: + def vanity(self) -> Optional[Vanity]: """Retrieve or lazily load the associated vanity resource.""" - if self._vanity is None: + if self._vanity: + return self._vanity + + try: uid = self.get("guid") if uid is None: - raise ValueError( - "The 'guid' is missing. Unable to perform the get vanity operation." - ) + raise ValueError("Missing value for required field: 'guid'.") endpoint = self.params.url + f"v1/content/{uid}/vanity" response = self.params.session.get(endpoint) result = response.json() - self._vanity = Vanity( - self.params, after_destroy=lambda: setattr(self, "_vanity", None), **result - ) - return self._vanity + self._vanity = Vanity(self.params, after_destroy=self.reset, **result) + return self._vanity + except ClientError as e: + if e.http_status == 404: + return None + raise e @vanity.setter def vanity(self, value: Union[str, dict]) -> None: @@ -99,6 +102,6 @@ def set_vanity(self, **attributes) -> None: """Set or update the vanity resource with given attributes.""" uid = self.get("guid") if uid is None: - raise ValueError("The 'guid' is missing. Unable to perform the set vanity operation.") + raise ValueError("Missing value for required field: 'guid'.") endpoint = self.params.url + f"v1/content/{uid}/vanity" self.params.session.put(endpoint, json=attributes) diff --git a/tests/posit/connect/test_vanities.py b/tests/posit/connect/test_vanities.py index c39c3b91..8fb0df85 100644 --- a/tests/posit/connect/test_vanities.py +++ b/tests/posit/connect/test_vanities.py @@ -104,25 +104,27 @@ def test_vanity_setter_with_string(self): content.vanity = path assert mock_put.call_count == 1 + assert content._vanity is None @responses.activate def test_vanity_setter_with_dict(self): guid = "8ce6eaca-60af-4c2f-93a0-f5f3cddf5ee5" base_url = "http://connect.example/__api__" endpoint = f"{base_url}/v1/content/{guid}/vanity" - vanity_attrs = {"path": "example", "locked": True} - mock_put = responses.put(endpoint, match=[json_params_matcher(vanity_attrs)]) + attrs = {"path": "example", "locked": True} + mock_put = responses.put(endpoint, match=[json_params_matcher(attrs)]) session = requests.Session() url = Url(base_url) params = ResourceParameters(session, url) content = VanityMixin(params, guid=guid) - content.vanity = vanity_attrs + content.vanity = attrs + assert content._vanity is None assert mock_put.call_count == 1 @responses.activate - def test_vanity_deleter_sends_delete_request(self): + def test_vanity_deleter(self): guid = "8ce6eaca-60af-4c2f-93a0-f5f3cddf5ee5" base_url = "http://connect.example/__api__" endpoint = f"{base_url}/v1/content/{guid}/vanity" @@ -135,19 +137,5 @@ def test_vanity_deleter_sends_delete_request(self): content._vanity = Vanity(params, content_guid=guid) del content.vanity + assert content._vanity is None assert mock_delete.call_count == 1 - - @responses.activate - def test_set_vanity(self): - guid = "8ce6eaca-60af-4c2f-93a0-f5f3cddf5ee5" - base_url = "http://connect.example/__api__" - endpoint = f"{base_url}/v1/content/{guid}/vanity" - mock_put = responses.put(endpoint) - - session = requests.Session() - url = Url(base_url) - params = ResourceParameters(session, url) - content = VanityMixin(params, guid=guid) - content.set_vanity(path="example") - - assert mock_put.call_count == 1