diff --git a/dataclasses_jsonschema/__init__.py b/dataclasses_jsonschema/__init__.py index f6d0aaa..df3be8d 100644 --- a/dataclasses_jsonschema/__init__.py +++ b/dataclasses_jsonschema/__init__.py @@ -761,6 +761,10 @@ def _get_field_definitions(cls, field_type: Any, definitions: JsonDict, validate_enums=schema_options.validate_enums ) ) + elif field_type in cls._field_encoders: + field_schema = cls._field_encoders[field_type].json_schema + if 'definitions' in field_schema: + definitions.update(field_schema.pop('definitions')) @classmethod def all_json_schemas( diff --git a/dataclasses_jsonschema/apispec.py b/dataclasses_jsonschema/apispec.py index 9494d8b..a97d8ec 100644 --- a/dataclasses_jsonschema/apispec.py +++ b/dataclasses_jsonschema/apispec.py @@ -35,7 +35,7 @@ def resolve_schema_refs(self, data): def _schema_type(self) -> SchemaType: return SchemaType.SWAGGER_V2 if self.spec.openapi_version.major == 2 else SchemaType.OPENAPI_3 - def schema_helper(self, name: str, _: Any, schema: Optional[Union[Type[T], Dict]] = None, **kwargs): + def schema_helper(self, name: str, definition: Any, schema: Optional[Union[Type[T], Dict]] = None, **kwargs): if isinstance(schema, dict) or schema is None: return schema json_schemas = schema.json_schema(schema_type=self._schema_type, embeddable=True) diff --git a/tests/test_core.py b/tests/test_core.py index ad4f2b7..1a65bf5 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -199,9 +199,12 @@ def test_embeddable_json_schema(): 'Zoo': ZOO_SCHEMA, 'Baz': BAZ_SCHEMA } - assert expected == JsonSchemaMixin.all_json_schemas() + # Since JsonSchemaMixin has global state, there could be more schemas + # We check if the expected are at least a subset + all_schemas = JsonSchemaMixin.all_json_schemas() + assert all(schema in all_schemas.items() for schema in expected.items()) with pytest.warns(DeprecationWarning): - assert expected == JsonSchemaMixin.json_schema() + assert all_schemas == JsonSchemaMixin.json_schema() def test_json_schema(): diff --git a/tests/test_field_encoders.py b/tests/test_field_encoders.py new file mode 100644 index 0000000..7ce5afb --- /dev/null +++ b/tests/test_field_encoders.py @@ -0,0 +1,126 @@ +from dataclasses import dataclass +from typing import List +from dataclasses_jsonschema import JsonSchemaMixin +from dataclasses_jsonschema.field_types import FieldEncoder +from dataclasses_jsonschema.type_defs import JsonDict +import pytest + +pytestmark = pytest.mark.filterwarnings("error:Unable to create schema") + +class ExBar: + def __init__(self, name: str): + self.name = name + + def __eq__(self, other: "ExBar") -> bool: + return self.name == other.name + + +class ExSubFoo: + def __init__(self, number: int): + self.number = number + + def __eq__(self, other: "ExSubFoo") -> bool: + return self.number == other.number + + +class ExFoo: + def __init__(self, ex_sub_foo: List[ExSubFoo]): + self.ex_sub_foo = ex_sub_foo + + def __eq__(self, other: "ExFoo") -> bool: + return self.ex_sub_foo == other.ex_sub_foo + + +class External: + def __init__(self, ex_foo: ExFoo, ex_bar: ExBar): + self.ex_foo = ex_foo + self.ex_bar = ex_bar + + def __eq__(self, other: "External") -> bool: + return self.ex_foo == other.ex_foo and self.ex_bar == other.ex_bar + + +class ExternalField(FieldEncoder): + def to_python(self, value: dict) -> External: + return External( + ex_bar=ExBar(name=value["ex_bar"]["name"]), + ex_foo=ExFoo( + ex_sub_foo=[ + ExSubFoo(number=ex_sub_foo["number"]) + for ex_sub_foo in value["ex_foo"]["ex_sub_foo"] + ] + ), + ) + + def to_wire(self, value: External) -> dict: + return { + "ex_bar": {"name": value.ex_bar.name}, + "ex_foo": { + "ex_sub_foo": [ + {"number": ex_sub_foo.number} + for ex_sub_foo in value.ex_foo.ex_sub_foo + ] + }, + } + + @property + def json_schema(self) -> JsonDict: + return { + "type": "object", + "properties": { + "ex_foo": {"$ref": "#/definitions/EX_FOO"}, + "ex_bar": {"$ref": "#/definitions/EX_BAR"}, + }, + "$schema": "http://json-schema.org/draft-06/schema#", + "definitions": { + "EX_FOO": { + "type": "object", + "properties": { + "ex_sub_foo": { + "type": "array", + "items": {"$ref": "#/definitions/EX_SUB_FOO"}, + } + }, + }, + "EX_BAR": { + "type": "object", + "properties": { + "name": {"type": "string"}, + }, + }, + "EX_SUB_FOO": { + "type": "object", + "properties": {"number": {"type": "integer"}}, + }, + }, + } + + + + +def test_external_fields(): + JsonSchemaMixin.register_field_encoders({External: ExternalField()}) + + @dataclass + class Internal(JsonSchemaMixin): + external: External + + internal_dict = { + "external": { + "ex_bar": {"name": "sample"}, + "ex_foo": {"ex_sub_foo": [{"number": number} for number in range(3)]}, + } + } + + internal_obj = Internal( + External( + ex_bar=ExBar("sample"), + ex_foo=ExFoo(ex_sub_foo=[ExSubFoo(number) for number in range(3)]), + ) + ) + + + Internal._validate(internal_dict) + + # assert Internal.from_dict(internal_dict) == internal_obj + # assert internal_obj.to_dict() == internal_dict diff --git a/tox.ini b/tox.ini index e67048e..56e147f 100644 --- a/tox.ini +++ b/tox.ini @@ -28,7 +28,7 @@ deps = [testenv:py36] # Exclude test_peps from py 3.6 since this is not supported commands = - pytest tests/test_core.py tests/test_apispec_plugin.py + pytest tests/test_core.py tests/test_apispec_plugin.py tests/test_field_encoders.py flake8 dataclasses_jsonschema mypy dataclasses_jsonschema deps =