Skip to content

Commit

Permalink
docs: add documentation for vanities
Browse files Browse the repository at this point in the history
  • Loading branch information
tdstein committed Oct 10, 2024
1 parent ea13944 commit 7f37087
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 25 deletions.
3 changes: 0 additions & 3 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,4 @@
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"cSpell.words": [
"mypy"
]
}
1 change: 1 addition & 0 deletions docs/_quarto.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ quartodoc:
- connect.permissions
- connect.tasks
- connect.users
- connect.vanities
- title: Posit Connect Metrics
package: posit
contents:
Expand Down
178 changes: 156 additions & 22 deletions src/posit/connect/vanities.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,114 @@


class Vanity(Resource):
"""Represents a Vanity resource with the ability to destroy itself."""
"""A vanity resource.
Vanities maintain custom URL paths assigned to content.
Warnings
--------
Vanity paths may only contain alphanumeric characters, hyphens, underscores, and slashes.
Vanities cannot have children. For example, if the vanity path "/finance/" exists, the vanity path "/finance/budget/" cannot. But, if "/finance" does not exist, both "/finance/budget/" and "/finance/report" are allowed.
The following vanities are reserved by Connect:
- `/__`
- `/favicon.ico`
- `/connect`
- `/apps`
- `/users`
- `/groups`
- `/setpassword`
- `/user-completion`
- `/confirm`
- `/recent`
- `/reports`
- `/plots`
- `/unpublished`
- `/settings`
- `/metrics`
- `/tokens`
- `/help`
- `/login`
- `/welcome`
- `/register`
- `/resetpassword`
- `/content`
"""

_fuid: str = "content_guid"
"""str : the foreign unique identifier field that points to the owner of this vanity, default is 'content_guid'"""

def __init__(
self,
/,
params: ResourceParameters,
*,
after_destroy: AfterDestroyCallback = lambda: None,
after_destroy: Optional[AfterDestroyCallback] = None,
**kwargs,
):
"""Initialize a Vanity.
Parameters
----------
params : ResourceParameters
after_destroy : AfterDestroyCallback, optional
Called after the Vanity is successfully destroyed, by default None
"""
super().__init__(params, **kwargs)
self._after_destroy = after_destroy

def destroy(self) -> None:
"""Destroy the vanity resource."""
"""Destroy the vanity.
Raises
------
ValueError
If the foreign unique identifier is missing or its value is `None`.
Warnings
--------
This operation is irreversible.
Note
----
This action requires administrator privileges.
"""
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()

if self._after_destroy:
self._after_destroy()


class Vanities(Resources):
"""Manages a collection of Vanity resources."""
"""Manages a collection of vanities."""

def all(self) -> List[Vanity]:
"""Retrieve all vanity resources."""
"""Retrieve all vanities.
Returns
-------
List[Vanity]
Notes
-----
This action requires administrator privileges.
"""
endpoint = self.params.url + "v1/vanities"
response = self.params.session.get(endpoint)
results = response.json()
return [Vanity(self.params, **result) for result in results]


class VanityMixin(Resource):
"""Mixin class to add vanity management capabilities to a resource."""
"""Mixin class to add a vanity attribute to a resource."""

_uid: str = "guid"
"""str : the unique identifier field for this resource"""

def __init__(self, /, params: ResourceParameters, **kwargs):
super().__init__(params, **kwargs)
Expand All @@ -62,7 +134,7 @@ def vanity(self) -> Optional[Vanity]:
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=self.reset, **result)
self._vanity = Vanity(self.params, after_destroy=self.reset_vanity, **result)
return self._vanity
except ClientError as e:
if e.http_status == 404:
Expand All @@ -71,37 +143,99 @@ def vanity(self) -> Optional[Vanity]:

@vanity.setter
def vanity(self, value: Union[str, dict]) -> None:
"""Set the vanity using a path or dictionary of attributes."""
"""Set the vanity.
Parameters
----------
value : str or dict
The value can be a string or a dictionary. If provided as a string, it represents the vanity path. If provided as a dictionary, it contains key-value pairs with detailed information about the object.
"""
if isinstance(value, str):
self.set_vanity(path=value)
elif isinstance(value, dict):
self.set_vanity(**value)
self.reset()
self.reset_vanity()

@vanity.deleter
def vanity(self) -> None:
"""Delete the vanity resource."""
"""Destroy the vanity.
Warnings
--------
This operation is irreversible.
Note
----
This action requires administrator privileges.
See Also
--------
reset_vanity
"""
if self._vanity:
self._vanity.destroy()
self.reset()
self.reset_vanity()

def reset(self) -> None:
"""Reset the cached vanity resource."""
def reset_vanity(self) -> None:
"""Unload the cached vanity.
Forces the next access, if any, to query the vanity from the Connect server.
"""
self._vanity = None

@overload
def set_vanity(self, *, path: str) -> None: ...
def set_vanity(self, *, path: str) -> None:
"""Set the vanity.
Parameters
----------
path : str
The vanity path.
Raises
------
ValueError
If the unique identifier field is missing or the value is None.
"""
...

@overload
def set_vanity(self, *, path: str, force: bool) -> None: ...
def set_vanity(self, *, path: str, force: bool) -> None:
"""Set the vanity.
Parameters
----------
path : str
The vanity path.
force : bool
If `True`, overwrite the ownership of this vanity to this resource, default `False`
Raises
------
ValueError
If the unique identifier field is missing or the value is None.
"""
...

@overload
def set_vanity(self, **attributes) -> None: ...
def set_vanity(self, **attributes) -> None:
"""Set the vanity.
Parameters
----------
**attributes : dict, optional
Arbitrary vanity attributes. All attributes are passed as the request body to POST 'v1/content/:guid/vanity'
Possible keys may include:
- `path` : str
- `force` : bool
"""
...

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("Missing value for required field: 'guid'.")
endpoint = self.params.url + f"v1/content/{uid}/vanity"
"""Set the vanity."""
v = self.get(self._uid)
if v is None:
raise ValueError(f"Missing value for required field: '{self._uid}'.")
endpoint = self.params.url + f"v1/content/{v}/vanity"
self.params.session.put(endpoint, json=attributes)

0 comments on commit 7f37087

Please sign in to comment.