From 4a4ed22fe5ba9bcae9ceaeaa93f28a1ce67cc99c Mon Sep 17 00:00:00 2001 From: Thomas Zajac Date: Thu, 25 May 2023 17:18:48 +0200 Subject: [PATCH] Added functionality to list all bucket object IDs (#58) --- hexkit/__init__.py | 2 +- hexkit/protocols/objstorage.py | 17 +++++++++++++++++ hexkit/providers/s3/provider.py | 15 +++++++++++++++ tests/integration/test_s3.py | 20 ++++++++++++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) diff --git a/hexkit/__init__.py b/hexkit/__init__.py index 3799f363..26a429ed 100644 --- a/hexkit/__init__.py +++ b/hexkit/__init__.py @@ -15,4 +15,4 @@ """A Toolkit for Building Microservices using the Hexagonal Architecture""" -__version__ = "0.9.3" +__version__ = "0.10.0" diff --git a/hexkit/protocols/objstorage.py b/hexkit/protocols/objstorage.py index f3ed3a17..4a2ae54e 100644 --- a/hexkit/protocols/objstorage.py +++ b/hexkit/protocols/objstorage.py @@ -77,6 +77,13 @@ async def delete_bucket( self._validate_bucket_id(bucket_id) await self._delete_bucket(bucket_id, delete_content=delete_content) + async def list_all_object_ids(self, bucket_id: str) -> list[str]: + """ + Retrieve a list of IDs for all objects currently present in the specified bucket + """ + self._validate_bucket_id(bucket_id) + return await self._list_all_object_ids(bucket_id=bucket_id) + async def get_object_upload_url( self, *, @@ -293,6 +300,16 @@ async def _delete_bucket( """ ... + @abstractmethod + async def _list_all_object_ids(self, *, bucket_id: str) -> list[str]: + """ + Retrieve a list of IDs for all objects currently present in the specified bucket + + *To be implemented by the provider. Input validation is done outside of this + method.* + """ + ... + @abstractmethod async def _get_object_upload_url( self, diff --git a/hexkit/providers/s3/provider.py b/hexkit/providers/s3/provider.py index 50d88516..5b99ab93 100644 --- a/hexkit/providers/s3/provider.py +++ b/hexkit/providers/s3/provider.py @@ -300,6 +300,21 @@ async def _delete_bucket( error, bucket_id=bucket_id ) from error + async def _list_all_object_ids(self, *, bucket_id: str) -> list[str]: + """ + Retrieve a list of IDs for all objects currently present in the specified bucket + """ + await self._assert_bucket_exists(bucket_id) + + try: + bucket = self._resource.Bucket(bucket_id) + content = await asyncio.to_thread(bucket.objects.all) + return [object_summary.key for object_summary in content] + except botocore.exceptions.ClientError as error: + raise self._translate_s3_client_errors( + error, bucket_id=bucket_id + ) from error + async def _does_object_exist( self, *, bucket_id: str, object_id: str, object_md5sum: Optional[str] = None ) -> bool: diff --git a/tests/integration/test_s3.py b/tests/integration/test_s3.py index 35f97dc1..52f9e802 100644 --- a/tests/integration/test_s3.py +++ b/tests/integration/test_s3.py @@ -103,6 +103,26 @@ async def test_get_object_size( assert expected_size == observed_size +@pytest.mark.asyncio +async def test_list_all_object_ids( + s3_fixture: S3Fixture, # noqa: F811 + file_fixture: FileObject, # noqa: F811 +): + """Test if listing all object IDs for a bucket works correctly.""" + + file_fixture2 = file_fixture.copy(deep=True) + file_fixture2.object_id = "mydefaulttestobject002" + + # add file objects to storage + await s3_fixture.populate_file_objects([file_fixture, file_fixture2]) + + # retrieve all object ids + retrieved_ids = await s3_fixture.storage.list_all_object_ids( + bucket_id=file_fixture.bucket_id + ) + assert retrieved_ids == [file_fixture.object_id, file_fixture2.object_id] + + @pytest.mark.asyncio async def test_bucket_existence_checks(s3_fixture: S3Fixture): # noqa: F811 """Test if the checks for existence of buckets work correctly."""