mirror of https://github.com/zulip/zulip.git
openapi: Use Parameter class for generating curl examples.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
(cherry picked from commit a67d1b57b9
)
This commit is contained in:
parent
33e77b6d15
commit
899819fb2f
|
@ -23,6 +23,7 @@ from zerver.lib.users import get_api_key
|
||||||
from zerver.models import Client, Message, UserGroup, UserPresence
|
from zerver.models import Client, Message, UserGroup, UserPresence
|
||||||
from zerver.models.realms import get_realm
|
from zerver.models.realms import get_realm
|
||||||
from zerver.models.users import get_user
|
from zerver.models.users import get_user
|
||||||
|
from zerver.openapi.openapi import Parameter
|
||||||
|
|
||||||
GENERATOR_FUNCTIONS: Dict[str, Callable[[], Dict[str, object]]] = {}
|
GENERATOR_FUNCTIONS: Dict[str, Callable[[], Dict[str, object]]] = {}
|
||||||
REGISTERED_GENERATOR_FUNCTIONS: Set[str] = set()
|
REGISTERED_GENERATOR_FUNCTIONS: Set[str] = set()
|
||||||
|
@ -71,28 +72,24 @@ def assert_all_helper_functions_called() -> None:
|
||||||
|
|
||||||
def patch_openapi_example_values(
|
def patch_openapi_example_values(
|
||||||
entry: str,
|
entry: str,
|
||||||
params: List[Dict[str, Any]],
|
parameters: List[Parameter],
|
||||||
request_body: Optional[Dict[str, Any]] = None,
|
request_body: Optional[Dict[str, Any]] = None,
|
||||||
) -> Tuple[List[Dict[str, object]], Optional[Dict[str, object]]]:
|
) -> Tuple[List[Parameter], Optional[Dict[str, object]]]:
|
||||||
if entry not in GENERATOR_FUNCTIONS:
|
if entry not in GENERATOR_FUNCTIONS:
|
||||||
return params, request_body
|
return parameters, request_body
|
||||||
func = GENERATOR_FUNCTIONS[entry]
|
func = GENERATOR_FUNCTIONS[entry]
|
||||||
realm_example_values: Dict[str, object] = func()
|
realm_example_values: Dict[str, object] = func()
|
||||||
|
|
||||||
for param in params:
|
for parameter in parameters:
|
||||||
param_name = param["name"]
|
if parameter.name in realm_example_values:
|
||||||
if param_name in realm_example_values:
|
parameter.example = realm_example_values[parameter.name]
|
||||||
if "content" in param:
|
|
||||||
param["content"]["application/json"]["example"] = realm_example_values[param_name]
|
|
||||||
else:
|
|
||||||
param["example"] = realm_example_values[param_name]
|
|
||||||
|
|
||||||
if request_body is not None:
|
if request_body is not None:
|
||||||
properties = request_body["content"]["multipart/form-data"]["schema"]["properties"]
|
properties = request_body["content"]["multipart/form-data"]["schema"]["properties"]
|
||||||
for key, property in properties.items():
|
for key, property in properties.items():
|
||||||
if key in realm_example_values:
|
if key in realm_example_values:
|
||||||
property["example"] = realm_example_values[key]
|
property["example"] = realm_example_values[key]
|
||||||
return params, request_body
|
return parameters, request_body
|
||||||
|
|
||||||
|
|
||||||
@openapi_param_value_generator(["/fetch_api_key:post"])
|
@openapi_param_value_generator(["/fetch_api_key:post"])
|
||||||
|
|
|
@ -21,11 +21,14 @@ from typing_extensions import override
|
||||||
import zerver.openapi.python_examples
|
import zerver.openapi.python_examples
|
||||||
from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES
|
from zerver.lib.markdown.priorities import PREPROCESSOR_PRIORITES
|
||||||
from zerver.openapi.openapi import (
|
from zerver.openapi.openapi import (
|
||||||
|
NO_EXAMPLE,
|
||||||
|
Parameter,
|
||||||
check_additional_imports,
|
check_additional_imports,
|
||||||
check_requires_administrator,
|
check_requires_administrator,
|
||||||
generate_openapi_fixture,
|
generate_openapi_fixture,
|
||||||
get_curl_include_exclude,
|
get_curl_include_exclude,
|
||||||
get_openapi_description,
|
get_openapi_description,
|
||||||
|
get_openapi_parameters,
|
||||||
get_openapi_summary,
|
get_openapi_summary,
|
||||||
get_parameters_description,
|
get_parameters_description,
|
||||||
get_responses_description,
|
get_responses_description,
|
||||||
|
@ -217,46 +220,45 @@ def curl_method_arguments(endpoint: str, method: str, api_url: str) -> List[str]
|
||||||
|
|
||||||
|
|
||||||
def get_openapi_param_example_value_as_string(
|
def get_openapi_param_example_value_as_string(
|
||||||
endpoint: str, method: str, param: Dict[str, Any], curl_argument: bool = False
|
endpoint: str, method: str, parameter: Parameter, curl_argument: bool = False
|
||||||
) -> str:
|
) -> str:
|
||||||
jsonify = False
|
if "type" in parameter.value_schema:
|
||||||
param_name = param["name"]
|
param_type = parameter.value_schema["type"]
|
||||||
if "content" in param:
|
|
||||||
param = param["content"]["application/json"]
|
|
||||||
jsonify = True
|
|
||||||
if "type" in param["schema"]:
|
|
||||||
param_type = param["schema"]["type"]
|
|
||||||
else:
|
else:
|
||||||
# Hack: Ideally, we'd extract a common function for handling
|
# Hack: Ideally, we'd extract a common function for handling
|
||||||
# oneOf values in types and do something with the resulting
|
# oneOf values in types and do something with the resulting
|
||||||
# union type. But for this logic's purpose, it's good enough
|
# union type. But for this logic's purpose, it's good enough
|
||||||
# to just check the first parameter.
|
# to just check the first parameter.
|
||||||
param_type = param["schema"]["oneOf"][0]["type"]
|
param_type = parameter.value_schema["oneOf"][0]["type"]
|
||||||
|
|
||||||
if param_type in ["object", "array"]:
|
if param_type in ["object", "array"]:
|
||||||
example_value = param.get("example", None)
|
if parameter.example is NO_EXAMPLE:
|
||||||
if not example_value:
|
|
||||||
msg = f"""All array and object type request parameters must have
|
msg = f"""All array and object type request parameters must have
|
||||||
concrete examples. The openAPI documentation for {endpoint}/{method} is missing an example
|
concrete examples. The openAPI documentation for {endpoint}/{method} is missing an example
|
||||||
value for the {param_name} parameter. Without this we cannot automatically generate a
|
value for the {parameter.name} parameter. Without this we cannot automatically generate a
|
||||||
cURL example."""
|
cURL example."""
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
ordered_ex_val_str = json.dumps(example_value, sort_keys=True)
|
ordered_ex_val_str = json.dumps(parameter.example, sort_keys=True)
|
||||||
# We currently don't have any non-JSON encoded arrays.
|
# We currently don't have any non-JSON encoded arrays.
|
||||||
assert jsonify
|
assert parameter.json_encoded
|
||||||
if curl_argument:
|
if curl_argument:
|
||||||
return " --data-urlencode " + shlex.quote(f"{param_name}={ordered_ex_val_str}")
|
return " --data-urlencode " + shlex.quote(f"{parameter.name}={ordered_ex_val_str}")
|
||||||
return ordered_ex_val_str # nocoverage
|
return ordered_ex_val_str # nocoverage
|
||||||
else:
|
else:
|
||||||
example_value = param.get("example", DEFAULT_EXAMPLE[param_type])
|
if parameter.example is NO_EXAMPLE:
|
||||||
if isinstance(example_value, bool):
|
example_value = DEFAULT_EXAMPLE[param_type]
|
||||||
# Booleans are effectively JSON-encoded, in that we pass
|
else:
|
||||||
# true/false, not the Python str(True) = "True"
|
example_value = parameter.example
|
||||||
jsonify = True
|
|
||||||
if jsonify:
|
# Booleans are effectively JSON-encoded, in that we pass
|
||||||
|
# true/false, not the Python str(True) = "True"
|
||||||
|
if parameter.json_encoded or isinstance(example_value, (bool, float, int)):
|
||||||
example_value = json.dumps(example_value)
|
example_value = json.dumps(example_value)
|
||||||
|
else:
|
||||||
|
assert isinstance(example_value, str)
|
||||||
|
|
||||||
if curl_argument:
|
if curl_argument:
|
||||||
return " --data-urlencode " + shlex.quote(f"{param_name}={example_value}")
|
return " --data-urlencode " + shlex.quote(f"{parameter.name}={example_value}")
|
||||||
return example_value
|
return example_value
|
||||||
|
|
||||||
|
|
||||||
|
@ -274,23 +276,23 @@ def generate_curl_example(
|
||||||
operation_entry = openapi_spec.openapi()["paths"][endpoint][method.lower()]
|
operation_entry = openapi_spec.openapi()["paths"][endpoint][method.lower()]
|
||||||
global_security = openapi_spec.openapi()["security"]
|
global_security = openapi_spec.openapi()["security"]
|
||||||
|
|
||||||
operation_params = operation_entry.get("parameters", [])
|
parameters = get_openapi_parameters(endpoint, method)
|
||||||
operation_request_body = operation_entry.get("requestBody", None)
|
operation_request_body = operation_entry.get("requestBody", None)
|
||||||
operation_security = operation_entry.get("security", None)
|
operation_security = operation_entry.get("security", None)
|
||||||
|
|
||||||
if settings.RUNNING_OPENAPI_CURL_TEST: # nocoverage
|
if settings.RUNNING_OPENAPI_CURL_TEST: # nocoverage
|
||||||
from zerver.openapi.curl_param_value_generators import patch_openapi_example_values
|
from zerver.openapi.curl_param_value_generators import patch_openapi_example_values
|
||||||
|
|
||||||
operation_params, operation_request_body = patch_openapi_example_values(
|
parameters, operation_request_body = patch_openapi_example_values(
|
||||||
operation, operation_params, operation_request_body
|
operation, parameters, operation_request_body
|
||||||
)
|
)
|
||||||
|
|
||||||
format_dict = {}
|
format_dict = {}
|
||||||
for param in operation_params:
|
for parameter in parameters:
|
||||||
if param["in"] != "path":
|
if parameter.kind != "path":
|
||||||
continue
|
continue
|
||||||
example_value = get_openapi_param_example_value_as_string(endpoint, method, param)
|
example_value = get_openapi_param_example_value_as_string(endpoint, method, parameter)
|
||||||
format_dict[param["name"]] = example_value
|
format_dict[parameter.name] = example_value
|
||||||
example_endpoint = endpoint.format_map(format_dict)
|
example_endpoint = endpoint.format_map(format_dict)
|
||||||
|
|
||||||
curl_first_line_parts = ["curl", *curl_method_arguments(example_endpoint, method, api_url)]
|
curl_first_line_parts = ["curl", *curl_method_arguments(example_endpoint, method, api_url)]
|
||||||
|
@ -319,19 +321,18 @@ def generate_curl_example(
|
||||||
if authentication_required:
|
if authentication_required:
|
||||||
lines.append(" -u " + shlex.quote(f"{auth_email}:{auth_api_key}"))
|
lines.append(" -u " + shlex.quote(f"{auth_email}:{auth_api_key}"))
|
||||||
|
|
||||||
for param in operation_params:
|
for parameter in parameters:
|
||||||
if param["in"] == "path":
|
if parameter.kind == "path":
|
||||||
continue
|
|
||||||
param_name = param["name"]
|
|
||||||
|
|
||||||
if include is not None and param_name not in include:
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if exclude is not None and param_name in exclude:
|
if include is not None and parameter.name not in include:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if exclude is not None and parameter.name in exclude:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
example_value = get_openapi_param_example_value_as_string(
|
example_value = get_openapi_param_example_value_as_string(
|
||||||
endpoint, method, param, curl_argument=True
|
endpoint, method, parameter, curl_argument=True
|
||||||
)
|
)
|
||||||
lines.append(example_value)
|
lines.append(example_value)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue