Initial allOf Implementation
This commit is contained in:
@@ -8,3 +8,4 @@ from .string_type_parser import StringTypeParser as StringTypeParser
|
||||
from .array_type_parser import ArrayTypeParser as ArrayTypeParser
|
||||
from .boolean_type_parser import BooleanTypeParser as BooleanTypeParser
|
||||
from .float_type_parser import FloatTypeParser as FloatTypeParser
|
||||
from .allof_type_parser import AllOfTypeParser as AllOfTypeParser
|
||||
|
||||
76
jambo/parser/allof_type_parser.py
Normal file
76
jambo/parser/allof_type_parser.py
Normal file
@@ -0,0 +1,76 @@
|
||||
from jambo.parser._type_parser import GenericTypeParser
|
||||
|
||||
|
||||
class AllOfTypeParser(GenericTypeParser):
|
||||
mapped_type = any
|
||||
|
||||
json_schema_type = "allOf"
|
||||
|
||||
@staticmethod
|
||||
def from_properties(name, properties):
|
||||
subProperties = properties.get("allOf")
|
||||
if not subProperties:
|
||||
raise ValueError("Invalid JSON Schema: 'allOf' is not specified.")
|
||||
|
||||
_mapped_type = properties.get("type")
|
||||
if _mapped_type is None:
|
||||
_mapped_type = subProperties[0].get("type")
|
||||
|
||||
if _mapped_type is None:
|
||||
raise ValueError("Invalid JSON Schema: 'type' is not specified.")
|
||||
|
||||
if not all(prop.get("type") == _mapped_type for prop in subProperties):
|
||||
raise ValueError("Invalid JSON Schema: allOf types do not match.")
|
||||
|
||||
combined_properties = AllOfTypeParser._rebuild_properties_from_subproperties(
|
||||
subProperties
|
||||
)
|
||||
|
||||
return GenericTypeParser.get_impl(_mapped_type).from_properties(
|
||||
name, combined_properties
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _rebuild_properties_from_subproperties(subProperties):
|
||||
properties = {}
|
||||
for subProperty in subProperties:
|
||||
for name, prop in subProperty.items():
|
||||
if name not in properties:
|
||||
properties[name] = prop
|
||||
else:
|
||||
# Merge properties if they exist in both sub-properties
|
||||
properties[name] = AllOfTypeParser._validate_prop(
|
||||
name, properties[name], prop
|
||||
)
|
||||
return properties
|
||||
|
||||
@staticmethod
|
||||
def _validate_prop(prop_name, old_value, new_value):
|
||||
if prop_name == "type":
|
||||
if old_value != new_value:
|
||||
raise ValueError(
|
||||
f"Invalid JSON Schema: conflicting types for '{prop_name}'"
|
||||
)
|
||||
return old_value
|
||||
|
||||
if prop_name == "description":
|
||||
return f"{old_value} | {new_value}"
|
||||
|
||||
if prop_name == "default":
|
||||
if old_value != new_value:
|
||||
raise ValueError(
|
||||
f"Invalid JSON Schema: conflicting defaults for '{prop_name}'"
|
||||
)
|
||||
return old_value
|
||||
|
||||
if prop_name == "required":
|
||||
return old_value + new_value
|
||||
|
||||
if prop_name in ("maxLength", "maximum", "exclusiveMaximum"):
|
||||
return old_value if old_value > new_value else new_value
|
||||
|
||||
if prop_name in ("minLength", "minimum", "exclusiveMinimum"):
|
||||
return old_value if old_value < new_value else new_value
|
||||
|
||||
# Handle other properties by just returning the first valued
|
||||
return old_value
|
||||
@@ -79,8 +79,20 @@ class SchemaConverter:
|
||||
def _build_field(
|
||||
name, properties: dict, required_keys: list[str]
|
||||
) -> tuple[type, dict]:
|
||||
match properties:
|
||||
case {"anyOf": _}:
|
||||
_field_type = "anyOf"
|
||||
case {"allOf": _}:
|
||||
_field_type = "allOf"
|
||||
case {"oneOf": _}:
|
||||
_field_type = "oneOf"
|
||||
case {"type": _}:
|
||||
_field_type = properties["type"]
|
||||
case _:
|
||||
raise ValueError(f"Invalid JSON Schema: {properties}")
|
||||
|
||||
_field_type, _field_args = GenericTypeParser.get_impl(
|
||||
properties["type"]
|
||||
_field_type
|
||||
).from_properties(name, properties)
|
||||
|
||||
_field_args = _field_args or {}
|
||||
|
||||
@@ -281,3 +281,33 @@ class TestSchemaConverter(TestCase):
|
||||
|
||||
self.assertEqual(obj.address.street, "123 Main St")
|
||||
self.assertEqual(obj.address.city, "Springfield")
|
||||
|
||||
def test_all_of(self):
|
||||
schema = {
|
||||
"title": "Person",
|
||||
"description": "A person",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"allOf": [
|
||||
{"type": "string", "maxLength": 4},
|
||||
{"type": "string", "minLength": 1},
|
||||
{"type": "string", "minLength": 2},
|
||||
]
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Model = SchemaConverter.build(schema)
|
||||
|
||||
obj = Model(
|
||||
name="J",
|
||||
)
|
||||
|
||||
self.assertEqual(obj.name, "J")
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
Model(name="John Invalid")
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
Model(name="")
|
||||
|
||||
Reference in New Issue
Block a user