Skip to content

Commit

Permalink
add examples using object instances (#32)
Browse files Browse the repository at this point in the history
Add reusable components during registration, add two more example modules
  • Loading branch information
kulgan authored Aug 17, 2020
1 parent adafbf3 commit dcdcd44
Show file tree
Hide file tree
Showing 18 changed files with 650 additions and 165 deletions.
12 changes: 7 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ Add top level openapi objects like `Info <https://swagger.io/specification/#info
This adds the following endpoints to your list

* /openapi.yaml
* /openapi.json
* /swagger-ui
* /docs
* /docs/openapi.yaml
* /docs/openapi.json

Start Documenting
"""""""""""""""""
Expand Down Expand Up @@ -133,15 +133,17 @@ A GET example with path parameter
"""
return sample
Run your app and visit `/swagger-ui` to see the generated openapi specs
Run your app and visit `/docs` to see the generated openapi specs

Running Examples
================

Two example projects are currently provided

* `inventory <src/flaskdoc/examples/inventory.py>`_
* `petstore <src/flaskdoc/examples/petstore.py>`_
* `petstore <src/flaskdoc/examples/petstore.py>`_ source `OpenAPI Petstore <https://petstore.swagger.io>`_
* `link-example <src/flaskdoc/examples/link_example/v0.py>`_ - source `OpenAPI link example <https://github.com/OAI/OpenAPI-Specification/blob/master/examples/v3.0/link-example.json>`_
* `api-with-example <src/flaskdoc/examples/api_with_example.py>`_ - source `OpenAPI api_with_examples <https://github.com/OAI/OpenAPI-Specification/blob/master/examples/v3.0/api-with-examples.json>`_

To run

Expand Down
13 changes: 10 additions & 3 deletions docs/source/examples.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. _flaskdoc-examples:

Examples
========

Expand All @@ -22,7 +24,13 @@ flaskdoc examples can be invoked as follows:
$ flaskdoc start -n <example>
Where example can either be petstore or inventory, use ``all`` to register all examples at once
Where example can either be one of

* petstore
* inventory
* link-example
* api-with-example


Petstore Example
----------------
Expand All @@ -42,5 +50,4 @@ Implements the simple inventory api provided by swaggerhub. To run inventory exa
$ flaskdoc start -n inventory
Visit http://localhost:{port}/swagger-ui to see the SwaggerUI or http://localhost:{port}/swagger-ui/redoc to see
the redoc version
Visit http://localhost:15172/docs to see the UI
9 changes: 5 additions & 4 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ This section provides documentation on how to get started using flaskdoc in your

tutorial
examples
snippets
flaskdoc
changelog

Expand Down Expand Up @@ -69,9 +70,9 @@ Add top level openapi objects like `Info <https://swagger.io/specification/#info
This adds the following endpoints to your list

* /openapi.yaml
* /openapi.json
* /swagger-ui
* /docs
* /docs/openapi.yaml
* /docs/openapi.json

Start Documenting
"""""""""""""""""
Expand Down Expand Up @@ -116,7 +117,7 @@ A GET example with path parameter
"""
return sample
Run your app and visit `/swagger-ui` to see the generated openapi specs.
Run your app and visit `/docs` to see the generated openapi specs.

Contributing
------------
Expand Down
55 changes: 55 additions & 0 deletions docs/source/snippets.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
.. _snippets:

Snippets
========
Code usage examples and snippets


Reusable Components
"""""""""""""""""""
Reusable examples and other components can be adding to the components using:

.. code-block:: python
from flaskdoc import swagger, register_openapi
examples = {
"foo": swagger.Example(value=10)
}
links = {
"l1": swagger.Link(operation_id="L!")
}
register_openapi(app, info, examples=examples, links=links)
Reference Objects
"""""""""""""""""
Resuable components can be referenced using proper ReferenceObject

.. literalinclude:: ../../src/flaskdoc/examples/api_with_examples.py
:language: python
:linenos:
:lines: 82-99
:emphasize-lines: 7, 11

Defining Examples
"""""""""""""""""
.. literalinclude:: ../../src/flaskdoc/examples/api_with_examples.py
:language: python
:linenos:
:lines: 9-60



Using Enums
"""""""""""
.. literalinclude:: ../../src/flaskdoc/examples/link_example/schemas.py
:language: python
:linenos:
:lines: 26-31

.. literalinclude:: ../../src/flaskdoc/examples/link_example/specs.py
:language: python
:linenos:
:lines: 45-59
:emphasize-lines: 6
6 changes: 5 additions & 1 deletion docs/source/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ Flaskdoc exposes swagger specific models via ``flaskdoc.swagger``
Registering openapi routes using openapi involves simply doing

.. note::
See :ref:`snippets` section for more detailed examples on how to use the register_openapi function


.. code-block:: python
import flask
Expand All @@ -55,7 +59,7 @@ Registering openapi routes using openapi involves simply doing
register_openapi(app, info=info)
Under the hood, flaskdoc simply does a:
Under the hood, flaskdoc simply does:
.. code-block:: python
Expand Down
5 changes: 4 additions & 1 deletion src/flaskdoc/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ def flaskdoc():
@click.option(
"--name",
"-n",
type=click.Choice(["inventory", "petstore", "all"], case_sensitive=False),
type=click.Choice(
["inventory", "petstore", "all", "mocks", "api-with-examples", "link-example"],
case_sensitive=False,
),
default="inventory",
)
def start_examples(name):
Expand Down
54 changes: 48 additions & 6 deletions src/flaskdoc/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@ class DictMixin:
def to_dict(self):
""" Converts object to dictionary """

return self._parse_dict(self.__dict__)
return self.parse(self.__dict__)

def _parse_dict(self, val):
def parse(self, val):
parsed = {}
# print(val)
convert_to_came_case = val.get("_camel_case_fields_", False)
for k, v in val.items():
if k.startswith("__") or k == "_camel_case_fields_":
Expand All @@ -31,7 +30,7 @@ def _parse_dict(self, val):
continue
if k == "extensions":
# handle extensions
extensions = self._parse_dict(v)
extensions = self.parse(v)
parsed.update(extensions)
continue
# map ref
Expand All @@ -50,9 +49,9 @@ def _to_dict(self, val):
if isinstance(val, list):
return [self._to_dict(v) for v in val]
if isinstance(val, collections.Mapping):
return self._parse_dict(val)
return self.parse(val)
if hasattr(val, "__dict__"):
return self._parse_dict(val.__dict__)
return self.parse(val.__dict__)

return val

Expand Down Expand Up @@ -94,3 +93,46 @@ def json(self, indent=2):

def convert_props(self, to_camel_case=True):
self._camel_case_fields_ = to_camel_case


class ExtensionMixin(ModelMixin):

extensions = attr.ib(default={})

def add_extension(self, name, value):
""" Allows extensions to the Swagger Schema.
The field name MUST begin with x-, for example, x-internal-id. The value can be null, a primitive,
an array or an object.
Args:
name (str): custom extension name, must begin with x-
value (Any): value, can be None, any object or list
Returns:
ModelMixin: for chaining
Raises:
ValueError: if key name is invalid
"""
self.validate_extension_name(name)
if not self.extensions:
self.extensions = {}
self.extensions[name] = value
return self

@staticmethod
def validate_extension_name(value):
"""
Validates a custom extension name
Args:
value (str): custom extension name
Raises:
ValueError: if key name is invalid
"""
if value and not value.startswith("x-"):
raise ValueError("Custom extension must start with x-")

@extensions.validator
def validate(self, _, ext):
""" Validates the name of all provided extensions """
if ext:
for k in ext:
self.validate_extension_name(k)
98 changes: 98 additions & 0 deletions src/flaskdoc/examples/api_with_examples.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import flask

from flaskdoc import swagger

api = flask.Blueprint("api-examples", __name__, url_prefix="/api-with-examples")

info = swagger.Info(title="Simple API Overview", version="2.0.0")

examples = {
"foo": swagger.Example(
value={
"versions": [
{
"status": "CURRENT",
"updated": "2011-01-21T11:33:21Z",
"id": "v2.0",
"links": [{"href": "http://127.0.0.1:8774/v2/", "rel": "self"}],
},
{
"status": "EXPERIMENTAL",
"updated": "2013-07-23T11:33:21Z",
"id": "v3.0",
"links": [{"href": "http://127.0.0.1:8774/v3/", "rel": "self"}],
},
]
}
),
"v2-foo": swagger.Example(
value={
"version": {
"status": "CURRENT",
"updated": "2011-01-21T11:33:21Z",
"media-types": [
{
"base": "application/xml",
"type": "application/vnd.openstack.compute+xml;version=2",
},
{
"base": "application/json",
"type": "application/vnd.openstack.compute+json;version=2",
},
],
"id": "v2.0",
"links": [
{"href": "http://23.253.228.211:8774/v2/", "rel": "self"},
{
"href": "http://docs.openstack.org/api/openstack-compute/2/os-compute-devguide-2.pdf",
"type": "application/pdf",
"rel": "describedby",
},
{
"href": "http://docs.openstack.org/api/openstack-compute/2/wadl/os-compute-2.wadl",
"type": "application/vnd.sun.wadl+xml",
"rel": "describedby",
},
],
}
}
),
}


@swagger.GET(
tags=["listVersionsv2"],
summary="List API versions",
responses={
"200": swagger.ResponseObject(
description="200 response",
content=swagger.JsonType(examples={"foo": swagger.ExampleReference("foo")}),
),
"300": swagger.ResponseObject(
description="300 response",
content=swagger.JsonType(examples={"foo": swagger.ExampleReference("foo")}),
),
},
)
@api.route("/", methods=["GET"])
def root():
return flask.jsonify(examples["foo"])


@swagger.GET(
tags=["getVersionDetailsv2"],
summary="Show API versions details",
responses={
"200": swagger.ResponseObject(
description="200 response",
content=swagger.JsonType(examples={"foo": swagger.ExampleReference("v2-foo")}),
),
"300": swagger.ResponseObject(
description="300 response",
content=swagger.JsonType(examples={"foo": swagger.ExampleReference("v2-foo")}),
),
},
)
@api.route("/v2", methods=["GET"])
def details():
return flask.jsonify(examples["v2-foo"])
Loading

0 comments on commit dcdcd44

Please sign in to comment.