Skip to content

Commit abb26ba

Browse files
committed
always use the path for content.vanity attribute
1 parent 3f94ebd commit abb26ba

File tree

3 files changed

+49
-57
lines changed

3 files changed

+49
-57
lines changed

integration/tests/posit/connect/test_vanities.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@ def test_property(self):
4141

4242
# Get
4343
vanity = content.vanity
44-
assert vanity
45-
assert vanity["path"] == "/example/"
44+
assert vanity == "/example/"
4645

4746
# Delete
4847
del content.vanity
@@ -61,7 +60,7 @@ def test_destroy(self):
6160
content.vanity = "example"
6261

6362
# Get
64-
vanity = content.vanity
63+
vanity = content.find_vanity()
6564
assert vanity
6665
assert vanity["path"] == "/example/"
6766

src/posit/connect/vanities.py

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Callable, List, Optional, TypedDict, Union
1+
from typing import Callable, List, Optional, TypedDict
22

33
from typing_extensions import NotRequired, Required, Unpack
44

@@ -47,9 +47,9 @@ class Vanity(Resource):
4747
class VanityAttributes(TypedDict):
4848
"""Vanity attributes."""
4949

50-
path: str
50+
path: Required[str]
5151
content_guid: Required[str]
52-
created_time: str
52+
created_time: Required[str]
5353

5454
def __init__(
5555
self,
@@ -123,7 +123,7 @@ class VanityMixin(Resource):
123123
class HasGuid(TypedDict):
124124
"""Has a guid."""
125125

126-
guid: str
126+
guid: Required[str]
127127

128128
def __init__(self, /, params: ResourceParameters, **kwargs: Unpack[HasGuid]):
129129
super().__init__(params, **kwargs)
@@ -135,38 +135,38 @@ def _endpoint(self):
135135
return self.params.url + f"v1/content/{self._content_guid}/vanity"
136136

137137
@property
138-
def vanity(self) -> Optional[Vanity]:
138+
def vanity(self) -> Optional[str]:
139139
"""Get the vanity."""
140140
if self._vanity:
141-
return self._vanity
141+
return self._vanity["path"]
142142

143143
try:
144144
self._vanity = self.find_vanity()
145-
self._vanity._after_destroy = self.reset_vanity
146-
return self._vanity
145+
return self._vanity["path"]
147146
except ClientError as e:
148147
if e.http_status == 404:
149148
return None
150149
raise e
151150

152151
@vanity.setter
153-
def vanity(self, value: Union[str, "CreateVanityRequest"]) -> None:
152+
def vanity(self, value: str) -> None:
154153
"""Set the vanity.
155154
156155
Parameters
157156
----------
158-
value : str or CreateVanityRequest
159-
The value can be a str or a CreateVanityRequest. If provided as a string, it is the vanity path.
157+
value : str
158+
The vanity path.
159+
160+
Note
161+
----
162+
This action requires owner or administrator privileges.
160163
161164
See Also
162165
--------
163166
create_vanity
164167
"""
165-
if isinstance(value, str):
166-
self.create_vanity(path=value)
167-
elif isinstance(value, dict):
168-
self.create_vanity(**value)
169-
self.reset_vanity()
168+
self._vanity = self.create_vanity(path=value)
169+
self._vanity._after_destroy = self.reset_vanity
170170

171171
@vanity.deleter
172172
def vanity(self) -> None:
@@ -178,7 +178,7 @@ def vanity(self) -> None:
178178
179179
Note
180180
----
181-
This action requires administrator privileges.
181+
This action requires owner or administrator privileges.
182182
183183
See Also
184184
--------
@@ -196,32 +196,39 @@ def reset_vanity(self) -> None:
196196
self._vanity = None
197197

198198
class CreateVanityRequest(TypedDict, total=False):
199-
"""A request schema for creating a vanity.
200-
201-
Attributes
202-
----------
203-
path : str
204-
The path for the vanity.
205-
force : bool
206-
Whether to force the creation of the vanity.
207-
"""
199+
"""A request schema for creating a vanity."""
208200

209201
path: Required[str]
202+
"""The vanity path (.e.g, 'my-dashboard')"""
203+
210204
force: NotRequired[bool]
205+
"""Whether to force creation of the vanity"""
211206

212-
def create_vanity(self, **kwargs: Unpack[CreateVanityRequest]) -> None:
207+
def create_vanity(self, **kwargs: Unpack[CreateVanityRequest]) -> Vanity:
213208
"""Create a vanity.
214209
215210
Parameters
216211
----------
217212
path : str, required
218213
The path for the vanity.
219214
force : bool, not required
220-
Whether to force the creation of the vanity, default False
215+
Whether to force the creation of the vanity. When True, any other vanity with the same path will be deleted.
216+
217+
Warnings
218+
--------
219+
If setting force=True, the destroy operation performed on the other vanity is irreversible.
221220
"""
222-
self.params.session.put(self._endpoint, json=kwargs)
221+
response = self.params.session.put(self._endpoint, json=kwargs)
222+
result = response.json()
223+
return Vanity(self.params, **result)
223224

224-
def find_vanity(self):
225+
def find_vanity(self) -> Vanity:
226+
"""Find the vanity.
227+
228+
Returns
229+
-------
230+
Vanity
231+
"""
225232
response = self.params.session.get(self._endpoint)
226233
result = response.json()
227-
return Vanity(self.params, **result)
234+
return Vanity(self.params, after_destroy=self.reset_vanity, **result)

tests/posit/connect/test_vanities.py

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def test_destroy_sends_delete_request(self):
2020
session = requests.Session()
2121
url = Url(base_url)
2222
params = ResourceParameters(session, url)
23-
vanity = Vanity(params, content_guid=content_guid)
23+
vanity = Vanity(params, content_guid=content_guid, path=Mock(), created_time=Mock())
2424

2525
vanity.destroy()
2626

@@ -67,15 +67,14 @@ def test_vanity_getter_returns_vanity(self):
6767
guid = "8ce6eaca-60af-4c2f-93a0-f5f3cddf5ee5"
6868
base_url = "http://connect.example/__api__"
6969
endpoint = f"{base_url}/v1/content/{guid}/vanity"
70-
vanity_data = {"content_guid": guid}
71-
mock_get = responses.get(endpoint, json=vanity_data)
70+
mock_get = responses.get(endpoint, json={"content_guid": guid, "path": "my-dashboard"})
7271

7372
session = requests.Session()
7473
url = Url(base_url)
7574
params = ResourceParameters(session, url)
7675
content = VanityMixin(params, guid=guid)
7776

78-
assert content.vanity == vanity_data
77+
assert content.vanity == "my-dashboard"
7978
assert mock_get.call_count == 1
8079

8180
@responses.activate
@@ -84,33 +83,20 @@ def test_vanity_setter_with_string(self):
8483
base_url = "http://connect.example/__api__"
8584
endpoint = f"{base_url}/v1/content/{guid}/vanity"
8685
path = "example"
87-
mock_put = responses.put(endpoint, match=[json_params_matcher({"path": path})])
86+
mock_put = responses.put(
87+
endpoint,
88+
json={"content_guid": guid, "path": path},
89+
match=[json_params_matcher({"path": path})],
90+
)
8891

8992
session = requests.Session()
9093
url = Url(base_url)
9194
params = ResourceParameters(session, url)
9295
content = VanityMixin(params, guid=guid)
9396
content.vanity = path
97+
assert content.vanity == path
9498

9599
assert mock_put.call_count == 1
96-
assert content._vanity is None
97-
98-
@responses.activate
99-
def test_vanity_setter_with_dict(self):
100-
guid = "8ce6eaca-60af-4c2f-93a0-f5f3cddf5ee5"
101-
base_url = "http://connect.example/__api__"
102-
endpoint = f"{base_url}/v1/content/{guid}/vanity"
103-
attrs = {"path": "example", "locked": True}
104-
mock_put = responses.put(endpoint, match=[json_params_matcher(attrs)])
105-
106-
session = requests.Session()
107-
url = Url(base_url)
108-
params = ResourceParameters(session, url)
109-
content = VanityMixin(params, guid=guid)
110-
content.vanity = attrs
111-
112-
assert content._vanity is None
113-
assert mock_put.call_count == 1
114100

115101
@responses.activate
116102
def test_vanity_deleter(self):

0 commit comments

Comments
 (0)