Changeset View
Changeset View
Standalone View
Standalone View
swh/web/api/apidoc.py
# Copyright (C) 2015-2019 The Software Heritage developers | # Copyright (C) 2015-2019 The Software Heritage developers | ||||
# See the AUTHORS file at the top-level directory of this distribution | # See the AUTHORS file at the top-level directory of this distribution | ||||
# License: GNU Affero General Public License version 3, or any later version | # License: GNU Affero General Public License version 3, or any later version | ||||
# See top-level LICENSE file for more information | # See top-level LICENSE file for more information | ||||
import docutils.nodes | |||||
import docutils.parsers.rst | |||||
import docutils.utils | |||||
import functools | import functools | ||||
from functools import wraps | from functools import wraps | ||||
import os | import os | ||||
import re | import re | ||||
import textwrap | import textwrap | ||||
from typing import List | |||||
import docutils.nodes | |||||
import docutils.parsers.rst | |||||
import docutils.utils | |||||
from rest_framework.decorators import api_view | from rest_framework.decorators import api_view | ||||
import sentry_sdk | import sentry_sdk | ||||
from swh.web.common.utils import parse_rst | from swh.web.common.utils import parse_rst | ||||
from swh.web.api.apiurls import APIUrls | from swh.web.api.apiurls import APIUrls | ||||
from swh.web.api.apiresponse import make_api_response, error_response | from swh.web.api.apiresponse import make_api_response, error_response | ||||
▲ Show 20 Lines • Show All 226 Lines • ▼ Show 20 Lines | |||||
class APIDocException(Exception): | class APIDocException(Exception): | ||||
""" | """ | ||||
Custom exception to signal errors in the use of the APIDoc decorators | Custom exception to signal errors in the use of the APIDoc decorators | ||||
""" | """ | ||||
def api_doc(route, noargs=False, need_params=False, tags=[], | def api_doc(route: str, noargs: bool = False, need_params: bool = False, | ||||
handle_response=False, api_version='1'): | tags: List[str] = [], handle_response: bool = False, | ||||
api_version: str = '1'): | |||||
""" | """ | ||||
Decorate an API function to register it in the API doc route index | Decorator for an API endpoint implementation used to generate a dedicated | ||||
and create the corresponding DRF route. | view displaying its HTML documentation. | ||||
The documentation will be generated from the endpoint docstring based on | |||||
sphinxcontrib-httpdomain format. | |||||
Args: | Args: | ||||
route (str): documentation page's route | route: documentation page's route | ||||
noargs (boolean): set to True if the route has no arguments, and its | noargs: set to True if the route has no arguments, and its | ||||
result should be displayed anytime its documentation | result should be displayed anytime its documentation | ||||
is requested. Default to False | is requested. Default to False | ||||
need_params (boolean): specify the route requires query parameters | need_params: specify the route requires query parameters | ||||
otherwise errors will occur. It enables to avoid displaying the | otherwise errors will occur. It enables to avoid displaying the | ||||
invalid response in its HTML documentation. Default to False. | invalid response in its HTML documentation. Default to False. | ||||
tags (list): Further information on api endpoints. Two values are | tags: Further information on api endpoints. Two values are | ||||
possibly expected: | possibly expected: | ||||
* hidden: remove the entry points from the listing | * hidden: remove the entry points from the listing | ||||
* upcoming: display the entry point but it is not followable | * upcoming: display the entry point but it is not followable | ||||
handle_response (boolean): indicate if the decorated function takes | handle_response: indicate if the decorated function takes | ||||
care of creating the HTTP response or delegates that task to the | care of creating the HTTP response or delegates that task to the | ||||
apiresponse module | apiresponse module | ||||
api_version (str): api version string | api_version: api version string | ||||
""" | """ | ||||
urlpattern = '^' + api_version + route + '$' | |||||
tags = set(tags) | tags_set = set(tags) | ||||
# @api_doc() Decorator call | # @api_doc() Decorator call | ||||
def decorator(f): | def decorator(f): | ||||
# if the route is not hidden, add it to the index | |||||
# If the route is not hidden, add it to the index | if 'hidden' not in tags_set: | ||||
if 'hidden' not in tags: | |||||
doc_data = get_doc_data(f, route, noargs) | doc_data = get_doc_data(f, route, noargs) | ||||
doc_desc = doc_data['description'] | doc_desc = doc_data['description'] | ||||
first_dot_pos = doc_desc.find('.') | first_dot_pos = doc_desc.find('.') | ||||
APIUrls.add_route(route, doc_desc[:first_dot_pos+1], | APIUrls.add_doc_route(route, doc_desc[:first_dot_pos+1], | ||||
tags=tags) | noargs=noargs, api_version=api_version, | ||||
tags=tags_set) | |||||
# If the decorated route has arguments, we create a specific | |||||
# documentation view | |||||
if not noargs: | |||||
# create a dedicated view to display endpoint HTML doc | |||||
@api_view(['GET', 'HEAD']) | @api_view(['GET', 'HEAD']) | ||||
@wraps(f) | @wraps(f) | ||||
def doc_view(request): | def doc_view(request): | ||||
doc_data = get_doc_data(f, route, noargs) | doc_data = get_doc_data(f, route, noargs) | ||||
return make_api_response(request, None, doc_data) | return make_api_response(request, None, doc_data) | ||||
view_name = 'api-%s-%s' % \ | route_name = '%s-doc' % route[1:-1].replace('/', '-') | ||||
(api_version, route[1:-1].replace('/', '-')) | urlpattern = f'^{api_version}{route}doc/$' | ||||
view_name = 'api-%s-%s' % (api_version, route_name) | |||||
APIUrls.add_url_pattern(urlpattern, doc_view, view_name) | APIUrls.add_url_pattern(urlpattern, doc_view, view_name) | ||||
@wraps(f) | @wraps(f) | ||||
def documented_view(request, **kwargs): | def documented_view(request, **kwargs): | ||||
doc_data = get_doc_data(f, route, noargs) | doc_data = get_doc_data(f, route, noargs) | ||||
try: | try: | ||||
response = f(request, **kwargs) | response = f(request, **kwargs) | ||||
except Exception as exc: | except Exception as exc: | ||||
▲ Show 20 Lines • Show All 84 Lines • Show Last 20 Lines |