]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
manager: datamodel: types: StringLengthBase type
authorAleš Mrázek <ales.mrazek@nic.cz>
Tue, 20 Jun 2023 10:11:56 +0000 (12:11 +0200)
committerAleš Mrázek <ales.mrazek@nic.cz>
Thu, 13 Jul 2023 07:50:09 +0000 (09:50 +0200)
manager/knot_resolver_manager/datamodel/types/base_types.py
manager/tests/unit/datamodel/types/test_base_types.py

index b3184fc772336a4722f31306c0fb88599c61dfa6..227a59eb9e488f0f79e9667d0c0270de426d9192 100644 (file)
@@ -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.
index acc8baf395edd0e3b5ce47da829f1f2e25d44f09..00e7bda5e99b5698290780429f7097d8e2a46cd6 100644 (file)
@@ -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)