Cornice Swagger is an extension package for Cornice that allows generating an OpenAPI/Swagger specification from Cornice service definitions.
Cornice swagger builds a valid OpenAPI document with basically these things:
You may install us with pip:
$ pip install cornice_swagger
From an existing Cornice application, you may add this extension to your Pyramid configurator after including cornice:
from pyramid.config import Configurator
def setup():
config = Configurator()
config.include('cornice')
config.include('cornice_swagger')
You can than create your OpenAPI/Swagger JSON using:
from cornice_swagger import CorniceSwagger
from cornice.service import get_services
my_generator = CorniceSwagger(get_services())
my_spec = my_generator('MyAPI', '1.0.0')
Alternatively you can use a directive to set up OpenAPI/Swagger JSON and serve API explorer on your application:
config = Configurator()
config.include('cornice')
config.include('cornice_swagger')
config.cornice_enable_openapi_view(
api_path='/api-explorer/swagger.json',
title='MyAPI',
description="OpenAPI documentation",
version='1.0.0'
)
config.cornice_enable_openapi_explorer(
api_explorer_path='/api-explorer')
Then you will be able to access Swagger UI API explorer on url:
http://localhost:8000/api-explorer (in the example above)
If you want to start a new project, there is a cookiecutter scaffold that can be used:
$ cookiecutter https://github.com/delijati/cookiecutter-cornice_swagger.git
$ cd demo
$ pip install -e .
$ cd demo/static
$ bower install
import colander
from cornice import Service
from cornice.validators import colander_body_validator
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
_VALUES = {}
# Create a simple service that will store and retrieve values
values = Service(name='foo',
path='/values/{key}',
description="Cornice Demo")
# Create a body schema for our requests
class BodySchema(colander.MappingSchema):
value = colander.SchemaNode(colander.String(),
description='My precious value')
# Create a response schema for our 200 responses
class OkResponseSchema(colander.MappingSchema):
body = BodySchema()
# Aggregate the response schemas for get requests
response_schemas = {
'200': OkResponseSchema(description='Return value')
}
# Create our cornice service views
class MyValueApi(object):
"""My precious API."""
@values.get(tags=['values'], response_schemas=response_schemas)
def get_value(request):
"""Returns the value."""
key = request.matchdict['key']
return _VALUES.get(key)
@values.put(tags=['values'], validators=(colander_body_validator, ),
schema=BodySchema(), response_schemas=response_schemas)
def set_value(request):
"""Set the value and returns *True* or *False*."""
key = request.matchdict['key']
_VALUES[key] = request.json_body
return _VALUES.get(key)
# Setup and run our app
def setup():
config = Configurator()
config.include('cornice')
config.include('cornice_swagger')
# Create views to serve our OpenAPI spec
config.cornice_enable_openapi_view(
api_path='/__api__',
title='MyAPI',
description="OpenAPI documentation",
version='1.0.0'
)
# Create views to serve OpenAPI spec UI explorer
config.cornice_enable_openapi_explorer(api_explorer_path='/api-explorer')
config.scan()
app = config.make_wsgi_app()
return app
if __name__ == '__main__':
app = setup()
server = make_server('127.0.0.1', 8000, app)
print('Visit me on http://127.0.0.1:8000')
print('''You can see the API explorer here:
http://127.0.0.1:8000/api-explorer''')
server.serve_forever()
The resulting swagger.json at http://localhost:8000/__api__ is:
{
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "MyAPI"
},
"basePath": "/",
"tags": [
{
"name": "values"
}
]
"paths": {
"/values/{key}": {
"parameters": [
{
"name": "value",
"in": "path",
"required": true,
"type": "string"
}
],
"get": {
"tags": [
"values"
],
"responses": {
"200": {
"description": "Return value",
"schema": {
"required": [
"value"
],
"type": "object",
"properties": {
"value": {
"type": "string",
"description": "My precious value",
"title": "Value"
}
},
"title": "BodySchema"
}
}
},
"produces": [
"application/json"
]
},
"put": {
"tags": [
"values"
],
"parameters": [
{
"name": "PutBodySchema",
"in": "body",
"schema": {
"required": [
"value"
],
"type": "object",
"properties": {
"value": {
"type": "string",
"description": "My precious value",
"title": "Value"
}
},
"title": "PutBodySchema"
},
"required": true
}
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "Return value",
"schema": {
"required": [
"value"
],
"type": "object",
"properties": {
"value": {
"type": "string",
"description": "My precious value",
"title": "Value"
}
},
"title": "BodySchema"
}
}
}
}
},
"/__api__": {
"get": {
"responses": {
"default": {
"description": "UNDOCUMENTED RESPONSE"
}
},
"produces": [
"application/json"
]
}
}
}
}
Here you may find the general aspects used by Colander Swagger to generate the swagger documentation. Most examples presented on this section refer to the example on quickstart.
In order to enable response documentation, you must add this extension to your Pyramid config. For that you may use:
from pyramid.config import Configurator
def setup():
config = Configurator()
config.include('cornice')
config.include('cornice_swagger')
config.cornice_enable_openapi_view(
title='MyAPI',
description="OpenAPI documentation",
version='1.0.0'
)
config.cornice_enable_openapi_explorer()
If you don’t know what this is about or need more information, please check the Pyramid documentation
By default API explorer will be served under /api-explorer path in your application. You can easily configure the paths, required permissions and Pyramid route factory.
Additional kwargs
passed to this directive will be passed to
CorniceSwagger.generate
method.
Path parameters may be automatically extracted from the service definition, you may overwrite then with schemas on the view if you wish to add more view-specific information.
Another example:
values = Service(name='foo',
path='/values/{value}')
{
"paths": {
"/values/{value}": {
"parameters": [
{
"name": "value",
"in": "path",
"required": true,
"type": "string"
}
]
}
}
}
When using colander validators such as colader_validator
or
colander_body_validator
, we can extract the operation parameters
from the request schema. The schemas should comply with
Cornice 2.0 colander schemas.
from cornice.validators import colander_body_validator
values = Service(name='foo',
path='/values/{value}')
class PutBodySchema(colander.MappingSchema):
value = colander.SchemaNode(colander.String(),
description='My precious value')
@values.put(validators=(colander_body_validator, ),
schema=PutBodySchema())
def set_value(request):
"""Set the value and returns *True* or *False*."""
{
"paths": {
"/values/{value}": {
"put": {
"parameters": [
{
"name": "PutBodySchema",
"in": "body",
"required": true,
"schema": {
"title": "PutBodySchema",
"type": "object",
"properties": {
"value": {
"type": "string",
"description": "My precious value",
"title": "Value"
}
},
"required": [
"value"
]
}
}
]
}
}
}
}
When using colander_validator, the request should have fields corresponding the parameters locations as follows:
from cornice.validators import colander_validator
class BodySchema(colander.MappingSchema):
value = colander.SchemaNode(colander.String(),
description='My precious value')
class QuerySchema(colander.MappingSchema):
foo = colander.SchemaNode(colander.String(), missing=colander.drop)
class HeaderSchema(colander.MappingSchema):
bar = colander.SchemaNode(colander.String(), default='blah')
class PutRequestSchema(colander.MappingSchema):
body = BodySchema()
querystring = QuerySchema()
header = HeaderSchema()
@values.put(validators=(colander_validator, ),
schema=PutRequestSchema())
def set_value(request):
"""Set the value and returns *True* or *False*."""
pass
When using custom validators, you can pass a method that transforms your custom schema
into a regular Cornice schema that can be validated with colander_validator
, so
Cornice Swagger knows how to convert it to swagger parameters.
from cornice.validators import colander_body_validator
MY_IDS = [1, 2, 42]
def my_custom_validator(request, **kwargs):
schema = kwargs.get('schema')
# Peforms random additional validation
if schema and schema['id'] not in MY_IDS:
request.errors.add("body", "id", "Invalid id.")
return colander_body_validator(request, **kwargs)
@values.put(validators=(my_custom_validator, ),
schema=PutRequestSchema())
def set_value(request):
pass
from cornice.service import get_services
from cornice_swagger import CorniceSwagger
from cornice_swagger.utils import body_schema_converter
def my_custom_schema_converter(schema, args):
validators = args.get('validators', [])
if my_custom_validator in validators:
return body_schema_converter(schema, args)
swagger = CorniceSwagger(get_services())
swagger.schema_transformers.append(my_custom_schema_converter)
print(swagger.generate())
The produced content-type field is filled by the cornice renderer you are using. We currently support json, simplejson and xml renderers. Cornice uses simplejson renderer by default, so if you don’t specify a renderer you may expect to find application/json on your operation produce fields.
values = Service(name='foo',
path='/values/{value}')
@values.put(renderer='xml')
def set_value(request):
"""Set the value and returns *True* or *False*."""
{
"paths": {
"/values/{value}": {
"put": {
"produces": [
"text/xml"
]
}
}
}
}
On cornice you can defined the accepted content-types for your view through the content_type field. And we use it to generate the Swagger consumes types.
values = Service(name='foo',
path='/values/{value}')
@values.put(content_type=('application/json', 'text/xml'))
def set_value(request):
"""Set the value and returns *True* or *False*."""
{
"paths": {
"/values/{value}": {
"put": {
"consumes": [
"application/json",
"text/xml"
]
}
}
}
}
Unfortunately, on Cornice we don’t have a way to provide response schemas, so this part must be provided separately and handled by Cornice Swagger.
For that you must provide a Response Colander Schema that follows the pattern:
class ResponseSchema(colander.MappingSchema):
body = BodySchema()
headers = HeaderSchema()
get_response_schemas = {
'200': ResponseSchema(description='Return my OK response'),
'404': ResponseSchema(description='Return my not found response')
}
Notice that the ResponseSchema
class follows the same pattern as the
Cornice requests using cornice.validators.colander_validator
(except for querystrings, since obviously we don’t have querystrings on responses).
A response schema mapping, as the get_response_schemas
dict should aggregate
response schemas as the one defined as ResponseSchema
with keys matching the
response status code of for each entry. All schema entries should contain descriptions.
You may also provide a default
response schema to be used if the response doesn’t
match any of the status codes provided.
From our minimalist example:
values = Service(name='foo',
path='/values/{value}')
# Create a body schema for our requests
class BodySchema(colander.MappingSchema):
value = colander.SchemaNode(colander.String(),
description='My precious value')
# Create a response schema for our 200 responses
class OkResponseSchema(colander.MappingSchema):
body = BodySchema()
# Aggregate the response schemas for get requests
response_schemas = {
'200': OkResponseSchema(description='Return value')
}
@values.put(response_schemas=response_schemas)
def set_value(request):
"""Set the value and returns *True* or *False*."""
{
"paths": {
"/values/{value}": {
"put": {
"responses": {
"200": {
"description": "Return value",
"schema": {
"required": [
"value"
],
"type": "object",
"properties": {
"value": {
"type": "string",
"description": "My precious value",
"title": "Value"
}
},
"title": "BodySchema"
}
}
}
}
}
}
}
Cornice Swagger supports two ways of documenting operation tags. You can either
provide a list of tags on the view decorator or have a default_tags
attribute when calling the generator.
values = Service(name='foo',
path='/values/{value}')
@values.put(tags=['value'])
def set_value(request):
"""Set the value and returns *True* or *False*."""
{
"tags": [
{
"name": "values"
}
],
"paths": {
"/values/{value}": {
"get": {
"tags": [
"values"
]
}
}
}
}
When using the default_tags
attribute, you can either use a raw list
of tags or a callable that takes a cornice service and returns a list of tags.
def default_tag_callable(service):
return [service.path.split('/')[1]]
swagger = CorniceSwagger(get_services())
swagger.default_tags = default_tag_callable
spec = swagger.generate('IceCreamAPI', '4.2')
from cornice.service import get_services
from cornice_swagger import CorniceSwagger
swagger = CorniceSwagger(get_services())
swagger.default_tags = ['IceCream']
spec = swagger.generate('IceCreamAPI', '4.2')
You may use view docstrings to create operation summaries. You may enable
this by passing summary_docstrings=True
when calling the generator.
For example, the following view definition docstring will correspond to
the following swagger summary:
values = Service(name='foo',
path='/values')
@values.get()
def get_value(request):
"""Returns the value."""
swagger = CorniceSwagger(get_services())
swagger.summary_docstrings = True
spec = swagger.generate('IceCreamAPI', '4.2')
{
"paths": {
"/values": {
"get": {
"summary": "Returns the value."
}
}
}
}
By default standard Swagger UI (https://swagger.io/swagger-ui/) config is used, but you can customize the generated script tag by providing your own callable path in config.
The default one is:
cornice_swagger.swagger_ui_script_generator = cornice_swagger.views:swagger_ui_script_template
It points to the following callable that accepts a request object:
def swagger_ui_script_template(request, **kwargs):
"""
:param request:
:return:
Generates the <script> code that bootstraps Swagger UI, it will be injected
into index template
"""
swagger_spec_url = request.route_url('cornice_swagger.open_api_path')
template = pkg_resources.resource_string(
'cornice_swagger',
'templates/index_script_template.html'
).decode('utf8')
return Template(template).safe_substitute(
swagger_spec_url=swagger_spec_url,
)
Here you may find information about the Cornice Swagger internals and methods that may be overwritten by applications.
cornice_swagger.swagger.
CorniceSwagger
(services=None, def_ref_depth=0, param_ref=False, resp_ref=False, pyramid_registry=None)¶Handles the creation of a swagger document from a cornice application.
schema_transformers
= [<function body_schema_transformer>]¶List of request schema transformers that should be applied to a request schema to make it comply with a cornice default request schema.
type_converter
¶alias of cornice_swagger.converters.schema.TypeConversionDispatcher
parameter_converter
¶alias of cornice_swagger.converters.parameters.ParameterConversionDispatcher
custom_type_converters
= {}¶Mapping for supporting custom types conversion on the default TypeConverter. Should map colander.TypeSchema to cornice_swagger.converters.schema.TypeConverter callables.
default_type_converter
= None¶Supplies a default type converter matching the interface of cornice_swagger.converters.schema.TypeConverter to be used with unknown types.
Provide a default list of tags or a callable that takes a cornice service and the method name (e.g GET) and returns a list of Swagger tags to be used if not provided by the view.
default_op_ids
= None¶Provide a callable that takes a cornice service and the method name (e.g. GET) and returns an operation Id that is used if an operation Id is not provided. Each operation Id should be unique.
default_security
= None¶Provide a default list or a callable that takes a cornice service and the method name (e.g. GET) and returns a list of OpenAPI security policies.
summary_docstrings
= False¶Enable extracting operation summaries from view docstrings.
ignore_methods
= ['HEAD', 'OPTIONS']¶List of service methods that should NOT be presented on the documentation. You may use this to remove methods that are not essential on the API documentation. Default ignores HEAD and OPTIONS.
ignore_ctypes
= []¶List of service content-types that should NOT be presented on the documentation. You may use this when a Cornice service definition has multiple view definitions for a same method, which is not supported on OpenAPI 2.0.
api_title
= ''¶Title of the OpenAPI document.
api_version
= ''¶Version of the OpenAPI document.
base_path
= '/'¶Base path of the documented API. Default is “/”.
swagger
= {'info': {}}¶Base OpenAPI document that should be merged with the extracted info from the generate call.
services
= []¶List of cornice services to document. You may use cornice.service.get_services() to get it.
definitions
¶Default cornice_swagger.swagger.DefinitionHandler
class to use when
handling OpenAPI schema definitions from cornice payload schemas.
alias of DefinitionHandler
parameters
¶Default cornice_swagger.swagger.ParameterHandler
class to use when
handling OpenAPI operation parameters from cornice request schemas.
alias of ParameterHandler
responses
¶Default cornice_swagger.swagger.ResponseHandler
class to use when
handling OpenAPI responses from cornice_swagger defined responses.
alias of ResponseHandler
generate
(title=None, version=None, base_path=None, info=None, swagger=None, **kwargs)¶Generate a Swagger 2.0 documentation. Keyword arguments may be used to provide additional information to build methods as such ignores.
Parameters: |
|
---|---|
Return type: | dict |
Returns: | Full OpenAPI/Swagger compliant specification for the application. |
cornice_swagger.
cornice_enable_openapi_view
(config, api_path='/api-explorer/swagger.json', permission='__no_permission_required__', route_factory=None, **kwargs)¶Parameters: |
|
---|
This registers and configures the view that serves api definitions
cornice_swagger.
cornice_enable_openapi_explorer
(config, api_explorer_path='/api-explorer', permission='__no_permission_required__', route_factory=None, **kwargs)¶Parameters: |
|
---|
This registers and configures the view that serves api explorer
CorniceSwagger.
_build_paths
()¶Build the Swagger “paths” and “tags” attributes from cornice service definitions.
CorniceSwagger.
_extract_path_from_service
(service)¶Extract path object and its parameters from service definitions.
Parameters: | service – Cornice service to extract information from. |
---|---|
Return type: | dict |
Returns: | Path definition. |
CorniceSwagger.
_extract_operation_from_view
(view, args)¶Extract swagger operation details from colander view definitions.
Parameters: |
|
---|---|
Return type: | dict |
Returns: | Operation definition. |
Swagger definitions and parameters are handled in separate classes. You may overwrite those if you want to change the converters behaviour.
cornice_swagger.swagger.
DefinitionHandler
(ref=0, type_converter=<cornice_swagger.converters.schema.TypeConversionDispatcher object>)¶Handles Swagger object definitions provided by cornice as colander schemas.
DefinitionHandler.
__init__
(ref=0, type_converter=<cornice_swagger.converters.schema.TypeConversionDispatcher object>)¶Parameters: | ref – The depth that should be used by self.ref when calling self.from_schema. |
---|
DefinitionHandler.
from_schema
(schema_node, base_name=None)¶Creates a Swagger definition from a colander schema.
Parameters: |
|
---|---|
Return type: | dict |
Returns: | Swagger schema. |
DefinitionHandler.
_ref_recursive
(schema, depth, base_name=None)¶Dismantle nested swagger schemas into several definitions using JSON pointers. Note: This can be dangerous since definition titles must be unique.
Parameters: |
|
---|---|
Return type: | dict |
Returns: | JSON pointer to the root definition schema, or the original definition if depth is zero. |
cornice_swagger.swagger.
ParameterHandler
(definition_handler=<cornice_swagger.swagger.DefinitionHandler object>, ref=False, type_converter=<cornice_swagger.converters.schema.TypeConversionDispatcher object>, parameter_converter=<cornice_swagger.converters.parameters.ParameterConversionDispatcher object>)¶Handles swagger parameter definitions.
ParameterHandler.
__init__
(definition_handler=<cornice_swagger.swagger.DefinitionHandler object>, ref=False, type_converter=<cornice_swagger.converters.schema.TypeConversionDispatcher object>, parameter_converter=<cornice_swagger.converters.parameters.ParameterConversionDispatcher object>)¶Parameters: |
|
---|
ParameterHandler.
from_schema
(schema_node)¶Creates a list of Swagger params from a colander request schema.
Parameters: |
|
---|---|
Return type: | list |
Returns: | List of Swagger parameters. |
ParameterHandler.
from_path
(path)¶Create a list of Swagger path params from a cornice service path.
Return type: | list |
---|
ParameterHandler.
_ref
(param, base_name=None)¶Store a parameter schema and return a reference to it.
Parameters: |
|
---|---|
Return type: | dict |
Returns: | JSON pointer to the original parameter definition. |
cornice_swagger.swagger.
ResponseHandler
(definition_handler=<cornice_swagger.swagger.DefinitionHandler object>, type_converter=<cornice_swagger.converters.schema.TypeConversionDispatcher object>, ref=False)¶Handles swagger response definitions.
ResponseHandler.
__init__
(definition_handler=<cornice_swagger.swagger.DefinitionHandler object>, type_converter=<cornice_swagger.converters.schema.TypeConversionDispatcher object>, ref=False)¶Parameters: |
|
---|
ResponseHandler.
from_schema_mapping
(schema_mapping)¶Creates a Swagger response object from a dict of response schemas.
Parameters: | schema_mapping – Dict with entries matching {status_code: response_schema} . |
---|---|
Return type: | dict |
Returns: | Response schema. |
ResponseHandler.
_ref
(resp, base_name=None)¶Store a response schema and return a reference to it.
Parameters: |
|
---|---|
Return type: | dict |
Returns: | JSON pointer to the original response definition. |
You may use the cornice_swagger.converters
submodule to access the colander
to swagger request and schema converters. These may be also used without
cornice_swagger
generators.
This module handles the conversion between colander object schemas and swagger object schemas.
cornice_swagger.converters.
convert_schema
(schema_node)¶cornice_swagger.converters.
convert_parameter
(location, schema_node, definition_handler=<function convert_schema>)¶Here is a list of frequently asked questions related to Cornice Swagger.
You may use colander.drop
as it’s missing field:
field = colander.SchemaNode(colander.String(), missing=colander.drop)
You can use Mapping.unknown
attribute
class Query(colander.MappingSchema):
unknown='preserve'
The fastest way to enable Swagger UI is to use directives
cornice_enable_openapi_view
together with
cornice_enable_openapi_explorer
they will provide a special view in your
application that will server the OpenAPI specification along with API explorer.
A common scenario is to have schemas that have optional fields that
substitute default values when fields are missing from request data.
This is normally solved by calling bind() on colander schemas,
one thing that is important to remember is that if the value
needs to be updated per request (like a date or UUID), you need to bind the
schema per request. At the same time for cornice_swagger
to get proper
values when inspecting schema you also need to bind it on decorator level.
Here is an example how to solve this problem:
@colander.deferred
def deferred_conn_id(node, kw):
return kw.get('conn_id') or str(uuid.uuid4())
class SomeSchema(colander.MappingSchema):
username = colander.SchemaNode(colander.String())
conn_id = colander.SchemaNode(
colander.String(), missing=deferred_conn_id)
def rebind_schema(schema):
"""
Ensure we bind schema per request
"""
def deferred_validator(request, **kwargs):
# we need to regenerate the schema here
kwargs['schema'] = schema().bind(request=request)
return colander_validator(request, **kwargs)
return deferred_validator
@legacy_connect_api.post(
schema=SomeSchema().bind(), validators=(rebind_schema(SomeSchema),))
def connect(request):
...
Support swagger example
field on colander SchemaNode
custom kwarg:
def SomeSchema(colander.MappingSchema):
name = colander.SchemaNode(colander.String(), example='Mr. IceCream')
The example
field is returned in the swagger spec accordingly.
cornice_enable_openapi_view()
cornice_enable_openapi_explorer()
Pyramid directives to serve the API Explorer and the spec information (#79)Pyramid compliance
cornice.service.Service.content_type
argument.
For more details, see: http://cornice.readthedocs.io/en/latest/api.html#cornice.service.Service.Pyramid compliance
Api
_extract_path_from_service
, now returns the path name along with the path
swagger object (#68).Api
CorniceSwagger
class (#63).CorniceSwagger.generate
to generate the spec (#63).CorniceSwagger
call method. You should now use generate
(#63).generate_swagger_spec
call. (#64).CorniceSwagger
class. (#65)Internals
Api
summary_docstrings = True
to the generator.swagger
items are now recursively merged (instead of replaced) with
the extracted fields.operation_id
argument on the view
or by passing a default_op_ids
callable to the generator.cornice_swagger.CorniceSwagger
.api_security
list on the view
or by passing a default_security
list or callable to the generator.OpenAPI compliance
title
field from response headers and request parameters.Internals
body
.Api
cornice_swagger.swagger.CorniceSwagger
class to generate
the swagger document rather then generate_swagger_spec
.response_schemas
view attribute.tags
view attribute or using a
default_tags
parameter when calling the generator.Internals
Documentation
Alphabetically-ordered List of the people who contributed to Cornice Swagger:
You can find us at Github or the Slack chat.
You may also try the Cornice Mailing List: