import collections
import functools
from girder import logprint
models = collections.defaultdict(dict)
# routes is dict of dicts of dicts
# e.g. routes[resource][path][method]
routes = collections.defaultdict(
functools.partial(collections.defaultdict, dict))
def _toRoutePath(resource, route):
"""
Convert a base resource type and list of route components into a
Swagger-compatible route path.
"""
# Convert wildcard tokens from :foo form to {foo} form
convRoute = [
'{%s}' % token[1:] if token[0] == ':' else token
for token in route
]
prefix = ['']
# If resource is a string then use this as the prefix to the route.
if isinstance(resource, str):
prefix.append(resource)
path = '/'.join(prefix + convRoute)
return path
def _toOperation(info, resource, handler, path=None, method=None):
"""
Augment route info, returning a Swagger-compatible operation description.
"""
operation = dict(info)
operation['tags'] = [resource]
# Operation Object spec:
# Unique string used to identify the operation. The id MUST be unique among
# all operations described in the API.
if 'operationId' not in operation:
operation['operationId'] = str(resource) + '_' + handler.__name__
if method and method.lower() != 'get':
operation['operationId'] += '_' + method.lower()
if path:
operation['operationId'] += '_' + path.split('/', 2)[-1].replace(
'/', '_').replace('{', '').replace('}', '')
return operation
[docs]def addRouteDocs(resource, route, method, info, handler):
"""
This is called for route handlers that have a description attr on them. It
gathers the necessary information to build the swagger documentation, which
is consumed by the docs.Describe endpoint.
:param resource: The name of the resource, e.g. "item"
:type resource: str
:param route: The route to describe.
:type route: tuple[str]
:param method: The HTTP method for this route, e.g. "POST"
:type method: str
:param info: The information representing the API documentation, typically
from ``girder.api.describe.Description.asDict``.
:type info: dict
:param handler: The actual handler method for this route.
:type handler: function
"""
path = _toRoutePath(resource, route)
operation = _toOperation(info, resource, handler, path, method)
# Add the operation to the given route
if method not in routes[resource][path]:
routes[resource][path][method] = operation
[docs]def removeRouteDocs(resource, route, method, info, handler):
"""
Remove documentation for a route handler.
:param resource: The name of the resource, e.g. "item"
:type resource: str
:param route: The route to describe.
:type route: tuple[str]
:param method: The HTTP method for this route, e.g. "POST"
:type method: str
:param info: The information representing the API documentation.
:type info: dict
:param handler: The actual handler method for this route.
:type handler: function
"""
if resource not in routes:
return
path = _toRoutePath(resource, route)
if path not in routes[resource]:
return
if method in routes[resource][path]:
del routes[resource][path][method]
# Clean up any empty route paths
if not routes[resource][path]:
del routes[resource][path]
if not routes[resource]:
del routes[resource]
[docs]def addModel(name, model, resources=None, silent=False):
"""
Add a model to the Swagger documentation.
:param resources: The type(s) of resource(s) to add the model to. New
resource types may be implicitly defined, with the expectation that
routes will be added for them at some point. If no resources are
passed, the model will be exposed for every resource type
:param resources: str or tuple/list[str]
:param name: The name of the model.
:type name: str
:param model: The model to add.
:type model: dict
:param silent: Set this to True to suppress warnings.
:type silent: bool
.. warning:: This is a low-level API which does not validate the format of
``model``. See the `Swagger Model documentation`_ for a complete
specification of the correct format for ``model``.
.. versionchanged:: The syntax and behavior of this function was modified
after v1.3.2. The previous implementation did not include a resources
parameter.
.. _Swagger Model documentation: https://github.com/OAI/
OpenAPI-Specification/blob/0122c22e7fb93b571740dd3c6e141c65563a18be/
versions/2.0.md#definitionsObject
"""
if resources:
if isinstance(resources, str):
resources = (resources,)
for resource in resources:
models[resource][name] = model
else:
if not silent:
logprint.warning(
'WARNING: adding swagger models without specifying resources '
'to bind to is discouraged (%s).' % name)
models[None][name] = model