diff --git a/src/posit/connect/content.py b/src/posit/connect/content.py index dcb5a74f..325d5939 100644 --- a/src/posit/connect/content.py +++ b/src/posit/connect/content.py @@ -346,7 +346,7 @@ def environment_variables(self) -> EnvVars: @property def permissions(self) -> Permissions: - return Permissions(context_to_resource_parameters(self._ctx), self["guid"]) + return Permissions(self._ctx) @property def owner(self) -> dict: diff --git a/src/posit/connect/permissions.py b/src/posit/connect/permissions.py index c5c9a268..79928e72 100644 --- a/src/posit/connect/permissions.py +++ b/src/posit/connect/permissions.py @@ -4,20 +4,46 @@ from typing import List, overload -from requests.sessions import Session as Session +from ._active import ActiveDict +from ._types_content_item import ContentItemContext +from ._types_context import ContextP -from .resources import Resource, ResourceParameters, Resources +class PermissionContext(ContentItemContext): + permission_id: str + + def __init__(self, ctx: ContentItemContext, /, *, permission_id: str) -> None: + super().__init__(ctx, content_guid=ctx.content_guid) + self.permission_id = permission_id + + +class Permission(ActiveDict[PermissionContext]): + @classmethod + def _api_path(cls, content_guid: str, permission_id: str) -> str: + return f"v1/content/{content_guid}/permissions/{permission_id}" + + def __init__(self, ctx: ContentItemContext, /, **kwargs) -> None: + permission_id = kwargs.get("id") + assert isinstance( + permission_id, str + ), f"Permission 'id' must be a string. Got: {permission_id}" + assert permission_id, "Permission 'id' must not be an empty string." + + permission_ctx = PermissionContext( + ctx, + permission_id=permission_id, + ) + path = self._api_path(permission_ctx.content_guid, permission_ctx.permission_id) + get_data = len(kwargs) == 1 # `id` is required + + super().__init__(permission_ctx, path, get_data, **kwargs) -class Permission(Resource): def delete(self) -> None: """Delete the permission.""" - path = f"v1/content/{self['content_guid']}/permissions/{self['id']}" - url = self.params.url + path - self.params.session.delete(url) + self._delete_api() @overload - def update(self, *args, role: str, **kwargs) -> None: + def update(self, *args, role: str, **kwargs) -> Permission: """Update the permission. Parameters @@ -27,10 +53,10 @@ def update(self, *args, role: str, **kwargs) -> None: """ @overload - def update(self, *args, **kwargs) -> None: + def update(self, *args, **kwargs) -> Permission: """Update the permission.""" - def update(self, *args, **kwargs) -> None: + def update(self, *args, **kwargs) -> Permission: """Update the permission.""" body = { "principal_guid": self.get("principal_guid"), @@ -39,19 +65,14 @@ def update(self, *args, **kwargs) -> None: } body.update(dict(*args)) body.update(**kwargs) - path = f"v1/content/{self['content_guid']}/permissions/{self['id']}" - url = self.params.url + path - response = self.params.session.put( - url, - json=body, - ) - super().update(**response.json()) + result = self._put_api(json=body) + return Permission(self._ctx, **result) # pyright: ignore[reportCallIssue] -class Permissions(Resources): - def __init__(self, params: ResourceParameters, content_guid: str) -> None: - super().__init__(params) - self.content_guid = content_guid +class Permissions(ContextP[ContentItemContext]): + def __init__(self, ctx: ContentItemContext) -> None: + super().__init__() + self._ctx = ctx def count(self) -> int: """Count the number of permissions. @@ -93,10 +114,10 @@ def create(self, **kwargs) -> Permission: ------- Permission """ - path = f"v1/content/{self.content_guid}/permissions" - url = self.params.url + path - response = self.params.session.post(url, json=kwargs) - return Permission(params=self.params, **response.json()) + path = f"v1/content/{self._ctx.content_guid}/permissions" + url = self._ctx.url + path + response = self._ctx.session.post(url, json=kwargs) + return Permission(self._ctx, **response.json()) def find(self, **kwargs) -> List[Permission]: """Find permissions. @@ -105,11 +126,11 @@ def find(self, **kwargs) -> List[Permission]: ------- List[Permission] """ - path = f"v1/content/{self.content_guid}/permissions" - url = self.params.url + path - response = self.params.session.get(url, json=kwargs) + path = f"v1/content/{self._ctx.content_guid}/permissions" + url = self._ctx.url + path + response = self._ctx.session.get(url, json=kwargs) results = response.json() - return [Permission(self.params, **result) for result in results] + return [Permission(self._ctx, **result) for result in results] def find_one(self, **kwargs) -> Permission | None: """Find a permission. @@ -133,7 +154,4 @@ def get(self, uid: str) -> Permission: ------- Permission """ - path = f"v1/content/{self.content_guid}/permissions/{uid}" - url = self.params.url + path - response = self.params.session.get(url) - return Permission(self.params, **response.json()) + return Permission(self._ctx, id=uid) diff --git a/tests/posit/connect/__api__/v1/content/f2f37341-e21d-3d80-c698-a935ad614066/permissions.json b/tests/posit/connect/__api__/v1/content/f2f37341-e21d-3d80-c698-a935ad614066/permissions.json index 9db6f6bf..dfb60f95 100644 --- a/tests/posit/connect/__api__/v1/content/f2f37341-e21d-3d80-c698-a935ad614066/permissions.json +++ b/tests/posit/connect/__api__/v1/content/f2f37341-e21d-3d80-c698-a935ad614066/permissions.json @@ -1,16 +1,16 @@ [ - { - "id": 94, - "content_guid": "f2f37341-e21d-3d80-c698-a935ad614066", - "principal_guid": "78974391-d89f-4f11-916a-ba50cfe993db", - "principal_type": "user", - "role": "owner" - }, - { - "id": 59, - "content_guid": "f2f37341-e21d-3d80-c698-a935ad614066", - "principal_guid": "75b95fc0-ae02-4d85-8732-79a845143eed", - "principal_type": "group", - "role": "viewer" - } + { + "id": "94", + "content_guid": "f2f37341-e21d-3d80-c698-a935ad614066", + "principal_guid": "78974391-d89f-4f11-916a-ba50cfe993db", + "principal_type": "user", + "role": "owner" + }, + { + "id": "59", + "content_guid": "f2f37341-e21d-3d80-c698-a935ad614066", + "principal_guid": "75b95fc0-ae02-4d85-8732-79a845143eed", + "principal_type": "group", + "role": "viewer" + } ] diff --git a/tests/posit/connect/__api__/v1/content/f2f37341-e21d-3d80-c698-a935ad614066/permissions/94.json b/tests/posit/connect/__api__/v1/content/f2f37341-e21d-3d80-c698-a935ad614066/permissions/94.json index 491db40c..496fee15 100644 --- a/tests/posit/connect/__api__/v1/content/f2f37341-e21d-3d80-c698-a935ad614066/permissions/94.json +++ b/tests/posit/connect/__api__/v1/content/f2f37341-e21d-3d80-c698-a935ad614066/permissions/94.json @@ -1,7 +1,7 @@ { - "id": 94, - "content_guid": "f2f37341-e21d-3d80-c698-a935ad614066", - "principal_guid": "78974391-d89f-4f11-916a-ba50cfe993db", - "principal_type": "user", - "role": "owner" + "id": "94", + "content_guid": "f2f37341-e21d-3d80-c698-a935ad614066", + "principal_guid": "78974391-d89f-4f11-916a-ba50cfe993db", + "principal_type": "user", + "role": "owner" } diff --git a/tests/posit/connect/test_permissions.py b/tests/posit/connect/test_permissions.py index 0f3a390a..92adcda8 100644 --- a/tests/posit/connect/test_permissions.py +++ b/tests/posit/connect/test_permissions.py @@ -5,8 +5,9 @@ import responses from responses import matchers +from posit.connect._types_content_item import ContentItemContext +from posit.connect.context import Context from posit.connect.permissions import Permission, Permissions -from posit.connect.resources import ResourceParameters from posit.connect.urls import Url from .api import load_mock, load_mock_dict, load_mock_list @@ -25,9 +26,12 @@ def test(self): ) # setup - params = ResourceParameters(requests.Session(), Url("https://connect.example/__api__")) + ctx = ContentItemContext( + Context(requests.Session(), Url("https://connect.example/__api__")), + content_guid=content_guid, + ) fake_permission = load_mock_dict(f"v1/content/{content_guid}/permissions/{uid}.json") - permission = Permission(params, **fake_permission) + permission = Permission(ctx, **fake_permission) # invoke permission.delete() @@ -40,7 +44,7 @@ class TestPermissionUpdate: @responses.activate def test_request_shape(self): # test data - uid = random.randint(0, 100) + uid = str(random.randint(0, 100)) content_guid = str(uuid.uuid4()) principal_guid = str(uuid.uuid4()) principal_type = "principal_type" @@ -51,7 +55,9 @@ def test_request_shape(self): responses.put( f"https://connect.example/__api__/v1/content/{content_guid}/permissions/{uid}", json={ - # doesn't matter for this test + # doesn't matter for this test, but something more than `id` is needed to avoid an API call + "id": uid, + "content_guid": content_guid, }, match=[ # assertion @@ -69,9 +75,13 @@ def test_request_shape(self): ) # setup - params = ResourceParameters(requests.Session(), Url("https://connect.example/__api__")) + ctx = ContentItemContext( + Context(requests.Session(), Url("https://connect.example/__api__")), + content_guid=content_guid, + ) + permission = Permission( - params, + ctx, id=uid, content_guid=content_guid, principal_guid=principal_guid, @@ -95,7 +105,7 @@ def test_role_update(self): fake_permission.update(role=new_role) # define api behavior - uid = random.randint(0, 100) + uid = str(random.randint(0, 100)) content_guid = str(uuid.uuid4()) responses.put( f"https://connect.example/__api__/v1/content/{content_guid}/permissions/{uid}", @@ -112,13 +122,17 @@ def test_role_update(self): ) # setup - params = ResourceParameters(requests.Session(), Url("https://connect.example/__api__")) - permission = Permission(params, id=uid, content_guid=content_guid, role=old_role) + ctx = ContentItemContext( + Context(requests.Session(), Url("https://connect.example/__api__")), + content_guid=content_guid, + ) + permission = Permission(ctx, id=uid, content_guid=content_guid, role=old_role) # assert role change with respect to api response assert permission["role"] == old_role - permission.update(role=new_role) - assert permission["role"] == new_role + updated_permission = permission.update(role=new_role) + assert permission["role"] == old_role + assert updated_permission["role"] == new_role class TestPermissionsCount: @@ -135,8 +149,11 @@ def test(self): ) # setup - params = ResourceParameters(requests.Session(), Url("https://connect.example/__api__")) - permissions = Permissions(params, content_guid=content_guid) + ctx = ContentItemContext( + Context(requests.Session(), Url("https://connect.example/__api__")), + content_guid=content_guid, + ) + permissions = Permissions(ctx) # invoke count = permissions.count() @@ -177,8 +194,12 @@ def test(self): ) # setup - params = ResourceParameters(requests.Session(), Url("https://connect.example/__api__")) - permissions = Permissions(params, content_guid=content_guid) + ctx = ContentItemContext( + Context(requests.Session(), Url("https://connect.example/__api__")), + content_guid=content_guid, + ) + + permissions = Permissions(ctx) # invoke permission = permissions.create( @@ -205,8 +226,11 @@ def test(self): ) # setup - params = ResourceParameters(requests.Session(), Url("https://connect.example/__api__")) - permissions = Permissions(params, content_guid=content_guid) + ctx = ContentItemContext( + Context(requests.Session(), Url("https://connect.example/__api__")), + content_guid=content_guid, + ) + permissions = Permissions(ctx) # invoke permissions = permissions.find() @@ -229,8 +253,11 @@ def test(self): ) # setup - params = ResourceParameters(requests.Session(), Url("https://connect.example/__api__")) - permissions = Permissions(params, content_guid=content_guid) + ctx = ContentItemContext( + Context(requests.Session(), Url("https://connect.example/__api__")), + content_guid=content_guid, + ) + permissions = Permissions(ctx) # invoke permission = permissions.find_one() @@ -254,8 +281,11 @@ def test(self): ) # setup - params = ResourceParameters(requests.Session(), Url("https://connect.example/__api__")) - permissions = Permissions(params, content_guid=content_guid) + ctx = ContentItemContext( + Context(requests.Session(), Url("https://connect.example/__api__")), + content_guid=content_guid, + ) + permissions = Permissions(ctx) # invoke permission = permissions.get(uid)