* 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:
parent
7492af5acf
commit
beb638c0af
@ -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'}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user