Added a wrapper for get_oas to throw spec info (#12) (#13)

* 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:
spinenkoia 2021-04-04 16:22:05 +05:00 committed by GitHub
parent 7492af5acf
commit beb638c0af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 89 additions and 18 deletions

View File

@ -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")

View File

@ -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:

View File

@ -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):

View File

@ -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)

View File

@ -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",

View File

@ -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",
} }

View File

@ -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": {

View File

@ -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",

View File

@ -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'}