Skip to content

Commit ea13944

Browse files
committed
feat: add vanities
1 parent 9f8443c commit ea13944

File tree

3 files changed

+103
-37
lines changed

3 files changed

+103
-37
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
from posit import connect
2+
3+
4+
class TestVanities:
5+
6+
@classmethod
7+
def setup_class(cls):
8+
cls.client = connect.Client()
9+
10+
@classmethod
11+
def teardown_class(cls):
12+
assert cls.client.content.count() == 0
13+
14+
def test_all(self):
15+
content = self.client.content.create(name="example")
16+
17+
# None by default
18+
vanities = self.client.vanities.all()
19+
assert len(vanities) == 0
20+
21+
# Set
22+
content.vanity = "example"
23+
24+
# Get
25+
vanities = self.client.vanities.all()
26+
assert len(vanities) == 1
27+
28+
# Cleanup
29+
content.delete()
30+
31+
vanities = self.client.vanities.all()
32+
assert len(vanities) == 0
33+
34+
def test_property(self):
35+
content = self.client.content.create(name="example")
36+
37+
# None by default
38+
assert content.vanity is None
39+
40+
# Set
41+
content.vanity = "example"
42+
43+
# Get
44+
vanity = content.vanity
45+
assert vanity
46+
assert vanity["path"] == "/example/"
47+
48+
# Delete
49+
del content.vanity
50+
assert content.vanity is None
51+
52+
# Cleanup
53+
content.delete()
54+
55+
56+
def test_destroy(self):
57+
content = self.client.content.create(name="example")
58+
59+
# None by default
60+
assert content.vanity is None
61+
62+
# Set
63+
content.vanity = "example"
64+
65+
# Get
66+
vanity = content.vanity
67+
assert vanity
68+
assert vanity["path"] == "/example/"
69+
70+
# Delete
71+
vanity.destroy()
72+
assert content.vanity is None
73+
74+
# Cleanup
75+
content.delete()

src/posit/connect/vanities.py

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
from typing import Callable, Optional, Union, overload
1+
from typing import Callable, List, Optional, Union, overload
2+
3+
from posit.connect.errors import ClientError
24

35
from .resources import Resource, ResourceParameters, Resources
46

@@ -21,20 +23,18 @@ def __init__(
2123

2224
def destroy(self) -> None:
2325
"""Destroy the vanity resource."""
24-
content_guid = self.get("content_guid")
25-
if content_guid is None:
26-
raise ValueError(
27-
"The 'content_guid' is missing. Unable to perform the destroy operation."
28-
)
29-
endpoint = self.params.url + f"v1/content/{content_guid}/vanity"
26+
fuid = self.get("content_guid")
27+
if fuid is None:
28+
raise ValueError("Missing value for required field: 'content_guid'.")
29+
endpoint = self.params.url + f"v1/content/{fuid}/vanity"
3030
self.params.session.delete(endpoint)
3131
self._after_destroy()
3232

3333

3434
class Vanities(Resources):
3535
"""Manages a collection of Vanity resources."""
3636

37-
def all(self) -> list[Vanity]:
37+
def all(self) -> List[Vanity]:
3838
"""Retrieve all vanity resources."""
3939
endpoint = self.params.url + "v1/vanities"
4040
response = self.params.session.get(endpoint)
@@ -50,21 +50,24 @@ def __init__(self, /, params: ResourceParameters, **kwargs):
5050
self._vanity: Optional[Vanity] = None
5151

5252
@property
53-
def vanity(self) -> Vanity:
53+
def vanity(self) -> Optional[Vanity]:
5454
"""Retrieve or lazily load the associated vanity resource."""
55-
if self._vanity is None:
55+
if self._vanity:
56+
return self._vanity
57+
58+
try:
5659
uid = self.get("guid")
5760
if uid is None:
58-
raise ValueError(
59-
"The 'guid' is missing. Unable to perform the get vanity operation."
60-
)
61+
raise ValueError("Missing value for required field: 'guid'.")
6162
endpoint = self.params.url + f"v1/content/{uid}/vanity"
6263
response = self.params.session.get(endpoint)
6364
result = response.json()
64-
self._vanity = Vanity(
65-
self.params, after_destroy=lambda: setattr(self, "_vanity", None), **result
66-
)
67-
return self._vanity
65+
self._vanity = Vanity(self.params, after_destroy=self.reset, **result)
66+
return self._vanity
67+
except ClientError as e:
68+
if e.http_status == 404:
69+
return None
70+
raise e
6871

6972
@vanity.setter
7073
def vanity(self, value: Union[str, dict]) -> None:
@@ -99,6 +102,6 @@ def set_vanity(self, **attributes) -> None:
99102
"""Set or update the vanity resource with given attributes."""
100103
uid = self.get("guid")
101104
if uid is None:
102-
raise ValueError("The 'guid' is missing. Unable to perform the set vanity operation.")
105+
raise ValueError("Missing value for required field: 'guid'.")
103106
endpoint = self.params.url + f"v1/content/{uid}/vanity"
104107
self.params.session.put(endpoint, json=attributes)

tests/posit/connect/test_vanities.py

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -104,25 +104,27 @@ def test_vanity_setter_with_string(self):
104104
content.vanity = path
105105

106106
assert mock_put.call_count == 1
107+
assert content._vanity is None
107108

108109
@responses.activate
109110
def test_vanity_setter_with_dict(self):
110111
guid = "8ce6eaca-60af-4c2f-93a0-f5f3cddf5ee5"
111112
base_url = "http://connect.example/__api__"
112113
endpoint = f"{base_url}/v1/content/{guid}/vanity"
113-
vanity_attrs = {"path": "example", "locked": True}
114-
mock_put = responses.put(endpoint, match=[json_params_matcher(vanity_attrs)])
114+
attrs = {"path": "example", "locked": True}
115+
mock_put = responses.put(endpoint, match=[json_params_matcher(attrs)])
115116

116117
session = requests.Session()
117118
url = Url(base_url)
118119
params = ResourceParameters(session, url)
119120
content = VanityMixin(params, guid=guid)
120-
content.vanity = vanity_attrs
121+
content.vanity = attrs
121122

123+
assert content._vanity is None
122124
assert mock_put.call_count == 1
123125

124126
@responses.activate
125-
def test_vanity_deleter_sends_delete_request(self):
127+
def test_vanity_deleter(self):
126128
guid = "8ce6eaca-60af-4c2f-93a0-f5f3cddf5ee5"
127129
base_url = "http://connect.example/__api__"
128130
endpoint = f"{base_url}/v1/content/{guid}/vanity"
@@ -135,19 +137,5 @@ def test_vanity_deleter_sends_delete_request(self):
135137
content._vanity = Vanity(params, content_guid=guid)
136138
del content.vanity
137139

140+
assert content._vanity is None
138141
assert mock_delete.call_count == 1
139-
140-
@responses.activate
141-
def test_set_vanity(self):
142-
guid = "8ce6eaca-60af-4c2f-93a0-f5f3cddf5ee5"
143-
base_url = "http://connect.example/__api__"
144-
endpoint = f"{base_url}/v1/content/{guid}/vanity"
145-
mock_put = responses.put(endpoint)
146-
147-
session = requests.Session()
148-
url = Url(base_url)
149-
params = ResourceParameters(session, url)
150-
content = VanityMixin(params, guid=guid)
151-
content.set_vanity(path="example")
152-
153-
assert mock_put.call_count == 1

0 commit comments

Comments
 (0)