From 7ec1a7e4644ac5c5c5e00ce53773a3909d11d582 Mon Sep 17 00:00:00 2001 From: Stefano Lottini Date: Thu, 29 Feb 2024 01:57:39 +0100 Subject: [PATCH] full cross-namespace management in DDL (#227) cross-namespace works for Database/Collection created in any way, and for all drop_collection calls --- astrapy/idiomatic/collection.py | 8 ++++ astrapy/idiomatic/database.py | 12 ++--- tests/idiomatic/integration/test_ddl_async.py | 45 ++++++++++++++++++- tests/idiomatic/integration/test_ddl_sync.py | 44 +++++++++++++++++- tests/idiomatic/unit/test_collections.py | 21 --------- .../test_collections_async.py | 28 ++++++++++++ .../test_collections_sync.py | 28 ++++++++++++ tests/idiomatic/unit/test_databases.py | 21 --------- .../test_databases_async.py | 27 ++++++++++- .../test_databases_sync.py | 27 ++++++++++- 10 files changed, 209 insertions(+), 52 deletions(-) delete mode 100644 tests/idiomatic/unit/test_collections.py rename tests/idiomatic/{integration => unit}/test_collections_async.py (83%) rename tests/idiomatic/{integration => unit}/test_collections_sync.py (82%) delete mode 100644 tests/idiomatic/unit/test_databases.py rename tests/idiomatic/{integration => unit}/test_databases_async.py (83%) rename tests/idiomatic/{integration => unit}/test_databases_sync.py (82%) diff --git a/astrapy/idiomatic/collection.py b/astrapy/idiomatic/collection.py index a84d7379..14bef0fe 100644 --- a/astrapy/idiomatic/collection.py +++ b/astrapy/idiomatic/collection.py @@ -64,6 +64,10 @@ def __init__( caller_version=caller_version, ) + @property + def namespace(self) -> str: + return self._astra_db_collection.astra_db.namespace + def __repr__(self) -> str: return f'{self.__class__.__name__}[_astra_db_collection="{self._astra_db_collection}"]' @@ -297,6 +301,10 @@ def __init__( caller_version=caller_version, ) + @property + def namespace(self) -> str: + return self._astra_db_collection.astra_db.namespace + def __repr__(self) -> str: return f'{self.__class__.__name__}[_astra_db_collection="{self._astra_db_collection}"]' diff --git a/astrapy/idiomatic/database.py b/astrapy/idiomatic/database.py index 393e6653..e18eebfe 100644 --- a/astrapy/idiomatic/database.py +++ b/astrapy/idiomatic/database.py @@ -175,12 +175,12 @@ def drop_collection(self, name_or_collection: Union[str, Collection]) -> None: # lazy importing here against circular-import error from astrapy.idiomatic.collection import Collection - _name: str if isinstance(name_or_collection, Collection): + _namespace = name_or_collection.namespace _name = name_or_collection._astra_db_collection.collection_name + self._astra_db.copy(namespace=_namespace).delete_collection(_name) else: - _name = name_or_collection - self._astra_db.delete_collection(_name) + self._astra_db.delete_collection(name_or_collection) def list_collection_names( self, @@ -355,12 +355,12 @@ async def drop_collection( # lazy importing here against circular-import error from astrapy.idiomatic.collection import AsyncCollection - _name: str if isinstance(name_or_collection, AsyncCollection): + _namespace = name_or_collection.namespace _name = name_or_collection._astra_db_collection.collection_name + await self._astra_db.copy(namespace=_namespace).delete_collection(_name) else: - _name = name_or_collection - await self._astra_db.delete_collection(_name) + await self._astra_db.delete_collection(name_or_collection) async def list_collection_names( self, diff --git a/tests/idiomatic/integration/test_ddl_async.py b/tests/idiomatic/integration/test_ddl_async.py index d053f652..fccf09e5 100644 --- a/tests/idiomatic/integration/test_ddl_async.py +++ b/tests/idiomatic/integration/test_ddl_async.py @@ -14,7 +14,11 @@ import pytest -from ..conftest import ASTRA_DB_SECONDARY_KEYSPACE, TEST_COLLECTION_NAME +from ..conftest import ( + AstraDBCredentials, + ASTRA_DB_SECONDARY_KEYSPACE, + TEST_COLLECTION_NAME, +) from astrapy import AsyncCollection, AsyncDatabase @@ -66,3 +70,42 @@ async def test_database_list_collections_cross_namespace_async( assert TEST_COLLECTION_NAME not in await async_database.list_collection_names( namespace=ASTRA_DB_SECONDARY_KEYSPACE ) + + @pytest.mark.skipif( + ASTRA_DB_SECONDARY_KEYSPACE is None, reason="No secondary keyspace provided" + ) + @pytest.mark.describe("test of cross-namespace collection lifecycle, async") + async def test_collection_namespace_async( + self, + async_database: AsyncDatabase, + astra_db_credentials_kwargs: AstraDBCredentials, + ) -> None: + TEST_LOCAL_COLLECTION_NAME1 = "test_crossns_coll1" + TEST_LOCAL_COLLECTION_NAME2 = "test_crossns_coll2" + database_on_secondary = AsyncDatabase( + astra_db_credentials_kwargs["api_endpoint"], + astra_db_credentials_kwargs["token"], + namespace=ASTRA_DB_SECONDARY_KEYSPACE, + ) + await async_database.create_collection( + TEST_LOCAL_COLLECTION_NAME1, + namespace=ASTRA_DB_SECONDARY_KEYSPACE, + ) + col2_on_secondary = await async_database.create_collection( + TEST_LOCAL_COLLECTION_NAME2, + namespace=ASTRA_DB_SECONDARY_KEYSPACE, + ) + assert ( + TEST_LOCAL_COLLECTION_NAME1 + in await database_on_secondary.list_collection_names() + ) + await database_on_secondary.drop_collection(TEST_LOCAL_COLLECTION_NAME1) + await async_database.drop_collection(col2_on_secondary) + assert ( + TEST_LOCAL_COLLECTION_NAME1 + not in await database_on_secondary.list_collection_names() + ) + assert ( + TEST_LOCAL_COLLECTION_NAME2 + not in await database_on_secondary.list_collection_names() + ) diff --git a/tests/idiomatic/integration/test_ddl_sync.py b/tests/idiomatic/integration/test_ddl_sync.py index 3f7d60ca..0d3be508 100644 --- a/tests/idiomatic/integration/test_ddl_sync.py +++ b/tests/idiomatic/integration/test_ddl_sync.py @@ -14,7 +14,11 @@ import pytest -from ..conftest import ASTRA_DB_SECONDARY_KEYSPACE, TEST_COLLECTION_NAME +from ..conftest import ( + AstraDBCredentials, + ASTRA_DB_SECONDARY_KEYSPACE, + TEST_COLLECTION_NAME, +) from astrapy import Collection, Database @@ -64,3 +68,41 @@ def test_database_list_collections_cross_namespace_sync( assert TEST_COLLECTION_NAME not in sync_database.list_collection_names( namespace=ASTRA_DB_SECONDARY_KEYSPACE ) + + @pytest.mark.skipif( + ASTRA_DB_SECONDARY_KEYSPACE is None, reason="No secondary keyspace provided" + ) + @pytest.mark.describe("test of cross-namespace collection lifecycle, sync") + def test_collection_namespace_sync( + self, + sync_database: Database, + astra_db_credentials_kwargs: AstraDBCredentials, + ) -> None: + TEST_LOCAL_COLLECTION_NAME1 = "test_crossns_coll1" + TEST_LOCAL_COLLECTION_NAME2 = "test_crossns_coll2" + database_on_secondary = Database( + astra_db_credentials_kwargs["api_endpoint"], + astra_db_credentials_kwargs["token"], + namespace=ASTRA_DB_SECONDARY_KEYSPACE, + ) + sync_database.create_collection( + TEST_LOCAL_COLLECTION_NAME1, + namespace=ASTRA_DB_SECONDARY_KEYSPACE, + ) + col2_on_secondary = sync_database.create_collection( + TEST_LOCAL_COLLECTION_NAME2, + namespace=ASTRA_DB_SECONDARY_KEYSPACE, + ) + assert ( + TEST_LOCAL_COLLECTION_NAME1 in database_on_secondary.list_collection_names() + ) + database_on_secondary.drop_collection(TEST_LOCAL_COLLECTION_NAME1) + sync_database.drop_collection(col2_on_secondary) + assert ( + TEST_LOCAL_COLLECTION_NAME1 + not in database_on_secondary.list_collection_names() + ) + assert ( + TEST_LOCAL_COLLECTION_NAME2 + not in database_on_secondary.list_collection_names() + ) diff --git a/tests/idiomatic/unit/test_collections.py b/tests/idiomatic/unit/test_collections.py deleted file mode 100644 index 9db7514f..00000000 --- a/tests/idiomatic/unit/test_collections.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright DataStax, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest - - -class TestCollectionsUnit: - @pytest.mark.describe("test placeholder, unit tests on Collection") - def test_collection_unit_placeholder(self) -> None: - assert True diff --git a/tests/idiomatic/integration/test_collections_async.py b/tests/idiomatic/unit/test_collections_async.py similarity index 83% rename from tests/idiomatic/integration/test_collections_async.py rename to tests/idiomatic/unit/test_collections_async.py index 79e511e1..bd009067 100644 --- a/tests/idiomatic/integration/test_collections_async.py +++ b/tests/idiomatic/unit/test_collections_async.py @@ -14,6 +14,7 @@ import pytest +from ..conftest import ASTRA_DB_SECONDARY_KEYSPACE from astrapy import AsyncCollection, AsyncDatabase @@ -137,3 +138,30 @@ async def test_collection_conversions_caller_mutableness_async( ) assert col1.copy() == col2 assert col1.to_sync().to_async() == col2 + + @pytest.mark.skipif( + ASTRA_DB_SECONDARY_KEYSPACE is None, reason="No secondary keyspace provided" + ) + @pytest.mark.describe("test collection namespace property, async") + async def test_collection_namespace_async( + self, + async_database: AsyncDatabase, + ) -> None: + col1 = await async_database.get_collection("id_test_collection") + assert col1.namespace == async_database.namespace + + col2 = await async_database.get_collection( + "id_test_collection", + namespace=ASTRA_DB_SECONDARY_KEYSPACE, + ) + assert col2.namespace == ASTRA_DB_SECONDARY_KEYSPACE + + col3 = AsyncCollection(async_database, "id_test_collection") + assert col3.namespace == async_database.namespace + + col4 = AsyncCollection( + async_database, + "id_test_collection", + namespace=ASTRA_DB_SECONDARY_KEYSPACE, + ) + assert col4.namespace == ASTRA_DB_SECONDARY_KEYSPACE diff --git a/tests/idiomatic/integration/test_collections_sync.py b/tests/idiomatic/unit/test_collections_sync.py similarity index 82% rename from tests/idiomatic/integration/test_collections_sync.py rename to tests/idiomatic/unit/test_collections_sync.py index fde33f82..b663f466 100644 --- a/tests/idiomatic/integration/test_collections_sync.py +++ b/tests/idiomatic/unit/test_collections_sync.py @@ -14,6 +14,7 @@ import pytest +from ..conftest import ASTRA_DB_SECONDARY_KEYSPACE from astrapy import Collection, Database @@ -137,3 +138,30 @@ def test_collection_conversions_caller_mutableness_sync( ) assert col1.copy() == col2 assert col1.to_async().to_sync() == col2 + + @pytest.mark.skipif( + ASTRA_DB_SECONDARY_KEYSPACE is None, reason="No secondary keyspace provided" + ) + @pytest.mark.describe("test collection namespace property, sync") + def test_collection_namespace_sync( + self, + sync_database: Database, + ) -> None: + col1 = sync_database.get_collection("id_test_collection") + assert col1.namespace == sync_database.namespace + + col2 = sync_database.get_collection( + "id_test_collection", + namespace=ASTRA_DB_SECONDARY_KEYSPACE, + ) + assert col2.namespace == ASTRA_DB_SECONDARY_KEYSPACE + + col3 = Collection(sync_database, "id_test_collection") + assert col3.namespace == sync_database.namespace + + col4 = Collection( + sync_database, + "id_test_collection", + namespace=ASTRA_DB_SECONDARY_KEYSPACE, + ) + assert col4.namespace == ASTRA_DB_SECONDARY_KEYSPACE diff --git a/tests/idiomatic/unit/test_databases.py b/tests/idiomatic/unit/test_databases.py deleted file mode 100644 index 8abf66b7..00000000 --- a/tests/idiomatic/unit/test_databases.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright DataStax, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest - - -class TestDatabasesUnit: - @pytest.mark.describe("test placeholder, unit tests on Database") - def test_database_unit_placeholder(self) -> None: - assert True diff --git a/tests/idiomatic/integration/test_databases_async.py b/tests/idiomatic/unit/test_databases_async.py similarity index 83% rename from tests/idiomatic/integration/test_databases_async.py rename to tests/idiomatic/unit/test_databases_async.py index 4c3e60aa..2a41da74 100644 --- a/tests/idiomatic/integration/test_databases_async.py +++ b/tests/idiomatic/unit/test_databases_async.py @@ -14,7 +14,12 @@ import pytest -from ..conftest import AstraDBCredentials, TEST_COLLECTION_INSTANCE_NAME +from ..conftest import ( + AstraDBCredentials, + ASTRA_DB_SECONDARY_KEYSPACE, + TEST_COLLECTION_INSTANCE_NAME, +) +from astrapy.defaults import DEFAULT_KEYSPACE_NAME from astrapy import AsyncCollection, AsyncDatabase @@ -125,3 +130,23 @@ async def test_database_conversions_caller_mutableness_async( ) assert db1.to_sync().to_async() == db2 assert db1.copy() == db2 + + @pytest.mark.skipif( + ASTRA_DB_SECONDARY_KEYSPACE is None, reason="No secondary keyspace provided" + ) + @pytest.mark.describe("test database namespace property, async") + async def test_database_namespace_async( + self, + astra_db_credentials_kwargs: AstraDBCredentials, + ) -> None: + db1 = AsyncDatabase( + **astra_db_credentials_kwargs, + ) + assert db1.namespace == DEFAULT_KEYSPACE_NAME + + db2 = AsyncDatabase( + token=astra_db_credentials_kwargs["token"], + api_endpoint=astra_db_credentials_kwargs["api_endpoint"], + namespace=ASTRA_DB_SECONDARY_KEYSPACE, + ) + assert db2.namespace == ASTRA_DB_SECONDARY_KEYSPACE diff --git a/tests/idiomatic/integration/test_databases_sync.py b/tests/idiomatic/unit/test_databases_sync.py similarity index 82% rename from tests/idiomatic/integration/test_databases_sync.py rename to tests/idiomatic/unit/test_databases_sync.py index 05ada2ad..5daf79fa 100644 --- a/tests/idiomatic/integration/test_databases_sync.py +++ b/tests/idiomatic/unit/test_databases_sync.py @@ -14,7 +14,12 @@ import pytest -from ..conftest import AstraDBCredentials, TEST_COLLECTION_INSTANCE_NAME +from ..conftest import ( + AstraDBCredentials, + ASTRA_DB_SECONDARY_KEYSPACE, + TEST_COLLECTION_INSTANCE_NAME, +) +from astrapy.defaults import DEFAULT_KEYSPACE_NAME from astrapy import Collection, Database @@ -126,3 +131,23 @@ def test_database_conversions_caller_mutableness_sync( ) assert db1.to_async().to_sync() == db2 assert db1.copy() == db2 + + @pytest.mark.skipif( + ASTRA_DB_SECONDARY_KEYSPACE is None, reason="No secondary keyspace provided" + ) + @pytest.mark.describe("test database namespace property, sync") + def test_database_namespace_sync( + self, + astra_db_credentials_kwargs: AstraDBCredentials, + ) -> None: + db1 = Database( + **astra_db_credentials_kwargs, + ) + assert db1.namespace == DEFAULT_KEYSPACE_NAME + + db2 = Database( + token=astra_db_credentials_kwargs["token"], + api_endpoint=astra_db_credentials_kwargs["api_endpoint"], + namespace=ASTRA_DB_SECONDARY_KEYSPACE, + ) + assert db2.namespace == ASTRA_DB_SECONDARY_KEYSPACE