From: Aleš Mrázek Date: Tue, 20 Jun 2023 10:11:56 +0000 (+0200) Subject: manager: datamodel: types: StringLengthBase type X-Git-Tag: v6.0.2~31^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f495877e039219885ac1c1e8d035021ac2cd783c;p=thirdparty%2Fknot-resolver.git manager: datamodel: types: StringLengthBase type --- diff --git a/manager/knot_resolver_manager/datamodel/types/base_types.py b/manager/knot_resolver_manager/datamodel/types/base_types.py index b3184fc77..227a59eb9 100644 --- a/manager/knot_resolver_manager/datamodel/types/base_types.py +++ b/manager/knot_resolver_manager/datamodel/types/base_types.py @@ -87,6 +87,40 @@ class StrBase(BaseValueType): return {"type": "string"} +class StringLengthBase(StrBase): + """ + Base class to work with string value length. + Just inherit the class and set the values for '_min_bytes' and '_max_bytes'. + + class String32B(StringLengthBase): + _min_bytes: int = 32 + """ + + _min_bytes: int = 1 + _max_bytes: int + + def __init__(self, source_value: Any, object_path: str = "/") -> None: + super().__init__(source_value, object_path) + value_bytes = len(self._value.encode("utf-8")) + if hasattr(self, "_min_bytes") and (value_bytes < self._min_bytes): + raise ValueError( + f"the string value {source_value} is shorter than the minimum {self._min_bytes} bytes.", object_path + ) + if hasattr(self, "_max_bytes") and (value_bytes > self._max_bytes): + raise ValueError( + f"the string value {source_value} is longer than the maximum {self._max_bytes} bytes.", object_path + ) + + @classmethod + def json_schema(cls: Type["StringLengthBase"]) -> Dict[Any, Any]: + typ: Dict[str, Any] = {"type": "string"} + if hasattr(cls, "_min_bytes"): + typ["minLength"] = cls._min_bytes + if hasattr(cls, "_max_bytes"): + typ["maxLength"] = cls._max_bytes + return typ + + class IntRangeBase(IntBase): """ Base class to work with integer value in range. diff --git a/manager/tests/unit/datamodel/types/test_base_types.py b/manager/tests/unit/datamodel/types/test_base_types.py index acc8baf39..00e7bda5e 100644 --- a/manager/tests/unit/datamodel/types/test_base_types.py +++ b/manager/tests/unit/datamodel/types/test_base_types.py @@ -5,7 +5,7 @@ from typing import List, Optional import pytest from pytest import raises -from knot_resolver_manager.datamodel.types.base_types import IntRangeBase +from knot_resolver_manager.datamodel.types.base_types import IntRangeBase, StringLengthBase from knot_resolver_manager.exceptions import KresManagerException @@ -36,3 +36,31 @@ def test_int_range_base(min: Optional[int], max: Optional[int]): for inval in invals: with raises(KresManagerException): Test(inval) + + +@pytest.mark.parametrize("min,max", [(10, None), (None, 10), (2, 32)]) +def test_str_bytes_length_base(min: Optional[int], max: Optional[int]): + class Test(StringLengthBase): + if min: + _min_bytes = min + if max: + _max_bytes = max + + if min: + assert len(str(Test("x" * min)).encode("utf-8")) == min + if max: + assert len(str(Test("x" * max)).encode("utf-8")) == max + + n = 100 + rmin = 1 if not min else min + rmax = 1024 if not max else max + vals: List[str] = ["x" * random.randint(rmin, rmax) for _ in range(n)] + assert [str(Test(val)) == f"{val}" for val in vals] + + invals: List[str] = [] + invals.extend(["x" * random.randint(rmax + 1, 2048) for _ in range(n % 2)] if max else []) + invals.extend(["x" * random.randint(1, rmin - 1) for _ in range(n % 2)] if max else []) + + for inval in invals: + with raises(KresManagerException): + Test(inval)