* Added a wrapper for get_oas to throw spec info (#12) * Added tests generate_oas * Moved params to Application Co-authored-by: Спиненко Иван ispinenko@ussc.ru <ispinenko@ussc.ru>
This commit is contained in:
		| @@ -1,5 +1,5 @@ | |||||||
| from importlib import resources | from importlib import resources | ||||||
| from typing import Iterable | from typing import Iterable, Optional | ||||||
|  |  | ||||||
| import jinja2 | import jinja2 | ||||||
| from aiohttp import web | from aiohttp import web | ||||||
| @@ -13,6 +13,8 @@ def setup( | |||||||
|     apps_to_expose: Iterable[web.Application] = (), |     apps_to_expose: Iterable[web.Application] = (), | ||||||
|     url_prefix: str = "/oas", |     url_prefix: str = "/oas", | ||||||
|     enable: bool = True, |     enable: bool = True, | ||||||
|  |     version_spec: Optional[str] = None, | ||||||
|  |     title_spec: Optional[str] = None, | ||||||
| ): | ): | ||||||
|     if enable: |     if enable: | ||||||
|         oas_app = web.Application() |         oas_app = web.Application() | ||||||
| @@ -20,6 +22,9 @@ def setup( | |||||||
|         oas_app["index template"] = jinja2.Template( |         oas_app["index template"] = jinja2.Template( | ||||||
|             resources.read_text("aiohttp_pydantic.oas", "index.j2") |             resources.read_text("aiohttp_pydantic.oas", "index.j2") | ||||||
|         ) |         ) | ||||||
|  |         oas_app["version_spec"] = version_spec | ||||||
|  |         oas_app["title_spec"] = title_spec | ||||||
|  |  | ||||||
|         oas_app.router.add_get("/spec", get_oas, name="spec") |         oas_app.router.add_get("/spec", get_oas, name="spec") | ||||||
|         oas_app.router.add_static("/static", swagger_ui_path, name="static") |         oas_app.router.add_static("/static", swagger_ui_path, name="static") | ||||||
|         oas_app.router.add_get("", oas_ui, name="index") |         oas_app.router.add_get("", oas_ui, name="index") | ||||||
|   | |||||||
| @@ -305,7 +305,7 @@ class Components: | |||||||
|  |  | ||||||
| class OpenApiSpec3: | class OpenApiSpec3: | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         self._spec = {"openapi": "3.0.0"} |         self._spec = {"openapi": "3.0.0", "info": {"version": "1.0.0", "title": "Aiohttp pydantic application"}} | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def info(self) -> Info: |     def info(self) -> Info: | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| import typing | import typing | ||||||
| from inspect import getdoc | from inspect import getdoc | ||||||
| from itertools import count | from itertools import count | ||||||
| from typing import List, Type | from typing import List, Type, Optional, Dict | ||||||
|  |  | ||||||
| from aiohttp.web import Response, json_response | from aiohttp.web import Response, json_response | ||||||
| from aiohttp.web_app import Application | from aiohttp.web_app import Application | ||||||
| @@ -147,11 +147,18 @@ def _add_http_method_to_oas( | |||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  |  | ||||||
| def generate_oas(apps: List[Application]) -> dict: | def generate_oas(apps: List[Application], version_spec: Optional[str] = None, title_spec: Optional[str] = None) -> dict: | ||||||
|     """ |     """ | ||||||
|     Generate and return Open Api Specification from PydanticView in application. |     Generate and return Open Api Specification from PydanticView in application. | ||||||
|     """ |     """ | ||||||
|     oas = OpenApiSpec3() |     oas = OpenApiSpec3() | ||||||
|  |  | ||||||
|  |     if version_spec is not None: | ||||||
|  |         oas.info.version = version_spec | ||||||
|  |  | ||||||
|  |     if title_spec is not None: | ||||||
|  |         oas.info.title = title_spec | ||||||
|  |  | ||||||
|     for app in apps: |     for app in apps: | ||||||
|         for resources in app.router.resources(): |         for resources in app.router.resources(): | ||||||
|             for resource_route in resources: |             for resource_route in resources: | ||||||
| @@ -175,7 +182,9 @@ async def get_oas(request): | |||||||
|     View to generate the Open Api Specification from PydanticView in application. |     View to generate the Open Api Specification from PydanticView in application. | ||||||
|     """ |     """ | ||||||
|     apps = request.app["apps to expose"] |     apps = request.app["apps to expose"] | ||||||
|     return json_response(generate_oas(apps)) |     version_spec = request.app["version_spec"] | ||||||
|  |     title_spec = request.app["title_spec"] | ||||||
|  |     return json_response(generate_oas(apps, version_spec, title_spec)) | ||||||
|  |  | ||||||
|  |  | ||||||
| async def oas_ui(request): | async def oas_ui(request): | ||||||
|   | |||||||
| @@ -15,7 +15,7 @@ async def pet_not_found_to_404(request, handler): | |||||||
|  |  | ||||||
|  |  | ||||||
| app = Application(middlewares=[pet_not_found_to_404]) | app = Application(middlewares=[pet_not_found_to_404]) | ||||||
| oas.setup(app) | oas.setup(app, version_spec="1.0.1", title_spec="My App") | ||||||
|  |  | ||||||
| app["model"] = Model() | app["model"] = Model() | ||||||
| app.router.add_view("/pets", PetCollectionView) | app.router.add_view("/pets", PetCollectionView) | ||||||
|   | |||||||
| @@ -22,8 +22,12 @@ def test_show_oas_of_app(cmd_line): | |||||||
|     args.func(args) |     args.func(args) | ||||||
|  |  | ||||||
|     expected = dedent( |     expected = dedent( | ||||||
|         """ |     """ | ||||||
|         { |     { | ||||||
|  |         "info": { | ||||||
|  |             "title": "Aiohttp pydantic application", | ||||||
|  |             "version": "1.0.0" | ||||||
|  |         }, | ||||||
|         "openapi": "3.0.0", |         "openapi": "3.0.0", | ||||||
|         "paths": { |         "paths": { | ||||||
|             "/route-1/{a}": { |             "/route-1/{a}": { | ||||||
| @@ -69,8 +73,12 @@ def test_show_oas_of_sub_app(cmd_line): | |||||||
|     args.output = StringIO() |     args.output = StringIO() | ||||||
|     args.func(args) |     args.func(args) | ||||||
|     expected = dedent( |     expected = dedent( | ||||||
|         """ |     """ | ||||||
|         { |     { | ||||||
|  |         "info": { | ||||||
|  |             "title": "Aiohttp pydantic application", | ||||||
|  |             "version": "1.0.0" | ||||||
|  |         }, | ||||||
|         "openapi": "3.0.0", |         "openapi": "3.0.0", | ||||||
|         "paths": { |         "paths": { | ||||||
|             "/sub-app/route-2/{b}": { |             "/sub-app/route-2/{b}": { | ||||||
| @@ -110,7 +118,7 @@ def test_show_oas_of_a_callable(cmd_line): | |||||||
|         """ |         """ | ||||||
|         { |         { | ||||||
|         "info": { |         "info": { | ||||||
|             "title": "MyApp", |             "title": "Aiohttp pydantic application", | ||||||
|             "version": "1.0.0" |             "version": "1.0.0" | ||||||
|         }, |         }, | ||||||
|         "openapi": "3.0.0", |         "openapi": "3.0.0", | ||||||
|   | |||||||
| @@ -5,10 +5,16 @@ from aiohttp_pydantic.oas.struct import OpenApiSpec3 | |||||||
|  |  | ||||||
| def test_info_title(): | def test_info_title(): | ||||||
|     oas = OpenApiSpec3() |     oas = OpenApiSpec3() | ||||||
|     assert oas.info.title is None |     assert oas.info.title == "Aiohttp pydantic application" | ||||||
|     oas.info.title = "Info Title" |     oas.info.title = "Info Title" | ||||||
|     assert oas.info.title == "Info Title" |     assert oas.info.title == "Info Title" | ||||||
|     assert oas.spec == {"info": {"title": "Info Title"}, "openapi": "3.0.0"} |     assert oas.spec == { | ||||||
|  |         "info": { | ||||||
|  |             "title": "Info Title", | ||||||
|  |             "version": "1.0.0", | ||||||
|  |         }, | ||||||
|  |         "openapi": "3.0.0", | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_info_description(): | def test_info_description(): | ||||||
| @@ -16,15 +22,22 @@ def test_info_description(): | |||||||
|     assert oas.info.description is None |     assert oas.info.description is None | ||||||
|     oas.info.description = "info description" |     oas.info.description = "info description" | ||||||
|     assert oas.info.description == "info description" |     assert oas.info.description == "info description" | ||||||
|     assert oas.spec == {"info": {"description": "info description"}, "openapi": "3.0.0"} |     assert oas.spec == { | ||||||
|  |         "info": { | ||||||
|  |             "description": "info description", | ||||||
|  |             "title": "Aiohttp pydantic application", | ||||||
|  |             "version": "1.0.0", | ||||||
|  |         }, | ||||||
|  |         "openapi": "3.0.0", | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_info_version(): | def test_info_version(): | ||||||
|     oas = OpenApiSpec3() |     oas = OpenApiSpec3() | ||||||
|     assert oas.info.version is None |     assert oas.info.version == "1.0.0" | ||||||
|     oas.info.version = "3.14" |     oas.info.version = "3.14" | ||||||
|     assert oas.info.version == "3.14" |     assert oas.info.version == "3.14" | ||||||
|     assert oas.spec == {"info": {"version": "3.14"}, "openapi": "3.0.0"} |     assert oas.spec == {"info": {"version": "3.14", "title": "Aiohttp pydantic application"}, "openapi": "3.0.0"} | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_info_terms_of_service(): | def test_info_terms_of_service(): | ||||||
| @@ -33,7 +46,11 @@ def test_info_terms_of_service(): | |||||||
|     oas.info.terms_of_service = "http://example.com/terms/" |     oas.info.terms_of_service = "http://example.com/terms/" | ||||||
|     assert oas.info.terms_of_service == "http://example.com/terms/" |     assert oas.info.terms_of_service == "http://example.com/terms/" | ||||||
|     assert oas.spec == { |     assert oas.spec == { | ||||||
|         "info": {"termsOfService": "http://example.com/terms/"}, |         "info": { | ||||||
|  |             "title": "Aiohttp pydantic application", | ||||||
|  |             "version": "1.0.0", | ||||||
|  |             "termsOfService": "http://example.com/terms/", | ||||||
|  |         }, | ||||||
|         "openapi": "3.0.0", |         "openapi": "3.0.0", | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ def test_paths_description(): | |||||||
|     oas.paths["/users/{id}"].description = "This route ..." |     oas.paths["/users/{id}"].description = "This route ..." | ||||||
|     assert oas.spec == { |     assert oas.spec == { | ||||||
|         "openapi": "3.0.0", |         "openapi": "3.0.0", | ||||||
|  |         "info": {"title": "Aiohttp pydantic application", "version": "1.0.0"}, | ||||||
|         "paths": {"/users/{id}": {"description": "This route ..."}}, |         "paths": {"/users/{id}": {"description": "This route ..."}}, | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -13,7 +14,11 @@ def test_paths_description(): | |||||||
| def test_paths_get(): | def test_paths_get(): | ||||||
|     oas = OpenApiSpec3() |     oas = OpenApiSpec3() | ||||||
|     oas.paths["/users/{id}"].get |     oas.paths["/users/{id}"].get | ||||||
|     assert oas.spec == {"openapi": "3.0.0", "paths": {"/users/{id}": {"get": {}}}} |     assert oas.spec == { | ||||||
|  |         "openapi": "3.0.0", | ||||||
|  |         "info": {"title": "Aiohttp pydantic application", "version": "1.0.0"}, | ||||||
|  |         "paths": {"/users/{id}": {"get": {}}}, | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_paths_operation_description(): | def test_paths_operation_description(): | ||||||
| @@ -22,6 +27,7 @@ def test_paths_operation_description(): | |||||||
|     operation.description = "Long descriptions ..." |     operation.description = "Long descriptions ..." | ||||||
|     assert oas.spec == { |     assert oas.spec == { | ||||||
|         "openapi": "3.0.0", |         "openapi": "3.0.0", | ||||||
|  |         "info": {"title": "Aiohttp pydantic application", "version": "1.0.0"}, | ||||||
|         "paths": {"/users/{id}": {"get": {"description": "Long descriptions ..."}}}, |         "paths": {"/users/{id}": {"get": {"description": "Long descriptions ..."}}}, | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -32,6 +38,7 @@ def test_paths_operation_summary(): | |||||||
|     operation.summary = "Updates a pet in the store with form data" |     operation.summary = "Updates a pet in the store with form data" | ||||||
|     assert oas.spec == { |     assert oas.spec == { | ||||||
|         "openapi": "3.0.0", |         "openapi": "3.0.0", | ||||||
|  |         "info": {"title": "Aiohttp pydantic application", "version": "1.0.0"}, | ||||||
|         "paths": { |         "paths": { | ||||||
|             "/users/{id}": { |             "/users/{id}": { | ||||||
|                 "get": {"summary": "Updates a pet in the store with form data"} |                 "get": {"summary": "Updates a pet in the store with form data"} | ||||||
| @@ -51,6 +58,7 @@ def test_paths_operation_parameters(): | |||||||
|  |  | ||||||
|     assert oas.spec == { |     assert oas.spec == { | ||||||
|         "openapi": "3.0.0", |         "openapi": "3.0.0", | ||||||
|  |         "info": {"title": "Aiohttp pydantic application", "version": "1.0.0"}, | ||||||
|         "paths": { |         "paths": { | ||||||
|             "/users/{petId}": { |             "/users/{petId}": { | ||||||
|                 "get": { |                 "get": { | ||||||
| @@ -86,6 +94,7 @@ def test_paths_operation_requestBody(): | |||||||
|     request_body.required = True |     request_body.required = True | ||||||
|     assert oas.spec == { |     assert oas.spec == { | ||||||
|         "openapi": "3.0.0", |         "openapi": "3.0.0", | ||||||
|  |         "info": {"title": "Aiohttp pydantic application", "version": "1.0.0"}, | ||||||
|         "paths": { |         "paths": { | ||||||
|             "/users/{petId}": { |             "/users/{petId}": { | ||||||
|                 "get": { |                 "get": { | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ def test_sever_url(): | |||||||
|     oas.servers[1].url = "https://development.gigantic-server.com/v2" |     oas.servers[1].url = "https://development.gigantic-server.com/v2" | ||||||
|     assert oas.spec == { |     assert oas.spec == { | ||||||
|         "openapi": "3.0.0", |         "openapi": "3.0.0", | ||||||
|  |         "info": {"title": "Aiohttp pydantic application", "version": "1.0.0"}, | ||||||
|         "servers": [ |         "servers": [ | ||||||
|             {"url": "https://development.gigantic-server.com/v1"}, |             {"url": "https://development.gigantic-server.com/v1"}, | ||||||
|             {"url": "https://development.gigantic-server.com/v2"}, |             {"url": "https://development.gigantic-server.com/v2"}, | ||||||
| @@ -22,6 +23,7 @@ def test_sever_description(): | |||||||
|     oas.servers[0].description = "Development server" |     oas.servers[0].description = "Development server" | ||||||
|     assert oas.spec == { |     assert oas.spec == { | ||||||
|         "openapi": "3.0.0", |         "openapi": "3.0.0", | ||||||
|  |         "info": {"title": "Aiohttp pydantic application", "version": "1.0.0"}, | ||||||
|         "servers": [ |         "servers": [ | ||||||
|             { |             { | ||||||
|                 "url": "https://development.gigantic-server.com/v1", |                 "url": "https://development.gigantic-server.com/v1", | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ from pydantic.main import BaseModel | |||||||
|  |  | ||||||
| from aiohttp_pydantic import PydanticView, oas | from aiohttp_pydantic import PydanticView, oas | ||||||
| from aiohttp_pydantic.oas.typing import r200, r201, r204, r404 | from aiohttp_pydantic.oas.typing import r200, r201, r204, r404 | ||||||
|  | from aiohttp_pydantic.oas.view import generate_oas | ||||||
|  |  | ||||||
|  |  | ||||||
| class Color(str, Enum): | class Color(str, Enum): | ||||||
| @@ -316,3 +317,23 @@ async def test_simple_type_route_should_have_get_method(generated_oas): | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | async def test_generated_view_info_default(): | ||||||
|  |     apps = (web.Application(),) | ||||||
|  |     spec = generate_oas(apps) | ||||||
|  |  | ||||||
|  |     assert spec == {'info': {'title': 'Aiohttp pydantic application', 'version': '1.0.0'}, 'openapi': '3.0.0'} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def test_generated_view_info_as_version(): | ||||||
|  |     apps = (web.Application(),) | ||||||
|  |     spec = generate_oas(apps, version_spec="test version") | ||||||
|  |  | ||||||
|  |     assert spec == {'info': {'title': 'Aiohttp pydantic application', 'version': 'test version'}, 'openapi': '3.0.0'} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def test_generated_view_info_as_title(): | ||||||
|  |     apps = (web.Application(),) | ||||||
|  |     spec = generate_oas(apps, title_spec="test title") | ||||||
|  |  | ||||||
|  |     assert spec == {'info': {'title': 'test title', 'version': '1.0.0'}, 'openapi': '3.0.0'} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user