Changeset View
Changeset View
Standalone View
Standalone View
swh/web/api/apidoc.py
Show All 28 Lines | class _HTTPDomainDocVisitor(docutils.nodes.NodeVisitor): | ||||
httpdomain roles. Its purpose is to extract relevant info regarding swh | httpdomain roles. Its purpose is to extract relevant info regarding swh | ||||
api endpoints (for instance url arguments) from their docstring written | api endpoints (for instance url arguments) from their docstring written | ||||
using sphinx httpdomain. | using sphinx httpdomain. | ||||
""" | """ | ||||
# httpdomain roles we want to parse (based on sphinxcontrib.httpdomain 1.6) | # httpdomain roles we want to parse (based on sphinxcontrib.httpdomain 1.6) | ||||
parameter_roles = ('param', 'parameter', 'arg', 'argument') | parameter_roles = ('param', 'parameter', 'arg', 'argument') | ||||
request_json_object_roles = ('reqjsonobj', 'reqjson', '<jsonobj', '<json') | |||||
request_json_array_roles = ('reqjsonarr', '<jsonarr') | |||||
response_json_object_roles = ('resjsonobj', 'resjson', '>jsonobj', '>json') | response_json_object_roles = ('resjsonobj', 'resjson', '>jsonobj', '>json') | ||||
response_json_array_roles = ('resjsonarr', '>jsonarr') | response_json_array_roles = ('resjsonarr', '>jsonarr') | ||||
query_parameter_roles = ('queryparameter', 'queryparam', 'qparam', 'query') | query_parameter_roles = ('queryparameter', 'queryparam', 'qparam', 'query') | ||||
request_header_roles = ('<header', 'reqheader', 'requestheader') | request_header_roles = ('<header', 'reqheader', 'requestheader') | ||||
response_header_roles = ('>header', 'resheader', 'responseheader') | response_header_roles = ('>header', 'resheader', 'responseheader') | ||||
status_code_roles = ('statuscode', 'status', 'code') | status_code_roles = ('statuscode', 'status', 'code') | ||||
def __init__(self, document, urls, data): | def __init__(self, document, urls, data): | ||||
super().__init__(document) | super().__init__(document) | ||||
self.urls = urls | self.urls = urls | ||||
self.url_idx = 0 | self.url_idx = 0 | ||||
self.data = data | self.data = data | ||||
self.args_set = set() | self.args_set = set() | ||||
self.params_set = set() | self.params_set = set() | ||||
self.inputs_set = set() | |||||
self.returns_set = set() | self.returns_set = set() | ||||
self.status_codes_set = set() | self.status_codes_set = set() | ||||
self.reqheaders_set = set() | self.reqheaders_set = set() | ||||
self.resheaders_set = set() | self.resheaders_set = set() | ||||
self.field_list_visited = False | self.field_list_visited = False | ||||
def process_paragraph(self, par): | def process_paragraph(self, par): | ||||
""" | """ | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | def visit_field_list(self, node): | ||||
self.args_set.add(field_data[2]) | self.args_set.add(field_data[2]) | ||||
# Query Parameters | # Query Parameters | ||||
if field_data[0] in self.query_parameter_roles: | if field_data[0] in self.query_parameter_roles: | ||||
if field_data[2] not in self.params_set: | if field_data[2] not in self.params_set: | ||||
self.data['params'].append({'name': field_data[2], | self.data['params'].append({'name': field_data[2], | ||||
'type': field_data[1], | 'type': field_data[1], | ||||
'doc': text}) | 'doc': text}) | ||||
self.params_set.add(field_data[2]) | self.params_set.add(field_data[2]) | ||||
# Request data type | |||||
if (field_data[0] in self.request_json_array_roles or | |||||
field_data[0] in self.request_json_object_roles): | |||||
# array | |||||
if field_data[0] in self.request_json_array_roles: | |||||
self.data['input_type'] = 'array' | |||||
# object | |||||
else: | |||||
self.data['input_type'] = 'object' | |||||
# input object field | |||||
if field_data[2] not in self.inputs_set: | |||||
self.data['inputs'].append({'name': field_data[2], | |||||
'type': field_data[1], | |||||
'doc': text}) | |||||
self.inputs_set.add(field_data[2]) | |||||
# Response type | # Response type | ||||
if field_data[0] in self.response_json_array_roles or \ | if (field_data[0] in self.response_json_array_roles or | ||||
field_data[0] in self.response_json_object_roles: | field_data[0] in self.response_json_object_roles): | ||||
# array | # array | ||||
if field_data[0] in self.response_json_array_roles: | if field_data[0] in self.response_json_array_roles: | ||||
self.data['return_type'] = 'array' | self.data['return_type'] = 'array' | ||||
# object | # object | ||||
else: | else: | ||||
self.data['return_type'] = 'object' | self.data['return_type'] = 'object' | ||||
# returned object field | # returned object field | ||||
if field_data[2] not in self.returns_set: | if field_data[2] not in self.returns_set: | ||||
▲ Show 20 Lines • Show All 213 Lines • ▼ Show 20 Lines | def get_doc_data(f, route, noargs): | ||||
Build documentation data for the decorated api endpoint function | Build documentation data for the decorated api endpoint function | ||||
""" | """ | ||||
data = { | data = { | ||||
'description': '', | 'description': '', | ||||
'response_data': None, | 'response_data': None, | ||||
'urls': [], | 'urls': [], | ||||
'args': [], | 'args': [], | ||||
'params': [], | 'params': [], | ||||
'input_type': '', | |||||
'inputs': [], | |||||
'resheaders': [], | 'resheaders': [], | ||||
'reqheaders': [], | 'reqheaders': [], | ||||
'return_type': '', | 'return_type': '', | ||||
'returns': [], | 'returns': [], | ||||
'status_codes': [], | 'status_codes': [], | ||||
'examples': [], | 'examples': [], | ||||
'route': route, | 'route': route, | ||||
'noargs': noargs | 'noargs': noargs | ||||
} | } | ||||
if not f.__doc__: | if not f.__doc__: | ||||
raise APIDocException('apidoc: expected a docstring' | raise APIDocException('apidoc: expected a docstring' | ||||
' for function %s' | ' for function %s' | ||||
% (f.__name__,)) | % (f.__name__,)) | ||||
# use raw docstring as endpoint documentation if sphinx | # use raw docstring as endpoint documentation if sphinx | ||||
# httpdomain is not used | # httpdomain is not used | ||||
if '.. http' not in f.__doc__: | if '.. http' not in f.__doc__: | ||||
data['description'] = f.__doc__ | data['description'] = f.__doc__ | ||||
# else parse the sphinx httpdomain docstring with docutils | # else parse the sphinx httpdomain docstring with docutils | ||||
# (except when building the swh-web documentation through autodoc | # (except when building the swh-web documentation through autodoc | ||||
# sphinx extension, not needed and raise errors with sphinx >= 1.7) | # sphinx extension, not needed and raise errors with sphinx >= 1.7) | ||||
elif 'SWH_WEB_DOC_BUILD' not in os.environ: | elif 'SWH_WEB_DOC_BUILD' not in os.environ: | ||||
_parse_httpdomain_doc(f.__doc__, data) | _parse_httpdomain_doc(f.__doc__, data) | ||||
# process returned object info for nicer html display | # process input/returned object info for nicer html display | ||||
inputs_list = '' | |||||
returns_list = '' | returns_list = '' | ||||
for inp in data['inputs']: | |||||
# special case for array of non object type, for instance | |||||
# :<jsonarr string -: an array of string | |||||
if inp['name'] != '-': | |||||
inputs_list += ('\t* **%s (%s)**: %s\n' % | |||||
(inp['name'], inp['type'], inp['doc'])) | |||||
for ret in data['returns']: | for ret in data['returns']: | ||||
returns_list += '\t* **%s (%s)**: %s\n' %\ | # special case for array of non object type, for instance | ||||
(ret['name'], ret['type'], ret['doc']) | # :>jsonarr string -: an array of string | ||||
if ret['name'] != '-': | |||||
returns_list += ('\t* **%s (%s)**: %s\n' % | |||||
(ret['name'], ret['type'], ret['doc'])) | |||||
data['inputs_list'] = inputs_list | |||||
data['returns_list'] = returns_list | data['returns_list'] = returns_list | ||||
return data | return data | ||||
DOC_COMMON_HEADERS = ''' | DOC_COMMON_HEADERS = ''' | ||||
:reqheader Accept: the requested response content type, | :reqheader Accept: the requested response content type, | ||||
either ``application/json`` (default) or ``application/yaml`` | either ``application/json`` (default) or ``application/yaml`` | ||||
Show All 19 Lines |