diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..6cd3014 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,43 @@ +# Changelog + +## 0.6.0 (2019-08-02) + +### Features + +* Add API module ([4a43817](https://github.com/huge-success/sanic-openapi/pull/111)) +* Make it possible to register the same model definition multiple times in `doc.definitions` ([3c3329c](https://github.com/huge-success/sanic-openapi/pull/110)) +* Support Swagger configuration ([a093d55](https://github.com/huge-success/sanic-openapi/pull/100)) +* Super call added to List field serialize method ([93ab6fa](https://github.com/huge-success/sanic-openapi/pull/93)) +* Support and examples for Class-Based Views (HTTPMethodView) ([de21965](https://github.com/huge-success/sanic-openapi/pull/64)) +* Add operation decorator ([039a0db](https://github.com/huge-success/sanic-openapi/pull/95)) +* Add a if-statement at build_spec checking add default 200 response or not ([1d8d7d0](https://github.com/huge-success/sanic-openapi/pull/116)) +* Add File field & Fix some line with black style ([06c662b](https://github.com/huge-success/sanic-openapi/pull/120)) + +### Bug Fixes + +* Allow empty list with List field ([74fd710](https://github.com/chenjr0719/sanic-openapi/commit/74fd710)) +* fix example of class based view ([9c0bbd3](https://github.com/chenjr0719/sanic-openapi/commit/9c0bbd3)) +* Ignore routes under swagger blueprint ([ed932cc](https://github.com/chenjr0719/sanic-openapi/commit/ed932cc)) +* fix static and exclude ([5a8aab8](https://github.com/huge-success/sanic-openapi/pull/80)) + + +### Build System + +* Add version file under sanic_openapi ([020338b](https://github.com/chenjr0719/sanic-openapi/commit/020338b)) +* Fix regex of version in setup.py ([ab01896](https://github.com/chenjr0719/sanic-openapi/commit/ab01896)) +* Use setup.py to control dependencies and remove requirements files ([1079e15](https://github.com/chenjr0719/sanic-openapi/commit/1079e15)) + + +### Tests + +* Add clean up in app fixture ([fa1b111](https://github.com/chenjr0719/sanic-openapi/commit/fa1b111)) +* Add config of coverage and integrate with setup.py ([30a9915](https://github.com/chenjr0719/sanic-openapi/commit/30a9915)) +* Add Sanic 19.06 to tox env ([8ad28cf](https://github.com/chenjr0719/sanic-openapi/commit/8ad28cf)) +* Add serialize_schema tests ([43b4c9d](https://github.com/chenjr0719/sanic-openapi/commit/43b4c9d)) +* Add test cases ([17ba469](https://github.com/chenjr0719/sanic-openapi/commit/17ba469)) +* Add tests for base fields ([2254f23](https://github.com/chenjr0719/sanic-openapi/commit/2254f23)) +* Add tests for decorators ([063afc3](https://github.com/chenjr0719/sanic-openapi/commit/063afc3)) +* Add tests for JsonBody field, List field, and Object field ([2a9f4d5](https://github.com/chenjr0719/sanic-openapi/commit/2a9f4d5)) +* Fix redirect test ([1539b04](https://github.com/chenjr0719/sanic-openapi/commit/1539b04)) +* Modify envlist in tox.ini to check compatibility of Sanic releases ([2c518b6](https://github.com/chenjr0719/sanic-openapi/commit/2c518b6)) +* Remove skip of static test case and fix options route test ([86a8a4a](https://github.com/chenjr0719/sanic-openapi/commit/86a8a4a)) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 911943a..517fc2f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -86,6 +86,19 @@ black --verbose sanic_openapi tests isort --recursive sanic_openapi tests ``` +## Build Document + +Contribute documents of this project is also welcome. The document will be published on **Read the Docs** automatically. To preview the outputs, you can run the following commands to build the documents with `sphinx`: + +```shell +cd docs +make html +``` + +And you can open `docs/_build/html/index.html` to preview your works. + +> **Note:** Make sure you have install related modules. If you didn't, you can run `pip install -e .['doc']` to install all depedencies to build the documents. + ## Make a Pull Request If you want to make a Pull Request, here are some suggestions: diff --git a/README.md b/README.md index 9a2d7ef..a513a4a 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Give your Sanic API a UI and OpenAPI documentation, all for the price of free! -![Example Swagger UI](images/code-to-ui.png?raw=true "Swagger UI") +![Example Swagger UI](docs/_static/images/code-to-ui.png?raw=true "Swagger UI") ## Installation @@ -29,155 +29,34 @@ Your routes will be automatically categorized by their blueprints. ## Example -For an example Swagger UI, see the [Pet Store](http://petstore.swagger.io/) - -## Usage - -### Use simple decorators to document routes: - -```python -from sanic_openapi import doc - -@app.get("/user/") -@doc.summary("Fetches a user by ID") -@doc.produces({ "user": { "name": str, "id": int } }) -async def get_user(request, user_id): - ... - -@app.post("/user") -@doc.summary("Creates a user") -@doc.consumes(doc.JsonBody({"user": { "name": str }}), location="body") -async def create_user(request): - ... -``` - -### Model your input/output - -```python -class Car: - make = str - model = str - year = int - -class Garage: - spaces = int - cars = [Car] - -@app.get("/garage") -@doc.summary("Gets the whole garage") -@doc.produces(Garage) -async def get_garage(request): - return json({ - "spaces": 2, - "cars": [{"make": "Nissan", "model": "370Z"}] - }) - -``` - -### Get more descriptive +Here is an example to use Sanic-OpenAPI: ```python -class Car: - make = doc.String("Who made the car") - model = doc.String("Type of car. This will vary by make") - year = doc.Integer("4-digit year of the car", required=False) - -class Garage: - spaces = doc.Integer("How many cars can fit in the garage") - cars = doc.List(Car, description="All cars in the garage") -``` +from sanic import Sanic +from sanic.response import json +from sanic_openapi import swagger_blueprint -### Specify a JSON body without extensive modelling +app = Sanic() +app.blueprint(swagger_blueprint) -```python -garage = doc.JsonBody({ - "spaces": doc.Integer, - "cars": [ - { - "make": doc.String, - "model": doc.String, - "year": doc.Integer - } - ] -}) - -@app.post("/store/garage") -@doc.summary("Stores a garage object") -@doc.consumes(garage, content_type="application/json", location="body") -async def store_garage(request): - store_garage(request.json) - return json(request.json) -``` -### Configure all the things +@app.route("/") +async def test(request): + return json({"hello": "world"}) -```python -app.config.API_VERSION = '1.0.0' -app.config.API_TITLE = 'Car API' -app.config.API_DESCRIPTION = 'Car API' -app.config.API_TERMS_OF_SERVICE = 'Use with caution!' -app.config.API_PRODUCES_CONTENT_TYPES = ['application/json'] -app.config.API_CONTACT_EMAIL = 'channelcat@gmail.com' -``` -By default, Sanic registers URIs both with and without a trailing `/`. You may specify the type of the shown URIs by setting `app.config.API_URI_FILTER` to one of the following values: -- `all`: Include both types of URIs. -- `slash`: Only include URIs with a trailing `/`. -- All other values (and default): Only include URIs without a trailing `/`. - -#### Including OpenAPI's host, basePath and security parameters - -Just follow the OpenAPI 2.0 specification on this - -``` python -app.config.API_HOST = 'subdomain.host.ext' -app.config.API_BASEPATH = '/v2/api/' - -app.config.API_SECURITY = [ - { - 'authToken': [] - } -] - -app.config.API_SECURITY_DEFINITIONS = { - 'authToken': { - 'type': 'apiKey', - 'in': 'header', - 'name': 'Authorization', - 'description': 'Paste your auth token and do not forget to add "Bearer " in front of it' - }, - 'OAuth2': { - 'type': 'oauth2', - 'flow': 'application', - 'tokenUrl': 'https://your.authserver.ext/v1/token', - 'scopes': { - 'some_scope': 'Grants access to this API' - } - } -} +if __name__ == "__main__": + app.run(host="0.0.0.0", port=8000) ``` -### Set Swagger-UI configuration parameters +And you can get your Swagger document at like this: +![](docs/_static/images/hello_world_example.png) -Here you can set any configuration described in the swagger-ui documentation +## Documentation -```python -app.config.SWAGGER_UI_CONFIGURATION = { - 'validatorUrl': None, # Disable Swagger validator - 'displayRequestDuration': True, - 'docExpansion': 'full' -} -``` +Please check the documentation on [Readthedocs](https://sanic-openapi.readthedocs.io) -### Set responses for different HTTP status codes +## Contribution -```python -@app.get("/garage/") -@doc.summary("Gets the whole garage") -@doc.produces(Garage) -@doc.response(404, {"message": str}, description="When the garage cannot be found") -async def get_garage(request, id): - garage = some_fetch_function(id) - return json(garage) -``` +Any contribution is welcome. If you don't know how to getting started, please check issues first and check our [Contributing Guide](CONTRIBUTING.md) to start you contribution. diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_static/css/custom.css b/docs/_static/css/custom.css new file mode 100644 index 0000000..f2d888f --- /dev/null +++ b/docs/_static/css/custom.css @@ -0,0 +1,4 @@ +img { + margin: 5px; + box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.2); +} \ No newline at end of file diff --git a/docs/_static/images/blueprint_class_based_view_example.png b/docs/_static/images/blueprint_class_based_view_example.png new file mode 100644 index 0000000..be89652 Binary files /dev/null and b/docs/_static/images/blueprint_class_based_view_example.png differ diff --git a/docs/_static/images/blueprint_example.png b/docs/_static/images/blueprint_example.png new file mode 100644 index 0000000..8ed85a9 Binary files /dev/null and b/docs/_static/images/blueprint_example.png differ diff --git a/docs/_static/images/class_based_view_example.png b/docs/_static/images/class_based_view_example.png new file mode 100644 index 0000000..7e3205e Binary files /dev/null and b/docs/_static/images/class_based_view_example.png differ diff --git a/images/code-to-ui.png b/docs/_static/images/code-to-ui.png similarity index 100% rename from images/code-to-ui.png rename to docs/_static/images/code-to-ui.png diff --git a/docs/_static/images/composition_view_example.png b/docs/_static/images/composition_view_example.png new file mode 100644 index 0000000..4e306fe Binary files /dev/null and b/docs/_static/images/composition_view_example.png differ diff --git a/docs/_static/images/configurations/API_BASEPATH.png b/docs/_static/images/configurations/API_BASEPATH.png new file mode 100644 index 0000000..ba1e380 Binary files /dev/null and b/docs/_static/images/configurations/API_BASEPATH.png differ diff --git a/docs/_static/images/configurations/API_CONTACT_EMAIL.png b/docs/_static/images/configurations/API_CONTACT_EMAIL.png new file mode 100644 index 0000000..10a1a44 Binary files /dev/null and b/docs/_static/images/configurations/API_CONTACT_EMAIL.png differ diff --git a/docs/_static/images/configurations/API_DESCRIPTION.png b/docs/_static/images/configurations/API_DESCRIPTION.png new file mode 100644 index 0000000..3cfe09b Binary files /dev/null and b/docs/_static/images/configurations/API_DESCRIPTION.png differ diff --git a/docs/_static/images/configurations/API_HOST.png b/docs/_static/images/configurations/API_HOST.png new file mode 100644 index 0000000..1a715e6 Binary files /dev/null and b/docs/_static/images/configurations/API_HOST.png differ diff --git a/docs/_static/images/configurations/API_Key_in_header.png b/docs/_static/images/configurations/API_Key_in_header.png new file mode 100644 index 0000000..d1b9093 Binary files /dev/null and b/docs/_static/images/configurations/API_Key_in_header.png differ diff --git a/docs/_static/images/configurations/API_Key_in_query.png b/docs/_static/images/configurations/API_Key_in_query.png new file mode 100644 index 0000000..1254d21 Binary files /dev/null and b/docs/_static/images/configurations/API_Key_in_query.png differ diff --git a/docs/_static/images/configurations/API_LICENSE_NAME.png b/docs/_static/images/configurations/API_LICENSE_NAME.png new file mode 100644 index 0000000..ebe8a8f Binary files /dev/null and b/docs/_static/images/configurations/API_LICENSE_NAME.png differ diff --git a/docs/_static/images/configurations/API_LICENSE_URL.png b/docs/_static/images/configurations/API_LICENSE_URL.png new file mode 100644 index 0000000..a27c981 Binary files /dev/null and b/docs/_static/images/configurations/API_LICENSE_URL.png differ diff --git a/docs/_static/images/configurations/API_SCHEMES.png b/docs/_static/images/configurations/API_SCHEMES.png new file mode 100644 index 0000000..95c0d5f Binary files /dev/null and b/docs/_static/images/configurations/API_SCHEMES.png differ diff --git a/docs/_static/images/configurations/API_TERMS_OF_SERVICE.png b/docs/_static/images/configurations/API_TERMS_OF_SERVICE.png new file mode 100644 index 0000000..4b39a04 Binary files /dev/null and b/docs/_static/images/configurations/API_TERMS_OF_SERVICE.png differ diff --git a/docs/_static/images/configurations/API_TITLE.png b/docs/_static/images/configurations/API_TITLE.png new file mode 100644 index 0000000..45f8d08 Binary files /dev/null and b/docs/_static/images/configurations/API_TITLE.png differ diff --git a/docs/_static/images/configurations/API_URI_FILTER_all.png b/docs/_static/images/configurations/API_URI_FILTER_all.png new file mode 100644 index 0000000..be1f668 Binary files /dev/null and b/docs/_static/images/configurations/API_URI_FILTER_all.png differ diff --git a/docs/_static/images/configurations/API_URI_FILTER_default.png b/docs/_static/images/configurations/API_URI_FILTER_default.png new file mode 100644 index 0000000..301d44b Binary files /dev/null and b/docs/_static/images/configurations/API_URI_FILTER_default.png differ diff --git a/docs/_static/images/configurations/API_URI_FILTER_slash.png b/docs/_static/images/configurations/API_URI_FILTER_slash.png new file mode 100644 index 0000000..3833e4f Binary files /dev/null and b/docs/_static/images/configurations/API_URI_FILTER_slash.png differ diff --git a/docs/_static/images/configurations/API_VERSION.png b/docs/_static/images/configurations/API_VERSION.png new file mode 100644 index 0000000..a63582e Binary files /dev/null and b/docs/_static/images/configurations/API_VERSION.png differ diff --git a/docs/_static/images/configurations/BasicAuth_1.png b/docs/_static/images/configurations/BasicAuth_1.png new file mode 100644 index 0000000..3029fed Binary files /dev/null and b/docs/_static/images/configurations/BasicAuth_1.png differ diff --git a/docs/_static/images/configurations/BasicAuth_2.png b/docs/_static/images/configurations/BasicAuth_2.png new file mode 100644 index 0000000..c8c6bcc Binary files /dev/null and b/docs/_static/images/configurations/BasicAuth_2.png differ diff --git a/docs/_static/images/configurations/OAuth2.png b/docs/_static/images/configurations/OAuth2.png new file mode 100644 index 0000000..fe201ef Binary files /dev/null and b/docs/_static/images/configurations/OAuth2.png differ diff --git a/docs/_static/images/decorators/consumes_body.png b/docs/_static/images/decorators/consumes_body.png new file mode 100644 index 0000000..77479ae Binary files /dev/null and b/docs/_static/images/decorators/consumes_body.png differ diff --git a/docs/_static/images/decorators/consumes_header.png b/docs/_static/images/decorators/consumes_header.png new file mode 100644 index 0000000..1a9e442 Binary files /dev/null and b/docs/_static/images/decorators/consumes_header.png differ diff --git a/docs/_static/images/decorators/consumes_query.png b/docs/_static/images/decorators/consumes_query.png new file mode 100644 index 0000000..e8a8609 Binary files /dev/null and b/docs/_static/images/decorators/consumes_query.png differ diff --git a/docs/_static/images/decorators/description.png b/docs/_static/images/decorators/description.png new file mode 100644 index 0000000..c23ac12 Binary files /dev/null and b/docs/_static/images/decorators/description.png differ diff --git a/docs/_static/images/decorators/exclude.png b/docs/_static/images/decorators/exclude.png new file mode 100644 index 0000000..052b4c4 Binary files /dev/null and b/docs/_static/images/decorators/exclude.png differ diff --git a/docs/_static/images/decorators/produces.png b/docs/_static/images/decorators/produces.png new file mode 100644 index 0000000..8a2dcb1 Binary files /dev/null and b/docs/_static/images/decorators/produces.png differ diff --git a/docs/_static/images/decorators/response.png b/docs/_static/images/decorators/response.png new file mode 100644 index 0000000..a704c5b Binary files /dev/null and b/docs/_static/images/decorators/response.png differ diff --git a/docs/_static/images/decorators/sumary.png b/docs/_static/images/decorators/sumary.png new file mode 100644 index 0000000..70e4183 Binary files /dev/null and b/docs/_static/images/decorators/sumary.png differ diff --git a/docs/_static/images/decorators/tag.png b/docs/_static/images/decorators/tag.png new file mode 100644 index 0000000..128b2bf Binary files /dev/null and b/docs/_static/images/decorators/tag.png differ diff --git a/docs/_static/images/fields/boolean.png b/docs/_static/images/fields/boolean.png new file mode 100644 index 0000000..30cbc5b Binary files /dev/null and b/docs/_static/images/fields/boolean.png differ diff --git a/docs/_static/images/fields/date.png b/docs/_static/images/fields/date.png new file mode 100644 index 0000000..365731c Binary files /dev/null and b/docs/_static/images/fields/date.png differ diff --git a/docs/_static/images/fields/datetime.png b/docs/_static/images/fields/datetime.png new file mode 100644 index 0000000..35d8f4f Binary files /dev/null and b/docs/_static/images/fields/datetime.png differ diff --git a/docs/_static/images/fields/describe_mdoel.png b/docs/_static/images/fields/describe_mdoel.png new file mode 100644 index 0000000..cdccf4b Binary files /dev/null and b/docs/_static/images/fields/describe_mdoel.png differ diff --git a/docs/_static/images/fields/file.png b/docs/_static/images/fields/file.png new file mode 100644 index 0000000..242e437 Binary files /dev/null and b/docs/_static/images/fields/file.png differ diff --git a/docs/_static/images/fields/float.png b/docs/_static/images/fields/float.png new file mode 100644 index 0000000..e8a4682 Binary files /dev/null and b/docs/_static/images/fields/float.png differ diff --git a/docs/_static/images/fields/integer.png b/docs/_static/images/fields/integer.png new file mode 100644 index 0000000..7230a7c Binary files /dev/null and b/docs/_static/images/fields/integer.png differ diff --git a/docs/_static/images/fields/josnbody.png b/docs/_static/images/fields/josnbody.png new file mode 100644 index 0000000..7ab6b8a Binary files /dev/null and b/docs/_static/images/fields/josnbody.png differ diff --git a/docs/_static/images/fields/list.png b/docs/_static/images/fields/list.png new file mode 100644 index 0000000..9573357 Binary files /dev/null and b/docs/_static/images/fields/list.png differ diff --git a/docs/_static/images/fields/object.png b/docs/_static/images/fields/object.png new file mode 100644 index 0000000..be84616 Binary files /dev/null and b/docs/_static/images/fields/object.png differ diff --git a/docs/_static/images/fields/string.png b/docs/_static/images/fields/string.png new file mode 100644 index 0000000..d450f9d Binary files /dev/null and b/docs/_static/images/fields/string.png differ diff --git a/docs/_static/images/hello_world_example.png b/docs/_static/images/hello_world_example.png new file mode 100644 index 0000000..2267bfd Binary files /dev/null and b/docs/_static/images/hello_world_example.png differ diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..9e4df51 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,75 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. + +import os +import sys + + +docs_directory = os.path.dirname(os.path.abspath(__file__)) +root_directory = os.path.dirname(docs_directory) +sys.path.insert(0, root_directory) + +import sanic_openapi + +# -- Project information ----------------------------------------------------- + +project = "Sanic-OpenAPI" +copyright = "2019, Sanic Community" +author = "Sanic Community" + +version = sanic_openapi.__version__ +release = sanic_openapi.__version__ + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = ["recommonmark", "sphinx.ext.autodoc"] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + +master_doc = "index" + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "sphinx_rtd_theme" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] +html_css_files = ["css/custom.css"] + +from recommonmark.transform import AutoStructify + + +def setup(app): + app.add_config_value( + "recommonmark_config", + { + "enable_eval_rst": True, + "enable_auto_toc_tree": True, + # "auto_toc_tree_section": "Contents", + }, + True, + ) + app.add_transform(AutoStructify) diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..0ae6b24 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,66 @@ + + +# Sanic OpenAPI + +Sanic-OpenAPI is an extension of Sanic web framework to easily document your Sanic APIs with Swagger UI. + +## Installation + +To install `sanic_openapi`, you can install from PyPI: + +```shell +pip install sanic-openapi +``` + +Or, use master banch from GitHub with latest features: + +```shell +pip install git+https://github.com/huge-success/sanic-openapi.git +``` + +## Getting Started + +Here is an example to use Sanic-OpenAPI: + +```python +from sanic import Sanic +from sanic.response import json +from sanic_openapi import swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +@app.route("/") +async def test(request): + return json({"hello": "world"}) + + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=8000) + +``` + +And you can get your Swagger document at like this: +![](_static/images/hello_world_example.png) + +## Contents + +* [Document Routes](sanic_openapi/document_routes) +* [Configurations](sanic_openapi/configurations) +* [Decorators](sanic_openapi/decorators) +* [Fields](sanic_openapi/fields) +* [API Factory](sanic_openapi/api_factory) +* [Examples](sanic_openapi/examples) +* [API Reference](sanic_openapi/api_reference) + +## Indices and tables + +```eval_rst +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` +``` diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..922152e --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/sanic_openapi/api_factory.md b/docs/sanic_openapi/api_factory.md new file mode 100644 index 0000000..0012115 --- /dev/null +++ b/docs/sanic_openapi/api_factory.md @@ -0,0 +1,3 @@ +# API Factory + +To be done. diff --git a/docs/sanic_openapi/api_reference.md b/docs/sanic_openapi/api_reference.md new file mode 100644 index 0000000..f680c83 --- /dev/null +++ b/docs/sanic_openapi/api_reference.md @@ -0,0 +1,28 @@ +# API Reference + +## sanic_openapi.doc + +```eval_rst +.. automodule:: sanic_openapi.doc + :members: + :undoc-members: + :show-inheritance: +``` + +## sanic_openapi.swagger + +```eval_rst +.. automodule:: sanic_openapi.swagger + :members: + :undoc-members: + :show-inheritance: +``` + +## sanic_openapi.api + +```eval_rst +.. automodule:: sanic_openapi.api + :members: + :undoc-members: + :show-inheritance: +``` diff --git a/docs/sanic_openapi/configurations.md b/docs/sanic_openapi/configurations.md new file mode 100644 index 0000000..e16179e --- /dev/null +++ b/docs/sanic_openapi/configurations.md @@ -0,0 +1,423 @@ +# Configurations + +Sanic-OpenAPI provides following configurable items: + +* API Server +* API information +* Authentication(Security Definitions) +* URI filter +* Swagger UI configurations + +## API Server + +By default, Swagger will use exactly the same host which served itself as the API server. But you can still override this by setting following configurations. For more information, please check document at [here](https://swagger.io/docs/specification/2-0/api-host-and-base-path/). + +### API_HOST + +* Key: `API_HOST` +* Type: `str` of IP, or hostname +* Default: `None` +* Usage: + + ```python + from sanic import Sanic + from sanic_openapi import swagger_blueprint + + app = Sanic() + app.blueprint(swagger_blueprint) + app.config["API_HOST"] = "petstore.swagger.io" + + ``` + +* Result: + ![](../_static/images/configurations/API_HOST.png) + +### API_BASEPATH + +* Key: `API_BASEPATH` +* Type: `str` +* Default: `None` +* Usage: + + ```python + from sanic import Sanic + from sanic_openapi import swagger_blueprint + + app = Sanic() + app.blueprint(swagger_blueprint) + app.config["API_BASEPATH"] = "/api" + + ``` + +* Result: + ![](../_static/images/configurations/API_BASEPATH.png) + +### API_SCHEMES + +* Key: `API_SCHEMES` +* Type: `list` of schemes +* Default: `["http"]` +* Usage: + + ```python + from sanic import Sanic + from sanic_openapi import swagger_blueprint + + app = Sanic() + app.blueprint(swagger_blueprint) + app.config["API_SCHEMES"] = ["https"] + + ``` + +* Result: + ![](../_static/images/configurations/API_SCHEMES.png) + +## API information + +You can provide some additional information of your APIs by using Sanic-OpenAPI configurations. +For more detail of those additional information, please check the [document](https://swagger.io/specification/#infoObject) from Swagger. + +### API_VERSION + +* Key: `API_VERSION` +* Type: `str` +* Default: `1.0.0` +* Usage: + + ```python + from sanic import Sanic + from sanic_openapi import swagger_blueprint + + app = Sanic() + app.blueprint(swagger_blueprint) + app.config["API_VERSION"] = "0.1.0" + + ``` + +* Result: + ![](../_static/images/configurations/API_VERSION.png) + +### API_TITLE + +* Key: `API_TITLE` +* Type: `str` +* Default: `API` +* Usage: + + ```python + from sanic import Sanic + from sanic_openapi import swagger_blueprint + + app = Sanic() + app.blueprint(swagger_blueprint) + app.config["API_TITLE"] = "Sanci-OpenAPI" + + ``` + +* Result: + ![](../_static/images/configurations/API_TITLE.png) + +### API_DESCRIPTION + +* Key: `API_DESCRIPTION` +* Type: `str` +* Deafult: `""` +* Usage: + + ```python + from sanic import Sanic + from sanic_openapi import swagger_blueprint + + app = Sanic() + app.blueprint(swagger_blueprint) + app.config["API_DESCRIPTION"] = "An example Swagger from Sanic-OpenAPI" + + ``` + +* Result: + ![](../_static/images/configurations/API_DESCRIPTION.png) + +### API_TERMS_OF_SERVICE + +* Key: `API_TERMS_OF_SERVICE` +* Type: `str` of a URL +* Deafult: `""` +* Usage: + + ```python + from sanic import Sanic + from sanic_openapi import swagger_blueprint + + app = Sanic() + app.blueprint(swagger_blueprint) + app.config["API_TERMS_OF_SERVICE"] = "https://github.com/huge-success/sanic-openapi/blob/master/README.md" + + ``` + +* Result: + ![](../_static/images/configurations/API_TERMS_OF_SERVICE.png) + +### API_CONTACT_EMAIL + +* Key: `API_CONTACT_EMAIL` +* Type: `str` of email address +* Deafult: `None"` +* Usage: + + ```python + from sanic import Sanic + from sanic_openapi import swagger_blueprint + + app = Sanic() + app.blueprint(swagger_blueprint) + app.config["API_CONTACT_EMAIL"] = "foo@bar.com" + + ``` + +* Result: + ![](../_static/images/configurations/API_CONTACT_EMAIL.png) + +### API_LICENSE_NAME + +* Key: `API_LICENSE_NAME` +* Type: `str` +* Default: `None` +* Usage: + + python + from sanic import Sanic + from sanic_openapi import swagger_blueprint + + app = Sanic() + app.blueprint(swagger_blueprint) + app.config["API_LICENSE_NAME"] = "MIT" + + + +* Result: + ![](../_static/images/configurations/API_LICENSE_NAME.png) + +### API_LICENSE_URL + +* Key: `API_LICENSE_URL` +* Type: `str` of URL +* Default: `None` +* Usgae: + + ```python + from sanic import Sanic + from sanic_openapi import swagger_blueprint + + app = Sanic() + app.blueprint(swagger_blueprint) + app.config["API_LICENSE_URL"] = "https://github.com/huge-success/sanic-openapi/blob/master/LICENSE" + + ``` + +* Result: + ![](../_static/images/configurations/API_LICENSE_URL.png) + +## Authentication + +If your API have to access with authentication, Swagger can provide related configuration as you need. For more information, check [here](https://swagger.io/docs/specification/2-0/authentication/). + +### Basic Authentication + +* Usage: + + ```python + from sanic import Sanic + from sanic.response import json + + from sanic_openapi import swagger_blueprint + + app = Sanic() + app.blueprint(swagger_blueprint) + app.config["API_SECURITY"] = [{"BasicAuth": []}] + app.config["API_SECURITY_DEFINITIONS"] = {"BasicAuth": {"type": "basic"}} + + + @app.get("/") + async def test(request): + return json({"token": request.token}) + + ``` + +* Result: + ![](../_static/images/configurations/BasicAuth_1.png) + ![](../_static/images/configurations/BasicAuth_2.png) + +### API Key + +#### In Header + +* Usage: + + ```python + from sanic import Sanic + from sanic.response import json + + from sanic_openapi import swagger_blueprint + + app = Sanic() + app.blueprint(swagger_blueprint) + app.config["API_SECURITY"] = [{"ApiKeyAuth": []}] + app.config["API_SECURITY_DEFINITIONS"] = { + "ApiKeyAuth": {"type": "apiKey", "in": "header", "name": "X-API-KEY"} + } + + + @app.get("/") + async def test(request): + api_key = request.headers.get("X-API-KEY") + return json({"api_key": api_key}) + + ``` +* Result: +![](../_static/images/configurations/API_Key_in_header.png) + +#### In Query + +* Usage: + + ```python + from sanic import Sanic + from sanic.response import json + + from sanic_openapi import swagger_blueprint + + app = Sanic() + app.blueprint(swagger_blueprint) + app.config["API_SECURITY"] = [{"ApiKeyAuth": []}] + app.config["API_SECURITY_DEFINITIONS"] = { + "ApiKeyAuth": {"type": "apiKey", "in": "query", "name": "api_key"} + } + + + @app.get("/") + async def test(request): + api_key = request.args.get("api_key") + return json({"api_key": api_key}) + + ``` + +* Result: + ![](../_static/images/configurations/API_Key_in_query.png) + +### OAuth2 + +* Usage: + + ```python + from sanic import Sanic + from sanic.response import json + + from sanic_openapi import swagger_blueprint + + app = Sanic() + app.blueprint(swagger_blueprint) + app.config["API_SECURITY"] = [{"OAuth2": []}] + app.config["API_SECURITY_DEFINITIONS"] = { + "OAuth2": { + "type": "oauth2", + "flow": "application", + "tokenUrl": "https://your.authserver.ext/v1/token", + "scopes": {"some_scope": "Grants access to this API"}, + } + } + + ``` + +* Result: +![](../_static/images/configurations/OAuth2.png) + +## URI filter + +By default, Sanic registers URIs both with and without a trailing `/`. You may specify the type of the shown URIs by setting `app.config.API_URI_FILTER` to one of the following values: + +* `all`: Include both types of URIs. +* `slash`: Only include URIs with a trailing `/`. +* All other values (and default): Only include URIs without a trailing `/`. + +### Non-Slash + +* Usage: + + ```python + from sanic import Sanic + from sanic.response import json + + from sanic_openapi import swagger_blueprint + + app = Sanic() + app.blueprint(swagger_blueprint) + + + @app.get("/test") + async def test(request): + return json({"Hello": "World"}) + + ``` + +* Result: + ![](../_static/images/configurations/API_URI_FILTER_default.png) + +### Slash + +* Usage: + + ```python + from sanic import Sanic + from sanic.response import json + + from sanic_openapi import swagger_blueprint + + app = Sanic() + app.blueprint(swagger_blueprint) + app.config["API_URI_FILTER"] = "slash" + + + @app.get("/test") + async def test(request): + return json({"Hello": "World"}) + + ``` + +* Result: + ![](../_static/images/configurations/API_URI_FILTER_slash.png) + +### All + +* Usage: + + ```python + from sanic import Sanic + from sanic.response import json + + from sanic_openapi import swagger_blueprint + + app = Sanic() + app.blueprint(swagger_blueprint) + app.config["API_URI_FILTER"] = "all" + + + @app.get("/test") + async def test(request): + return json({"Hello": "World"}) + + ``` + +* Result: + ![](../_static/images/configurations/API_URI_FILTER_all.png) + +## Swagger UI configurations + +Here you can set any configuration described in the Swagger UI [documentation](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/). + +```python +app.config.SWAGGER_UI_CONFIGURATION = { + 'validatorUrl': None, # Disable Swagger validator + 'displayRequestDuration': True, + 'docExpansion': 'full' +} +``` diff --git a/docs/sanic_openapi/decorators.md b/docs/sanic_openapi/decorators.md new file mode 100644 index 0000000..fa155c3 --- /dev/null +++ b/docs/sanic_openapi/decorators.md @@ -0,0 +1,268 @@ +# Decorators + +Sanic-OpenAPI provides different **decorator** can help you document your API routes. + +## Exclude + +When you don't want to document some route in Swagger, you can use `exclude(True)` decorator to exclude route from swagger. + +```python +from sanic import Sanic +from sanic.response import json + +from sanic_openapi import doc, swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +@app.get("/test") +async def test(request): + return json({"Hello": "World"}) + + +@app.get("/config") +@doc.exclude(True) +async def get_config(request): + return json(request.app.config) + +``` + +Once you add the `exclude()` decorator, the route will not be document at swagger. +![](../_static/images/decorators/exclude.png) + +## Summary + +You can add a short summary to your route by using `summary()` decorator. It is helpful to point out the purpose of your API route. + +```python +from sanic import Sanic +from sanic.response import json + +from sanic_openapi import doc, swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +@app.get("/test") +@doc.summary("Test route") +async def test(request): + return json({"Hello": "World"}) + +``` + +The summary will show behind the path: +![](../_static/images/decorators/sumary.png) + +## Description + +Not only short summary, but also long description of your API route can be addressed by using `description()` decorator. + +```python +from sanic import Sanic +from sanic.response import json + +from sanic_openapi import doc, swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +@app.get("/test") +@doc.description('This is a test route with detail description.') +async def test(request): + return json({"Hello": "World"}) +``` + +To see the description, you have to expand the content of route and it would looks like: +![](../_static/images/decorators/description.png) + +## Tag + +If you want to group your API routes, you can use `tag()` decorator to accomplish your need. + +```python +from sanic import Sanic +from sanic.response import json + +from sanic_openapi import doc, swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +@app.get("/test") +@doc.tag("test") +async def test(request): + return json({"Hello": "World"}) + +``` + +And you can see the tag is change from `default` to `test`: +![](../_static/images/decorators/tag.png) + +By default, all routes register under Sanic will be tag with `default`. And all routes under Blueprint will be tag with the blueprint name. + +## Operation + +Sanic-OpenAPI will use route(function) name as the default `operationId`. You can override the `operationId` by using `operation()` decorator. +The `operation()` decorator would be useful when your routes have duplicate name in some cases. + +```python +from sanic import Sanic +from sanic.response import json + +from sanic_openapi import doc, swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +@app.get("/test") +@doc.operation('test1') +async def test(request): + return json({"Hello": "World"}) + +``` + +## Consumes + +The `consumes()` decorator is the most common used decorator in Sanic-OpenAPI. It is used to document the parameter usages in swagger. You can use built-in classes like `str`, `int`, `dict` or use different [fields](fields.md) which provides by Sanic-OpenAPI to document your parameters. + +There are three kinds of parameter usages: + +### Query + +To document the parameter in query string, you can use `location="query"` in `consumes()` decorator. This is also the default to `consumes()` decorator. + +```python +from sanic import Sanic +from sanic.response import json + +from sanic_openapi import doc, swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +@app.get("/test") +@doc.consumes(doc.String(name="filter"), location="query") +async def test(request): + return json({"Hello": "World"}) + +``` + +You can expand the contents of route and it will looks like: +![](../_static/images/decorators/consumes_query.png) + +When using `consumes()` with `location="query"`, it only support simple types like `str`, `int` but no complex types like `dict`. + +### Header + +For doucument parameters in header, you can set `location="header"` with simple types just like `location="query"`. + +```python +from sanic import Sanic +from sanic.response import json + +from sanic_openapi import doc, swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +@app.get("/test") +@doc.consumes(doc.String(name="X-API-VERSION"), location="header", required=True) +async def test(request): + return json({"Hello": "World"}) + +``` + +It will looks like: +![](../_static/images/decorators/consumes_header.png) + +### Request Body + +In most cases, your APIs might contains lots of parameter in your request body. In Sanic-OpenAPI, you can define them in Python class or use [fields](fields) which provides by Sanic-OpenAPI to simplify your works. + +```python +from sanic import Sanic +from sanic.response import json + +from sanic_openapi import doc, swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +class User: + name = str + + +class Test: + user = doc.Object(User) + + +@app.get("/test") +@doc.consumes(Test, location="body") +async def test(request): + return json({"Hello": "World"}) + +``` + +This will be document like: +![](../_static/images/decorators/consumes_body.png) + +## Produces + +The `produces()` decorator is used to document the default response(with status 200). + +```python +from sanic import Sanic +from sanic.response import json + +from sanic_openapi import doc, swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +class Test: + Hello = doc.String(description='World') + +@app.get("/test") +@doc.produces(Test) +async def test(request): + return json({"Hello": "World"}) + +``` + +As you can see in this example, you can also use Python class in `produces()` decorator. +![](../_static/images/decorators/produces.png) + +## Response + +To document responses not with status `200`, you can use `response()` decorator. For example: + +```python +ffrom sanic import Sanic +from sanic.response import json + +from sanic_openapi import doc, swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +@app.get("/test") +@doc.response(401, {"message": str}, description="Unauthorized") +async def test(request): + return json({"Hello": "World"}) + +``` + +And the responses will be: +![](../_static/images/decorators/response.png) + +Please note that when you use `response()` decorator, the default response(with status 200) will not be document by default. diff --git a/docs/sanic_openapi/document_routes.md b/docs/sanic_openapi/document_routes.md new file mode 100644 index 0000000..b010cf6 --- /dev/null +++ b/docs/sanic_openapi/document_routes.md @@ -0,0 +1,213 @@ +# Document Routes + +Sanic-OpenAPI support different ways to document APIs includes: + +* routes of `Sanic` instance +* routes of `Blueprint` instance +* routes of `HTTPMethodView` under `Sanic` instance +* routes of `HTTPMethodView` under `Bluebprint` instance +* routes of `CompositionView` under `Sanic` instance + +But with some exceptions: + +* Sanic-OpenAPI does not support routes of `CompositionView` under `Bluebprint` instance now. +* Sanic-OpenAPI does not document routes with `OPTIONS` method. +* Sanic-OpenAPI does not document routes which registered by `static()`. + +This section will explain how to document routes with above cases. + +## Basic Routes + +To use Sanic-OpenAPI with basic routes, you only have to register `swagger_blueprint` and it will be all set. + +For example: + +```python +from sanic import Sanic +from sanic.response import json + +from sanic_openapi import swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +@app.route("/") +async def test(request): + return json({"hello": "world"}) + + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=8000) + +``` + +As you can see the result at , the Swagger is documented a route `/` with `GET` method. +![](../_static/images/hello_world_example.png) + +If you want to add some additional information to this route, you can use other [decorators](decorators) like `summary()`, `description()`, and etc. + +## Blueprint Routes + +You can aldo document routes under any `Blueprint` like this: + +```python +from sanic import Blueprint, Sanic +from sanic.response import json + +from sanic_openapi import swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + +bp = Blueprint("bp", url_prefix="/bp") + + +@bp.route("/") +async def test(request): + return json({"hello": "world"}) + + +app.blueprint(bp) + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=8000) + +``` + +The result looks like: +![](../_static/images/blueprint_example.png) + +When you document routes under `Blueprint` instance, they will be document with `tags` which using the `Blueprint`'s name. + +## Class-Based Views Routes + +In Sanic, it provides a class-based views named `HTTPMethodView`. You can document routes under `HTTPMethodView` like: + +```python +from sanic import Sanic +from sanic.response import text +from sanic.views import HTTPMethodView + +from sanic_openapi import swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +class SimpleView(HTTPMethodView): + def get(self, request): + return text("I am get method") + + def post(self, request): + return text("I am post method") + + def put(self, request): + return text("I am put method") + + def patch(self, request): + return text("I am patch method") + + def delete(self, request): + return text("I am delete method") + + def options(self, request): # This will not be documented. + return text("I am options method") + + +app.add_route(SimpleView.as_view(), "/") + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=8000) + +``` + +And the result: +![](../_static/images/class_based_view_example.png) + +Please note that Sanic-OpenAPI will not document any routes with `OPTIONS` method. + +The `HTTPMethodView` can also be registered under `Blueprint`: + +```python +from sanic import Blueprint, Sanic +from sanic.response import text +from sanic.views import HTTPMethodView + +from sanic_openapi import swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + +bp = Blueprint("bp", url_prefix="/bp") + + +class SimpleView(HTTPMethodView): + def get(self, request): + return text("I am get method") + + def post(self, request): + return text("I am post method") + + def put(self, request): + return text("I am put method") + + def patch(self, request): + return text("I am patch method") + + def delete(self, request): + return text("I am delete method") + + def options(self, request): # This will not be documented. + return text("I am options method") + + +bp.add_route(SimpleView.as_view(), "/") +app.blueprint(bp) + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=8000) + +``` + +The result: +![](../_static/images/blueprint_class_based_view_example.png) + + +## CompositionView Routes + +There is another class-based view named `CompositionView`. Sanic-OpenAPI also support to document routes under class-based view. + +```python +from sanic import Sanic +from sanic.response import text +from sanic.views import CompositionView + +from sanic_openapi import swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +def get_handler(request): + return text("I am a get method") + + +view = CompositionView() +view.add(["GET"], get_handler) +view.add(["POST", "PUT"], lambda request: text("I am a post/put method")) + +# Use the new view to handle requests to the base URL +app.add_route(view, "/") + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=8000) + +``` + +The Swagger will looks like: +![](../_static/images/composition_view_example.png) + +```eval_rst +.. note:: Sanic-OpenAPI does not support routes of `CompositionView` under `Bluebprint` instance now. +``` diff --git a/docs/sanic_openapi/examples.md b/docs/sanic_openapi/examples.md new file mode 100644 index 0000000..5ce28ba --- /dev/null +++ b/docs/sanic_openapi/examples.md @@ -0,0 +1,4 @@ +# Examples + +* [Cars](/sanic_openapi/examples/cars/README) +* [Class Based View](/sanic_openapi/examples/class_based_view/README) diff --git a/docs/sanic_openapi/examples/cars/README.md b/docs/sanic_openapi/examples/cars/README.md new file mode 120000 index 0000000..91af4e4 --- /dev/null +++ b/docs/sanic_openapi/examples/cars/README.md @@ -0,0 +1 @@ +../../../../examples/cars/README.md \ No newline at end of file diff --git a/docs/sanic_openapi/examples/cars/swagger.png b/docs/sanic_openapi/examples/cars/swagger.png new file mode 120000 index 0000000..c887e35 --- /dev/null +++ b/docs/sanic_openapi/examples/cars/swagger.png @@ -0,0 +1 @@ +../../../../examples/cars/swagger.png \ No newline at end of file diff --git a/docs/sanic_openapi/examples/class_based_view/README.md b/docs/sanic_openapi/examples/class_based_view/README.md new file mode 120000 index 0000000..7b6e32d --- /dev/null +++ b/docs/sanic_openapi/examples/class_based_view/README.md @@ -0,0 +1 @@ +../../../../examples/class_based_view/README.md \ No newline at end of file diff --git a/docs/sanic_openapi/examples/class_based_view/swagger.png b/docs/sanic_openapi/examples/class_based_view/swagger.png new file mode 120000 index 0000000..41c8f17 --- /dev/null +++ b/docs/sanic_openapi/examples/class_based_view/swagger.png @@ -0,0 +1 @@ +../../../../examples/class_based_view/swagger.png \ No newline at end of file diff --git a/docs/sanic_openapi/fields.md b/docs/sanic_openapi/fields.md new file mode 100644 index 0000000..f5abc3f --- /dev/null +++ b/docs/sanic_openapi/fields.md @@ -0,0 +1,342 @@ +# Fields + +In Sanic-OpenAPI, there are lots of fields can be used to document your APIs. Those fields can represent different data type in your API request and response. + +Currently, Sanic-OpenAPI provides following fileds: + +* [Integer](#integer) +* [Float](#float) +* [String](#string) +* [Boolean](#boolean) +* [Tuple](#tuple) +* [Date](#date) +* [DateTime](#datetime) +* [File](#file) +* [Dictionary](#dictionary) +* [JsonBody](#jsonbody) +* [List](#list) +* [Object](#object) + +## Integer + +To document your API with integer data type, you can use `int` or `doc.Integer` with your handler function. +For example: + +```python +from sanic import Sanic +from sanic.response import json + +from sanic_openapi import doc, swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +@app.get("/test") +@doc.consumes(doc.Integer(name="num"), location="query") +async def test(request): + return json({"Hello": "World"}) + +``` + +And the swagger would be: +![](../_static/images/fields/integer.png) + +## Float + +Using the `float` or `doc.Float` is quite similar with `doc.integer`: + +```python +from sanic import Sanic +from sanic.response import json + +from sanic_openapi import doc, swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +@app.get("/test") +@doc.consumes(doc.Float(name="num"), location="query") +async def test(request): + return json({"Hello": "World"}) + +``` + +The swagger: +![](../_static/images/fields/float.png) + +## String + +The `doc.String` might be the most common filed in API documents. You can use it like this: + +```python +from sanic import Sanic +from sanic.response import json + +from sanic_openapi import doc, swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +@app.get("/test") +@doc.consumes(doc.String(name="name"), location="query") +async def test(request): + return json({"Hello": "World"}) + +``` + +The swagger will looks like: +![](../_static/images/fields/string.png) + +## Boolean + +If you want to provide an `true` or `false` options in your API document, the `doc.Boolean` is what you need. When using `doc.Boolean` or `bool`, it wull be convert in to a dropdown list with `true` and `false` options in swagger. + +For example: + +```python +from sanic import Sanic +from sanic.response import json + +from sanic_openapi import doc, swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +@app.get("/test") +@doc.consumes(doc.Boolean(name="all"), location="query") +async def test(request): + return json({"Hello": "World"}) + +``` + +The swagger will be: +![](../_static/images/fields/boolean.png) + +## Tuple + +To be done. + +## Date + +To repersent the date data type, Sanic-OpenAPI also provides `doc.Date` to you. When you put `doc.Date` in `doc.produces()`, it will use the local date as value. + +```python +from datetime import datetime + +from sanic import Sanic +from sanic.response import json + +from sanic_openapi import doc, swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +@app.get("/test") +@doc.produces({"date": doc.Date()}) +async def test(request): + return json({"date": datetime.utcnow().date().isoformat()}) + +``` + +The example swagger: +![](../_static/images/fields/date.png) + +## DateTime + +Just like `doc.Date`, you can also use the `doc.DateTime` like this: + +```python +from datetime import datetime + +from sanic import Sanic +from sanic.response import json + +from sanic_openapi import doc, swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +@app.get("/test") +@doc.produces({"datetime": doc.DateTime()}) +async def test(request): + return json({"datetime": datetime.utcnow().isoformat()}) + +``` + +And the swagger: +![](../_static/images/fields/datetime.png) + +## File + +Sanic-OpenAPI also support file field now. You can use this field to upload file through the swagger. +For example: + +```python +from sanic import Sanic +from sanic.response import json + +from sanic_openapi import doc, swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +@app.post("/test") +@doc.consumes( + doc.File(name="file"), location="formData", content_type="multipart/form-data" +) +@doc.produces({"size": doc.Integer(), "type": doc.String()}) +async def test(request): + file = request.files.get("file") + size = len(file.body) + return json({"size": size, "type": file.type}) + +``` + +And it would be a upload button on swagger: +![](../_static/images/fields/file.png) + +## Dictionary + +To be done. + +## JsonBody + +To document you request or response body, the `doc.JsonBody` is the best choice. You can put a `dict` into `doc.JsonBody` like this: + +```python +from sanic import Sanic +from sanic.response import json + +from sanic_openapi import doc, swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +@app.post("/test") +@doc.consumes( + doc.JsonBody( + { + "useranme": doc.String("The name of your user account."), + "password": doc.String("The password of your user account."), + } + ), + location="body", +) +async def test(request): + return json({}) + +``` + +And it will convert to: +![](../_static/images/fields/josnbody.png) + +```eval_rst +.. note:: The `doc.JsonBody` only support `dict` input. If you want to put a python `class` in body, please use `doc.Object`. +``` + +## List + +When design a RESTful with list resources API, the `doc.List` can help you document this API. + +For example: + +```python +from sanic import Sanic +from sanic.response import json + +from sanic_openapi import doc, swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +class User: + username = doc.String("The name of your user account.") + password = doc.String("The password of your user account.") + + +@app.get("/test") +@doc.produces(doc.List(User)) +async def test(request): + return json([]) + +``` + +The swagger will be: +![](../_static/images/fields/list.png) + +```eval_rst +.. note:: When using a Python `class` to model your data, Sanic-OpenAPI will put it at model definitions. +``` + +## Object + +In Sanic-OpenAPI, you can document your data as a Python `class` and it wil be convert to `doc.Object` automaticlly. After the conversion, you can find your model definitions at the bottom of swagger. + +```python +from sanic import Sanic +from sanic.response import json + +from sanic_openapi import doc, swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +class User: + username = doc.String("The name of your user account.") + password = doc.String("The password of your user account.") + + +@app.get("/test") +@doc.produces(User) +async def test(request): + return json({}) + +``` + +And the result: +![](../_static/images/fields/object.png) + + +## Descriptive Field + +As previous example, you can use python class to document your request or response body. To make it more descriptive, you can add the descriptoin to every fields if you need. For example: + +```python +from sanic import Sanic +from sanic.response import json + +from sanic_openapi import doc, swagger_blueprint + +app = Sanic() +app.blueprint(swagger_blueprint) + + +class Car: + make = doc.String("Who made the car") + model = doc.String("Type of car. This will vary by make") + year = doc.Integer("4-digit year of the car", required=False) + +class Garage: + spaces = doc.Integer("How many cars can fit in the garage") + cars = doc.List(Car, description="All cars in the garage") + +@app.get("/test") +@doc.produces(Garage) +async def test(request): + return json({}) + + +``` + +And you can get this model definitions on swagger: +![](../_static/images/fields/describe_mdoel.png) diff --git a/examples/cars/README.md b/examples/cars/README.md new file mode 100644 index 0000000..4ee7b5e --- /dev/null +++ b/examples/cars/README.md @@ -0,0 +1,21 @@ +## Cars + +[Source](https://github.com/huge-success/sanic-openapi/tree/master/examples/cars) + +This example shows how to use Sanic-OpenAPI with Sanic's Blueprint and model all the data into Sanic-OpenAPI's fields. + +To run this example, please make sure you have already install `sanic` and `sanic-openapi`. +Or, run the install command: + +```shell +pip install sanic sanic-openapi +``` + +And, you can run this example by: + +```shell +python main.py +``` + +Now, check and you should see the swagger like: +![](./swagger.png) diff --git a/examples/cars/swagger.png b/examples/cars/swagger.png new file mode 100644 index 0000000..5324f9b Binary files /dev/null and b/examples/cars/swagger.png differ diff --git a/examples/class_based_view/README.md b/examples/class_based_view/README.md new file mode 100644 index 0000000..01aa8b5 --- /dev/null +++ b/examples/class_based_view/README.md @@ -0,0 +1,21 @@ +## Class Based View + +[Source](https://github.com/huge-success/sanic-openapi/tree/master/examples/class_based_view) + +This example shows how to use Sanic-OpenAPI with Sanic's Class based view with Sanic-OpenAPI's decorators. + +To run this example, please make sure you have already install `sanic` and `sanic-openapi`. +Or, run the install command: + +```shell +pip install sanic sanic-openapi +``` + +And, you can run this example by: + +```shell +python main.py +``` + +Now, check and you should see the swagger like: +![](./swagger.png) diff --git a/examples/class_based_view/swagger.png b/examples/class_based_view/swagger.png new file mode 100644 index 0000000..a870a31 Binary files /dev/null and b/examples/class_based_view/swagger.png differ diff --git a/images/example_pet_store.PNG b/images/example_pet_store.PNG deleted file mode 100644 index b3c6a71..0000000 Binary files a/images/example_pet_store.PNG and /dev/null differ diff --git a/sanic_openapi/__init__.py b/sanic_openapi/__init__.py index 7bf6c06..ba83348 100644 --- a/sanic_openapi/__init__.py +++ b/sanic_openapi/__init__.py @@ -1,4 +1,4 @@ from .swagger import swagger_blueprint -__version__ = "0.5.3" +__version__ = "0.6.0" __all__ = ["swagger_blueprint"] diff --git a/setup.py b/setup.py index 039cdcc..43ba38c 100644 --- a/setup.py +++ b/setup.py @@ -11,6 +11,8 @@ dev_requires = ["black==19.3b0", "flake8==3.7.7", "isort==4.3.19"] +doc_requires = ["recommonmark==0.5.0", "sphinx==2.1.2", "sphinx-rtd-theme-0.4.3"] + test_requires = [ "coverage==4.5.3", "pytest==4.6.2", @@ -46,7 +48,11 @@ package_data={"sanic_openapi": ["ui/*"]}, platforms="any", install_requires=install_requires, - extras_require={"dev": dev_requires + test_requires, "test": test_requires}, + extras_require={ + "dev": dev_requires + test_requires + doc_requires, + "test": test_requires, + "doc": doc_requires, + }, classifiers=[ "Development Status :: 2 - Pre-Alpha", "Environment :: Web Environment",