From c8f8293e2ed38760d6cf4472e39e959c14f3f6b0 Mon Sep 17 00:00:00 2001 From: Pavel Kirilin Date: Tue, 5 Sep 2023 19:38:51 +0400 Subject: [PATCH 1/2] Separated serialization and validation schemas. Signed-off-by: Pavel Kirilin --- aiohttp_deps/swagger.py | 12 +++++++++--- tests/test_swagger.py | 42 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/aiohttp_deps/swagger.py b/aiohttp_deps/swagger.py index 3c0389d..042f60d 100644 --- a/aiohttp_deps/swagger.py +++ b/aiohttp_deps/swagger.py @@ -93,7 +93,10 @@ def dummy(_var: annotation.annotation) -> None: # type: ignore """Dummy function to use for type resolution.""" var = get_type_hints(dummy).get("_var") - return pydantic.TypeAdapter(var).json_schema(ref_template=REF_TEMPLATE) + return pydantic.TypeAdapter(var).json_schema( + ref_template=REF_TEMPLATE, + mode="validation", + ) def _add_route_def( # noqa: C901, WPS210, WPS211 @@ -139,7 +142,7 @@ def _insert_in_params(data: Dict[str, Any]) -> None: ): input_schema = pydantic.TypeAdapter( dependency.signature.annotation, - ).json_schema(ref_template=REF_TEMPLATE) + ).json_schema(ref_template=REF_TEMPLATE, mode="validation") openapi_schema["components"]["schemas"].update( input_schema.pop("$defs", {}), ) @@ -365,7 +368,10 @@ def decorator(func: _T) -> _T: if not status_response: status_response["description"] = description status_response["content"] = status_response.get("content", {}) - response_schema = adapter.json_schema(ref_template=REF_TEMPLATE) + response_schema = adapter.json_schema( + ref_template=REF_TEMPLATE, + mode="serialization", + ) openapi_schemas.update(response_schema.pop("$defs", {})) status_response["content"][content_type] = {"schema": response_schema} responses[status] = status_response diff --git a/tests/test_swagger.py b/tests/test_swagger.py index 4da3f68..532a002 100644 --- a/tests/test_swagger.py +++ b/tests/test_swagger.py @@ -3,7 +3,8 @@ import pytest from aiohttp import web -from pydantic import BaseModel +from pydantic import BaseModel, WithJsonSchema +from typing_extensions import Annotated from aiohttp_deps import ( Depends, @@ -707,3 +708,42 @@ async def my_handler(): ]["schema"]["properties"]["data"]["$ref"] first_obj = follow_ref(first_ref, resp_json) assert "name" in first_obj["properties"] + + +@pytest.mark.anyio +async def test_annotated( + my_app: web.Application, + aiohttp_client: ClientGenerator, +) -> None: + OPENAPI_URL = "/my_api_def.json" + my_app.on_startup.append(setup_swagger(schema_url=OPENAPI_URL)) + + validation_type = "int" + serialization_type = "float" + + MyType = Annotated[ + str, + WithJsonSchema({"type": validation_type}, mode="validation"), + WithJsonSchema({"type": serialization_type}, mode="serialization"), + ] + + class TestModel(BaseModel): + mt: MyType + + @openapi_response(200, TestModel) + async def my_handler(param: TestModel = Depends(Json())) -> None: + """Nothing.""" + + my_app.router.add_get("/a", my_handler) + client = await aiohttp_client(my_app) + response = await client.get(OPENAPI_URL) + resp_json = await response.json() + request_schema = resp_json["paths"]["/a"]["get"] + oapi_serialization_type = request_schema["responses"]["200"]["content"][ + "application/json" + ]["schema"]["properties"]["mt"]["type"] + assert oapi_serialization_type == serialization_type + oapi_validation_type = request_schema["requestBody"]["content"]["application/json"][ + "schema" + ]["properties"]["mt"]["type"] + assert oapi_validation_type == validation_type From 5892dd8f6ce0f4652498acb2227e93c4b115c1fe Mon Sep 17 00:00:00 2001 From: Pavel Kirilin Date: Tue, 5 Sep 2023 19:43:23 +0400 Subject: [PATCH 2/2] Version bumped to 1.0.2. Signed-off-by: Pavel Kirilin --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a0e9ce8..165b16e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ name = "aiohttp-deps" description = "Dependency injection for AioHTTP" authors = ["Taskiq team "] maintainers = ["Taskiq team "] -version = "1.0.1" +version = "1.0.2" readme = "README.md" license = "LICENSE" classifiers = [