Skip to content

Commit 19edc2c

Browse files
authored
Merge branch 'main' into fix/sanitize-endpoints-tag-during-parsing
2 parents 86fa00d + 661de57 commit 19edc2c

35 files changed

+268
-161
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## 0.8.0 - Unreleased
99

10+
### Breaking Changes
11+
12+
- Generated clients will no longer pass through `None` to query parameters. Previously, any query params set to `None` would surface as empty strings (per the default behavior of `httpx`). This is contrary to the defaults indicated by the OpenAPI 3.0.3 spec. Ommitting these parameters makes us more compliant. If you require a style of `null` to be passed to your query parameters, please request support for the OpenAPI "style" attribute. Thank you to @forest-benchling and @bowenwr for a ton of input on this.
13+
1014
### Additions
1115

1216
- New `--meta` command line option for specifying what type of metadata should be generated:
@@ -17,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1721
- Attempt to detect and alert users if they are using an unsupported version of OpenAPI (#281).
1822
- Fixes `Enum` deserialization when the value is `UNSET`.
1923
- Add handling of application/vnd.api+json media type.
24+
- Support passing models into query parameters (#316). Thanks @forest-benchling!
2025

2126
### Changes
2227

@@ -29,6 +34,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2934
- Endpoint tags are now sanitized during parsing to fix an issue where `My Tag` and `MyTag` are seen as two different tags but are then later unified, causing errors when creating directories. Thanks @p1-ra! (#328)
3035
- Parser will softly ignore value error during schema responses' status code convertion from string to integer (not a number). Errors will be reported to the end user and parsing will continue to proceed (#327).
3136
- The generated `from_dict` and `to_dict` methods of models will now properly handle `nullable` and `not required` properties that are themselves generated models (#315). Thanks @forest-benchling!
37+
- Fix deserialization of `None` and `Unset` properties for all types by unifying the checks (#334). Thanks @forest-benchling!
38+
- If duplicate model names are detected during generation, you'll now get an error message instead of broken code (#336). Thanks @forest-benchling!
3239

3340
## 0.7.3 - 2020-12-21
3441

end_to_end_tests/golden-record-custom/custom_e2e/api/tests/defaults_tests_defaults_post.py

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
from ...models.an_enum import AnEnum
1515
from ...models.http_validation_error import HTTPValidationError
16+
from ...models.model_with_union_property import ModelWithUnionProperty
1617
from ...types import UNSET, Unset
1718

1819

@@ -50,6 +51,8 @@ def httpx_request(
5051
union_prop: Union[Unset, float, str] = "not a float",
5152
union_prop_with_ref: Union[Unset, float, AnEnum] = 0.6,
5253
enum_prop: Union[Unset, AnEnum] = UNSET,
54+
model_prop: Union[ModelWithUnionProperty, Unset] = UNSET,
55+
required_model_prop: ModelWithUnionProperty,
5356
) -> Response[Union[None, HTTPValidationError]]:
5457

5558
json_datetime_prop: Union[Unset, str] = UNSET
@@ -89,27 +92,28 @@ def httpx_request(
8992
if not isinstance(enum_prop, Unset):
9093
json_enum_prop = enum_prop
9194

92-
params: Dict[str, Any] = {}
93-
if string_prop is not UNSET:
94-
params["string_prop"] = string_prop
95-
if datetime_prop is not UNSET:
96-
params["datetime_prop"] = json_datetime_prop
97-
if date_prop is not UNSET:
98-
params["date_prop"] = json_date_prop
99-
if float_prop is not UNSET:
100-
params["float_prop"] = float_prop
101-
if int_prop is not UNSET:
102-
params["int_prop"] = int_prop
103-
if boolean_prop is not UNSET:
104-
params["boolean_prop"] = boolean_prop
105-
if list_prop is not UNSET:
106-
params["list_prop"] = json_list_prop
107-
if union_prop is not UNSET:
108-
params["union_prop"] = json_union_prop
109-
if union_prop_with_ref is not UNSET:
110-
params["union_prop_with_ref"] = json_union_prop_with_ref
111-
if enum_prop is not UNSET:
112-
params["enum_prop"] = json_enum_prop
95+
json_model_prop: Union[Unset, Dict[str, Any]] = UNSET
96+
if not isinstance(model_prop, Unset):
97+
json_model_prop = model_prop.to_dict()
98+
99+
json_required_model_prop = required_model_prop.to_dict()
100+
101+
params: Dict[str, Any] = {
102+
"string_prop": string_prop,
103+
"datetime_prop": json_datetime_prop,
104+
"date_prop": json_date_prop,
105+
"float_prop": float_prop,
106+
"int_prop": int_prop,
107+
"boolean_prop": boolean_prop,
108+
"list_prop": json_list_prop,
109+
"union_prop": json_union_prop,
110+
"union_prop_with_ref": json_union_prop_with_ref,
111+
"enum_prop": json_enum_prop,
112+
}
113+
if not isinstance(json_model_prop, Unset):
114+
params.update(json_model_prop)
115+
params.update(json_required_model_prop)
116+
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
113117

114118
response = client.request(
115119
"post",

end_to_end_tests/golden-record-custom/custom_e2e/api/tests/get_user_list.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ def httpx_request(
6262
"an_enum_value": json_an_enum_value,
6363
"some_date": json_some_date,
6464
}
65+
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
6566

6667
response = client.request(
6768
"get",

end_to_end_tests/golden-record-custom/custom_e2e/api/tests/int_enum_tests_int_enum_post.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ def httpx_request(
4444
params: Dict[str, Any] = {
4545
"int_enum": json_int_enum,
4646
}
47+
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
4748

4849
response = client.request(
4950
"post",

end_to_end_tests/golden-record-custom/custom_e2e/api/tests/optional_value_tests_optional_query_param.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,10 @@ def httpx_request(
4343
if not isinstance(query_param, Unset):
4444
json_query_param = query_param
4545

46-
params: Dict[str, Any] = {}
47-
if query_param is not UNSET:
48-
params["query_param"] = json_query_param
46+
params: Dict[str, Any] = {
47+
"query_param": json_query_param,
48+
}
49+
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
4950

5051
response = client.request(
5152
"get",

end_to_end_tests/golden-record-custom/custom_e2e/api/tests/upload_file_tests_upload_post.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ def _parse_response(*, response: httpx.Response) -> Optional[Union[
2424
if response.status_code == 422:
2525
response_422 = HTTPValidationError.from_dict(response.json())
2626

27+
28+
2729
return response_422
2830
return None
2931

end_to_end_tests/golden-record-custom/custom_e2e/models/a_model.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import datetime
2-
from typing import Any, Dict, List, Optional, Type, TypeVar, Union, cast
2+
from typing import Any, Dict, List, Optional, Type, TypeVar, Union
33

44
import attr
55
from dateutil.parser import isoparse
@@ -28,6 +28,7 @@ class AModel:
2828
required_nullable: Optional[str]
2929
nullable_model: Optional[AModelNullableModel]
3030
nested_list_of_enums: Union[Unset, List[List[DifferentEnum]]] = UNSET
31+
a_not_required_date: Union[Unset, datetime.date] = UNSET
3132
attr_1_leading_digit: Union[Unset, str] = UNSET
3233
not_required_nullable: Union[Unset, Optional[str]] = UNSET
3334
not_required_not_nullable: Union[Unset, str] = UNSET
@@ -60,6 +61,10 @@ def to_dict(self) -> Dict[str, Any]:
6061
nested_list_of_enums.append(nested_list_of_enums_item)
6162

6263
a_nullable_date = self.a_nullable_date.isoformat() if self.a_nullable_date else None
64+
a_not_required_date: Union[Unset, str] = UNSET
65+
if not isinstance(self.a_not_required_date, Unset):
66+
a_not_required_date = self.a_not_required_date.isoformat()
67+
6368
attr_1_leading_digit = self.attr_1_leading_digit
6469
required_nullable = self.required_nullable
6570
not_required_nullable = self.not_required_nullable
@@ -91,6 +96,8 @@ def to_dict(self) -> Dict[str, Any]:
9196
)
9297
if nested_list_of_enums is not UNSET:
9398
field_dict["nested_list_of_enums"] = nested_list_of_enums
99+
if a_not_required_date is not UNSET:
100+
field_dict["a_not_required_date"] = a_not_required_date
94101
if attr_1_leading_digit is not UNSET:
95102
field_dict["1_leading_digit"] = attr_1_leading_digit
96103
if not_required_nullable is not UNSET:
@@ -145,7 +152,12 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat
145152
a_nullable_date = None
146153
_a_nullable_date = d.pop("a_nullable_date")
147154
if _a_nullable_date is not None:
148-
a_nullable_date = isoparse(cast(str, _a_nullable_date)).date()
155+
a_nullable_date = isoparse(_a_nullable_date).date()
156+
157+
a_not_required_date: Union[Unset, datetime.date] = UNSET
158+
_a_not_required_date = d.pop("a_not_required_date", UNSET)
159+
if not isinstance(_a_not_required_date, Unset):
160+
a_not_required_date = isoparse(_a_not_required_date).date()
149161

150162
attr_1_leading_digit = d.pop("1_leading_digit", UNSET)
151163

@@ -158,19 +170,17 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat
158170
nullable_model = None
159171
_nullable_model = d.pop("nullable_model")
160172
if _nullable_model is not None:
161-
nullable_model = AModelNullableModel.from_dict(cast(Dict[str, Any], _nullable_model))
173+
nullable_model = AModelNullableModel.from_dict(_nullable_model)
162174

163175
not_required_model: Union[AModelNotRequiredModel, Unset] = UNSET
164176
_not_required_model = d.pop("not_required_model", UNSET)
165177
if not isinstance(_not_required_model, Unset):
166-
not_required_model = AModelNotRequiredModel.from_dict(cast(Dict[str, Any], _not_required_model))
178+
not_required_model = AModelNotRequiredModel.from_dict(_not_required_model)
167179

168180
not_required_nullable_model = None
169181
_not_required_nullable_model = d.pop("not_required_nullable_model", UNSET)
170182
if _not_required_nullable_model is not None and not isinstance(_not_required_nullable_model, Unset):
171-
not_required_nullable_model = AModelNotRequiredNullableModel.from_dict(
172-
cast(Dict[str, Any], _not_required_nullable_model)
173-
)
183+
not_required_nullable_model = AModelNotRequiredNullableModel.from_dict(_not_required_nullable_model)
174184

175185
a_model = cls(
176186
an_enum_value=an_enum_value,
@@ -180,6 +190,7 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat
180190
model=model,
181191
nested_list_of_enums=nested_list_of_enums,
182192
a_nullable_date=a_nullable_date,
193+
a_not_required_date=a_not_required_date,
183194
attr_1_leading_digit=attr_1_leading_digit,
184195
required_nullable=required_nullable,
185196
not_required_nullable=not_required_nullable,

end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_primitive_additional_properties.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Dict, List, Type, TypeVar, Union, cast
1+
from typing import Any, Dict, List, Type, TypeVar, Union
22

33
import attr
44

@@ -36,9 +36,7 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
3636
a_date_holder: Union[ModelWithPrimitiveAdditionalPropertiesADateHolder, Unset] = UNSET
3737
_a_date_holder = d.pop("a_date_holder", UNSET)
3838
if not isinstance(_a_date_holder, Unset):
39-
a_date_holder = ModelWithPrimitiveAdditionalPropertiesADateHolder.from_dict(
40-
cast(Dict[str, Any], _a_date_holder)
41-
)
39+
a_date_holder = ModelWithPrimitiveAdditionalPropertiesADateHolder.from_dict(_a_date_holder)
4240

4341
model_with_primitive_additional_properties = cls(
4442
a_date_holder=a_date_holder,

end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_union_property.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,15 @@ def _parse_a_property(data: Any) -> Union[Unset, AnEnum, AnIntEnum]:
4646
try:
4747
a_property = UNSET
4848
_a_property = data
49-
if _a_property is not None and _a_property is not UNSET:
49+
if not isinstance(_a_property, Unset):
5050
a_property = AnEnum(_a_property)
5151

5252
return a_property
5353
except: # noqa: E722
5454
pass
5555
a_property = UNSET
5656
_a_property = data
57-
if _a_property is not None and _a_property is not UNSET:
57+
if not isinstance(_a_property, Unset):
5858
a_property = AnIntEnum(_a_property)
5959

6060
return a_property

end_to_end_tests/golden-record/my_test_api_client/api/tests/defaults_tests_defaults_post.py

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from ...client import Client
88
from ...models.an_enum import AnEnum
99
from ...models.http_validation_error import HTTPValidationError
10+
from ...models.model_with_union_property import ModelWithUnionProperty
1011
from ...types import UNSET, Response, Unset
1112

1213

@@ -23,6 +24,8 @@ def _get_kwargs(
2324
union_prop: Union[Unset, float, str] = "not a float",
2425
union_prop_with_ref: Union[Unset, float, AnEnum] = 0.6,
2526
enum_prop: Union[Unset, AnEnum] = UNSET,
27+
model_prop: Union[ModelWithUnionProperty, Unset] = UNSET,
28+
required_model_prop: ModelWithUnionProperty,
2629
) -> Dict[str, Any]:
2730
url = "{}/tests/defaults".format(client.base_url)
2831

@@ -65,27 +68,28 @@ def _get_kwargs(
6568
if not isinstance(enum_prop, Unset):
6669
json_enum_prop = enum_prop
6770

68-
params: Dict[str, Any] = {}
69-
if string_prop is not UNSET:
70-
params["string_prop"] = string_prop
71-
if datetime_prop is not UNSET:
72-
params["datetime_prop"] = json_datetime_prop
73-
if date_prop is not UNSET:
74-
params["date_prop"] = json_date_prop
75-
if float_prop is not UNSET:
76-
params["float_prop"] = float_prop
77-
if int_prop is not UNSET:
78-
params["int_prop"] = int_prop
79-
if boolean_prop is not UNSET:
80-
params["boolean_prop"] = boolean_prop
81-
if list_prop is not UNSET:
82-
params["list_prop"] = json_list_prop
83-
if union_prop is not UNSET:
84-
params["union_prop"] = json_union_prop
85-
if union_prop_with_ref is not UNSET:
86-
params["union_prop_with_ref"] = json_union_prop_with_ref
87-
if enum_prop is not UNSET:
88-
params["enum_prop"] = json_enum_prop
71+
json_model_prop: Union[Unset, Dict[str, Any]] = UNSET
72+
if not isinstance(model_prop, Unset):
73+
json_model_prop = model_prop.to_dict()
74+
75+
json_required_model_prop = required_model_prop.to_dict()
76+
77+
params: Dict[str, Any] = {
78+
"string_prop": string_prop,
79+
"datetime_prop": json_datetime_prop,
80+
"date_prop": json_date_prop,
81+
"float_prop": float_prop,
82+
"int_prop": int_prop,
83+
"boolean_prop": boolean_prop,
84+
"list_prop": json_list_prop,
85+
"union_prop": json_union_prop,
86+
"union_prop_with_ref": json_union_prop_with_ref,
87+
"enum_prop": json_enum_prop,
88+
}
89+
if not isinstance(json_model_prop, Unset):
90+
params.update(json_model_prop)
91+
params.update(json_required_model_prop)
92+
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
8993

9094
return {
9195
"url": url,
@@ -130,6 +134,8 @@ def sync_detailed(
130134
union_prop: Union[Unset, float, str] = "not a float",
131135
union_prop_with_ref: Union[Unset, float, AnEnum] = 0.6,
132136
enum_prop: Union[Unset, AnEnum] = UNSET,
137+
model_prop: Union[ModelWithUnionProperty, Unset] = UNSET,
138+
required_model_prop: ModelWithUnionProperty,
133139
) -> Response[Union[None, HTTPValidationError]]:
134140
kwargs = _get_kwargs(
135141
client=client,
@@ -143,6 +149,8 @@ def sync_detailed(
143149
union_prop=union_prop,
144150
union_prop_with_ref=union_prop_with_ref,
145151
enum_prop=enum_prop,
152+
model_prop=model_prop,
153+
required_model_prop=required_model_prop,
146154
)
147155

148156
response = httpx.post(
@@ -165,6 +173,8 @@ def sync(
165173
union_prop: Union[Unset, float, str] = "not a float",
166174
union_prop_with_ref: Union[Unset, float, AnEnum] = 0.6,
167175
enum_prop: Union[Unset, AnEnum] = UNSET,
176+
model_prop: Union[ModelWithUnionProperty, Unset] = UNSET,
177+
required_model_prop: ModelWithUnionProperty,
168178
) -> Optional[Union[None, HTTPValidationError]]:
169179
""" """
170180

@@ -180,6 +190,8 @@ def sync(
180190
union_prop=union_prop,
181191
union_prop_with_ref=union_prop_with_ref,
182192
enum_prop=enum_prop,
193+
model_prop=model_prop,
194+
required_model_prop=required_model_prop,
183195
).parsed
184196

185197

@@ -196,6 +208,8 @@ async def asyncio_detailed(
196208
union_prop: Union[Unset, float, str] = "not a float",
197209
union_prop_with_ref: Union[Unset, float, AnEnum] = 0.6,
198210
enum_prop: Union[Unset, AnEnum] = UNSET,
211+
model_prop: Union[ModelWithUnionProperty, Unset] = UNSET,
212+
required_model_prop: ModelWithUnionProperty,
199213
) -> Response[Union[None, HTTPValidationError]]:
200214
kwargs = _get_kwargs(
201215
client=client,
@@ -209,6 +223,8 @@ async def asyncio_detailed(
209223
union_prop=union_prop,
210224
union_prop_with_ref=union_prop_with_ref,
211225
enum_prop=enum_prop,
226+
model_prop=model_prop,
227+
required_model_prop=required_model_prop,
212228
)
213229

214230
async with httpx.AsyncClient() as _client:
@@ -230,6 +246,8 @@ async def asyncio(
230246
union_prop: Union[Unset, float, str] = "not a float",
231247
union_prop_with_ref: Union[Unset, float, AnEnum] = 0.6,
232248
enum_prop: Union[Unset, AnEnum] = UNSET,
249+
model_prop: Union[ModelWithUnionProperty, Unset] = UNSET,
250+
required_model_prop: ModelWithUnionProperty,
233251
) -> Optional[Union[None, HTTPValidationError]]:
234252
""" """
235253

@@ -246,5 +264,7 @@ async def asyncio(
246264
union_prop=union_prop,
247265
union_prop_with_ref=union_prop_with_ref,
248266
enum_prop=enum_prop,
267+
model_prop=model_prop,
268+
required_model_prop=required_model_prop,
249269
)
250270
).parsed

0 commit comments

Comments
 (0)