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:
Hemanth V. Alluri
2019-08-04 11:44:08 +05:30
committed by Tim Abbott
parent b20cf095e7
commit 5af753d940
2 changed files with 86 additions and 7 deletions

View File

@@ -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

View File

@@ -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)