From badbb30242d4049bf07a2b0cd41f4d0f96d9a93f Mon Sep 17 00:00:00 2001 From: Sean Gillies Date: Wed, 5 Jun 2024 08:50:05 -0600 Subject: [PATCH] Add share_group, unshare_group, list_shared_with, and a test (#575) Coverage of the new code is good. We lack tests of errors sent by the API server. --- src/tiledb/cloud/groups.py | 89 ++++++++++++++++++++++++++++++++++---- tests/test_groups.py | 19 ++++++++ 2 files changed, 100 insertions(+), 8 deletions(-) diff --git a/src/tiledb/cloud/groups.py b/src/tiledb/cloud/groups.py index 7ac07e6cb..459ecb8d8 100644 --- a/src/tiledb/cloud/groups.py +++ b/src/tiledb/cloud/groups.py @@ -5,12 +5,16 @@ import urllib.parse from typing import Iterable, List, Optional, Tuple, Union -import tiledb.cloud.tiledb_cloud_error as tce -from tiledb.cloud import client -from tiledb.cloud import rest_api -from tiledb.cloud._common import api_v2 -from tiledb.cloud._common import utils -from tiledb.cloud.rest_api.models import group_update +from . import client +from . import rest_api +from . import tiledb_cloud_error +from ._common import api_v2 +from ._common import utils +from .rest_api import ApiException as GenApiException +from .rest_api import models +from .rest_api.models import group_update + +split_uri = utils.split_uri def create( @@ -130,7 +134,7 @@ def update_info( try: return groups_v1_client.update_group(namespace, group_name, group_update=info) except rest_api.ApiException as exc: - raise tce.check_exc(exc) + raise tiledb_cloud_error.check_exc(exc) def deregister( @@ -173,7 +177,9 @@ def deregister( grp: rest_api.GroupInfo = m.group deregister(grp.tiledb_uri, recursive=recursive) else: - raise tce.TileDBCloudError("unexpected group member type") + raise tiledb_cloud_error.TileDBCloudError( + "unexpected group member type" + ) groups_api.deregister_group(group_namespace=namespace, group_name=name) @@ -236,3 +242,70 @@ def _add_to(*, namespace: str, name: str, parent_uri: str) -> None: ), ), ) + + +def list_shared_with(uri, async_req=False): + """List a group's sharing policies. + + :param str uri: tiledb URI of the asset. + :param async_req: return future instead of results for async support. + :return: a list of GroupSharing objects. + """ + (group_namespace, group_name) = split_uri(uri) + api_instance = client.build(rest_api.GroupsApi) + + try: + return api_instance.get_group_sharing_policies( + group_namespace=group_namespace, group_name=group_name, async_req=async_req + ) + except GenApiException as exc: + raise tiledb_cloud_error.check_exc(exc) from None + + +def share_group(uri, namespace, permissions, async_req=False): + """Shares group with given namespace and permissions. + + :param str uri: tiledb URI of the asset. + :param str namespace: + :param list(str) permissions: + :param async_req: return future instead of results for async support. + :return: None. + """ + + if not isinstance(permissions, list): + permissions = [permissions] + + if set([perm.lower() for perm in permissions]) - { + models.GroupActions.READ, + models.GroupActions.WRITE, + }: + raise Exception("Only read or write permissions are accepted") + + (group_namespace, group_name) = split_uri(uri) + api_instance = client.build(rest_api.GroupsApi) + + try: + return api_instance.share_group( + group_namespace=group_namespace, + group_name=group_name, + group_sharing_request=models.GroupSharingRequest( + namespace=namespace, + group_actions=permissions, + array_actions=permissions, + ), + async_req=async_req, + ) + except GenApiException as exc: + raise tiledb_cloud_error.check_exc(exc) from None + + +def unshare_group(uri, namespace, async_req=False): + """ + Removes sharing of a group from given namespace + + :param str namespace: namespace to remove shared access to the group + :param async_req: return future instead of results for async support + :return: + :raises: :py:exc: + """ + return share_group(uri, namespace, list(), async_req=async_req) diff --git a/tests/test_groups.py b/tests/test_groups.py index d7242dae1..16773aed3 100644 --- a/tests/test_groups.py +++ b/tests/test_groups.py @@ -87,3 +87,22 @@ def test_update_group_info(self): # Cleanup groups.deregister(group_uri) self.assert_group_not_exists(group_name) + + def test_group_sharing(self): + """Share a created group with 'public', unshare, then delete.""" + group_name = testonly.random_name("test_group_sharing") + group_storage_name = testonly.random_name(group_name) + group_storage_path = f"{self.test_path}/{group_storage_name}" + group_uri = f"tiledb://{self.namespace}/{group_name}" + groups.create(group_name, storage_uri=group_storage_path) + self.assert_group_exists(group_name) + + groups.share_group(group_uri, "public", "read") + sharing = groups.list_shared_with(group_uri) + self.assertEqual(len(sharing), 1) + self.assertEqual(sharing[0].namespace, "public") + groups.unshare_group(group_uri, "public") + sharing = groups.list_shared_with(group_uri) + self.assertEqual(len(sharing), 0) + + groups.delete(group_uri)