mirror of
https://github.com/correl/tornado-openapi3.git
synced 2024-12-28 19:19:22 +00:00
Use spec objects rather than dictionaries
This commit is contained in:
parent
7543654e7c
commit
3a622e0306
4 changed files with 140 additions and 70 deletions
|
@ -1,6 +1,7 @@
|
||||||
import json
|
import json
|
||||||
import unittest.mock
|
import unittest.mock
|
||||||
|
|
||||||
|
from openapi_core import create_spec # type: ignore
|
||||||
from openapi_core.exceptions import OpenAPIError # type: ignore
|
from openapi_core.exceptions import OpenAPIError # type: ignore
|
||||||
import tornado.httpclient # type: ignore
|
import tornado.httpclient # type: ignore
|
||||||
import tornado.web # type: ignore
|
import tornado.web # type: ignore
|
||||||
|
@ -10,56 +11,60 @@ from tornado_openapi3.handler import OpenAPIRequestHandler
|
||||||
|
|
||||||
|
|
||||||
class ResourceHandler(OpenAPIRequestHandler):
|
class ResourceHandler(OpenAPIRequestHandler):
|
||||||
spec = {
|
spec = create_spec(
|
||||||
"openapi": "3.0.0",
|
{
|
||||||
"info": {
|
"openapi": "3.0.0",
|
||||||
"title": "Test API",
|
"info": {
|
||||||
"version": "1.0.0",
|
"title": "Test API",
|
||||||
},
|
"version": "1.0.0",
|
||||||
"components": {
|
},
|
||||||
"schemas": {
|
"components": {
|
||||||
"resource": {
|
"schemas": {
|
||||||
"type": "object",
|
"resource": {
|
||||||
"properties": {"name": {"type": "string"}},
|
"type": "object",
|
||||||
"required": ["name"],
|
"properties": {"name": {"type": "string"}},
|
||||||
|
"required": ["name"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"securitySchemes": {
|
||||||
|
"basicAuth": {
|
||||||
|
"type": "http",
|
||||||
|
"scheme": "bearer",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"securitySchemes": {
|
"security": [{"basicAuth": []}],
|
||||||
"basicAuth": {
|
"paths": {
|
||||||
"type": "http",
|
"/resource": {
|
||||||
"scheme": "bearer",
|
"post": {
|
||||||
}
|
"requestBody": {
|
||||||
},
|
"required": True,
|
||||||
},
|
|
||||||
"security": [{"basicAuth": []}],
|
|
||||||
"paths": {
|
|
||||||
"/resource": {
|
|
||||||
"post": {
|
|
||||||
"requestBody": {
|
|
||||||
"required": True,
|
|
||||||
"content": {
|
|
||||||
"application/vnd.example.resource+json": {
|
|
||||||
"schema": {"$ref": "#/components/schemas/resource"},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Success",
|
|
||||||
"content": {
|
"content": {
|
||||||
"application/vnd.example.resource+json": {
|
"application/vnd.example.resource+json": {
|
||||||
"schema": {"$ref": "#/components/schemas/resource"},
|
"schema": {"$ref": "#/components/schemas/resource"},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"401": {
|
"responses": {
|
||||||
"description": "Missing or invalid credentials",
|
"200": {
|
||||||
|
"description": "Success",
|
||||||
|
"content": {
|
||||||
|
"application/vnd.example.resource+json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/resource"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Missing or invalid credentials",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
}
|
||||||
}
|
)
|
||||||
custom_media_type_deserializers = {
|
custom_media_type_deserializers = {
|
||||||
"application/vnd.example.resource+json": json.loads,
|
"application/vnd.example.resource+json": json.loads,
|
||||||
}
|
}
|
||||||
|
@ -75,6 +80,51 @@ class ResourceHandler(OpenAPIRequestHandler):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DefaultSchemaTest(tornado.testing.AsyncHTTPTestCase):
|
||||||
|
def get_app(self) -> tornado.web.Application:
|
||||||
|
test = self
|
||||||
|
|
||||||
|
class RequestHandler(OpenAPIRequestHandler):
|
||||||
|
async def prepare(self) -> None:
|
||||||
|
with test.assertRaises(NotImplementedError):
|
||||||
|
self.spec
|
||||||
|
|
||||||
|
async def get(self) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
return tornado.web.Application(
|
||||||
|
[
|
||||||
|
(r"/", RequestHandler),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_schema_must_be_implemented(self) -> None:
|
||||||
|
response = self.fetch("/")
|
||||||
|
self.assertEqual(200, response.code)
|
||||||
|
|
||||||
|
|
||||||
|
class DefaultDeserializers(tornado.testing.AsyncHTTPTestCase):
|
||||||
|
def get_app(self) -> tornado.web.Application:
|
||||||
|
test = self
|
||||||
|
|
||||||
|
class RequestHandler(OpenAPIRequestHandler):
|
||||||
|
async def prepare(self) -> None:
|
||||||
|
test.assertEqual(dict(), self.custom_media_type_deserializers)
|
||||||
|
|
||||||
|
async def get(self) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
return tornado.web.Application(
|
||||||
|
[
|
||||||
|
(r"/", RequestHandler),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_schema_must_be_implemented(self) -> None:
|
||||||
|
response = self.fetch("/")
|
||||||
|
self.assertEqual(200, response.code)
|
||||||
|
|
||||||
|
|
||||||
class RequestHandlerTests(tornado.testing.AsyncHTTPTestCase):
|
class RequestHandlerTests(tornado.testing.AsyncHTTPTestCase):
|
||||||
def get_app(self) -> tornado.web.Application:
|
def get_app(self) -> tornado.web.Application:
|
||||||
return tornado.web.Application(
|
return tornado.web.Application(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from openapi_core import create_spec # type: ignore
|
||||||
from openapi_core.schema.responses.exceptions import InvalidResponse # type: ignore
|
from openapi_core.schema.responses.exceptions import InvalidResponse # type: ignore
|
||||||
import tornado.web # type: ignore
|
import tornado.web # type: ignore
|
||||||
|
|
||||||
|
@ -8,29 +9,48 @@ from tornado_openapi3.testing import AsyncOpenAPITestCase
|
||||||
|
|
||||||
|
|
||||||
def spec(responses: dict = dict()) -> dict:
|
def spec(responses: dict = dict()) -> dict:
|
||||||
return {
|
if not responses:
|
||||||
"openapi": "3.0.0",
|
responses = {"200": {"description": "Success"}}
|
||||||
"info": {
|
return create_spec(
|
||||||
"title": "Test API",
|
{
|
||||||
"version": "1.0.0",
|
"openapi": "3.0.0",
|
||||||
},
|
"info": {
|
||||||
"components": {
|
"title": "Test API",
|
||||||
"schemas": {
|
"version": "1.0.0",
|
||||||
"resource": {
|
},
|
||||||
"type": "object",
|
"components": {
|
||||||
"properties": {"name": {"type": "string"}},
|
"schemas": {
|
||||||
"required": ["name"],
|
"resource": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {"name": {"type": "string"}},
|
||||||
|
"required": ["name"],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
"paths": {
|
||||||
"paths": {
|
"/resource": {
|
||||||
"/resource": {
|
"get": {
|
||||||
"get": {
|
"responses": responses,
|
||||||
"responses": responses,
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
}
|
||||||
}
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestTestCase(AsyncOpenAPITestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
def tearDown(self) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
def test_schema_must_be_implemented(self) -> None:
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
self.spec
|
||||||
|
|
||||||
|
def test_no_custom_media_type_deserializers(self) -> None:
|
||||||
|
self.assertEqual(dict(), self.custom_media_type_deserializers)
|
||||||
|
|
||||||
|
|
||||||
class BaseTestCase(AsyncOpenAPITestCase):
|
class BaseTestCase(AsyncOpenAPITestCase):
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from openapi_core import create_spec # type: ignore
|
|
||||||
from openapi_core.exceptions import OpenAPIError # type: ignore
|
from openapi_core.exceptions import OpenAPIError # type: ignore
|
||||||
from openapi_core.deserializing.exceptions import DeserializeError # type: ignore
|
from openapi_core.deserializing.exceptions import DeserializeError # type: ignore
|
||||||
|
from openapi_core.schema.specs.models import Spec # type: ignore
|
||||||
from openapi_core.schema.media_types.exceptions import ( # type: ignore
|
from openapi_core.schema.media_types.exceptions import ( # type: ignore
|
||||||
InvalidContentType,
|
InvalidContentType,
|
||||||
)
|
)
|
||||||
|
@ -22,14 +22,14 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class OpenAPIRequestHandler(tornado.web.RequestHandler):
|
class OpenAPIRequestHandler(tornado.web.RequestHandler):
|
||||||
@property
|
@property
|
||||||
def spec(self) -> dict:
|
def spec(self) -> Spec:
|
||||||
"""The OpenAPI 3 specification as a Python dictionary.
|
"""The OpenAPI 3 specification.
|
||||||
|
|
||||||
Override this in your request handlers to load or define your OpenAPI 3
|
Override this in your request handlers to load or define your OpenAPI 3
|
||||||
spec.
|
spec.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return dict()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def custom_media_type_deserializers(self) -> dict:
|
def custom_media_type_deserializers(self) -> dict:
|
||||||
|
@ -82,7 +82,7 @@ class OpenAPIRequestHandler(tornado.web.RequestHandler):
|
||||||
await maybe_coro
|
await maybe_coro
|
||||||
|
|
||||||
validator = RequestValidator(
|
validator = RequestValidator(
|
||||||
create_spec(self.spec),
|
self.spec,
|
||||||
custom_media_type_deserializers=self.custom_media_type_deserializers,
|
custom_media_type_deserializers=self.custom_media_type_deserializers,
|
||||||
)
|
)
|
||||||
result = validator.validate(self.request)
|
result = validator.validate(self.request)
|
||||||
|
|
|
@ -3,20 +3,20 @@ from typing import Any
|
||||||
import tornado.httpclient # type: ignore
|
import tornado.httpclient # type: ignore
|
||||||
import tornado.testing # type: ignore
|
import tornado.testing # type: ignore
|
||||||
|
|
||||||
from openapi_core import create_spec # type: ignore
|
from openapi_core.schema.specs.models import Spec # type: ignore
|
||||||
from tornado_openapi3.responses import ResponseValidator
|
from tornado_openapi3.responses import ResponseValidator
|
||||||
|
|
||||||
|
|
||||||
class AsyncOpenAPITestCase(tornado.testing.AsyncHTTPTestCase):
|
class AsyncOpenAPITestCase(tornado.testing.AsyncHTTPTestCase):
|
||||||
@property
|
@property
|
||||||
def spec(self) -> dict:
|
def spec(self) -> Spec:
|
||||||
"""The OpenAPI 3 specification as a Python dictionary.
|
"""The OpenAPI 3 specification.
|
||||||
|
|
||||||
Override this in your request handlers to load or define your OpenAPI 3
|
Override this in your request handlers to load or define your OpenAPI 3
|
||||||
spec.
|
spec.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return dict()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def custom_media_type_deserializers(self) -> dict:
|
def custom_media_type_deserializers(self) -> dict:
|
||||||
|
@ -38,7 +38,7 @@ class AsyncOpenAPITestCase(tornado.testing.AsyncHTTPTestCase):
|
||||||
"""
|
"""
|
||||||
super().setUp()
|
super().setUp()
|
||||||
self.validator = ResponseValidator(
|
self.validator = ResponseValidator(
|
||||||
create_spec(self.spec),
|
self.spec,
|
||||||
custom_media_type_deserializers=self.custom_media_type_deserializers,
|
custom_media_type_deserializers=self.custom_media_type_deserializers,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue