Quickstart¶
So let’s get your pyramid app up and running!
The core steps to use pyramid_swagger are quite simple:
- Create a Swagger Schema for your service’s endpoints
- Add pyramid_swagger to your Pyramid application
Creating your first Swagger Schema¶
Creating your initial Swagger Schema can be intimidating but don’t fear, it’s not nearly as much work as it might initially appear.
To create your first Swagger Schema, I encourage you to take a look at Swagger’s official PetStore example. You can even see the raw JSON for the Swagger Schema. You’ll notice that Swagger has a lot of details, but the core part of building a schema is documenting each endpoint’s inputs and outputs.
For your intial attempt, documenting an endpoint can be simplified to some basic components:
- Documenting the core URI (e.g. /foo/bar)
- Documenting request parameters (in the path, in the query arguments, and in the query body)
- Documenting the response
There are many other pieces of your REST interface that Swagger can describe, but these are the core components. The PetStore example has some good examples of all of these various types, so it can be a useful reference as you get used to the syntax.
For any questions about various details of documenting your interface with Swagger, you can consult the official Swagger Spec, although you may find it somewhat difficult to parse for use as anything but a reference.
You may find that the process of writing your API down in the Swagger format is surprisingly hard…this is good! It probably suggests that your API is not terribly well understood or maybe even underspecified right now. Anecdotally, users commonly report that writing their first Swagger api-docs has the unintended side effect of forcing them to reconsider exactly how their service should be interacting with the outside world – a useful exercise!
Where to put your Swagger Schema¶
Great, so we have one large JSON file containing our API declaration for all endpoints our service supports. What now?
Now place the Swagger Schema in api_docs/swagger.json
. The path has no relation to the paths described in your API declaration, it is only used internally to help Swagger discover your schemas.
Add pyramid_swagger to your webapp¶
Last but not least, we need to turn on the pyramid_swagger library within your application. This is quite easy by default, either by augmenting your PasteDeploy .ini file, or by adding a line to your webapp method.
We’ll show you the .ini method here, but you can read how to imperatively add the library to your app (and much more) in the configuration page of these docs. For those using the .ini file, simply add the following line under your [app:main]
section:
[app:main]
pyramid.includes = pyramid_swagger
With that, when your app starts you will get the benefit of:
- 4xx errors for requests not matching your schema
- 5xx errors for responses not matching your schema
- Automatic validation for correctness of your Swagger Schema at application startup
- Automatic serving of your Swagger Schema from the /swagger.json endpoint
Update the routes¶
For each of the routes declared in your swagger.json, you need to add the route to the Pyramid dispatch using traditional methods. For example, in your __init__.py:
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
config = Configurator(settings=settings)
config.include('pyramid_chameleon')
config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('api.things.get', '/api/things', request_method='GET')
#
# Additional routes go here
#
config.scan()
return config.make_wsgi_app()
Accessing request data¶
Now that pyramid_swagger
is enabled you can create a view. All the
values that are specified in the Swagger Schema for an endpoint are available
from a single dict
on the request request.swagger_data
. These
values are casted to the type specified by the Swagger Schema.
Example:
from pyramid.view import view_config
@view_config(route_name='api.things.get')
def get_things(request):
# Returns thing_id as an int (assuming the swagger type is integer)
thing_id = request.swagger_data['thing_id']
...
return {...}
The raw values (not-casted to any type) are still available from their usual place on the request (matchdict, GET, POST, json(), etc)
If you have pyramid_swagger.use_models
set to true, you can interact with
models defined in #/definitions
as Python classes instead of dicts.
{
"swagger": "2.0",
"definitions": {
"User": {
"type": "object",
"properties": {
"first_name": {
"type": "string"
},
"last_name": {
"type": "string"
}
}
}
}
}
@view_config(route_name='add.user')
def add_user(request):
user = request.swagger_data['user']
assert isinstance(user, bravado_core.models.User)
first_name = user.first_name
...
Otherwise, models are represented as dicts.
@view_config(route_name='add.user')
def add_user(request):
user = request.swagger_data['user']
assert isinstance(user, dict)
first_name = user['first_name']
...
Note
Values in request.swagger_data
are only available if
pyramid_swagger.enable_request_validation
is enabled.
Accessing Swagger Operation¶
During the implementation of an endpoint you could eventually have need of accessing the Swagger Specs that defined that specific view.
pyramid_swagger
will inject in the request object a new property (that will be evaluated only if accessed) called operation
.
request.operation
will be set to None
for Swagger 1.2 defined endpoints, while it will be an Operation object if the endpoint is defined by Swagger 2.0 specs.
pyramid_swagger renderer¶
Using pyramid_swagger
you will get automatic conversions of the input JSON objects to easy to handle python objects.
An example could be a swagger object string property using the date
format , the library will take care of converting the
ISO 8601 date representation to an easy to handle python datetime.date
object.
While defining the pyramid
view that will handle the endpoint you have to make sure that the chosen renderer will be able to
properly render your response. In the case of an endpoint that returns objects that requires a special handling
(like datetime.date
) the developer is forced to:
- manually convert the python object to an object that could be handled by the renderer
- add an adapter for instructing pyramid to handle your object
- define a custom renderer that is able to properly serialize the object
pyramid_swagger
provides:
- a new renderer, called
pyramid_swagger
- a new renderer renderer factory, called
pyramid_swagger.renderer.PyramidSwaggerRendererFactory
How pyramid_swagger renderer works¶
The new pyramid_swagger
renderer is a wrapper around the default pyramid.renderers.JSON
renderer.
pyramid_swagger
will receive, from your pyramid view, the object that has to be rendered, perform the marshaling operations and then call the default JSON renderer.
Note
The usage of this renderer allows to get full support of custom formats
Let’s assume that your view returns {'date': datetime.date.today()}
and that your response spec is similar to
{
"200": {
"description": "HTTP/200",
"schema": {
"properties": {
"date": {
"type": "string",
"format": "date"
}
}
}
}
}
If your view is configured to use json
renderer then your endpoint will surprisingly return HTTP/500 errors.
The errors are caused by the fact that pyramid.renderers.JSON
is not aware on how to convert a datetime.date
object.
If your view is configured to use pyramid_swagger
renderer then your endpoint will provide HTTP/200 responses similar
to {"date": "2017-09-16"}
.
This is possible because the marsharling of the view return value converts the datetime.date
object to its ISO 8601
string representation that could be handled by the default JSON renderer.
Note
The marshaling operation will be performed according to the specific response schema defined for the particular endpoint. It means that if your response doesn’t specify a field it will be transparently passed to the wrapped renderer.
How PyramidSwaggerRendererFactory works¶
PyramidSwaggerRendererFactory
allows you to create a custom renderer that operates on the marshaled result from your view.
The defined renderer will operate on the marshaled, according to the Swagger Specification, response.
Example of definition of a custom renderer
class MyPersonalRendererFactory(object):
def __init__(self, info):
# Initialize your factory (refer to standard documentation for more information)
pass
def __call__(self, value, system):
# ``value`` contain the marshaled representation of the object returned by your view.
# If your view is returning a ``datetime.date`` object for a field with date format
# you can assume that the field has already been converted to its ISO 8601 representation
# perform your personal rendering operations
# you can assume that value is a marshaled response, so already JSON serializable object
return rendered_value
Once you have defined your own renderer you have to wrap the new renderer in PyramidSwaggerRendererFactory
and register it to the pyramid framework as described by Adding and Changing Renderers pyramid documentation.
config.add_renderer(name='custom_renderer', factory=PyramidSwaggerRendererFactory(MyPersonalRendererFactory))