diff --git a/zerver/openapi/markdown_extension.py b/zerver/openapi/markdown_extension.py index 4c191134ed..c605dba544 100644 --- a/zerver/openapi/markdown_extension.py +++ b/zerver/openapi/markdown_extension.py @@ -24,6 +24,7 @@ from zerver.openapi.openapi import ( get_curl_include_exclude, get_openapi_description, get_openapi_summary, + get_responses_description, openapi_spec, ) @@ -34,6 +35,7 @@ PYTHON_EXAMPLE_REGEX = re.compile(r"\# \{code_example\|\s*(.+?)\s*\}") JS_EXAMPLE_REGEX = re.compile(r"\/\/ \{code_example\|\s*(.+?)\s*\}") MACRO_REGEXP_DESC = re.compile(r"\{generate_api_description(\(\s*(.+?)\s*\))}") MACRO_REGEXP_TITLE = re.compile(r"\{generate_api_title(\(\s*(.+?)\s*\))}") +MACRO_REGEXP_RESPONSE_DESC = re.compile(r"\{generate_response_description(\(\s*(.+?)\s*\))}") PYTHON_CLIENT_CONFIG = """ #!/usr/bin/env python3 @@ -407,6 +409,11 @@ class APIMarkdownExtension(Extension): md.preprocessors.register( APITitlePreprocessor(md, self.getConfigs()), "generate_api_title", 531 ) + md.preprocessors.register( + ResponseDescriptionPreprocessor(md, self.getConfigs()), + "generate_response_description", + 531, + ) class APICodeExamplesPreprocessor(Preprocessor): @@ -541,5 +548,42 @@ class APITitlePreprocessor(Preprocessor): return title +class ResponseDescriptionPreprocessor(Preprocessor): + def __init__(self, md: markdown.Markdown, config: Mapping[str, Any]) -> None: + super().__init__(md) + self.api_url = config["api_url"] + + def run(self, lines: List[str]) -> List[str]: + done = False + while not done: + for line in lines: + loc = lines.index(line) + match = MACRO_REGEXP_RESPONSE_DESC.search(line) + + if match: + function = match.group(2) + text = self.render_responses_description(function) + # The line that contains the directive to include the macro + # may be preceded or followed by text or tags, in that case + # we need to make sure that any preceding or following text + # stays the same. + line_split = MACRO_REGEXP_RESPONSE_DESC.split(line, maxsplit=0) + preceding = line_split[0] + following = line_split[-1] + text = [preceding, *text, following] + lines = lines[:loc] + text + lines[loc + 1 :] + break + else: + done = True + return lines + + def render_responses_description(self, function: str) -> List[str]: + description: List[str] = [] + path, method = function.rsplit(":", 1) + raw_description = get_responses_description(path, method) + description.extend(raw_description.splitlines()) + return description + + def makeExtension(*args: Any, **kwargs: str) -> APIMarkdownExtension: return APIMarkdownExtension(*args, **kwargs) diff --git a/zerver/openapi/openapi.py b/zerver/openapi/openapi.py index 62e3f1e3bd..611d9b1132 100644 --- a/zerver/openapi/openapi.py +++ b/zerver/openapi/openapi.py @@ -233,6 +233,13 @@ def check_requires_administrator(endpoint: str, method: str) -> bool: ) +def get_responses_description(endpoint: str, method: str) -> str: + """Fetch responses description of an endpoint.""" + return openapi_spec.openapi()["paths"][endpoint][method.lower()].get( + "x-response-description", "" + ) + + def generate_openapi_fixture(endpoint: str, method: str, status_code: str = "200") -> List[str]: """Generate fixture to be rendered""" fixture = []