2020-09-18 04:22:35 +00:00
|
|
|
===================
|
|
|
|
Tornado OpenAPI 3
|
|
|
|
===================
|
|
|
|
|
2020-11-20 18:02:46 +00:00
|
|
|
.. image:: https://travis-ci.com/correl/tornado-openapi3.svg?branch=master
|
|
|
|
:target: https://travis-ci.com/correl/tornado-openapi3
|
2020-11-26 05:24:21 +00:00
|
|
|
.. image:: https://codecov.io/gh/correl/tornado-openapi3/branch/master/graph/badge.svg?token=CTYWWDXTL9
|
|
|
|
:target: https://codecov.io/gh/correl/tornado-openapi3
|
|
|
|
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
|
|
|
:target: https://github.com/psf/black
|
|
|
|
|
2020-11-20 18:02:46 +00:00
|
|
|
|
2020-09-18 04:22:35 +00:00
|
|
|
Tornado OpenAPI 3 request and response validation library.
|
|
|
|
|
|
|
|
Provides integration between the `Tornado`_ web framework and `Openapi-core`_
|
|
|
|
library for validating request and response objects against an `OpenAPI 3`_
|
|
|
|
specification.
|
|
|
|
|
2020-12-05 03:46:40 +00:00
|
|
|
Usage
|
|
|
|
=====
|
2020-11-20 17:21:29 +00:00
|
|
|
|
|
|
|
Adding validation to request handlers
|
2020-12-05 03:46:40 +00:00
|
|
|
-------------------------------------
|
2020-11-20 17:21:29 +00:00
|
|
|
|
|
|
|
.. code:: python
|
|
|
|
|
|
|
|
from openapi_core import create_spec # type: ignore
|
|
|
|
from openapi_core.exceptions import OpenAPIError # type: ignore
|
|
|
|
from openapi_core.deserializing.exceptions import DeserializeError # type: ignore
|
|
|
|
from openapi_core.schema.media_types.exceptions import ( # type: ignore
|
|
|
|
InvalidContentType,
|
|
|
|
)
|
|
|
|
from openapi_core.unmarshalling.schemas.exceptions import ValidateError # type: ignore
|
|
|
|
from tornado.web import RequestHandler
|
2020-11-26 05:05:02 +00:00
|
|
|
from tornado_openapi3 import RequestValidator
|
2020-11-20 17:21:29 +00:00
|
|
|
import yaml
|
|
|
|
|
|
|
|
|
|
|
|
class OpenAPIRequestHandler(RequestHandler):
|
|
|
|
async def prepare(self) -> None:
|
|
|
|
maybe_coro = super().prepare()
|
|
|
|
if maybe_coro and asyncio.iscoroutine(maybe_coro): # pragma: no cover
|
|
|
|
await maybe_coro
|
|
|
|
|
|
|
|
spec = create_spec(yaml.safe_load(self.render_string("openapi.yaml")))
|
|
|
|
validator = RequestValidator(spec)
|
|
|
|
result = validator.validate(self.request)
|
|
|
|
try:
|
|
|
|
result.raise_for_errors()
|
|
|
|
except InvalidContentType:
|
|
|
|
self.set_status(415)
|
|
|
|
self.finish()
|
|
|
|
except (DeserializeError, ValidateError) as e:
|
|
|
|
self.set_status(400)
|
|
|
|
self.finish()
|
|
|
|
except OpenAPIError:
|
|
|
|
raise
|
2020-11-26 05:05:02 +00:00
|
|
|
|
|
|
|
Validating a response
|
2020-12-05 03:46:40 +00:00
|
|
|
---------------------
|
2020-11-26 05:05:02 +00:00
|
|
|
|
|
|
|
.. code:: python
|
|
|
|
|
|
|
|
from tornado.testing import AsyncHTTPTestCase
|
|
|
|
from tornado_openapi3 import ResponseValidator
|
|
|
|
|
|
|
|
from myapplication import create_app, spec
|
|
|
|
|
|
|
|
|
|
|
|
class TestResponses(AsyncHTTPTestCase):
|
|
|
|
def get_app(self) -> Application:
|
|
|
|
return create_app()
|
|
|
|
|
|
|
|
def test_status(self) -> None:
|
|
|
|
validator = ResponseValidator(spec)
|
|
|
|
response = self.fetch("/status")
|
|
|
|
result = validator.validate(response)
|
|
|
|
result.raise_for_errors()
|
2020-12-05 03:46:40 +00:00
|
|
|
|
|
|
|
Contributing
|
|
|
|
============
|
|
|
|
|
|
|
|
Getting Started
|
|
|
|
---------------
|
|
|
|
|
|
|
|
This project uses `Poetry`_ to manage its dependencies. To set up a local
|
|
|
|
development environment, just run:
|
|
|
|
|
|
|
|
.. code:: sh
|
|
|
|
|
|
|
|
poetry install
|
|
|
|
|
|
|
|
Formatting Code
|
|
|
|
---------------
|
|
|
|
|
|
|
|
The `Black`_ tool is used by this project to format Python code. It is included
|
|
|
|
as a development dependency, and should be run on all committed code. To format
|
|
|
|
code prior to committing it and submitting a PR, run:
|
|
|
|
|
|
|
|
.. code:: sh
|
|
|
|
|
|
|
|
poetry run black .
|
|
|
|
|
|
|
|
Running Tests
|
|
|
|
-------------
|
|
|
|
|
|
|
|
`pytest`_ is the preferred test runner for this project. It is included as a
|
|
|
|
development dependency, and is configured to track code coverage, `Flake8`_
|
|
|
|
style compliance, and `Black`_ code formatting. Tests can be run in your
|
|
|
|
development environment by running:
|
|
|
|
|
|
|
|
.. code:: sh
|
|
|
|
|
|
|
|
poetry run pytest
|
|
|
|
|
|
|
|
Additionally, tests can be run using `tox`_, which will run the tests using
|
|
|
|
multiple versions of both Python and Tornado to ensure broad compatibility.
|
|
|
|
|
|
|
|
Configuring Hypothesis
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
|
|
Many of the tests make use of `Hypothesis`_ to specify their expectations and
|
|
|
|
generate a large volume of randomized test input. Because of this, the tests may
|
2020-12-05 04:34:57 +00:00
|
|
|
take a long time to run on slower computers. Two profiles are defined for
|
2020-12-05 03:46:40 +00:00
|
|
|
Hypothesis to use which can be selected by setting the ``HYPOTHESIS_PROFILE``
|
|
|
|
environment variable to one of the following values:
|
|
|
|
|
2021-01-13 21:55:03 +00:00
|
|
|
``ci``
|
2020-12-05 04:34:57 +00:00
|
|
|
Runs tests using the default Hypothesis settings (100 examples per test) and
|
|
|
|
no completion deadline.
|
2020-12-05 03:46:40 +00:00
|
|
|
|
|
|
|
``dev``
|
|
|
|
The fastest profile, meant for local development only. Uses only 10 examples
|
|
|
|
per test with no completion deadline.
|
|
|
|
|
|
|
|
|
|
|
|
.. _Black: https://github.com/psf/black
|
|
|
|
.. _Flake8: https://flake8.pycqa.org/
|
|
|
|
.. _Hypothesis: https://hypothesis.readthedocs.io/
|
|
|
|
.. _OpenAPI 3: https://swagger.io/specification/
|
|
|
|
.. _Openapi-core: https://github.com/p1c2u/openapi-core
|
|
|
|
.. _Poetry: https://python-poetry.org/
|
|
|
|
.. _Tornado: https://www.tornadoweb.org/
|
|
|
|
.. _pytest: https://pytest.org/
|
|
|
|
.. _tox: https://tox.readthedocs.io/
|