mirror of
https://github.com/correl/tornado-openapi3.git
synced 2024-12-26 19:16:57 +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
|
||||
==========================
|
||||
|
||||
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
|
||||
---------------------------
|
||||
|
||||
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
|
||||
|
||||
import json
|
||||
|
@ -21,6 +54,10 @@ Adding custom deserializers
|
|||
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
|
||||
|
||||
import datetime
|
||||
|
|
|
@ -21,20 +21,20 @@ library for validating request and response objects against an `OpenAPI 3`_
|
|||
specification.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:maxdepth: 1
|
||||
:caption: Getting Started
|
||||
|
||||
installation
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:maxdepth: 1
|
||||
:caption: Usage
|
||||
|
||||
handling_incoming_requests
|
||||
testing_api_responses
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:maxdepth: 1
|
||||
:caption: Modules
|
||||
|
||||
handler
|
||||
|
|
|
@ -1,9 +1,31 @@
|
|||
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
|
||||
---------------------------
|
||||
|
||||
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
|
||||
|
||||
import json
|
||||
|
@ -21,6 +43,10 @@ Adding custom deserializers
|
|||
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
|
||||
|
||||
import datetime
|
||||
|
|
|
@ -33,7 +33,7 @@ logger = logging.getLogger(__name__)
|
|||
class OpenAPIRequestHandler(tornado.web.RequestHandler):
|
||||
"""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
|
||||
appropriate HTTP responses.
|
||||
|
||||
|
@ -58,7 +58,7 @@ class OpenAPIRequestHandler(tornado.web.RequestHandler):
|
|||
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`
|
||||
:rtype: :class:`openapi_core.schema.specs.model.Spec`
|
||||
|
||||
"""
|
||||
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
|
||||
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
|
||||
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()
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ from tornado_openapi3.responses import ResponseValidator
|
|||
class AsyncOpenAPITestCase(tornado.testing.AsyncHTTPTestCase):
|
||||
"""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
|
||||
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
|
||||
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)
|
||||
|
@ -65,7 +65,7 @@ class AsyncOpenAPITestCase(tornado.testing.AsyncHTTPTestCase):
|
|||
def setUp(self) -> None:
|
||||
"""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.
|
||||
|
||||
"""
|
||||
|
|
Loading…
Reference in a new issue