diff --git a/deker/ABC/base_array.py b/deker/ABC/base_array.py index 66aacd8..2a769d5 100644 --- a/deker/ABC/base_array.py +++ b/deker/ABC/base_array.py @@ -35,6 +35,7 @@ from deker.schemas import ArraySchema, TimeDimensionSchema, VArraySchema from deker.subset import Subset, VSubset from deker.tools.array import check_memory, get_id +from deker.validators import is_valid_uuid from deker.tools.schema import create_dimensions from deker.types.private.classes import ArrayMeta, Serializer from deker.types.private.typings import FancySlice, Numeric, Slice @@ -251,9 +252,7 @@ def _validate( primary_attributes: Optional[dict] = None, custom_attributes: Optional[dict] = None, ) -> None: - if id_ is not None and ( - not isinstance(id_, str) or len(id_.split("-")) != 5 # noqa[PLR2004] - ): + if id_ is not None and not is_valid_uuid(id_): raise DekerValidationError( f"{self.__class__.__name__} id shall be a non-empty uuid.uuid5 string or None" ) diff --git a/deker/managers.py b/deker/managers.py index 3c92b2b..ff0e64a 100644 --- a/deker/managers.py +++ b/deker/managers.py @@ -109,17 +109,20 @@ def _create( # type: ignore schema: "BaseArraysSchema", primary_attributes: Optional[dict] = None, custom_attributes: Optional[dict] = None, + id_: Optional[str]=None ) -> Union[Array, VArray]: """Create Array or VArray. :param primary_attributes: array primary attribute :param custom_attributes: array custom attributes :param schema: schema decides which array will be created + :param id_: (V)Array uuid string """ arr_params = { "collection": self.__collection, "primary_attributes": primary_attributes, "custom_attributes": custom_attributes, + "id_": id_, } if isinstance(schema, VArraySchema): @@ -135,15 +138,19 @@ def _create( # type: ignore return array def create( - self, primary_attributes: Optional[dict] = None, custom_attributes: Optional[dict] = None + self, + primary_attributes: Optional[dict] = None, + custom_attributes: Optional[dict] = None, + id_: Optional[str] = None ) -> Union[Array, VArray]: """Create array or varray. - :param primary_attributes: Primary attributes - :param custom_attributes: Custom attributes + :param primary_attributes: primary attributes + :param custom_attributes: custom attributes + :param id_: unique UUID string """ schema = self.__collection.varray_schema or self.__collection.array_schema - return self._create(schema, primary_attributes, custom_attributes) + return self._create(schema, primary_attributes, custom_attributes, id_) class VArrayManager(SelfLoggerMixin, DataManager): @@ -177,14 +184,16 @@ def create( self, primary_attributes: Optional[dict] = None, custom_attributes: Optional[dict] = None, + id_: Optional[str] = None ) -> VArray: """Create varray in collection. - :param primary_attributes: Primary attributes the varray - :param custom_attributes: Custom attributes the varray + :param primary_attributes: VArray primary attributes + :param custom_attributes: VArray custom attributes + :param id_: VArray unique UUID string """ return self._create( # type: ignore[return-value] - self.__collection.varray_schema, primary_attributes, custom_attributes # type: ignore[arg-type] + self.__collection.varray_schema, primary_attributes, custom_attributes, id_ # type: ignore[arg-type] ) def __iter__(self) -> Generator[VArray, None, None]: @@ -223,14 +232,16 @@ def create( self, primary_attributes: Optional[dict] = None, custom_attributes: Optional[dict] = None, + id_: Optional[str] = None ) -> Array: - """Create varray in collection. + """Create array in collection. - :param primary_attributes: Primary attributes the varray - :param custom_attributes: Custom attributes the varray + :param primary_attributes: Array primary attributes + :param custom_attributes: Array custom attributes + :param id_: Array unique UUID string """ return self._create( # type: ignore[return-value] - self.__collection.array_schema, primary_attributes, custom_attributes + self.__collection.array_schema, primary_attributes, custom_attributes, id_ ) def __iter__(self) -> Generator[Array, None, None]: diff --git a/deker/validators.py b/deker/validators.py index 3b181c6..1027b0b 100644 --- a/deker/validators.py +++ b/deker/validators.py @@ -15,6 +15,7 @@ # along with this program. If not, see . import datetime +import uuid from typing import TYPE_CHECKING, Optional, Tuple, Union @@ -131,3 +132,17 @@ def process_attributes( ) __process_attrs(attrs_schema, attributes, primary_attributes, custom_attributes) # type: ignore[arg-type] return primary_attributes, custom_attributes + + +def is_valid_uuid(id_: str) -> bool: + """Validate if id is in uuid format. + + :param id_: id to validate + """ + if not isinstance(id_, str) or len(id_.split("-")) != 5 or len(id_) != 36: + return False + try: + uuid.UUID(id_) + return True + except ValueError: + return False diff --git a/tests/test_cases/test_managers/test_managers.py b/tests/test_cases/test_managers/test_managers.py index 7d0839d..aaa21bd 100644 --- a/tests/test_cases/test_managers/test_managers.py +++ b/tests/test_cases/test_managers/test_managers.py @@ -1,4 +1,5 @@ import os +import uuid from datetime import datetime, timedelta @@ -9,7 +10,7 @@ from deker.arrays import Array, VArray from deker.client import Client from deker.collection import Collection -from deker.errors import DekerFilterError +from deker.errors import DekerFilterError, DekerValidationError, DekerArrayError from deker.schemas import ( ArraySchema, AttributeSchema, @@ -20,13 +21,44 @@ class TestDataManagerMethods: - def test_manager_create_array(self, collection_manager): + @pytest.mark.parametrize("array_params", [ + {}, + {"id_": str(uuid.uuid4())} + ]) + def test_manager_create_array(self, collection_manager, array_params): """Tests if manager can create array. :param collection_manager: fixture """ - array = collection_manager.create() + array = collection_manager.create(**array_params) assert isinstance(array, Array) + if array_params.get("id_"): + assert array.id == array_params["id_"] + + @pytest.mark.parametrize("id_", [ + (str(uuid.uuid4()))[:-10], + "123", + "{20f5484b-88ae-49b0-8af0-3a389b4917dd}", + "20f5484b88ae49b08af03a389b4917dd" + ]) + def test_manager_create_array_fail_bad_id(self, collection_manager, id_): + """Tests if manager returns error on incorrect id param. + + :param collection_manager: fixture + :param id_: incorrect uuid + """ + with pytest.raises(DekerValidationError): + collection_manager.create(id_=id_) + + def test_manager_create_array_fail_exists(self, inserted_array: Array, collection_manager): + """Tests if manager returns error on trying to create array with uuid of existing array. + + :param inserted_array: fixture + :param collection_manager: fixture + """ + with pytest.raises(DekerArrayError) as e: + collection_manager.create(id_=inserted_array.id) + assert inserted_array.id in str(e) and "already exists" in str(e) def test_get_array_by_id(self, inserted_array: Array, collection_manager): """Tests possibility of getting an array by id. @@ -91,9 +123,9 @@ def test_get_array_by_primary_attribute( @pytest.mark.parametrize("property_name", ["first", "last"]) def test_get_array_by_primary_attribute_datetime( - self, - client: Client, - property_name: str, + self, + client: Client, + property_name: str, ): """Tests possibility of getting an array by datetme primary attributes. @@ -134,9 +166,9 @@ def test_get_array_by_primary_attribute_datetime( @pytest.mark.parametrize("property_name", ["first", "last"]) def test_get_varray_by_primary_attribute_datetime( - self, - client: Client, - property_name: str, + self, + client: Client, + property_name: str, ): """Tests possibility of getting an array by datetme primary attributes. @@ -180,10 +212,10 @@ def test_get_varray_by_primary_attribute_datetime( @pytest.mark.parametrize("property_name", ["first", "last"]) def test_get_array_by_primary_attribute_no_such_attribute( - self, - inserted_array_with_attributes: Array, - collection_manager_with_attributes, - property_name: str, + self, + inserted_array_with_attributes: Array, + collection_manager_with_attributes, + property_name: str, ): """Tests None is returned if there is no array with such primary attributes. @@ -200,10 +232,10 @@ def test_get_array_by_primary_attribute_no_such_attribute( @pytest.mark.parametrize("property_name", ["first", "last"]) def test_get_array_by_primary_attribute_empty_collection( - self, - array_with_attributes: Array, - collection_manager_with_attributes, - property_name: str, + self, + array_with_attributes: Array, + collection_manager_with_attributes, + property_name: str, ): """Tests None is returned if collection is empty. @@ -218,9 +250,9 @@ def test_get_array_by_primary_attribute_empty_collection( assert array is None def test_get_array_by_primary_attribute_fails_too_many_attrs( - self, - inserted_array_with_attributes: Array, - collection_manager_with_attributes, + self, + inserted_array_with_attributes: Array, + collection_manager_with_attributes, ): """Tests if filtering fails of there is too many attributes @@ -240,9 +272,9 @@ def test_get_array_by_primary_attribute_fails_too_many_attrs( assert "Some arguments don't exist in schema" == str(error.value) def test_get_array_by_primary_attribute_absence( - self, - inserted_array_with_attributes: Array, - collection_manager_with_attributes, + self, + inserted_array_with_attributes: Array, + collection_manager_with_attributes, ): with pytest.raises(NotImplementedError): collection_manager_with_attributes.filter({"extra": 12}).first() @@ -252,13 +284,44 @@ def test_get_array_by_primary_attribute_absence( class TestDataManagerMethodsVArray: - def test_manager_create_varray(self, va_collection_manager): - """Tests if manager creates array. + @pytest.mark.parametrize("varray_params", [ + {}, + {"id_": str(uuid.uuid4())} + ]) + def test_manager_create_varray(self, va_collection_manager, varray_params): + """Tests if manager can create VArray. :param va_collection_manager: fixture """ - array = va_collection_manager.create() - assert isinstance(array, VArray) + varray = va_collection_manager.create(**varray_params) + assert isinstance(varray, VArray) + if varray_params.get("id_"): + assert varray.id == varray_params["id_"] + + @pytest.mark.parametrize("id_", [ + str(uuid.uuid4())[:-1], + "123", + "{20f5484b-88ae-49b0-8af0-3a389b4917dd}", + "20f5484b88ae49b08af03a389b4917dd" + ]) + def test_manager_create_varray_fail_bad_id(self, va_collection_manager, id_): + """Tests if manager returns error on incorrect id param. + + :param va_collection_manager: fixture + :param id_: incorrect uuid + """ + with pytest.raises(DekerValidationError): + va_collection_manager.create(id_=id_) + + def test_manager_create_varray_fail_exists(self, inserted_varray: VArray, va_collection_manager): + """Tests if manager returns error on trying to create varray with uuid of existing varray. + + :param inserted_varray: fixture + :param va_collection_manager: fixture + """ + with pytest.raises(DekerArrayError) as e: + va_collection_manager.create(id_=inserted_varray.id) + assert inserted_varray.id in str(e) and "already exists" in str(e) def test_get_array_by_id(self, inserted_varray: VArray, va_collection_manager): """Tests possibility of getting an array by id. @@ -295,10 +358,10 @@ def test_get_varray_by_id_empty_collection(self, varray: VArray, va_collection_m @pytest.mark.parametrize("property_name", ["first", "last"]) def test_get_varray_by_primary_attribute( - self, - inserted_varray_with_attributes: VArray, - va_collection_manager_with_attributes, - property_name: str, + self, + inserted_varray_with_attributes: VArray, + va_collection_manager_with_attributes, + property_name: str, ): """Tests possibility of getting an array by primary attributes. @@ -319,10 +382,10 @@ def test_get_varray_by_primary_attribute( @pytest.mark.parametrize("property_name", ["first", "last"]) def test_get_varray_by_primary_attribute_no_such_attribute( - self, - inserted_varray_with_attributes: VArray, - va_collection_manager_with_attributes, - property_name: str, + self, + inserted_varray_with_attributes: VArray, + va_collection_manager_with_attributes, + property_name: str, ): """Tests None is returned if there is no array with such primary attributes. @@ -339,10 +402,10 @@ def test_get_varray_by_primary_attribute_no_such_attribute( @pytest.mark.parametrize("property_name", ["first", "last"]) def test_get_varray_by_primary_attribute_empty_collection( - self, - varray_with_attributes: VArray, - va_collection_manager_with_attributes, - property_name: str, + self, + varray_with_attributes: VArray, + va_collection_manager_with_attributes, + property_name: str, ): """Tests None is returned if collection is empty. @@ -359,9 +422,9 @@ def test_get_varray_by_primary_attribute_empty_collection( assert array is None def test_get_array_by_primary_attribute_fails_too_many_attrs( - self, - inserted_array_with_attributes: Array, - va_collection_manager_with_attributes, + self, + inserted_array_with_attributes: Array, + va_collection_manager_with_attributes, ): """Tests if filtering fails if there is too many attributes @@ -381,9 +444,9 @@ def test_get_array_by_primary_attribute_fails_too_many_attrs( assert "Some arguments don't exist in schema" == str(error.value) def test_get_varray_by_primary_attribute_absence( - self, - inserted_varray_with_attributes: Array, - va_collection_manager_with_attributes, + self, + inserted_varray_with_attributes: Array, + va_collection_manager_with_attributes, ): with pytest.raises(NotImplementedError) as error: va_collection_manager_with_attributes.filter({"extra": 12}).first()