Skip to content

Commit 605c246

Browse files
dbantyNementonp1-ra
authored
feat: Allow custom templates for API and endpoint __init__ files. [#442] Thanks @p1-ra!
* templatize api,endpoint init files * e2e / add {api_init.py.jina, endpoint_init.py.jinja} custom templates tests * expose utils module to jinja2 * test_custom_templates / replace jinja2 marcro with call to utils module * task / regen / regen custom template golden record * test_end_to_end / correct typing * endoint __init__ template / factorize propagated vars * templates / globaly propagate package metadata vars * test___init__.py / correct templates API call breaking changes * Update test_end_to_end.py,openapi_python_client/__init__.py : various imprv Co-authored-by: Dylan Anthony <[email protected]> * regen templates golden records (rebased on main) Co-authored-by: Nementon <[email protected]> Co-authored-by: p1-ra <[email protected]> Co-authored-by: p1-ra <[email protected]>
1 parent 1e3b165 commit 605c246

File tree

17 files changed

+375
-72
lines changed

17 files changed

+375
-72
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
my-test-api-client
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
""" Contains methods for accessing the API """
2+
3+
from typing import Type
4+
5+
from my_test_api_client.api.default import DefaultEndpoints
6+
from my_test_api_client.api.parameters import ParametersEndpoints
7+
from my_test_api_client.api.tests import TestsEndpoints
8+
9+
10+
class MyTestApiClientApi:
11+
@classmethod
12+
def tests(cls) -> Type[TestsEndpoints]:
13+
return TestsEndpoints
14+
15+
@classmethod
16+
def default(cls) -> Type[DefaultEndpoints]:
17+
return DefaultEndpoints
18+
19+
@classmethod
20+
def parameters(cls) -> Type[ParametersEndpoints]:
21+
return ParametersEndpoints
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
""" Contains methods for accessing the API Endpoints """
2+
3+
import types
4+
5+
from my_test_api_client.api.default import get_common_parameters, post_common_parameters
6+
7+
8+
class DefaultEndpoints:
9+
@classmethod
10+
def get_common_parameters(cls) -> types.ModuleType:
11+
return get_common_parameters
12+
13+
@classmethod
14+
def post_common_parameters(cls) -> types.ModuleType:
15+
return post_common_parameters
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
""" Contains methods for accessing the API Endpoints """
2+
3+
import types
4+
5+
from my_test_api_client.api.parameters import get_same_name_multiple_locations_param
6+
7+
8+
class ParametersEndpoints:
9+
@classmethod
10+
def get_same_name_multiple_locations_param(cls) -> types.ModuleType:
11+
return get_same_name_multiple_locations_param
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
""" Contains methods for accessing the API Endpoints """
2+
3+
import types
4+
5+
from my_test_api_client.api.tests import (
6+
defaults_tests_defaults_post,
7+
get_basic_list_of_booleans,
8+
get_basic_list_of_floats,
9+
get_basic_list_of_integers,
10+
get_basic_list_of_strings,
11+
get_user_list,
12+
int_enum_tests_int_enum_post,
13+
json_body_tests_json_body_post,
14+
no_response_tests_no_response_get,
15+
octet_stream_tests_octet_stream_get,
16+
optional_value_tests_optional_query_param,
17+
post_form_data,
18+
test_inline_objects,
19+
token_with_cookie_auth_token_with_cookie_get,
20+
unsupported_content_tests_unsupported_content_get,
21+
upload_file_tests_upload_post,
22+
)
23+
24+
25+
class TestsEndpoints:
26+
@classmethod
27+
def get_user_list(cls) -> types.ModuleType:
28+
"""
29+
Get a list of things
30+
"""
31+
return get_user_list
32+
33+
@classmethod
34+
def get_basic_list_of_strings(cls) -> types.ModuleType:
35+
"""
36+
Get a list of strings
37+
"""
38+
return get_basic_list_of_strings
39+
40+
@classmethod
41+
def get_basic_list_of_integers(cls) -> types.ModuleType:
42+
"""
43+
Get a list of integers
44+
"""
45+
return get_basic_list_of_integers
46+
47+
@classmethod
48+
def get_basic_list_of_floats(cls) -> types.ModuleType:
49+
"""
50+
Get a list of floats
51+
"""
52+
return get_basic_list_of_floats
53+
54+
@classmethod
55+
def get_basic_list_of_booleans(cls) -> types.ModuleType:
56+
"""
57+
Get a list of booleans
58+
"""
59+
return get_basic_list_of_booleans
60+
61+
@classmethod
62+
def post_form_data(cls) -> types.ModuleType:
63+
"""
64+
Post form data
65+
"""
66+
return post_form_data
67+
68+
@classmethod
69+
def upload_file_tests_upload_post(cls) -> types.ModuleType:
70+
"""
71+
Upload a file
72+
"""
73+
return upload_file_tests_upload_post
74+
75+
@classmethod
76+
def json_body_tests_json_body_post(cls) -> types.ModuleType:
77+
"""
78+
Try sending a JSON body
79+
"""
80+
return json_body_tests_json_body_post
81+
82+
@classmethod
83+
def defaults_tests_defaults_post(cls) -> types.ModuleType:
84+
"""
85+
Defaults
86+
"""
87+
return defaults_tests_defaults_post
88+
89+
@classmethod
90+
def octet_stream_tests_octet_stream_get(cls) -> types.ModuleType:
91+
"""
92+
Octet Stream
93+
"""
94+
return octet_stream_tests_octet_stream_get
95+
96+
@classmethod
97+
def no_response_tests_no_response_get(cls) -> types.ModuleType:
98+
"""
99+
No Response
100+
"""
101+
return no_response_tests_no_response_get
102+
103+
@classmethod
104+
def unsupported_content_tests_unsupported_content_get(cls) -> types.ModuleType:
105+
"""
106+
Unsupported Content
107+
"""
108+
return unsupported_content_tests_unsupported_content_get
109+
110+
@classmethod
111+
def int_enum_tests_int_enum_post(cls) -> types.ModuleType:
112+
"""
113+
Int Enum
114+
"""
115+
return int_enum_tests_int_enum_post
116+
117+
@classmethod
118+
def test_inline_objects(cls) -> types.ModuleType:
119+
"""
120+
Test Inline Objects
121+
"""
122+
return test_inline_objects
123+
124+
@classmethod
125+
def optional_value_tests_optional_query_param(cls) -> types.ModuleType:
126+
"""
127+
Test optional query parameters
128+
"""
129+
return optional_value_tests_optional_query_param
130+
131+
@classmethod
132+
def token_with_cookie_auth_token_with_cookie_get(cls) -> types.ModuleType:
133+
"""
134+
Test optional cookie parameters
135+
"""
136+
return token_with_cookie_auth_token_with_cookie_get

Diff for: end_to_end_tests/regen_golden_record.py

+54-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
""" Regenerate golden-record """
2+
import filecmp
3+
import os
24
import shutil
5+
import tempfile
36
from pathlib import Path
47

58
from typer.testing import CliRunner
69

710
from openapi_python_client.cli import app
811

9-
if __name__ == "__main__":
12+
13+
def regen_golden_record():
1014
runner = CliRunner()
1115
openapi_path = Path(__file__).parent / "openapi.json"
1216

@@ -24,3 +28,52 @@
2428
if result.exception:
2529
raise result.exception
2630
output_path.rename(gr_path)
31+
32+
33+
def regen_custom_template_golden_record():
34+
runner = CliRunner()
35+
openapi_path = Path(__file__).parent / "openapi.json"
36+
tpl_dir = Path(__file__).parent / "test_custom_templates"
37+
38+
gr_path = Path(__file__).parent / "golden-record"
39+
tpl_gr_path = Path(__file__).parent / "custom-templates-golden-record"
40+
41+
output_path = Path(tempfile.mkdtemp())
42+
config_path = Path(__file__).parent / "config.yml"
43+
44+
shutil.rmtree(tpl_gr_path, ignore_errors=True)
45+
46+
os.chdir(str(output_path.absolute()))
47+
result = runner.invoke(
48+
app, ["generate", f"--config={config_path}", f"--path={openapi_path}", f"--custom-template-path={tpl_dir}"]
49+
)
50+
51+
if result.stdout:
52+
generated_output_path = output_path / "my-test-api-client"
53+
for f in generated_output_path.glob("**/*"): # nb: works for Windows and Unix
54+
relative_to_generated = f.relative_to(generated_output_path)
55+
gr_file = gr_path / relative_to_generated
56+
if not gr_file.exists():
57+
print(f"{gr_file} does not exist, ignoring")
58+
continue
59+
60+
if not gr_file.is_file():
61+
continue
62+
63+
if not filecmp.cmp(gr_file, f, shallow=False):
64+
target_file = tpl_gr_path / relative_to_generated
65+
target_dir = target_file.parent
66+
67+
target_dir.mkdir(parents=True, exist_ok=True)
68+
shutil.copy(f"{f}", f"{target_file}")
69+
70+
shutil.rmtree(output_path, ignore_errors=True)
71+
72+
if result.exception:
73+
shutil.rmtree(output_path, ignore_errors=True)
74+
raise result.exception
75+
76+
77+
if __name__ == "__main__":
78+
regen_golden_record()
79+
regen_custom_template_golden_record()
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
""" Contains methods for accessing the API """
2+
3+
from typing import Type
4+
{% for tag in endpoint_collections_by_tag.keys() %}
5+
from {{ package_name }}.api.{{ tag }} import {{ utils.pascal_case(tag) }}Endpoints
6+
{% endfor %}
7+
8+
class {{ utils.pascal_case(package_name) }}Api:
9+
{% for tag in endpoint_collections_by_tag.keys() %}
10+
@classmethod
11+
def {{ tag }}(cls) -> Type[{{ utils.pascal_case(tag) }}Endpoints]:
12+
return {{ utils.pascal_case(tag) }}Endpoints
13+
{% endfor %}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
""" Contains methods for accessing the API Endpoints """
2+
3+
import types
4+
{% for endpoint in endpoint_collection.endpoints %}
5+
from {{ package_name }}.api.{{ endpoint_collection.tag }} import {{ utils.snake_case(endpoint.name) }}
6+
{% endfor %}
7+
8+
class {{ utils.pascal_case(endpoint_collection.tag) }}Endpoints:
9+
10+
{% for endpoint in endpoint_collection.endpoints %}
11+
12+
@classmethod
13+
def {{ utils.snake_case(endpoint.name) }}(cls) -> types.ModuleType:
14+
{% if endpoint.description %}
15+
"""
16+
{{ endpoint.description }}
17+
"""
18+
{% elif endpoint.summary %}
19+
"""
20+
{{ endpoint.summary }}
21+
"""
22+
{% endif %}
23+
return {{ utils.snake_case(endpoint.name) }}
24+
{% endfor %}

0 commit comments

Comments
 (0)