Skip to content

Commit 2187ea3

Browse files
authored
Migrate to a global configuration (#900)
* Move to a single generator To have a unique configuration module, move to a single generator for both specs. * Migrate tests * Typo * Only support v2 auth * Small tweaks * Expose objects in init * Rebase
1 parent 9dcb72a commit 2187ea3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+1034
-828
lines changed

.generator/src/generator/cli.py

Lines changed: 62 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,20 @@
1010

1111

1212
@click.command()
13-
@click.option(
14-
"-i",
15-
"--input",
13+
@click.argument(
14+
"specs",
15+
nargs=-1,
1616
type=click.Path(exists=True, file_okay=True, dir_okay=False, path_type=pathlib.Path),
1717
)
1818
@click.option(
1919
"-o",
2020
"--output",
2121
type=click.Path(path_type=pathlib.Path),
2222
)
23-
def cli(input, output):
23+
def cli(specs, output):
2424
"""
2525
Generate a Python code snippet from OpenAPI specification.
2626
"""
27-
spec = openapi.load(input)
28-
29-
version = input.parent.name
30-
3127
env = Environment(loader=FileSystemLoader(str(pathlib.Path(__file__).parent / "templates")))
3228

3329
env.filters["accept_headers"] = openapi.accept_headers
@@ -41,9 +37,7 @@ def cli(input, output):
4137
env.filters["safe_snake_case"] = openapi.safe_snake_case
4238

4339
env.globals["enumerate"] = enumerate
44-
env.globals["version"] = version
4540
env.globals["package"] = PACKAGE_NAME
46-
env.globals["openapi"] = spec
4741
env.globals["get_name"] = formatter.get_name
4842
env.globals["get_type_for_attribute"] = openapi.get_type_for_attribute
4943
env.globals["get_types_for_attribute"] = openapi.get_types_for_attribute
@@ -70,12 +64,8 @@ def cli(input, output):
7064
"exceptions.py": env.get_template("exceptions.j2"),
7165
"model_utils.py": env.get_template("model_utils.j2"),
7266
"rest.py": env.get_template("rest.j2"),
73-
"configuration.py": env.get_template("base_configuration.j2"),
7467
}
7568

76-
apis = openapi.apis(spec)
77-
models = openapi.models(spec)
78-
7969
top_package = output / PACKAGE_NAME
8070
top_package.mkdir(parents=True, exist_ok=True)
8171

@@ -84,45 +74,61 @@ def cli(input, output):
8474
with filename.open("w") as fp:
8575
fp.write(template.render())
8676

87-
package = top_package / version
88-
package.mkdir(exist_ok=True)
89-
90-
for name, model in models.items():
91-
filename = openapi.safe_snake_case(name) + ".py"
92-
model_path = package / "model" / filename
93-
model_path.parent.mkdir(parents=True, exist_ok=True)
94-
with model_path.open("w") as fp:
95-
fp.write(model_j2.render(name=name, model=model))
96-
97-
model_init_path = package / "model" / "__init__.py"
98-
with model_init_path.open("w") as fp:
99-
fp.write("")
100-
101-
models_path = package / "models" / "__init__.py"
102-
models_path.parent.mkdir(parents=True, exist_ok=True)
103-
with models_path.open("w") as fp:
104-
fp.write(models_j2.render(models=sorted(models)))
105-
106-
for name, operations in apis.items():
107-
filename = openapi.safe_snake_case(name) + "_api.py"
108-
api_path = package / "api" / filename
109-
api_path.parent.mkdir(parents=True, exist_ok=True)
110-
with api_path.open("w") as fp:
111-
fp.write(api_j2.render(name=name, operations=operations))
112-
113-
api_init_path = package / "api" / "__init__.py"
114-
with api_init_path.open("w") as fp:
115-
fp.write("")
116-
117-
apis_path = package / "apis" / "__init__.py"
118-
apis_path.parent.mkdir(parents=True, exist_ok=True)
119-
with apis_path.open("w") as fp:
120-
fp.write(apis_j2.render(apis=sorted(apis)))
121-
122-
init_path = package / "__init__.py"
123-
with init_path.open("w") as fp:
124-
fp.write(init_j2.render())
125-
126-
config_path = package / "configuration.py"
127-
with config_path.open("w") as fp:
128-
fp.write(configuration_j2.render(apis=apis))
77+
all_specs = {}
78+
all_apis = {}
79+
80+
for spec_path in specs:
81+
spec = openapi.load(spec_path)
82+
env.globals["openapi"] = spec
83+
84+
version = spec_path.parent.name
85+
env.globals["version"] = version
86+
87+
all_specs[version] = spec
88+
89+
apis = openapi.apis(spec)
90+
all_apis[version] = apis
91+
models = openapi.models(spec)
92+
93+
package = top_package / version
94+
package.mkdir(exist_ok=True)
95+
96+
for name, model in models.items():
97+
filename = openapi.safe_snake_case(name) + ".py"
98+
model_path = package / "model" / filename
99+
model_path.parent.mkdir(parents=True, exist_ok=True)
100+
with model_path.open("w") as fp:
101+
fp.write(model_j2.render(name=name, model=model))
102+
103+
model_init_path = package / "model" / "__init__.py"
104+
with model_init_path.open("w") as fp:
105+
fp.write("")
106+
107+
models_path = package / "models" / "__init__.py"
108+
models_path.parent.mkdir(parents=True, exist_ok=True)
109+
with models_path.open("w") as fp:
110+
fp.write(models_j2.render(models=sorted(models)))
111+
112+
for name, operations in apis.items():
113+
filename = openapi.safe_snake_case(name) + "_api.py"
114+
api_path = package / "api" / filename
115+
api_path.parent.mkdir(parents=True, exist_ok=True)
116+
with api_path.open("w") as fp:
117+
fp.write(api_j2.render(name=name, operations=operations))
118+
119+
api_init_path = package / "api" / "__init__.py"
120+
with api_init_path.open("w") as fp:
121+
fp.write("")
122+
123+
apis_path = package / "apis" / "__init__.py"
124+
apis_path.parent.mkdir(parents=True, exist_ok=True)
125+
with apis_path.open("w") as fp:
126+
fp.write(apis_j2.render(apis=sorted(apis)))
127+
128+
init_path = package / "__init__.py"
129+
with init_path.open("w") as fp:
130+
fp.write(init_j2.render())
131+
132+
filename = top_package / "configuration.py"
133+
with filename.open("w") as fp:
134+
fp.write(configuration_j2.render(specs=all_specs, apis=all_apis))

.generator/src/generator/templates/api.j2

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class {{ classname }}:
3131
"endpoint_path": "{{ path }}",
3232
"operation_id": "{{ operation.operationId|safe_snake_case }}",
3333
"http_method": "{{httpMethod}}",
34+
"version": "{{ version }}",
3435
{%- set servers = operation.servers %}
3536
{%- if servers %}
3637
"servers": [

.generator/src/generator/templates/api_client.j2

Lines changed: 29 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,6 @@ class ApiClient(object):
253253
post_params: Optional[List[Tuple[str, Any]]] = None,
254254
files: Optional[Dict[str, List[io.FileIO]]] = None,
255255
response_type: Optional[Tuple[Any]] = None,
256-
auth_settings: Optional[List[str]] = None,
257256
async_req: Optional[bool] = None,
258257
_return_http_data_only: Optional[bool] = None,
259258
collection_formats: Optional[Dict[str, str]] = None,
@@ -275,7 +274,6 @@ class ApiClient(object):
275274
:param body: Request body.
276275
:param post_params dict: Request post form parameters,
277276
for `application/x-www-form-urlencoded`, `multipart/form-data`.
278-
:param auth_settings list: Auth Settings names for the request.
279277
:param response_type: For the response, a tuple containing:
280278
valid classes
281279
a list containing valid classes (for list schemas)
@@ -353,9 +351,6 @@ class ApiClient(object):
353351
if body:
354352
body = self.sanitize_for_serialization(body)
355353

356-
# auth setting
357-
self.update_params_for_auth(header_params, query_params, auth_settings, resource_path, method, body)
358-
359354
# request url
360355
if _host is None:
361356
url = self.configuration.host + resource_path
@@ -494,35 +489,6 @@ class ApiClient(object):
494489
else:
495490
return content_types[0]
496491

497-
def update_params_for_auth(self, headers, queries, auth_settings, resource_path, method, body):
498-
"""Updates header and query params based on authentication setting.
499-
500-
:param headers: Header parameters dict to be updated.
501-
:param queries: Query parameters tuple list to be updated.
502-
:param auth_settings: Authentication setting identifiers list.
503-
:param resource_path: A string representation of the HTTP request resource path.
504-
:param method: A string representation of the HTTP request method.
505-
:param body: A object representing the body of the HTTP request.
506-
The object type is the return value of _encoder.default().
507-
"""
508-
if not auth_settings:
509-
return
510-
511-
for auth in auth_settings:
512-
auth_setting = self.configuration.auth_settings().get(auth)
513-
if auth_setting:
514-
if auth_setting["in"] == "cookie":
515-
headers["Cookie"] = auth_setting["value"]
516-
elif auth_setting["in"] == "header":
517-
if auth_setting["type"] != "http-signature":
518-
if auth_setting["value"] is None:
519-
raise ApiValueError("Invalid authentication token for {}".format(auth_setting["key"]))
520-
headers[auth_setting["key"]] = auth_setting["value"]
521-
elif auth_setting["in"] == "query":
522-
queries.append((auth_setting["key"], auth_setting["value"]))
523-
else:
524-
raise ApiValueError("Authentication token must be in `query` or `header`")
525-
526492

527493
class AsyncApiClient(ApiClient):
528494
def _build_rest_client(self):
@@ -598,6 +564,7 @@ class Endpoint(object):
598564
'operation_id' (str): endpoint string identifier
599565
'http_method' (str): POST/PUT/PATCH/GET etc
600566
'servers' (list): list of str servers that this endpoint is at
567+
'version' (str): the API version
601568
:type settings: dict
602569
:param params_map: See below key value pairs:
603570
'required' (bool): whether the parameter is required
@@ -706,7 +673,8 @@ class Endpoint(object):
706673

707674
def call_with_http_info(self, **kwargs):
708675

709-
is_unstable = self.api_client.configuration.unstable_operations.get(self.settings["operation_id"])
676+
is_unstable = self.api_client.configuration.unstable_operations.get(
677+
"{}.{}".format(self.settings["version"], self.settings["operation_id"]))
710678
if is_unstable:
711679
warnings.warn("Using unstable operation '{0}'".format(self.settings["operation_id"]))
712680
elif is_unstable is False:
@@ -764,6 +732,8 @@ class Endpoint(object):
764732
header_list = self.api_client.select_header_content_type(content_type_headers_list)
765733
params["header"]["Content-Type"] = header_list
766734

735+
self.update_params_for_auth(params["header"], params["query"])
736+
767737
return self.api_client.call_api(
768738
self.settings["endpoint_path"],
769739
self.settings["http_method"],
@@ -774,7 +744,6 @@ class Endpoint(object):
774744
post_params=params["form"],
775745
files=params["file"],
776746
response_type=self.settings["response_type"],
777-
auth_settings=self.settings["auth"],
778747
async_req=kwargs["async_req"],
779748
_check_type=kwargs["_check_return_type"],
780749
_return_http_data_only=kwargs["_return_http_data_only"],
@@ -784,6 +753,30 @@ class Endpoint(object):
784753
collection_formats=params["collection_format"],
785754
)
786755

756+
def update_params_for_auth(self, headers, queries):
757+
"""Updates header and query params based on authentication setting.
758+
759+
:param headers: Header parameters dict to be updated.
760+
:param queries: Query parameters tuple list to be updated.
761+
"""
762+
if not self.settings["auth"]:
763+
return
764+
765+
for auth in self.settings["auth"]:
766+
auth_setting = self.api_client.configuration.auth_settings().get(auth)
767+
if auth_setting:
768+
if auth_setting["in"] == "cookie":
769+
headers["Cookie"] = auth_setting["value"]
770+
elif auth_setting["in"] == "header":
771+
if auth_setting["type"] != "http-signature":
772+
if auth_setting["value"] is None:
773+
raise ApiValueError("Invalid authentication token for {}".format(auth_setting["key"]))
774+
headers[auth_setting["key"]] = auth_setting["value"]
775+
elif auth_setting["in"] == "query":
776+
queries.append((auth_setting["key"], auth_setting["value"]))
777+
else:
778+
raise ApiValueError("Authentication token must be in `query` or `header`")
779+
787780

788781
def user_agent():
789782
"""Generate default User-Agent header."""

0 commit comments

Comments
 (0)