Skip to content
This repository was archived by the owner on Dec 25, 2024. It is now read-only.

Commit e6bf84a

Browse files
authored
v3 fix operation overloads (#176)
* Fixes SerializedRequestBody['fields'] type * Updates ApiCLient fields type * Fixes fields type, moves RequestField into rest, fixes _get_headers * Fixes _get_used_path * Updates x_params to default to None * When x_params are required, none is not an allowed value * Moves required x_params earlier than optional ones * tweaks skip_deserilization * Adds new template for required variables * Updated templates * Simplifies operation overloads * Reorders required and optional parameters * Writes content_type on one line if there is only one * Adds generic types to request bodies * Adds generic types to request bodies * Fixes test_fake_api errors * Fixes inputs to RequestField * FIxes new signature for arays, adds newlines * Updates required properties to use newlines * Adds newlines to new optional properties * Uses newlines in additionalProperties new signature * Moves array with usages higher * Adds new arry ref generic types * Adds array generics in new * Input arg in new for more than one type uses newline * Adds newline to new anytype types * Adds parameterization to reuired ref properties in schema new * Adds required prop generic types when from properties * Adds required prop from additional prop generic types * Adds helper for new property type hints * Adds AnyTypeSchema parameterization * Adds new ref generic helper * Adds new optional property ref generic types * Adds kwarg generics when set * Changes new list/tuple input into sequence * Adds newlines to kwargs in new signature * Adds typing.Sequence for ListSchema inputs * Tweaks operation type hint, sets ApiResponseWithoutDeserialization body + headers always as unset * Regenerates samples
1 parent 5bff212 commit e6bf84a

607 files changed

Lines changed: 15583 additions & 13216 deletions

File tree

Some content is hidden

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

modules/openapi-json-schema-generator/src/main/java/org/openapijsonschematools/codegen/model/CodegenOperation.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,13 @@ public class CodegenOperation {
3232
public final CodegenRequestBody requestBody;
3333
public final List<CodegenParameter> allParams;
3434
public final List<CodegenParameter> pathParams;
35+
public final boolean hasRequiredPathParams;
3536
public final List<CodegenParameter> queryParams;
37+
public final boolean hasRequiredQueryParams;
3638
public final List<CodegenParameter> headerParams;
39+
public final boolean hasRequiredHeaderParams;
3740
public final List<CodegenParameter> cookieParams;
41+
public final boolean hasRequiredCookieParams;
3842
public final boolean hasRequiredParamOrBody;
3943
public final boolean hasOptionalParamOrBody;
4044
public final List<HashMap<String, CodegenSecurityRequirementValue>> security;
@@ -61,9 +65,57 @@ public CodegenOperation(Boolean deprecated, boolean hasErrorResponseObject, Stri
6165
this.requestBody = requestBody;
6266
this.allParams = allParams;
6367
this.pathParams = pathParams;
68+
if (pathParams == null) {
69+
this.hasRequiredPathParams = false;
70+
} else {
71+
boolean val = false;
72+
for (CodegenParameter p: pathParams) {
73+
if (Boolean.TRUE.equals(p.required)) {
74+
val = true;
75+
break;
76+
}
77+
}
78+
this.hasRequiredPathParams = val;
79+
}
6480
this.queryParams = queryParams;
81+
if (queryParams == null) {
82+
this.hasRequiredQueryParams = false;
83+
} else {
84+
boolean val = false;
85+
for (CodegenParameter p: queryParams) {
86+
if (Boolean.TRUE.equals(p.required)) {
87+
val = true;
88+
break;
89+
}
90+
}
91+
this.hasRequiredQueryParams = val;
92+
}
6593
this.headerParams = headerParams;
94+
if (headerParams == null) {
95+
this.hasRequiredHeaderParams = false;
96+
} else {
97+
boolean val = false;
98+
for (CodegenParameter p: headerParams) {
99+
if (Boolean.TRUE.equals(p.required)) {
100+
val = true;
101+
break;
102+
}
103+
}
104+
this.hasRequiredHeaderParams = val;
105+
}
66106
this.cookieParams = cookieParams;
107+
if (cookieParams == null) {
108+
this.hasRequiredCookieParams = false;
109+
} else {
110+
boolean val = false;
111+
for (CodegenParameter p: cookieParams) {
112+
if (Boolean.TRUE.equals(p.required)) {
113+
val = true;
114+
break;
115+
}
116+
}
117+
this.hasRequiredCookieParams = val;
118+
}
67119
this.hasRequiredParamOrBody = hasRequiredParamOrBody;
68120
this.hasOptionalParamOrBody = hasOptionalParamOrBody;
69121
this.security = security;

modules/openapi-json-schema-generator/src/main/resources/python/api_client.hbs

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,6 @@ from {{packageName}} import exceptions, rest, schemas, security_schemes, api_res
2929
from {{packageName}}.configurations import api_configuration, schema_configuration as schema_configuration_
3030

3131

32-
class RequestField(fields.RequestField):
33-
def __eq__(self, other):
34-
if not isinstance(other, fields.RequestField):
35-
return False
36-
return self.__dict__ == other.__dict__
37-
38-
3932
class JSONEncoder(json.JSONEncoder):
4033
compact_separators = (',', ':')
4134

@@ -771,7 +764,10 @@ class HeaderParameter(__HeaderParameterBase):
771764

772765
class TypedDictInputVerifier:
773766
@staticmethod
774-
def _verify_typed_dict_inputs(tdict_cls: typing.Type[typing_extensions.TypedDict], data: typing.Mapping[str, typing.Any]):
767+
def _verify_typed_dict_inputs(
768+
tdict_cls: typing.Type[typing_extensions.TypedDict],
769+
data: typing.Optional[typing.Mapping[str, typing.Any]]
770+
):
775771
"""
776772
Ensures that:
777773
- required keys are present
@@ -782,7 +778,7 @@ class TypedDictInputVerifier:
782778
missing_required_keys = []
783779
required_keys_with_unset_values = []
784780
for required_key in tdict_cls.__required_keys__:
785-
if required_key not in data:
781+
if data is None or required_key not in data:
786782
missing_required_keys.append(required_key)
787783
continue
788784
value = data[required_key]
@@ -802,10 +798,11 @@ class TypedDictInputVerifier:
802798
)
803799

804800
disallowed_additional_keys = []
805-
for key in data:
806-
if key in tdict_cls.__required_keys__ or key in tdict_cls.__optional_keys__:
807-
continue
808-
disallowed_additional_keys.append(key)
801+
if data is not None:
802+
for key in data:
803+
if key in tdict_cls.__required_keys__ or key in tdict_cls.__optional_keys__:
804+
continue
805+
disallowed_additional_keys.append(key)
809806
if disallowed_additional_keys:
810807
raise exceptions.ApiTypeError(
811808
'{} got {} unexpected keyword arguments: {}'.format(
@@ -1020,7 +1017,7 @@ class ApiClient:
10201017
host: str,
10211018
headers: typing.Optional[_collections.HTTPHeaderDict] = None,
10221019
body: typing.Union[str, bytes, None] = None,
1023-
fields: typing.Optional[typing.Tuple[typing.Tuple[str, str], ...]] = None,
1020+
fields: typing.Optional[typing.Tuple[rest.RequestField, ...]] = None,
10241021
security_requirement_object: typing.Optional[security_schemes.SecurityRequirementObject] = None,
10251022
stream: bool = False,
10261023
timeout: typing.Union[int, float, typing.Tuple, None] = None,
@@ -1090,7 +1087,7 @@ class ApiClient:
10901087
method: str,
10911088
url: str,
10921089
headers: typing.Optional[_collections.HTTPHeaderDict] = None,
1093-
fields: typing.Optional[typing.Tuple[typing.Tuple[str, str], ...]] = None,
1090+
fields: typing.Optional[typing.Tuple[rest.RequestField, ...]] = None,
10941091
body: typing.Union[str, bytes, None] = None,
10951092
stream: bool = False,
10961093
timeout: typing.Union[int, float, typing.Tuple, None] = None,
@@ -1189,16 +1186,17 @@ class Api(TypedDictInputVerifier):
11891186
def _get_used_path(
11901187
used_path: str,
11911188
path_parameters: typing.Tuple[typing.Type[PathParameter], ...] = (),
1192-
path_params: typing.Optional[typing.Dict[str, _SERIALIZE_TYPES]] = None,
1189+
path_params: typing.Optional[typing_extensions.TypedDict] = None,
11931190
query_parameters: typing.Tuple[typing.Type[QueryParameter], ...] = (),
1194-
query_params: typing.Optional[typing.Dict[str, _SERIALIZE_TYPES]] = None
1191+
query_params: typing.Optional[typing_extensions.TypedDict] = None
11951192
) -> str:
11961193
used_path_params = {}
11971194
if path_params is not None:
11981195
for parameter in path_parameters:
11991196
parameter_data = path_params.get(parameter.name, schemas.unset)
12001197
if isinstance(parameter_data, schemas.Unset):
12011198
continue
1199+
parameter_data = typing.cast(_SERIALIZE_TYPES, parameter_data)
12021200
serialized_data = parameter.serialize(parameter_data)
12031201
used_path_params.update(serialized_data)
12041202

@@ -1213,6 +1211,7 @@ class Api(TypedDictInputVerifier):
12131211
continue
12141212
if prefix_separator_iterator is None:
12151213
prefix_separator_iterator = parameter.get_prefix_separator_iterator()
1214+
parameter_data = typing.cast(_SERIALIZE_TYPES, parameter_data)
12161215
serialized_data = parameter.serialize(parameter_data, prefix_separator_iterator)
12171216
for serialized_value in serialized_data.values():
12181217
used_path += serialized_value
@@ -1221,7 +1220,7 @@ class Api(TypedDictInputVerifier):
12211220
@staticmethod
12221221
def _get_headers(
12231222
header_parameters: typing.Tuple[typing.Type[HeaderParameter], ...] = (),
1224-
header_params: typing.Optional[typing.Dict[str, _SERIALIZE_TYPES]] = None,
1223+
header_params: typing.Optional[typing_extensions.TypedDict] = None,
12251224
accept_content_types: typing.Tuple[str, ...] = (),
12261225
) -> _collections.HTTPHeaderDict:
12271226
headers = _collections.HTTPHeaderDict()
@@ -1230,6 +1229,7 @@ class Api(TypedDictInputVerifier):
12301229
parameter_data = header_params.get(parameter.name, schemas.unset)
12311230
if isinstance(parameter_data, schemas.Unset):
12321231
continue
1232+
parameter_data = typing.cast(_SERIALIZE_TYPES, parameter_data)
12331233
serialized_data = parameter.serialize(parameter_data)
12341234
headers.extend(serialized_data)
12351235
if accept_content_types:
@@ -1239,7 +1239,7 @@ class Api(TypedDictInputVerifier):
12391239

12401240
@staticmethod
12411241
def _get_fields_and_body(
1242-
request_body: 'RequestBody',
1242+
request_body: typing.Type[RequestBody],
12431243
body: typing.Any,
12441244
headers: _collections.HTTPHeaderDict,
12451245
content_type: str
@@ -1271,7 +1271,7 @@ class Api(TypedDictInputVerifier):
12711271

12721272
class SerializedRequestBody(typing_extensions.TypedDict, total=False):
12731273
body: typing.Union[str, bytes]
1274-
fields: typing.Tuple[typing.Union[RequestField, typing.Tuple[str, str]], ...]
1274+
fields: typing.Tuple[rest.RequestField, ...]
12751275

12761276

12771277
class RequestBody(StyleFormSerializer, JSONDetector):
@@ -1307,24 +1307,24 @@ class RequestBody(StyleFormSerializer, JSONDetector):
13071307
return {'body': str(in_data)}
13081308

13091309
@classmethod
1310-
def __multipart_json_item(cls, key: str, value: schemas.Schema) -> RequestField:
1310+
def __multipart_json_item(cls, key: str, value: schemas.Schema) -> rest.RequestField:
13111311
json_value = cls.__json_encoder.default(value)
1312-
request_field = RequestField(name=key, data=json.dumps(json_value))
1312+
request_field = rest.RequestField(name=key, data=json.dumps(json_value))
13131313
request_field.make_multipart(content_type='application/json')
13141314
return request_field
13151315

13161316
@classmethod
1317-
def __multipart_form_item(cls, key: str, value: schemas.Schema) -> RequestField:
1317+
def __multipart_form_item(cls, key: str, value: schemas.Schema) -> rest.RequestField:
13181318
if isinstance(value, str):
1319-
request_field = RequestField(name=key, data=str(value))
1319+
request_field = rest.RequestField(name=key, data=str(value))
13201320
request_field.make_multipart(content_type='text/plain')
13211321
elif isinstance(value, bytes):
1322-
request_field = RequestField(name=key, data=value)
1322+
request_field = rest.RequestField(name=key, data=value)
13231323
request_field.make_multipart(content_type='application/octet-stream')
13241324
elif isinstance(value, schemas.FileIO):
13251325
# TODO use content.encoding to limit allowed content types if they are present
1326-
request_field = RequestField.from_tuples(key, (os.path.basename(str(value.name)), value.read()))
1327-
request_field = typing.cast(RequestField, request_field)
1326+
request_field = rest.RequestField.from_tuples(key, (os.path.basename(str(value.name)), value.read()))
1327+
request_field = typing.cast(rest.RequestField, request_field)
13281328
value.close()
13291329
else:
13301330
request_field = cls.__multipart_json_item(key=key, value=value)
@@ -1353,7 +1353,7 @@ class RequestBody(StyleFormSerializer, JSONDetector):
13531353
for key, value in in_data.items():
13541354
if isinstance(value, tuple):
13551355
if value:
1356-
# values use explode = True, so the code makes a RequestField for each item with name=key
1356+
# values use explode = True, so the code makes a rest.RequestField for each item with name=key
13571357
for item in value:
13581358
request_field = cls.__multipart_form_item(key=key, value=item)
13591359
fields.append(request_field)

modules/openapi-json-schema-generator/src/main/resources/python/api_response.hbs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@ class ApiResponse:
2020
@dataclasses.dataclass
2121
class ApiResponseWithoutDeserialization(ApiResponse):
2222
response: urllib3.HTTPResponse
23-
body: typing.Union[schemas.Unset, schemas.Schema] = schemas.unset
24-
headers: typing.Union[schemas.Unset, typing_extensions.TypedDict] = schemas.unset
23+
body: schemas.Unset = schemas.unset
24+
headers: schemas.Unset = schemas.unset

modules/openapi-json-schema-generator/src/main/resources/python/components/request_bodies/request_body.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class {{jsonPathPiece.camelCase}}(api_client.RequestBody):
2727
class {{@key.camelCase}}MediaType(api_client.MediaType):
2828
{{#with this}}
2929
{{#with schema}}
30-
schema: typing.Type[{{../@key.snakeCase}}_{{jsonPathPiece.snakeCase}}.{{jsonPathPiece.camelCase}}] = {{../@key.snakeCase}}_{{jsonPathPiece.snakeCase}}.{{jsonPathPiece.camelCase}}
30+
{{> components/_helper_content_schema_type paramName="schema" modulePrefix=../@key.snakeCase }}
3131
{{/with}}
3232
{{/with}}
3333
{{/each}}

modules/openapi-json-schema-generator/src/main/resources/python/components/schemas/_helper_new.hbs

Lines changed: 54 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,18 @@ def __new__(
33
{{#if types}}
44
{{#eq types.size 1}}
55
{{#contains types "array"}}
6-
arg_: typing.Union[
7-
typing.Tuple[
8-
{{#with ../items}}{{#if refInfo.refClass}}typing.Union['{{> components/schemas/_helper_refclass_with_module }}', {{#with getDeepestRef}}{{> _helper_schema_python_types }}{{/with}}]{{else}}typing.Union[Schema_.{{jsonPathPiece.camelCase}}, {{> _helper_schema_python_types }}]{{/if}}{{/with}}, ...
9-
],
10-
typing.List[
11-
{{#with ../items}}{{#if refInfo.refClass}}typing.Union['{{> components/schemas/_helper_refclass_with_module }}', {{#with getDeepestRef}}{{> _helper_schema_python_types }}{{/with}}]{{else}}typing.Union[Schema_.{{jsonPathPiece.camelCase}}, {{> _helper_schema_python_types }}]{{/if}}{{/with}}
12-
],
6+
arg_: typing.Sequence[
7+
{{#with ../items}}
8+
{{#if refInfo.refClass}}
9+
typing.Union[
10+
{{> components/schemas/_helper_new_ref_property_value_type optional=false }}
11+
]
12+
{{else}}
13+
typing.Union[
14+
{{> components/schemas/_helper_new_property_value_type propertyClass=null optional=false }}
15+
]
16+
{{/if}}
17+
{{/with}}
1318
],
1419
{{/contains}}
1520
{{#contains types "object"}}
@@ -32,13 +37,19 @@ def __new__(
3237
{{/contains}}
3338
{{else}}
3439
{{#contains types "object"}}
35-
*args_: typing.Union[{{> _helper_schema_python_types }}],
40+
*args_: typing.Union[
41+
{{> _helper_schema_python_types_newline }}
42+
],
3643
{{else}}
37-
arg_: typing.Union[{{> _helper_schema_python_types }}],
44+
arg_: typing.Union[
45+
{{> _helper_schema_python_types_newline }}
46+
],
3847
{{/contains}}
3948
{{/eq}}
4049
{{else}}
41-
*args_: typing.Union[{{> _helper_schema_python_types }}],
50+
*args_: typing.Union[
51+
{{> _helper_schema_python_types_newline }}
52+
],
4253
{{/if}}
4354
{{#if types}}
4455
{{#eq types.size 1}}
@@ -47,16 +58,27 @@ def __new__(
4758
{{#if @key.isValid}}
4859
{{#with this}}
4960
{{#if refInfo.refClass}}
50-
{{@key.original}}: typing.Union['{{> components/schemas/_helper_refclass_with_module }}', {{#with getDeepestRef}}{{> _helper_schema_python_types }}{{/with}}],
61+
{{@key.original}}: typing.Union[
62+
{{> components/schemas/_helper_new_ref_property_value_type optional=false }}
63+
],
5164
{{else}}
5265
{{#if jsonPathPiece}}
5366
{{#if schemaIsFromAdditionalProperties}}
54-
{{@key.original}}: typing.Union[Schema_.{{jsonPathPiece.camelCase}}, {{> _helper_schema_python_types }}],
67+
{{@key.original}}: typing.Union[
68+
{{> components/schemas/_helper_new_property_value_type propertyClass=null optional=false }}
69+
],
5570
{{else}}
56-
{{@key.original}}: typing.Union[Schema_.Properties.{{jsonPathPiece.camelCase}}, {{> _helper_schema_python_types }}],
71+
{{@key.original}}: typing.Union[
72+
{{> components/schemas/_helper_new_property_value_type propertyClass="Properties" optional=false }}
73+
],
5774
{{/if}}
5875
{{else}}
59-
{{@key.original}}: typing.Union[schemas.AnyTypeSchema, {{> _helper_schema_python_types }}],
76+
{{@key.original}}: typing.Union[
77+
schemas.AnyTypeSchema[typing.Union[
78+
{{> components/schemas/_helper_schema_python_base_types_newline }}
79+
]],
80+
{{> _helper_schema_python_types_newline }}
81+
],
6082
{{/if}}
6183
{{/if}}
6284
{{/with}}
@@ -68,27 +90,39 @@ def __new__(
6890
{{#each optionalProperties}}
6991
{{#if @key.isValid}}
7092
{{#if refInfo.refClass}}
71-
{{@key.original}}: typing.Union['{{> components/schemas/_helper_refclass_with_module }}', {{#with getDeepestRef}}{{> _helper_schema_python_types }}{{/with}}, schemas.Unset] = schemas.unset,
93+
{{@key.original}}: typing.Union[
94+
{{> components/schemas/_helper_new_ref_property_value_type optional=true }}
95+
] = schemas.unset,
7296
{{else}}
73-
{{@key.original}}: typing.Union[Schema_.Properties.{{jsonPathPiece.camelCase}}, {{> _helper_schema_python_types }}, schemas.Unset] = schemas.unset,
97+
{{@key.original}}: typing.Union[
98+
{{> components/schemas/_helper_new_property_value_type propertyClass="Properties" optional=true }}
99+
] = schemas.unset,
74100
{{/if}}
75101
{{/if}}
76102
{{/each}}
77103
configuration_: typing.Optional[schemas.schema_configuration.SchemaConfiguration] = None,
78104
{{#with additionalProperties}}
79105
{{#unless isBooleanSchemaFalse}}
80106
{{#if refInfo.refClass}}
81-
**kwargs: typing.Union['{{> components/schemas/_helper_refclass_with_module }}', {{#with getDeepestRef}}{{> _helper_schema_python_types }}{{/with}}],
107+
**kwargs: typing.Union[
108+
{{> components/schemas/_helper_new_ref_property_value_type optional=false }}
109+
],
82110
{{else}}
83-
**kwargs: typing.Union[Schema_.{{jsonPathPiece.camelCase}}, {{> _helper_schema_python_types }}],
111+
**kwargs: typing.Union[
112+
{{> components/schemas/_helper_new_property_value_type propertyClass=null optional=false }}
113+
],
84114
{{/if}}
85115
{{/unless}}
86116
{{else}}
87117
{{#eq types null}}
88-
**kwargs: typing.Union[{{> components/schemas/_helper_types_all_incl_schema_oneline }}],
118+
**kwargs: typing.Union[
119+
{{> components/schemas/_helper_types_all_incl_schema_newline }}
120+
],
89121
{{else}}
90122
{{#contains types "object"}}
91-
**kwargs: typing.Union[{{> components/schemas/_helper_types_all_incl_schema_oneline }}],
123+
**kwargs: typing.Union[
124+
{{> components/schemas/_helper_types_all_incl_schema_newline }}
125+
],
92126
{{/contains}}
93127
{{/eq}}
94128
{{/with}}

0 commit comments

Comments
 (0)