diff --git a/aiohttp_pydantic/injectors.py b/aiohttp_pydantic/injectors.py index d9378a6..d8d29bc 100644 --- a/aiohttp_pydantic/injectors.py +++ b/aiohttp_pydantic/injectors.py @@ -85,7 +85,7 @@ class BodyGetter(AbstractInjector): # to a dict. Prevent this by requiring the body to be a dict for object models. if self._expect_object and not isinstance(body, dict): raise HTTPBadRequest( - text='[{"in": "body", "loc": ["__root__"], "msg": "value is not a ' + text='[{"loc_in": "body", "loc": ["root"], "msg": "value is not a ' 'valid dict", "type": "type_error.dict"}]', content_type="application/json", ) from None diff --git a/aiohttp_pydantic/oas/view.py b/aiohttp_pydantic/oas/view.py index 5d184f5..adc0ca0 100644 --- a/aiohttp_pydantic/oas/view.py +++ b/aiohttp_pydantic/oas/view.py @@ -5,7 +5,7 @@ from typing import List, Type, Optional, get_type_hints from aiohttp.web import Response, json_response from aiohttp.web_app import Application -from pydantic import BaseModel +from pydantic import BaseModel, RootModel from aiohttp_pydantic.oas.struct import OpenApiSpec3, OperationObject, PathItem from . import docstring_parser @@ -32,7 +32,7 @@ class _OASResponseBuilder: response_schema = obj.schema( ref_template="#/components/schemas/{model}" ).copy() - if def_sub_schemas := response_schema.pop("definitions", None): + if def_sub_schemas := response_schema.pop("$defs", None): self._oas.components.schemas.update(def_sub_schemas) self._oas.components.schemas.update({response_schema['title']: response_schema}) return {'$ref': f'#/components/schemas/{response_schema["title"]}'} @@ -99,7 +99,7 @@ def _add_http_method_to_oas( .schema(ref_template="#/components/schemas/{model}") .copy() ) - if def_sub_schemas := body_schema.pop("definitions", None): + if def_sub_schemas := body_schema.pop("$defs", None): oas.components.schemas.update(def_sub_schemas) oas_operation.request_body.content = { @@ -117,19 +117,19 @@ def _add_http_method_to_oas( oas_operation.parameters[i].in_ = args_location oas_operation.parameters[i].name = name - attrs = {"__annotations__": {"__root__": type_}} + attrs = {"__annotations__": {"root": type_}} if name in defaults: - attrs["__root__"] = defaults[name] + attrs["root"] = defaults[name] oas_operation.parameters[i].required = False else: oas_operation.parameters[i].required = True - oas_operation.parameters[i].schema = type(name, (BaseModel,), attrs).schema( + oas_operation.parameters[i].schema = type(name, (RootModel,), attrs).schema( ref_template="#/components/schemas/{model}" ) # move definitions - if def_sub_schemas := oas_operation.parameters[i].schema.pop("definitions", None): + if def_sub_schemas := oas_operation.parameters[i].schema.pop("$defs", None): oas.components.schemas.update(def_sub_schemas) return_type = get_type_hints(handler).get("return") diff --git a/requirements/ci.txt b/requirements/ci.txt index e54ae95..ceaefd4 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -1,42 +1,11 @@ -aiohttp==3.8.1 -aiosignal==1.2.0 -async-timeout==4.0.2 -atomicwrites==1.4.1 -attrs==21.4.0 -bleach==5.0.1 -certifi==2022.6.15 -charset-normalizer==2.1.0 -codecov==2.1.12 -colorama==0.4.5 -commonmark==0.9.1 -coverage==6.4.2 -docutils==0.19 -frozenlist==1.3.0 -idna==3.3 -importlib-metadata==4.12.0 -iniconfig==1.1.1 -keyring==23.7.0 -multidict==6.0.2 -packaging==21.3 -pkginfo==1.8.3 -pluggy==1.0.0 -py==1.11.0 -Pygments==2.12.0 -pyparsing==3.0.9 -pytest==7.1.2 +aiohttp==3.8.4 +pydantic==2.0.2 +jinja2==3.1.2 +swagger-4-ui-bundle==0.0.4 +pytest==7.4.0 pytest-aiohttp==1.0.4 -pytest-asyncio==0.19.0 -pytest-cov==3.0.0 -pywin32-ctypes==0.2.0 -readme-renderer==35.0 -requests==2.28.1 -requests-toolbelt==0.9.1 -rfc3986==2.0.0 -rich==12.5.1 -six==1.16.0 -tomli==2.0.1 -twine==4.0.1 -urllib3==1.26.11 -webencodings==0.5.1 -yarl==1.7.2 -zipp==3.8.1 +pytest-asyncio==0.21.1 +pytest-cov==4.1.0 +readme-renderer==40.0 +codecov==2.1.13 +twine==4.0.2 \ No newline at end of file diff --git a/requirements/test.txt b/requirements/test.txt index 1565277..70cee9f 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -6,3 +6,4 @@ pytest==7.4.0 pytest-aiohttp==1.0.4 pytest-asyncio==0.21.1 pytest-cov==4.1.0 +readme-renderer==40.0 \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index d2863f1..6dbb243 100644 --- a/setup.cfg +++ b/setup.cfg @@ -36,14 +36,14 @@ install_requires = [options.extras_require] test = - pytest==7.1.2 + pytest==7.4.0 pytest-aiohttp==1.0.4 - pytest-cov==3.0.0 - readme-renderer==35.0 + pytest-cov==4.1.0 + readme-renderer==40.0 ci = %(test)s - codecov==2.1.12 - twine==4.0.1 + codecov==2.1.13 + twine==4.0.2 [options.packages.find] exclude = diff --git a/tests/test_oas/test_view.py b/tests/test_oas/test_view.py index 391accd..679c236 100644 --- a/tests/test_oas/test_view.py +++ b/tests/test_oas/test_view.py @@ -16,6 +16,9 @@ from aiohttp_pydantic.oas.view import generate_oas class Color(str, Enum): + """ + Pet color + """ RED = "red" GREEN = "green" PINK = "pink" @@ -142,22 +145,18 @@ async def generated_oas(aiohttp_client, event_loop) -> web.Application: async def test_generated_oas_should_have_components_schemas(generated_oas): assert generated_oas["components"]["schemas"] == { 'Cat': {'properties': {'meows': {'title': 'Meows', 'type': 'integer'}, - 'pet_type': {'enum': ['cat'], - 'title': 'Pet Type', - 'type': 'string'}}, + 'pet_type': {'const': 'cat', 'title': 'Pet Type'}}, 'required': ['pet_type', 'meows'], 'title': 'Cat', 'type': 'object'}, "Color": { - "description": "An enumeration.", + "description": "Pet color", "enum": ["red", "green", "pink"], "title": "Color", "type": "string", }, 'Dog': {'properties': {'barks': {'title': 'Barks', 'type': 'number'}, - 'pet_type': {'enum': ['dog'], - 'title': 'Pet Type', - 'type': 'string'}}, + 'pet_type': {'const': 'dog', 'title': 'Pet Type'}}, 'required': ['pet_type', 'barks'], 'title': 'Dog', 'type': 'object'}, @@ -170,7 +169,6 @@ async def test_generated_oas_should_have_components_schemas(generated_oas): 'type': 'object' }, 'Lang': { - 'description': 'An enumeration.', 'enum': ['en', 'fr'], 'title': 'Lang', 'type': 'string' @@ -187,7 +185,13 @@ async def test_generated_oas_should_have_components_schemas(generated_oas): 'Pet': { 'properties': { 'id': {'title': 'Id', 'type': 'integer'}, - 'name': {'title': 'Name', 'type': 'string'}, + 'name': { + 'anyOf': [ + {'type': 'string'}, + {'type': 'null'} + ], + 'default': None, + 'title': 'Name'}, 'toys': { 'items': {'$ref': '#/components/schemas/Toy'}, 'title': 'Toys', @@ -231,13 +235,24 @@ async def test_pets_route_should_have_get_method(generated_oas): "in": "query", "name": "name", "required": False, - "schema": {"title": "name", "type": "string"}, + "schema": { + 'anyOf': [{'type': 'string'}, {'type': 'null'}], + 'default': None, + 'title': 'name' + }, }, { "in": "header", "name": "promo", "required": False, - "schema": {"format": "uuid", "title": "promo", "type": "string"}, + "schema": { + 'anyOf': [ + {'format': 'uuid', 'type': 'string'}, + {'type': 'null'} + ], + 'default': None, + 'title': 'promo' + }, }, ], "responses": { @@ -265,7 +280,14 @@ async def test_pets_route_should_have_post_method(generated_oas): "schema": { "properties": { "id": {"title": "Id", "type": "integer"}, - "name": {"title": "Name", "type": "string"}, + "name": { + 'anyOf': [ + {'type': 'string'}, + {'type': 'null'} + ], + 'default': None, + 'title': 'Name' + }, "toys": { "items": {"$ref": "#/components/schemas/Toy"}, "title": "Toys", @@ -337,9 +359,9 @@ async def test_pets_id_route_should_have_get_method(generated_oas): "name": "day", "required": False, "schema": { - "anyOf": [{"type": "integer"}, {"enum": ["now"], "type": "string"}], - "default": "now", - "title": "day", + 'anyOf': [{'type': 'integer'}, {'const': 'now'}], + 'default': 'now', + 'title': 'day' }, }, ], @@ -370,7 +392,13 @@ async def test_pets_id_route_should_have_put_method(generated_oas): "schema": { "properties": { "id": {"title": "Id", "type": "integer"}, - "name": {"title": "Name", "type": "string"}, + "name": { + 'anyOf': [ + {'type': 'string'}, + {'type': 'null'} + ], + 'default': None, + 'title': 'Name'}, "toys": { "items": {"$ref": "#/components/schemas/Toy"}, "title": "Toys", diff --git a/tests/test_validation_body.py b/tests/test_validation_body.py index 24dec50..31e6331 100644 --- a/tests/test_validation_body.py +++ b/tests/test_validation_body.py @@ -97,10 +97,10 @@ async def test_post_an_array_json_to_an_object_model_should_return_an_error( assert resp.content_type == "application/json" assert await resp.json() == [ { - "in": "body", - "loc": ["__root__"], - "msg": "value is not a valid dict", - "type": "type_error.dict", + 'loc': ['root'], + 'loc_in': 'body', + 'msg': 'value is not a valid dict', + 'type': 'type_error.dict' } ]