mirror of
https://github.com/correl/tornado-openapi3.git
synced 2024-11-21 19:18:40 +00:00
Document usage with examples
This commit is contained in:
parent
d1cd04f3e3
commit
a04edb6ff9
9 changed files with 214 additions and 10 deletions
39
docs/examples/cached.py
Normal file
39
docs/examples/cached.py
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import logging
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
import tornado.ioloop
|
||||||
|
import tornado.web
|
||||||
|
from tornado_openapi3.handler import OpenAPIRequestHandler
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
VERSION = "1.0.0"
|
||||||
|
|
||||||
|
|
||||||
|
class MyRequestHandler(OpenAPIRequestHandler):
|
||||||
|
@property
|
||||||
|
def spec_dict(self):
|
||||||
|
return yaml.safe_load(self.render_string("openapi.yaml", version=VERSION))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def spec(self):
|
||||||
|
spec = getattr(self.application, "openapi_spec", None)
|
||||||
|
if not spec:
|
||||||
|
logging.info("Compiling OpenAPI spec")
|
||||||
|
spec = super().spec
|
||||||
|
setattr(self.application, "openapi_spec", spec)
|
||||||
|
return spec
|
||||||
|
|
||||||
|
|
||||||
|
class RootHandler(MyRequestHandler):
|
||||||
|
async def get(self):
|
||||||
|
self.finish("Hello, World!")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
example_root = pathlib.Path(__file__).parent
|
||||||
|
app = tornado.web.Application(
|
||||||
|
[(r"/", RootHandler)], template_path=str(example_root / "templates")
|
||||||
|
)
|
||||||
|
app.listen(8888)
|
||||||
|
tornado.ioloop.IOLoop.current().start()
|
40
docs/examples/simple.py
Normal file
40
docs/examples/simple.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import tornado.ioloop
|
||||||
|
import tornado.web
|
||||||
|
from tornado_openapi3.handler import OpenAPIRequestHandler
|
||||||
|
|
||||||
|
|
||||||
|
class MyRequestHandler(OpenAPIRequestHandler):
|
||||||
|
spec_dict = {
|
||||||
|
"openapi": "3.0.0",
|
||||||
|
"info": {
|
||||||
|
"title": "Simple Example",
|
||||||
|
"version": "1.0.0",
|
||||||
|
},
|
||||||
|
"paths": {
|
||||||
|
"/": {
|
||||||
|
"get": {
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Index",
|
||||||
|
"content": {
|
||||||
|
"text/html": {
|
||||||
|
"schema": {"type": "string"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class RootHandler(MyRequestHandler):
|
||||||
|
async def get(self):
|
||||||
|
self.finish("Hello, World!")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app = tornado.web.Application([(r"/", RootHandler)])
|
||||||
|
app.listen(8888)
|
||||||
|
tornado.ioloop.IOLoop.current().start()
|
15
docs/examples/templates/openapi.yaml
Normal file
15
docs/examples/templates/openapi.yaml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
openapi: 3.0.0
|
||||||
|
info:
|
||||||
|
title: Simple Example
|
||||||
|
version: "{{ version }}"
|
||||||
|
paths:
|
||||||
|
"/":
|
||||||
|
get:
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Index
|
||||||
|
content:
|
||||||
|
text/html:
|
||||||
|
schema:
|
||||||
|
type: string
|
47
docs/examples/test.py
Normal file
47
docs/examples/test.py
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import tornado.web
|
||||||
|
from tornado_openapi3.testing import AsyncOpenAPITestCase
|
||||||
|
|
||||||
|
|
||||||
|
class RootHandler(tornado.web.RequestHandler):
|
||||||
|
async def get(self):
|
||||||
|
self.finish("Hello, World!")
|
||||||
|
|
||||||
|
|
||||||
|
class BaseTestCase(AsyncOpenAPITestCase):
|
||||||
|
spec_dict = {
|
||||||
|
"openapi": "3.0.0",
|
||||||
|
"info": {
|
||||||
|
"title": "Simple Example",
|
||||||
|
"version": "1.0.0",
|
||||||
|
},
|
||||||
|
"paths": {
|
||||||
|
"/": {
|
||||||
|
"get": {
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Index",
|
||||||
|
"content": {
|
||||||
|
"text/html": {
|
||||||
|
"schema": {"type": "string"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_app(self):
|
||||||
|
return tornado.web.Application([(r"/", RootHandler)])
|
||||||
|
|
||||||
|
def test_root_endpoint(self):
|
||||||
|
response = self.fetch("/")
|
||||||
|
self.assertEqual(200, response.code)
|
||||||
|
self.assertEqual(b"Hello, World!", response.body)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
|
@ -1,9 +1,42 @@
|
||||||
Handling Incoming Requests
|
Handling Incoming Requests
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
|
Tornado OpenAPI 3 allows you to validate requests coming in to your application
|
||||||
|
against your OpenAPI 3.0 specification with little additional code.
|
||||||
|
|
||||||
|
Defining a base handler with your OpenAPI 3.0 specification
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
By extending :class:`~tornado_openapi3.handler.OpenAPIRequestHandler`, you can
|
||||||
|
define your own base request handler with your specification attached, and use
|
||||||
|
that for each of your specialized request handlers for your application.
|
||||||
|
|
||||||
|
.. literalinclude:: examples/simple.py
|
||||||
|
|
||||||
|
A more complex example
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Your specification doesn't need to be embedded in your code. You may wish to
|
||||||
|
store it separately in your repository, or even templatize some aspects of it
|
||||||
|
(like your application version). Doing so is as simple as overriding your
|
||||||
|
request handler's
|
||||||
|
:attr:`~tornado_openapi3.handler.OpenAPIRequestHandler.spec_dict` property to
|
||||||
|
load your specification however you see fit.
|
||||||
|
|
||||||
|
By default, the specification is compiled on every request. To achieve better
|
||||||
|
performance, you may also wish to override the request handler's
|
||||||
|
:attr:`~tornado_openapi3.handler.OpenAPIRequestHandler.spec_dict` property to
|
||||||
|
cache the result on your application object.
|
||||||
|
|
||||||
|
.. literalinclude:: examples/cached.py
|
||||||
|
|
||||||
Adding custom deserializers
|
Adding custom deserializers
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
|
If your endpoints make use of content types beyond ``application/json``, you
|
||||||
|
must add them to this dictionary with a deserializing method that converts the
|
||||||
|
raw body (as :class:`bytes` or :class:`str`) to Python objects.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
@ -21,6 +54,10 @@ Adding custom deserializers
|
||||||
Adding custom formatters
|
Adding custom formatters
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
|
If your schemas make use of format modifiers, you may specify them in this
|
||||||
|
dictionary paired with a :class:`~tornado_openapi3.types.Formatter` object that
|
||||||
|
provides methods to validate values and unmarshal them into Python objects.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
|
@ -21,20 +21,20 @@ library for validating request and response objects against an `OpenAPI 3`_
|
||||||
specification.
|
specification.
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 1
|
||||||
:caption: Getting Started
|
:caption: Getting Started
|
||||||
|
|
||||||
installation
|
installation
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 1
|
||||||
:caption: Usage
|
:caption: Usage
|
||||||
|
|
||||||
handling_incoming_requests
|
handling_incoming_requests
|
||||||
testing_api_responses
|
testing_api_responses
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 1
|
||||||
:caption: Modules
|
:caption: Modules
|
||||||
|
|
||||||
handler
|
handler
|
||||||
|
|
|
@ -1,9 +1,31 @@
|
||||||
Testing API Responses
|
Testing API Responses
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
|
Tornado OpenAPI 3 includes a base test class to help you validate each of your
|
||||||
|
application's responses while you test its behavior.
|
||||||
|
|
||||||
|
Making your tests aware of your API specification
|
||||||
|
-------------------------------------------------
|
||||||
|
|
||||||
|
By extending :class:`~tornado_openapi3.testing.AsyncOpenAPITestCase`, you can
|
||||||
|
define your test cases with your specification attached. Every response returned
|
||||||
|
by :meth:`~tornado_openapi3.testing.AsyncOpenAPITestCase.fetch` will be
|
||||||
|
automatically checked against your specification to ensure they match the
|
||||||
|
formats documented, and exceptions will be raised when they do not.
|
||||||
|
|
||||||
|
Because it extends :class:`tornado.testing.AsyncHTTPTestCase`, you can write
|
||||||
|
your application tests as you normally would with added confidence that your API
|
||||||
|
is behaving exactly as you expect it to.
|
||||||
|
|
||||||
|
.. literalinclude:: examples/test.py
|
||||||
|
|
||||||
Adding custom deserializers
|
Adding custom deserializers
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
|
If your endpoints make use of content types beyond ``application/json``, you
|
||||||
|
must add them to this dictionary with a deserializing method that converts the
|
||||||
|
raw body (as :class:`bytes` or :class:`str`) to Python objects.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
@ -21,6 +43,10 @@ Adding custom deserializers
|
||||||
Adding custom formatters
|
Adding custom formatters
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
|
If your schemas make use of format modifiers, you may specify them in this
|
||||||
|
dictionary paired with a :class:`~tornado_openapi3.types.Formatter` object that
|
||||||
|
provides methods to validate values and unmarshal them into Python objects.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
|
@ -33,7 +33,7 @@ logger = logging.getLogger(__name__)
|
||||||
class OpenAPIRequestHandler(tornado.web.RequestHandler):
|
class OpenAPIRequestHandler(tornado.web.RequestHandler):
|
||||||
"""Base class for HTTP request handlers.
|
"""Base class for HTTP request handlers.
|
||||||
|
|
||||||
A request handler extending :py:class:`tornado.web.RequestHandler` providing
|
A request handler extending :class:`tornado.web.RequestHandler` providing
|
||||||
OpenAPI spec validation on incoming requests and translating errors into
|
OpenAPI spec validation on incoming requests and translating errors into
|
||||||
appropriate HTTP responses.
|
appropriate HTTP responses.
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ class OpenAPIRequestHandler(tornado.web.RequestHandler):
|
||||||
Override this in your request handlers to customize how your OpenAPI 3
|
Override this in your request handlers to customize how your OpenAPI 3
|
||||||
spec is loaded and validated.
|
spec is loaded and validated.
|
||||||
|
|
||||||
:rtype: :py:class:`openapi_core.schema.specs.model.Spec`
|
:rtype: :class:`openapi_core.schema.specs.model.Spec`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return create_spec(self.spec_dict, validate_spec=False)
|
return create_spec(self.spec_dict, validate_spec=False)
|
||||||
|
@ -71,7 +71,7 @@ class OpenAPIRequestHandler(tornado.web.RequestHandler):
|
||||||
this dictionary paired with a Formatter object that provides methods to
|
this dictionary paired with a Formatter object that provides methods to
|
||||||
validate values and unmarshal them into Python objects.
|
validate values and unmarshal them into Python objects.
|
||||||
|
|
||||||
:rtype: Mapping[str, :py:class:`tornado_openapi3.types.Formatter`]
|
:rtype: Mapping[str, :class:`~tornado_openapi3.types.Formatter`]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ class OpenAPIRequestHandler(tornado.web.RequestHandler):
|
||||||
you must add them to this dictionary with a deserializing method that
|
you must add them to this dictionary with a deserializing method that
|
||||||
converts the raw body (as ``bytes`` or ``str``) to Python objects.
|
converts the raw body (as ``bytes`` or ``str``) to Python objects.
|
||||||
|
|
||||||
:rtype: Mapping[str, :py:attr:`tornado_openapi3.types.Deserializer`]
|
:rtype: Mapping[str, :attr:`~tornado_openapi3.types.Deserializer`]
|
||||||
"""
|
"""
|
||||||
return dict()
|
return dict()
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ 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.
|
"""A test case that starts up an HTTP server.
|
||||||
|
|
||||||
An async test case extending :py:class:`tornado.testing.AsyncHTTPTestCase`,
|
An async test case extending :class:`tornado.testing.AsyncHTTPTestCase`,
|
||||||
providing OpenAPI spec validation on the responses from your application and
|
providing OpenAPI spec validation on the responses from your application and
|
||||||
raising errors in tests.
|
raising errors in tests.
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ class AsyncOpenAPITestCase(tornado.testing.AsyncHTTPTestCase):
|
||||||
Override this in your test cases to customize how your OpenAPI 3 spec is
|
Override this in your test cases to customize how your OpenAPI 3 spec is
|
||||||
loaded and validated.
|
loaded and validated.
|
||||||
|
|
||||||
:rtype: :py:class:`openapi_core.schema.specs.model.Spec`
|
:rtype: :class:`openapi_core.schema.specs.model.Spec`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return create_spec(self.spec_dict)
|
return create_spec(self.spec_dict)
|
||||||
|
@ -65,7 +65,7 @@ class AsyncOpenAPITestCase(tornado.testing.AsyncHTTPTestCase):
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
"""Hook method for setting up the test fixture before exercising it.
|
"""Hook method for setting up the test fixture before exercising it.
|
||||||
|
|
||||||
Instantiates the :class:`tornado_openapi3.responses.ResponseValidator`
|
Instantiates the :class:`~tornado_openapi3.responses.ResponseValidator`
|
||||||
for this test case.
|
for this test case.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in a new issue