mirror of
https://github.com/correl/tornado-openapi3.git
synced 2024-11-24 03:00:14 +00:00
Allow overriding of spec as a dictionary
This commit is contained in:
parent
3a622e0306
commit
7a8f195712
7 changed files with 120 additions and 85 deletions
|
@ -29,10 +29,15 @@ author = "Correl Roush"
|
||||||
# ones.
|
# ones.
|
||||||
extensions = [
|
extensions = [
|
||||||
"sphinx.ext.autodoc",
|
"sphinx.ext.autodoc",
|
||||||
|
"sphinx.ext.intersphinx",
|
||||||
"sphinx_rtd_theme",
|
"sphinx_rtd_theme",
|
||||||
]
|
]
|
||||||
|
|
||||||
autodoc_member_order = "groupwise"
|
autodoc_member_order = "bysource"
|
||||||
|
intersphinx_mapping = {
|
||||||
|
"python": ("https://docs.python.org/3", None),
|
||||||
|
"tornado": ("https://tornado.readthedocs.org/", None),
|
||||||
|
}
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ["_templates"]
|
templates_path = ["_templates"]
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
Handling Incoming Requests
|
Handling Incoming Requests
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
OpenAPIRequestHandler extends Tornado's RequestHandler class, providing
|
|
||||||
validation of incoming requests and translating errors into appropriate HTTP
|
|
||||||
responses.
|
|
||||||
|
|
||||||
.. automodule:: tornado_openapi3.handler
|
.. automodule:: tornado_openapi3.handler
|
||||||
:members:
|
:members:
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
Testing API Responses
|
Testing API Responses
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
AsyncOpenAPITestCase extends Tornado's AsyncHTTPTestCase class, providing
|
|
||||||
validation of the responses from your application and raising errors in tests.
|
|
||||||
|
|
||||||
.. automodule:: tornado_openapi3.testing
|
.. automodule:: tornado_openapi3.testing
|
||||||
:members:
|
:members:
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
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
|
||||||
|
@ -11,60 +10,56 @@ from tornado_openapi3.handler import OpenAPIRequestHandler
|
||||||
|
|
||||||
|
|
||||||
class ResourceHandler(OpenAPIRequestHandler):
|
class ResourceHandler(OpenAPIRequestHandler):
|
||||||
spec = create_spec(
|
spec_dict = {
|
||||||
{
|
"openapi": "3.0.0",
|
||||||
"openapi": "3.0.0",
|
"info": {
|
||||||
"info": {
|
"title": "Test API",
|
||||||
"title": "Test API",
|
"version": "1.0.0",
|
||||||
"version": "1.0.0",
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"resource": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {"name": {"type": "string"}},
|
||||||
|
"required": ["name"],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"components": {
|
"securitySchemes": {
|
||||||
"schemas": {
|
"basicAuth": {
|
||||||
"resource": {
|
"type": "http",
|
||||||
"type": "object",
|
"scheme": "bearer",
|
||||||
"properties": {"name": {"type": "string"}},
|
}
|
||||||
"required": ["name"],
|
},
|
||||||
|
},
|
||||||
|
"security": [{"basicAuth": []}],
|
||||||
|
"paths": {
|
||||||
|
"/resource": {
|
||||||
|
"post": {
|
||||||
|
"requestBody": {
|
||||||
|
"required": True,
|
||||||
|
"content": {
|
||||||
|
"application/vnd.example.resource+json": {
|
||||||
|
"schema": {"$ref": "#/components/schemas/resource"},
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
"responses": {
|
||||||
"securitySchemes": {
|
"200": {
|
||||||
"basicAuth": {
|
"description": "Success",
|
||||||
"type": "http",
|
|
||||||
"scheme": "bearer",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"security": [{"basicAuth": []}],
|
|
||||||
"paths": {
|
|
||||||
"/resource": {
|
|
||||||
"post": {
|
|
||||||
"requestBody": {
|
|
||||||
"required": True,
|
|
||||||
"content": {
|
"content": {
|
||||||
"application/vnd.example.resource+json": {
|
"application/vnd.example.resource+json": {
|
||||||
"schema": {"$ref": "#/components/schemas/resource"},
|
"schema": {"$ref": "#/components/schemas/resource"},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"responses": {
|
"401": {
|
||||||
"200": {
|
"description": "Missing or invalid credentials",
|
||||||
"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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
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
|
||||||
|
|
||||||
|
@ -11,31 +10,29 @@ from tornado_openapi3.testing import AsyncOpenAPITestCase
|
||||||
def spec(responses: dict = dict()) -> dict:
|
def spec(responses: dict = dict()) -> dict:
|
||||||
if not responses:
|
if not responses:
|
||||||
responses = {"200": {"description": "Success"}}
|
responses = {"200": {"description": "Success"}}
|
||||||
return create_spec(
|
return {
|
||||||
{
|
"openapi": "3.0.0",
|
||||||
"openapi": "3.0.0",
|
"info": {
|
||||||
"info": {
|
"title": "Test API",
|
||||||
"title": "Test API",
|
"version": "1.0.0",
|
||||||
"version": "1.0.0",
|
},
|
||||||
},
|
"components": {
|
||||||
"components": {
|
"schemas": {
|
||||||
"schemas": {
|
"resource": {
|
||||||
"resource": {
|
"type": "object",
|
||||||
"type": "object",
|
"properties": {"name": {"type": "string"}},
|
||||||
"properties": {"name": {"type": "string"}},
|
"required": ["name"],
|
||||||
"required": ["name"],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"paths": {
|
},
|
||||||
"/resource": {
|
"paths": {
|
||||||
"get": {
|
"/resource": {
|
||||||
"responses": responses,
|
"get": {
|
||||||
}
|
"responses": responses,
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
},
|
||||||
)
|
}
|
||||||
|
|
||||||
|
|
||||||
class TestTestCase(AsyncOpenAPITestCase):
|
class TestTestCase(AsyncOpenAPITestCase):
|
||||||
|
@ -54,7 +51,7 @@ class TestTestCase(AsyncOpenAPITestCase):
|
||||||
|
|
||||||
|
|
||||||
class BaseTestCase(AsyncOpenAPITestCase):
|
class BaseTestCase(AsyncOpenAPITestCase):
|
||||||
spec = spec()
|
spec_dict = spec()
|
||||||
custom_media_type_deserializers = {
|
custom_media_type_deserializers = {
|
||||||
"application/vnd.example.resource+json": json.loads,
|
"application/vnd.example.resource+json": json.loads,
|
||||||
}
|
}
|
||||||
|
@ -76,7 +73,7 @@ class BaseTestCase(AsyncOpenAPITestCase):
|
||||||
|
|
||||||
|
|
||||||
class SuccessTests(BaseTestCase):
|
class SuccessTests(BaseTestCase):
|
||||||
spec = spec(
|
spec_dict = spec(
|
||||||
responses={
|
responses={
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Success",
|
"description": "Success",
|
||||||
|
@ -99,7 +96,7 @@ class SuccessTests(BaseTestCase):
|
||||||
|
|
||||||
|
|
||||||
class IncorrectResponseTests(BaseTestCase):
|
class IncorrectResponseTests(BaseTestCase):
|
||||||
spec = spec(responses={"200": {"description": "Success"}})
|
spec_dict = spec(responses={"200": {"description": "Success"}})
|
||||||
|
|
||||||
async def get(self, handler: tornado.web.RequestHandler) -> None:
|
async def get(self, handler: tornado.web.RequestHandler) -> None:
|
||||||
handler.set_status(400)
|
handler.set_status(400)
|
||||||
|
@ -111,7 +108,7 @@ class IncorrectResponseTests(BaseTestCase):
|
||||||
|
|
||||||
|
|
||||||
class RaiseErrorTests(BaseTestCase):
|
class RaiseErrorTests(BaseTestCase):
|
||||||
spec = spec(
|
spec_dict = spec(
|
||||||
responses={
|
responses={
|
||||||
"500": {
|
"500": {
|
||||||
"description": "An error has occurred.",
|
"description": "An error has occurred.",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
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.specs.models import Spec # type: ignore
|
||||||
|
@ -21,16 +22,38 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class OpenAPIRequestHandler(tornado.web.RequestHandler):
|
class OpenAPIRequestHandler(tornado.web.RequestHandler):
|
||||||
|
"""Base class for HTTP request handlers.
|
||||||
|
|
||||||
|
A request handler extending :py:class:`tornado.web.RequestHandler` providing
|
||||||
|
OpenAPI spec validation on incoming requests and translating errors into
|
||||||
|
appropriate HTTP responses.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def spec(self) -> Spec:
|
def spec_dict(self) -> dict:
|
||||||
"""The OpenAPI 3 specification.
|
"""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.
|
||||||
|
|
||||||
|
:rtype: dict
|
||||||
|
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def spec(self) -> Spec:
|
||||||
|
"""The OpenAPI 3 specification.
|
||||||
|
|
||||||
|
Override this in your request handlers to customize how your OpenAPI 3
|
||||||
|
spec is loaded and validated.
|
||||||
|
|
||||||
|
:rtype: :py:class:`openapi_core.schema.specs.model.Spec`
|
||||||
|
|
||||||
|
"""
|
||||||
|
return create_spec(self.spec_dict, validate_spec=False)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def custom_media_type_deserializers(self) -> dict:
|
def custom_media_type_deserializers(self) -> dict:
|
||||||
"""A dictionary mapping media types to deserializing functions.
|
"""A dictionary mapping media types to deserializing functions.
|
||||||
|
|
|
@ -3,20 +3,42 @@ 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 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):
|
||||||
|
"""A test case that starts up an HTTP server.
|
||||||
|
|
||||||
|
An async test case extending :py:class:`tornado.testing.AsyncHTTPTestCase`,
|
||||||
|
providing OpenAPI spec validation on the responses from your application and
|
||||||
|
raising errors in tests.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def spec_dict(self) -> dict:
|
||||||
|
"""The OpenAPI 3 specification
|
||||||
|
|
||||||
|
Override this in your test cases to load or define your OpenAPI 3 spec.
|
||||||
|
|
||||||
|
:rtype: dict
|
||||||
|
|
||||||
|
"""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def spec(self) -> Spec:
|
def spec(self) -> Spec:
|
||||||
"""The OpenAPI 3 specification.
|
"""The OpenAPI 3 specification.
|
||||||
|
|
||||||
Override this in your request handlers to load or define your OpenAPI 3
|
Override this in your test cases to customize how your OpenAPI 3 spec is
|
||||||
spec.
|
loaded and validated.
|
||||||
|
|
||||||
|
:rtype: :py:class:`openapi_core.schema.specs.model.Spec`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
return create_spec(self.spec_dict)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def custom_media_type_deserializers(self) -> dict:
|
def custom_media_type_deserializers(self) -> dict:
|
||||||
|
|
Loading…
Reference in a new issue