api docs: Display data type of responses in API Documentation.

Previously, the data type of responses wasn't displayed in the API
Documentation, even though that OpenAPI data is carefully validated
against the implementation. Here we add a recursive function to
render the data types visibly in API Documentation.
Fixes part of #15967.
This commit is contained in:
Suyash Vardhan Mathur 2021-01-28 01:38:58 +05:30 committed by Tim Abbott
parent 38dc1131b9
commit 26a81ab3aa
2 changed files with 33 additions and 7 deletions

View File

@ -1726,6 +1726,10 @@ label.label-title {
}
}
.api-response-datatype {
color: hsl(176, 46.4%, 41%);
}
.desktop-redirect-box {
text-align: center;

View File

@ -9,6 +9,8 @@ from markdown.preprocessors import Preprocessor
from zerver.openapi.openapi import get_openapi_return_values, likely_deprecated_parameter
from .api_arguments_table_generator import generate_data_type
REGEXP = re.compile(r'\{generate_return_values_table\|\s*(.+?)\s*\|\s*(.+)\s*\}')
class MarkdownReturnValuesTableGenerator(Extension):
@ -59,11 +61,27 @@ class APIReturnValuesTablePreprocessor(Preprocessor):
done = True
return lines
def render_desc(self, description: str, spacing: int, return_value: Optional[str]=None) -> str:
def render_desc(self, description: str, spacing: int, data_type: str,
return_value: Optional[str]=None) -> str:
description = description.replace('\n', '\n' + ((spacing + 4) * ' '))
if return_value is None:
return (spacing * " ") + "* " + description
return (spacing * " ") + "* `" + return_value + "`: " + description
# HACK: It's not clear how to use OpenAPI data to identify
# the `key` fields for objects where e.g. the keys are
# user/stream IDs being mapped to data associated with
# those IDs. We hackily describe those fields by
# requiring that the descriptions be written as `key_name:
# key_description` and parsing for that pattern; we need
# to be careful to skip cases where we'd have `Note: ...`
# on a later line.
#
# More correctly, we should be doing something that looks at the types;
# print statements and test_api_doc_endpoint is useful for testing.
arr = description.split(": ", 1)
if data_type != "object" or len(arr) == 1 or '\n' in arr[0]:
return (spacing * " ") + "* " + description
(key_name, key_description) = arr
return (spacing * " ") + "* " + key_name + ": " + '<span class="api-response-datatype">' + data_type + "</span> " + key_description
return (spacing * " ") + "* `" + return_value + "`: " + '<span class="api-response-datatype">' + data_type + "</span> " + description
def render_table(self, return_values: Dict[str, Any], spacing: int) -> List[str]:
IGNORE = ["result", "msg"]
@ -77,28 +95,32 @@ class APIReturnValuesTablePreprocessor(Preprocessor):
# description of the endpoint. Then for each element of oneOf there is a
# specialized description for that particular case. The description used
# right below is the main description.
data_type = generate_data_type(return_values[return_value])
ans.append(self.render_desc(return_values[return_value]['description'],
spacing, return_value))
spacing, data_type, return_value))
for element in return_values[return_value]['oneOf']:
if 'description' not in element:
continue
# Add the specialized description of the oneOf element.
ans.append(self.render_desc(element['description'], spacing + 4))
data_type = generate_data_type(element)
ans.append(self.render_desc(element['description'], spacing + 4, data_type))
# If the oneOf element is an object schema then render the documentation
# of its keys.
if 'properties' in element:
ans += self.render_table(element['properties'], spacing + 8)
continue
description = return_values[return_value]['description']
data_type = generate_data_type(return_values[return_value])
# Test to make sure deprecated keys are marked appropriately.
if likely_deprecated_parameter(description):
assert(return_values[return_value]['deprecated'])
ans.append(self.render_desc(description, spacing, return_value))
ans.append(self.render_desc(description, spacing, data_type, return_value))
if 'properties' in return_values[return_value]:
ans += self.render_table(return_values[return_value]['properties'], spacing + 4)
if return_values[return_value].get('additionalProperties', False):
data_type = generate_data_type(return_values[return_value]['additionalProperties'])
ans.append(self.render_desc(return_values[return_value]['additionalProperties']
['description'], spacing + 4))
['description'], spacing + 4, data_type))
if 'properties' in return_values[return_value]['additionalProperties']:
ans += self.render_table(return_values[return_value]['additionalProperties']
['properties'], spacing + 8)