mirror of
https://github.com/zulip/zulip.git
synced 2025-11-02 04:53:36 +00:00
api_code_examples: Parse kwargs/mods for example code generation.
Now we can also include extra keyword arguments to specify modifications in how the example code should be generated in the generate_code_example template tag. E.g. generate_code_example(curl, exclude=["param1", "param2"])
This commit is contained in:
committed by
Tim Abbott
parent
b20cf095e7
commit
5af753d940
@@ -4,7 +4,7 @@ import inspect
|
||||
|
||||
from markdown.extensions import Extension
|
||||
from markdown.preprocessors import Preprocessor
|
||||
from typing import Any, Dict, Optional, List
|
||||
from typing import Any, Dict, Optional, List, Tuple
|
||||
import markdown
|
||||
|
||||
import zerver.openapi.python_examples
|
||||
@@ -43,6 +43,22 @@ DEFAULT_EXAMPLE = {
|
||||
"boolean": False,
|
||||
}
|
||||
|
||||
def parse_language_and_options(input_str: Optional[str]) -> Tuple[str, Dict[str, Any]]:
|
||||
if not input_str:
|
||||
return ("", {})
|
||||
language_and_options = re.match(r"(?P<language>\w+)(,\s*(?P<options>[\"\'\w\d\[\],= ]+))?", input_str)
|
||||
assert(language_and_options is not None)
|
||||
kwargs_pattern = re.compile(r"(?P<key>\w+)\s*=\s*(?P<value>[\'\"\w\d]+|\[[\'\",\w\d ]+\])")
|
||||
language = language_and_options.group("language")
|
||||
assert(language is not None)
|
||||
if language_and_options.group("options"):
|
||||
_options = kwargs_pattern.finditer(language_and_options.group("options"))
|
||||
options = {}
|
||||
for m in _options:
|
||||
options[m.group("key")] = json.loads(m.group("value").replace("'", '"'))
|
||||
return (language, options)
|
||||
return (language, {})
|
||||
|
||||
def extract_python_code_example(source: List[str], snippet: List[str]) -> List[str]:
|
||||
start = -1
|
||||
end = -1
|
||||
@@ -107,7 +123,8 @@ def curl_method_arguments(endpoint: str, method: str,
|
||||
def generate_curl_example(endpoint: str, method: str,
|
||||
auth_email: str=DEFAULT_AUTH_EMAIL,
|
||||
auth_api_key: str=DEFAULT_AUTH_API_KEY,
|
||||
api_url: str=DEFAULT_API_URL) -> List[str]:
|
||||
api_url: str=DEFAULT_API_URL,
|
||||
exclude: List[str]=[]) -> List[str]:
|
||||
lines = ["```curl"]
|
||||
openapi_entry = openapi_spec.spec()['paths'][endpoint][method.lower()]
|
||||
|
||||
@@ -122,6 +139,8 @@ def generate_curl_example(endpoint: str, method: str,
|
||||
openapi_example_params = get_openapi_parameters(endpoint, method)
|
||||
for packet in openapi_example_params:
|
||||
param_name = packet["name"]
|
||||
if param_name in exclude:
|
||||
continue
|
||||
param_type = packet["schema"]["type"]
|
||||
if param_type in ["object", "array"]:
|
||||
example_value = packet.get("example", None)
|
||||
@@ -147,18 +166,19 @@ cURL example.""".format(endpoint, method, param_name)
|
||||
|
||||
return lines
|
||||
|
||||
def render_curl_example(function: str) -> List[str]:
|
||||
def render_curl_example(function: str, exclude: List[str]=[]) -> List[str]:
|
||||
""" A simple wrapper around generate_curl_example. """
|
||||
parts = function.split(":")
|
||||
endpoint = parts[0]
|
||||
method = parts[1]
|
||||
kwargs = dict()
|
||||
kwargs = dict() # type: Dict[str, Any]
|
||||
if len(parts) > 2:
|
||||
kwargs["auth_email"] = parts[2]
|
||||
if len(parts) > 3:
|
||||
kwargs["auth_api_key"] = parts[3]
|
||||
if len(parts) > 4:
|
||||
kwargs["api_url"] = parts[4]
|
||||
kwargs["exclude"] = exclude
|
||||
return generate_curl_example(endpoint, method, **kwargs)
|
||||
|
||||
SUPPORTED_LANGUAGES = {
|
||||
@@ -190,7 +210,7 @@ class APICodeExamplesPreprocessor(Preprocessor):
|
||||
match = MACRO_REGEXP.search(line)
|
||||
|
||||
if match:
|
||||
language = match.group(2)
|
||||
language, options = parse_language_and_options(match.group(2))
|
||||
function = match.group(3)
|
||||
key = match.group(4)
|
||||
argument = match.group(6)
|
||||
@@ -204,7 +224,7 @@ class APICodeExamplesPreprocessor(Preprocessor):
|
||||
if argument == 'admin_config=True':
|
||||
text = SUPPORTED_LANGUAGES[language]['render'](function, admin_config=True)
|
||||
else:
|
||||
text = SUPPORTED_LANGUAGES[language]['render'](function)
|
||||
text = SUPPORTED_LANGUAGES[language]['render'](function, **options)
|
||||
|
||||
# The line that contains the directive to include the macro
|
||||
# may be preceded or followed by text or tags, in that case
|
||||
|
||||
@@ -13,7 +13,7 @@ from django.http import HttpResponse
|
||||
|
||||
import zerver.lib.openapi as openapi
|
||||
from zerver.lib.bugdown.api_code_examples import generate_curl_example, \
|
||||
render_curl_example
|
||||
render_curl_example, parse_language_and_options
|
||||
from zerver.lib.request import REQ
|
||||
from zerver.lib.test_classes import ZulipTestCase
|
||||
from zerver.lib.openapi import (
|
||||
@@ -545,6 +545,49 @@ so maybe we shouldn't include it in pending_endpoints.
|
||||
|
||||
self.check_for_non_existant_openapi_endpoints()
|
||||
|
||||
|
||||
class ModifyExampleGenerationTestCase(ZulipTestCase):
|
||||
|
||||
def test_no_mod_argument(self) -> None:
|
||||
res = parse_language_and_options("python")
|
||||
self.assertEqual(res, ("python", {}))
|
||||
|
||||
def test_single_simple_mod_argument(self) -> None:
|
||||
res = parse_language_and_options("curl, mod=1")
|
||||
self.assertEqual(res, ("curl", {"mod": 1}))
|
||||
|
||||
res = parse_language_and_options("curl, mod='somevalue'")
|
||||
self.assertEqual(res, ("curl", {"mod": "somevalue"}))
|
||||
|
||||
res = parse_language_and_options("curl, mod=\"somevalue\"")
|
||||
self.assertEqual(res, ("curl", {"mod": "somevalue"}))
|
||||
|
||||
def test_multiple_simple_mod_argument(self) -> None:
|
||||
res = parse_language_and_options("curl, mod1=1, mod2='a'")
|
||||
self.assertEqual(res, ("curl", {"mod1": 1, "mod2": "a"}))
|
||||
|
||||
res = parse_language_and_options("curl, mod1=\"asdf\", mod2='thing', mod3=3")
|
||||
self.assertEqual(res, ("curl", {"mod1": "asdf", "mod2": "thing", "mod3": 3}))
|
||||
|
||||
def test_single_list_mod_argument(self) -> None:
|
||||
res = parse_language_and_options("curl, exclude=['param1', 'param2']")
|
||||
self.assertEqual(res, ("curl", {"exclude": ["param1", "param2"]}))
|
||||
|
||||
res = parse_language_and_options("curl, exclude=[\"param1\", \"param2\"]")
|
||||
self.assertEqual(res, ("curl", {"exclude": ["param1", "param2"]}))
|
||||
|
||||
res = parse_language_and_options("curl, exclude=['param1', \"param2\"]")
|
||||
self.assertEqual(res, ("curl", {"exclude": ["param1", "param2"]}))
|
||||
|
||||
def test_multiple_list_mod_argument(self) -> None:
|
||||
res = parse_language_and_options("curl, exclude=['param1', \"param2\"], special=['param3']")
|
||||
self.assertEqual(res, ("curl", {"exclude": ["param1", "param2"], "special": ["param3"]}))
|
||||
|
||||
def test_multiple_mixed_mod_arguments(self) -> None:
|
||||
res = parse_language_and_options("curl, exclude=[\"asdf\", 'sdfg'], other_key='asdf', more_things=\"asdf\", another_list=[1, \"2\"]")
|
||||
self.assertEqual(res, ("curl", {"exclude": ["asdf", "sdfg"], "other_key": "asdf", "more_things": "asdf", "another_list": [1, "2"]}))
|
||||
|
||||
|
||||
class TestCurlExampleGeneration(ZulipTestCase):
|
||||
|
||||
spec_mock_without_examples = {
|
||||
@@ -748,3 +791,19 @@ class TestCurlExampleGeneration(ZulipTestCase):
|
||||
"```"
|
||||
]
|
||||
self.assertEqual(generated_curl_example, expected_curl_example)
|
||||
|
||||
def test_generate_and_render_curl_example_with_excludes(self) -> None:
|
||||
generated_curl_example = generate_curl_example("/messages", "GET", exclude=["client_gravatar", "apply_markdown"])
|
||||
expected_curl_example = [
|
||||
'```curl',
|
||||
'curl -X GET -G localhost:9991/api/v1/messages \\',
|
||||
' -u BOT_EMAIL_ADDRESS:BOT_API_KEY \\',
|
||||
" -d 'anchor=42' \\",
|
||||
" -d 'use_first_unread_anchor=true' \\",
|
||||
" -d 'num_before=4' \\",
|
||||
" -d 'num_after=8' \\",
|
||||
' --data-urlencode narrow=\'[{"operand": "party", "operator": "stream"}]\'',
|
||||
'```'
|
||||
]
|
||||
self.assertEqual(generated_curl_example, expected_curl_example)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user