Merge pull request #1 from HideyoshiNakazone/initial-fields-validators
Initial Fields Validators for: - string - number - integer - boolean - object - array
This commit was merged in pull request #1.
This commit is contained in:
66
.github/workflows/build.yml
vendored
Normal file
66
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
name: Example
|
||||
|
||||
|
||||
on:
|
||||
push
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: run-tests
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version:
|
||||
- "3.10"
|
||||
- "3.11"
|
||||
- "3.12"
|
||||
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v5
|
||||
with:
|
||||
# Install a specific version of uv.
|
||||
version: "0.6.14"
|
||||
enable-cache: true
|
||||
cache-dependency-glob: "uv.lock"
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: uv sync --all-extras --dev
|
||||
|
||||
- name: Run tests
|
||||
run: uv run poe tests
|
||||
|
||||
publish:
|
||||
name: publish
|
||||
runs-on: ubuntu-latest
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
needs: [test]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v5
|
||||
with:
|
||||
# Install a specific version of uv.
|
||||
version: "0.6.14"
|
||||
enable-cache: true
|
||||
cache-dependency-glob: "uv.lock"
|
||||
|
||||
- name: Set up Python
|
||||
run: uv python install
|
||||
|
||||
- name: Build
|
||||
run: uv build
|
||||
|
||||
- name: Publish
|
||||
run: uv publish -t ${{ secrets.PYPI_TOKEN }}
|
||||
@@ -1,5 +1,8 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Generic, Self, TypeVar
|
||||
from typing import Generic, TypeVar
|
||||
from typing_extensions import Self
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
@@ -17,7 +20,7 @@ class GenericTypeParser(ABC, Generic[T]):
|
||||
@abstractmethod
|
||||
def from_properties(
|
||||
name: str, properties: dict[str, any]
|
||||
) -> tuple[type[T], dict[str, any]]: ...
|
||||
) -> tuple[type[T], Field]: ...
|
||||
|
||||
@classmethod
|
||||
def get_impl(cls, type_name: str) -> Self:
|
||||
32
jambo/parser/array_type_parser.py
Normal file
32
jambo/parser/array_type_parser.py
Normal file
@@ -0,0 +1,32 @@
|
||||
from jambo.parser._type_parser import GenericTypeParser
|
||||
|
||||
from typing import TypeVar
|
||||
|
||||
from jambo.utils.properties_builder.mappings_properties_builder import (
|
||||
mappings_properties_builder,
|
||||
)
|
||||
|
||||
V = TypeVar("V")
|
||||
|
||||
|
||||
class ArrayTypeParser(GenericTypeParser):
|
||||
mapped_type = list
|
||||
|
||||
json_schema_type = "array"
|
||||
|
||||
@classmethod
|
||||
def from_properties(cls, name, properties):
|
||||
_item_type, _item_args = GenericTypeParser.get_impl(
|
||||
properties["items"]["type"]
|
||||
).from_properties(name, properties["items"])
|
||||
|
||||
_mappings = {
|
||||
"maxItems": "max_length",
|
||||
"minItems": "min_length",
|
||||
}
|
||||
|
||||
wrapper_type = set if properties.get("uniqueItems", False) else list
|
||||
|
||||
return wrapper_type[_item_type], mappings_properties_builder(
|
||||
properties, _mappings
|
||||
)
|
||||
@@ -1,4 +1,4 @@
|
||||
from jambo.types._type_parser import GenericTypeParser
|
||||
from jambo.parser._type_parser import GenericTypeParser
|
||||
|
||||
|
||||
class BooleanTypeParser(GenericTypeParser):
|
||||
@@ -8,4 +8,4 @@ class BooleanTypeParser(GenericTypeParser):
|
||||
|
||||
@staticmethod
|
||||
def from_properties(name, properties):
|
||||
return bool, {}
|
||||
return bool, {} # The second argument is not used in this case
|
||||
12
jambo/parser/float_type_parser.py
Normal file
12
jambo/parser/float_type_parser.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from jambo.parser._type_parser import GenericTypeParser
|
||||
from jambo.utils.properties_builder.numeric_properties_builder import numeric_properties_builder
|
||||
|
||||
|
||||
class FloatTypeParser(GenericTypeParser):
|
||||
mapped_type = float
|
||||
|
||||
json_schema_type = "number"
|
||||
|
||||
@staticmethod
|
||||
def from_properties(name, properties):
|
||||
return float, numeric_properties_builder(properties)
|
||||
12
jambo/parser/int_type_parser.py
Normal file
12
jambo/parser/int_type_parser.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from jambo.parser._type_parser import GenericTypeParser
|
||||
from jambo.utils.properties_builder.numeric_properties_builder import numeric_properties_builder
|
||||
|
||||
|
||||
class IntTypeParser(GenericTypeParser):
|
||||
mapped_type = int
|
||||
|
||||
json_schema_type = "integer"
|
||||
|
||||
@staticmethod
|
||||
def from_properties(name, properties):
|
||||
return int, numeric_properties_builder(properties)
|
||||
@@ -1,4 +1,4 @@
|
||||
from jambo.types._type_parser import GenericTypeParser
|
||||
from jambo.parser._type_parser import GenericTypeParser
|
||||
|
||||
|
||||
class ObjectTypeParser(GenericTypeParser):
|
||||
@@ -10,5 +10,7 @@ class ObjectTypeParser(GenericTypeParser):
|
||||
def from_properties(name, properties):
|
||||
from jambo.schema_converter import SchemaConverter
|
||||
|
||||
_type = SchemaConverter.build_object(name, properties)
|
||||
return _type, {}
|
||||
return (
|
||||
SchemaConverter.build_object(name, properties),
|
||||
{}, # The second argument is not used in this case
|
||||
)
|
||||
20
jambo/parser/string_type_parser.py
Normal file
20
jambo/parser/string_type_parser.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from jambo.parser._type_parser import GenericTypeParser
|
||||
from jambo.utils.properties_builder.mappings_properties_builder import (
|
||||
mappings_properties_builder,
|
||||
)
|
||||
|
||||
|
||||
class StringTypeParser(GenericTypeParser):
|
||||
mapped_type = str
|
||||
|
||||
json_schema_type = "string"
|
||||
|
||||
@staticmethod
|
||||
def from_properties(name, properties):
|
||||
_mappings = {
|
||||
"maxLength": "max_length",
|
||||
"minLength": "min_length",
|
||||
"pattern": "pattern",
|
||||
}
|
||||
|
||||
return str, mappings_properties_builder(properties, _mappings)
|
||||
@@ -1,4 +1,4 @@
|
||||
from jambo.types import GenericTypeParser
|
||||
from jambo.parser import GenericTypeParser
|
||||
|
||||
from jsonschema.exceptions import SchemaError
|
||||
from jsonschema.protocols import Validator
|
||||
@@ -7,27 +7,47 @@ from pydantic.fields import Field
|
||||
|
||||
from typing import Type
|
||||
|
||||
from jambo.types.json_schema_type import JSONSchema
|
||||
|
||||
|
||||
class SchemaConverter:
|
||||
@staticmethod
|
||||
def build(schema):
|
||||
try:
|
||||
Validator.check_schema(schema)
|
||||
except SchemaError as e:
|
||||
raise ValueError(f"Invalid JSON Schema: {e}")
|
||||
"""
|
||||
Converts JSON Schema to Pydantic models.
|
||||
|
||||
if schema["type"] != "object":
|
||||
raise TypeError(
|
||||
f"Invalid JSON Schema: {schema['type']}. Only 'object' can be converted to Pydantic models."
|
||||
)
|
||||
This class is responsible for converting JSON Schema definitions into Pydantic models.
|
||||
It validates the schema and generates the corresponding Pydantic model with appropriate
|
||||
fields and types. The generated model can be used for data validation and serialization.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def build(schema: JSONSchema) -> Type:
|
||||
"""
|
||||
Converts a JSON Schema to a Pydantic model.
|
||||
:param schema: The JSON Schema to convert.
|
||||
:return: A Pydantic model class.
|
||||
"""
|
||||
if "title" not in schema:
|
||||
raise ValueError("JSON Schema must have a title.")
|
||||
|
||||
return SchemaConverter.build_object(schema["title"], schema)
|
||||
|
||||
@staticmethod
|
||||
def build_object(
|
||||
name: str,
|
||||
schema: dict,
|
||||
):
|
||||
schema: JSONSchema,
|
||||
) -> Type:
|
||||
"""
|
||||
Converts a JSON Schema object to a Pydantic model given a name.
|
||||
:param name:
|
||||
:param schema:
|
||||
:return:
|
||||
"""
|
||||
|
||||
try:
|
||||
Validator.check_schema(schema)
|
||||
except SchemaError as e:
|
||||
raise ValueError(f"Invalid JSON Schema: {e}")
|
||||
|
||||
if schema["type"] != "object":
|
||||
raise TypeError(
|
||||
f"Invalid JSON Schema: {schema['type']}. Only 'object' can be converted to Pydantic models."
|
||||
@@ -60,7 +80,7 @@ class SchemaConverter:
|
||||
@staticmethod
|
||||
def _build_field(
|
||||
name, properties: dict, required_keys: list[str]
|
||||
) -> tuple[type, Field]:
|
||||
) -> tuple[type, dict]:
|
||||
_field_type, _field_args = GenericTypeParser.get_impl(
|
||||
properties["type"]
|
||||
).from_properties(name, properties)
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
from jambo.types._type_parser import GenericTypeParser
|
||||
|
||||
from typing import TypeVar
|
||||
|
||||
V = TypeVar("V")
|
||||
|
||||
|
||||
class ArrayTypeParser(GenericTypeParser):
|
||||
mapped_type = list
|
||||
|
||||
json_schema_type = "array"
|
||||
|
||||
@classmethod
|
||||
def from_properties(cls, name, properties):
|
||||
_item_type, _item_args = GenericTypeParser.get_impl(
|
||||
properties["items"]["type"]
|
||||
).from_properties(name, properties["items"])
|
||||
|
||||
return list[_item_type], {}
|
||||
@@ -1,11 +0,0 @@
|
||||
from jambo.types._type_parser import GenericTypeParser
|
||||
|
||||
|
||||
class FloatTypeParser(GenericTypeParser):
|
||||
mapped_type = float
|
||||
|
||||
json_schema_type = "number"
|
||||
|
||||
@staticmethod
|
||||
def from_properties(name, properties):
|
||||
return float, {}
|
||||
@@ -1,11 +0,0 @@
|
||||
from jambo.types._type_parser import GenericTypeParser
|
||||
|
||||
|
||||
class IntTypeParser(GenericTypeParser):
|
||||
mapped_type = int
|
||||
|
||||
json_schema_type = "integer"
|
||||
|
||||
@staticmethod
|
||||
def from_properties(name, properties):
|
||||
return int, {}
|
||||
80
jambo/types/json_schema_type.py
Normal file
80
jambo/types/json_schema_type.py
Normal file
@@ -0,0 +1,80 @@
|
||||
from typing import List, Dict, Union, TypedDict, Literal
|
||||
|
||||
|
||||
JSONSchemaType = Literal[
|
||||
"string", "number", "integer", "boolean", "object", "array", "null"
|
||||
]
|
||||
|
||||
|
||||
JSONType = Union[str, int, float, bool, None, Dict[str, "JSONType"], List["JSONType"]]
|
||||
|
||||
|
||||
class JSONSchema(TypedDict, total=False):
|
||||
# Basic metadata
|
||||
title: str
|
||||
description: str
|
||||
default: JSONType
|
||||
examples: List[JSONType]
|
||||
|
||||
# Type definitions
|
||||
type: Union[JSONSchemaType, List[JSONSchemaType]]
|
||||
|
||||
# Object-specific keywords
|
||||
properties: Dict[str, "JSONSchema"]
|
||||
required: List[str]
|
||||
additionalProperties: Union[bool, "JSONSchema"]
|
||||
minProperties: int
|
||||
maxProperties: int
|
||||
patternProperties: Dict[str, "JSONSchema"]
|
||||
dependencies: Dict[str, Union[List[str], "JSONSchema"]]
|
||||
|
||||
# Array-specific keywords
|
||||
items: Union["JSONSchema", List["JSONSchema"]]
|
||||
additionalItems: Union[bool, "JSONSchema"]
|
||||
minItems: int
|
||||
maxItems: int
|
||||
uniqueItems: bool
|
||||
|
||||
# String-specific keywords
|
||||
minLength: int
|
||||
maxLength: int
|
||||
pattern: str
|
||||
format: str
|
||||
|
||||
# Number-specific keywords
|
||||
minimum: float
|
||||
maximum: float
|
||||
exclusiveMinimum: float
|
||||
exclusiveMaximum: float
|
||||
multipleOf: float
|
||||
|
||||
# Enum and const
|
||||
enum: List[JSONType]
|
||||
const: JSONType
|
||||
|
||||
# Conditionals
|
||||
if_: "JSONSchema" # 'if' is a reserved word in Python
|
||||
then: "JSONSchema"
|
||||
else_: "JSONSchema" # 'else' is also a reserved word
|
||||
|
||||
# Combination keywords
|
||||
allOf: List["JSONSchema"]
|
||||
anyOf: List["JSONSchema"]
|
||||
oneOf: List["JSONSchema"]
|
||||
not_: "JSONSchema" # 'not' is a reserved word
|
||||
|
||||
|
||||
# Fix forward references
|
||||
JSONSchema.__annotations__["properties"] = Dict[str, JSONSchema]
|
||||
JSONSchema.__annotations__["items"] = Union[JSONSchema, List[JSONSchema]]
|
||||
JSONSchema.__annotations__["additionalItems"] = Union[bool, JSONSchema]
|
||||
JSONSchema.__annotations__["additionalProperties"] = Union[bool, JSONSchema]
|
||||
JSONSchema.__annotations__["patternProperties"] = Dict[str, JSONSchema]
|
||||
JSONSchema.__annotations__["dependencies"] = Dict[str, Union[List[str], JSONSchema]]
|
||||
JSONSchema.__annotations__["if_"] = JSONSchema
|
||||
JSONSchema.__annotations__["then"] = JSONSchema
|
||||
JSONSchema.__annotations__["else_"] = JSONSchema
|
||||
JSONSchema.__annotations__["allOf"] = List[JSONSchema]
|
||||
JSONSchema.__annotations__["anyOf"] = List[JSONSchema]
|
||||
JSONSchema.__annotations__["oneOf"] = List[JSONSchema]
|
||||
JSONSchema.__annotations__["not_"] = JSONSchema
|
||||
@@ -1,11 +0,0 @@
|
||||
from jambo.types._type_parser import GenericTypeParser
|
||||
|
||||
|
||||
class StringTypeParser(GenericTypeParser):
|
||||
mapped_type = str
|
||||
|
||||
json_schema_type = "string"
|
||||
|
||||
@staticmethod
|
||||
def from_properties(name, properties):
|
||||
return str, {}
|
||||
0
jambo/utils/__init__.py
Normal file
0
jambo/utils/__init__.py
Normal file
0
jambo/utils/properties_builder/__init__.py
Normal file
0
jambo/utils/properties_builder/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
def mappings_properties_builder(properties, mappings):
|
||||
return {
|
||||
mappings[key]: value for key, value in properties.items() if key in mappings
|
||||
}
|
||||
15
jambo/utils/properties_builder/numeric_properties_builder.py
Normal file
15
jambo/utils/properties_builder/numeric_properties_builder.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from jambo.utils.properties_builder.mappings_properties_builder import (
|
||||
mappings_properties_builder,
|
||||
)
|
||||
|
||||
|
||||
def numeric_properties_builder(properties):
|
||||
_mappings = {
|
||||
"minimum": "ge",
|
||||
"exclusiveMinimum": "gt",
|
||||
"maximum": "le",
|
||||
"exclusiveMaximum": "lt",
|
||||
"multipleOf": "multiple_of",
|
||||
}
|
||||
|
||||
return mappings_properties_builder(properties, _mappings)
|
||||
0
jambo/utils/types/__init__.py
Normal file
0
jambo/utils/types/__init__.py
Normal file
@@ -21,7 +21,7 @@ dev = [
|
||||
# POE Tasks
|
||||
[tool.poe.tasks]
|
||||
create-hooks = "bash .githooks/set-hooks.sh"
|
||||
|
||||
tests = "python -m unittest discover -s tests -v"
|
||||
|
||||
# Build System
|
||||
[tool.hatch.version]
|
||||
|
||||
@@ -32,7 +32,13 @@ class TestSchemaConverter(TestCase):
|
||||
"description": "A person",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"name": {"type": "string", "maxLength": 4, "minLength": 1},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"maxLength": 50,
|
||||
"minLength": 5,
|
||||
"pattern": r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$",
|
||||
},
|
||||
},
|
||||
"required": ["name"],
|
||||
}
|
||||
@@ -41,13 +47,29 @@ class TestSchemaConverter(TestCase):
|
||||
|
||||
self.assertEqual(model(name="John", age=30).name, "John")
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
model(name=123, age=30, email="teste@hideyoshi.com")
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
model(name="John Invalid", age=45, email="teste@hideyoshi.com")
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
model(name="", age=45, email="teste@hideyoshi.com")
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
model(name="John", age=45, email="hideyoshi.com")
|
||||
|
||||
def test_validation_integer(self):
|
||||
schema = {
|
||||
"title": "Person",
|
||||
"description": "A person",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"age": {"type": "integer"},
|
||||
"age": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 120,
|
||||
},
|
||||
},
|
||||
"required": ["age"],
|
||||
}
|
||||
@@ -56,7 +78,11 @@ class TestSchemaConverter(TestCase):
|
||||
|
||||
self.assertEqual(model(age=30).age, 30)
|
||||
|
||||
self.assertEqual(model(age="30").age, 30)
|
||||
with self.assertRaises(ValueError):
|
||||
model(age=-1)
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
model(age=121)
|
||||
|
||||
def test_validation_float(self):
|
||||
schema = {
|
||||
@@ -64,7 +90,11 @@ class TestSchemaConverter(TestCase):
|
||||
"description": "A person",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"age": {"type": "number"},
|
||||
"age": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 120,
|
||||
},
|
||||
},
|
||||
"required": ["age"],
|
||||
}
|
||||
@@ -73,7 +103,11 @@ class TestSchemaConverter(TestCase):
|
||||
|
||||
self.assertEqual(model(age=30).age, 30.0)
|
||||
|
||||
self.assertEqual(model(age="30").age, 30.0)
|
||||
with self.assertRaises(ValueError):
|
||||
model(age=-1.0)
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
model(age=121.0)
|
||||
|
||||
def test_validation_boolean(self):
|
||||
schema = {
|
||||
@@ -98,14 +132,28 @@ class TestSchemaConverter(TestCase):
|
||||
"description": "A person",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"friends": {"type": "array", "items": {"type": "string"}},
|
||||
"friends": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"minItems": 1,
|
||||
"maxItems": 2,
|
||||
"uniqueItems": True,
|
||||
},
|
||||
},
|
||||
"required": ["friends"],
|
||||
}
|
||||
|
||||
model = SchemaConverter.build(schema)
|
||||
|
||||
self.assertEqual(model(friends=["John", "Jane"]).friends, ["John", "Jane"])
|
||||
self.assertEqual(
|
||||
model(friends=["John", "Jane", "John"]).friends, {"John", "Jane"}
|
||||
)
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
model(friends=[])
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
model(friends=["John", "Jane", "Invalid"])
|
||||
|
||||
def test_validation_object(self):
|
||||
schema = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from jambo.types import (
|
||||
from jambo.parser import (
|
||||
ArrayTypeParser,
|
||||
FloatTypeParser,
|
||||
GenericTypeParser,
|
||||
@@ -21,21 +21,65 @@ class TestTypeParser(unittest.TestCase):
|
||||
|
||||
def test_int_parser(self):
|
||||
parser = IntTypeParser()
|
||||
expected_definition = (int, {})
|
||||
|
||||
self.assertEqual(parser.from_properties("placeholder", {}), expected_definition)
|
||||
type_parsing, type_validator = parser.from_properties(
|
||||
"placeholder",
|
||||
{
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"exclusiveMinimum": 1,
|
||||
"maximum": 10,
|
||||
"exclusiveMaximum": 11,
|
||||
"multipleOf": 2,
|
||||
},
|
||||
)
|
||||
|
||||
self.assertEqual(type_parsing, int)
|
||||
self.assertEqual(type_validator["ge"], 0)
|
||||
self.assertEqual(type_validator["gt"], 1)
|
||||
self.assertEqual(type_validator["le"], 10)
|
||||
self.assertEqual(type_validator["lt"], 11)
|
||||
self.assertEqual(type_validator["multiple_of"], 2)
|
||||
|
||||
def test_float_parser(self):
|
||||
parser = FloatTypeParser()
|
||||
expected_definition = (float, {})
|
||||
|
||||
self.assertEqual(parser.from_properties("placeholder", {}), expected_definition)
|
||||
type_parsing, type_validator = parser.from_properties(
|
||||
"placeholder",
|
||||
{
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"exclusiveMinimum": 1,
|
||||
"maximum": 10,
|
||||
"exclusiveMaximum": 11,
|
||||
"multipleOf": 2,
|
||||
},
|
||||
)
|
||||
|
||||
self.assertEqual(type_parsing, float)
|
||||
self.assertEqual(type_validator["ge"], 0)
|
||||
self.assertEqual(type_validator["gt"], 1)
|
||||
self.assertEqual(type_validator["le"], 10)
|
||||
self.assertEqual(type_validator["lt"], 11)
|
||||
self.assertEqual(type_validator["multiple_of"], 2)
|
||||
|
||||
def test_string_parser(self):
|
||||
parser = StringTypeParser()
|
||||
expected_definition = (str, {})
|
||||
|
||||
self.assertEqual(parser.from_properties("placeholder", {}), expected_definition)
|
||||
type_parsing, type_validator = parser.from_properties(
|
||||
"placeholder",
|
||||
{
|
||||
"type": "string",
|
||||
"maxLength": 10,
|
||||
"minLength": 1,
|
||||
"pattern": "[a-zA-Z0-9]",
|
||||
},
|
||||
)
|
||||
|
||||
self.assertEqual(type_parsing, str)
|
||||
self.assertEqual(type_validator["max_length"], 10)
|
||||
self.assertEqual(type_validator["min_length"], 1)
|
||||
self.assertEqual(type_validator["pattern"], "[a-zA-Z0-9]")
|
||||
|
||||
def test_object_parser(self):
|
||||
parser = ObjectTypeParser()
|
||||
@@ -69,18 +113,26 @@ class TestTypeParser(unittest.TestCase):
|
||||
parser = ArrayTypeParser()
|
||||
|
||||
properties = {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"age": {"type": "integer"},
|
||||
},
|
||||
}
|
||||
},
|
||||
"maxItems": 10,
|
||||
"minItems": 1,
|
||||
"uniqueItems": True,
|
||||
}
|
||||
|
||||
_type, _args = parser.from_properties("placeholder", properties)
|
||||
type_parsing, type_validator = parser.from_properties("placeholder", properties)
|
||||
|
||||
Model = get_args(_type)[0]
|
||||
self.assertEqual(type_parsing.__origin__, set)
|
||||
self.assertEqual(type_validator["max_length"], 10)
|
||||
self.assertEqual(type_validator["min_length"], 1)
|
||||
|
||||
Model = get_args(type_parsing)[0]
|
||||
obj = Model(name="name", age=10)
|
||||
|
||||
self.assertEqual(obj.name, "name")
|
||||
|
||||
Reference in New Issue
Block a user