diff --git a/src/specklepy/api/resources/current/model_resource.py b/src/specklepy/api/resources/current/model_resource.py index e97d9f6..63c5f7e 100644 --- a/src/specklepy/api/resources/current/model_resource.py +++ b/src/specklepy/api/resources/current/model_resource.py @@ -8,6 +8,7 @@ from specklepy.core.api.inputs.model_inputs import ( ) from specklepy.core.api.inputs.project_inputs import ProjectModelsFilter from specklepy.core.api.models import Model, ModelWithVersions, ResourceCollection +from specklepy.core.api.models.current import ModelPermissionChecks from specklepy.core.api.resources import ModelResource as CoreResource from specklepy.logging import metrics @@ -72,3 +73,7 @@ class ModelResource(CoreResource): def update(self, input: UpdateModelInput) -> Model: metrics.track(metrics.SDK, self.account, {"name": "Model Update"}) return super().update(input) + + def get_permissions(self, model_id: str, project_id: str) -> ModelPermissionChecks: + metrics.track(metrics.SDK, self.account, {"name": "Model Get Permissions"}) + return super().get_permissions(model_id, project_id) diff --git a/src/specklepy/core/api/enums.py b/src/specklepy/core/api/enums.py index 702b365..08ff777 100644 --- a/src/specklepy/core/api/enums.py +++ b/src/specklepy/core/api/enums.py @@ -7,6 +7,7 @@ class ProjectVisibility(str, Enum): PRIVATE = "PRIVATE" PUBLIC = "PUBLIC" UNLISTED = "UNLISTED" + """Deprecated, use PUBLIC instead""" WORKSPACE = "WORKSPACE" diff --git a/src/specklepy/core/api/models/current.py b/src/specklepy/core/api/models/current.py index b1a7341..e9c6389 100644 --- a/src/specklepy/core/api/models/current.py +++ b/src/specklepy/core/api/models/current.py @@ -137,6 +137,12 @@ class Version(GraphQLBaseModel): source_application: str | None +class ModelPermissionChecks(GraphQLBaseModel): + can_update: "PermissionCheckResult" + can_delete: "PermissionCheckResult" + can_create_version: "PermissionCheckResult" + + class Model(GraphQLBaseModel): author: LimitedUser | None created_at: datetime @@ -156,7 +162,6 @@ class ProjectPermissionChecks(GraphQLBaseModel): can_create_model: "PermissionCheckResult" can_delete: "PermissionCheckResult" can_load: "PermissionCheckResult" - can_publish: "PermissionCheckResult" class Project(GraphQLBaseModel): diff --git a/src/specklepy/core/api/resources/current/model_resource.py b/src/specklepy/core/api/resources/current/model_resource.py index a9a51f3..32036fb 100644 --- a/src/specklepy/core/api/resources/current/model_resource.py +++ b/src/specklepy/core/api/resources/current/model_resource.py @@ -10,6 +10,7 @@ from specklepy.core.api.inputs.model_inputs import ( ) from specklepy.core.api.inputs.project_inputs import ProjectModelsFilter from specklepy.core.api.models import Model, ModelWithVersions, ResourceCollection +from specklepy.core.api.models.current import ModelPermissionChecks from specklepy.core.api.resource import ResourceBase from specklepy.core.api.responses import DataResponse @@ -299,3 +300,40 @@ class ModelResource(ResourceBase): return self.make_request_and_parse_response( DataResponse[DataResponse[Model]], QUERY, variables ).data.data + + def get_permissions(self, project_id: str, model_id: str) -> ModelPermissionChecks: + QUERY = gql( + """ + query ModelPermissions($projectId: String!, $modelId: String!) { + data:project(id: $projectId) { + data:model(id: $modelId) { + data:permissions { + canUpdate { + authorized + code + message + } + canDelete { + authorized + code + message + } + canCreateVersion { + authorized + code + message + } + } + } + } + } + """ + ) + + variables = {"projectId": project_id, "modelId": model_id} + + return self.make_request_and_parse_response( + DataResponse[DataResponse[DataResponse[ModelPermissionChecks]]], + QUERY, + variables, + ).data.data.data diff --git a/tests/integration/client/current/test_active_user_resource_permissions.py b/tests/integration/client/current/test_active_user_resource_permissions.py index 86816d8..835126b 100644 --- a/tests/integration/client/current/test_active_user_resource_permissions.py +++ b/tests/integration/client/current/test_active_user_resource_permissions.py @@ -50,12 +50,10 @@ class TestActiveUserResourcePermissions: assert hasattr(permissions, "can_create_model") assert hasattr(permissions, "can_delete") assert hasattr(permissions, "can_load") - assert hasattr(permissions, "can_publish") assert permissions.can_create_model.authorized is True assert permissions.can_delete.authorized is True assert permissions.can_load.authorized is True - assert permissions.can_publish.authorized is True def test_active_user_get_projects_with_permissions_with_filter( self, client: SpeckleClient, test_project: Project diff --git a/tests/integration/client/current/test_model_resource.py b/tests/integration/client/current/test_model_resource.py index 9d3e14e..ca14d98 100644 --- a/tests/integration/client/current/test_model_resource.py +++ b/tests/integration/client/current/test_model_resource.py @@ -1,6 +1,7 @@ import pytest from specklepy.api.client import SpeckleClient +from specklepy.core.api.enums import ProjectVisibility from specklepy.core.api.inputs.model_inputs import ( CreateModelInput, DeleteModelInput, @@ -12,6 +13,7 @@ from specklepy.core.api.inputs.project_inputs import ( ) from specklepy.core.api.models.current import ( Model, + ModelPermissionChecks, Project, ProjectWithModels, ResourceCollection, @@ -24,7 +26,9 @@ class TestModelResource: @pytest.fixture() def test_project(self, client: SpeckleClient) -> Project: project = client.project.create( - ProjectCreateInput(name="Test project", description="", visibility=None) + ProjectCreateInput( + name="Test project", description="", visibility=ProjectVisibility.PUBLIC + ) ) return project @@ -149,3 +153,24 @@ class TestModelResource: with pytest.raises(GraphQLException): client.model.delete(delete_data) + + def test_model_get_permissions( + self, + client: SpeckleClient, + second_client: SpeckleClient, + test_project: Project, + test_model: Model, + ): + result = client.model.get_permissions(test_project.id, test_model.id) + + assert isinstance(result, ModelPermissionChecks) + assert result.can_update.authorized is True + assert result.can_create_version.authorized is True + assert result.can_delete.authorized is True + + guest = second_client.model.get_permissions(test_project.id, test_model.id) + + assert isinstance(guest, ModelPermissionChecks) + assert guest.can_update.authorized is False + assert guest.can_create_version.authorized is False + assert guest.can_delete.authorized is False diff --git a/tests/integration/client/current/test_project_resource.py b/tests/integration/client/current/test_project_resource.py index bb5e611..80284f4 100644 --- a/tests/integration/client/current/test_project_resource.py +++ b/tests/integration/client/current/test_project_resource.py @@ -24,6 +24,17 @@ class TestProjectResource: ) return project + @pytest.fixture() + def test_public_project(self, client: SpeckleClient) -> Project: + project = client.project.create( + ProjectCreateInput( + name="test project123", + description="desc", + visibility=ProjectVisibility.PUBLIC, + ) + ) + return project + @pytest.mark.parametrize( "name, description, visibility", [ @@ -50,7 +61,7 @@ class TestProjectResource: assert result.id is not None assert result.name == name assert result.description == (description or "") - # we've disabled creation of public projects for now, they fall back to unlisted + # we've disabled creation of unlisted projects for now, they fall back to public if visibility == ProjectVisibility.UNLISTED: assert result.visibility == ProjectVisibility.PUBLIC else: @@ -67,13 +78,32 @@ class TestProjectResource: assert result.created_at == test_project.created_at def test_project_get_permissions( - self, client: SpeckleClient, test_project: Project + self, + client: SpeckleClient, + second_client: SpeckleClient, + test_project: Project, + test_public_project: Project, ): - result = client.project.get_permissions(test_project.id) + result_private = client.project.get_permissions(test_project.id) + + assert isinstance(result_private, ProjectPermissionChecks) + assert result_private.can_create_model.authorized is True + assert result_private.can_delete.authorized is True + assert result_private.can_load.authorized is True + + result = client.project.get_permissions(test_public_project.id) assert isinstance(result, ProjectPermissionChecks) assert result.can_create_model.authorized is True assert result.can_delete.authorized is True + assert result.can_load.authorized is True + + guest = second_client.project.get_permissions(test_public_project.id) + + assert isinstance(result, ProjectPermissionChecks) + assert guest.can_create_model.authorized is False + assert guest.can_delete.authorized is False + assert guest.can_load.authorized is False def test_project_update(self, client: SpeckleClient, test_project: Project): new_name = "MY new name"