Skip to content

Commit 859d9ca

Browse files
author
Constantinos Symeonides
committed
fix: Non-string multipart fields must be stringified
1 parent 7435857 commit 859d9ca

File tree

7 files changed

+144
-6
lines changed

7 files changed

+144
-6
lines changed

end_to_end_tests/golden_record/my_test_api_client/api/tests/upload_file_tests_upload_post.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import json
12
from typing import Any, Dict, Optional, Union
23

34
import httpx
@@ -27,8 +28,10 @@ def _get_kwargs(
2728
for key, value in multipart_data.to_dict().items():
2829
if is_file(value):
2930
files[key] = value
30-
else:
31+
elif isinstance(value, str):
3132
data[key] = value
33+
else:
34+
data[key] = json.dumps(value)
3235

3336
return {
3437
"url": url,

end_to_end_tests/golden_record/my_test_api_client/models/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from .an_int_enum import AnIntEnum
77
from .another_all_of_sub_model import AnotherAllOfSubModel
88
from .body_upload_file_tests_upload_post import BodyUploadFileTestsUploadPost
9+
from .body_upload_file_tests_upload_post_some_object import BodyUploadFileTestsUploadPostSomeObject
910
from .different_enum import DifferentEnum
1011
from .free_form_model import FreeFormModel
1112
from .http_validation_error import HTTPValidationError

end_to_end_tests/golden_record/my_test_api_client/models/body_upload_file_tests_upload_post.py

+31-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from io import BytesIO
2-
from typing import Any, Dict, Type, TypeVar, Union
2+
from typing import Any, Dict, List, Type, TypeVar, Union, cast
33

44
import attr
55

6+
from ..models.body_upload_file_tests_upload_post_some_object import BodyUploadFileTestsUploadPostSomeObject
67
from ..types import UNSET, File, FileJsonType, Unset
78

89
T = TypeVar("T", bound="BodyUploadFileTestsUploadPost")
@@ -15,6 +16,9 @@ class BodyUploadFileTestsUploadPost:
1516
some_file: File
1617
some_optional_file: Union[Unset, File] = UNSET
1718
some_string: Union[Unset, str] = "some_default_string"
19+
some_number: Union[Unset, float] = UNSET
20+
some_array: Union[Unset, List[float]] = UNSET
21+
some_object: Union[Unset, BodyUploadFileTestsUploadPostSomeObject] = UNSET
1822

1923
def to_dict(self) -> Dict[str, Any]:
2024
some_file = self.some_file.to_tuple()
@@ -24,6 +28,14 @@ def to_dict(self) -> Dict[str, Any]:
2428
some_optional_file = self.some_optional_file.to_tuple()
2529

2630
some_string = self.some_string
31+
some_number = self.some_number
32+
some_array: Union[Unset, List[float]] = UNSET
33+
if not isinstance(self.some_array, Unset):
34+
some_array = self.some_array
35+
36+
some_object: Union[Unset, Dict[str, Any]] = UNSET
37+
if not isinstance(self.some_object, Unset):
38+
some_object = self.some_object.to_dict()
2739

2840
field_dict: Dict[str, Any] = {}
2941
field_dict.update(
@@ -35,6 +47,12 @@ def to_dict(self) -> Dict[str, Any]:
3547
field_dict["some_optional_file"] = some_optional_file
3648
if some_string is not UNSET:
3749
field_dict["some_string"] = some_string
50+
if some_number is not UNSET:
51+
field_dict["some_number"] = some_number
52+
if some_array is not UNSET:
53+
field_dict["some_array"] = some_array
54+
if some_object is not UNSET:
55+
field_dict["some_object"] = some_object
3856

3957
return field_dict
4058

@@ -50,10 +68,22 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
5068

5169
some_string = d.pop("some_string", UNSET)
5270

71+
some_number = d.pop("some_number", UNSET)
72+
73+
some_array = cast(List[float], d.pop("some_array", UNSET))
74+
75+
some_object: Union[Unset, BodyUploadFileTestsUploadPostSomeObject] = UNSET
76+
_some_object = d.pop("some_object", UNSET)
77+
if not isinstance(_some_object, Unset):
78+
some_object = BodyUploadFileTestsUploadPostSomeObject.from_dict(_some_object)
79+
5380
body_upload_file_tests_upload_post = cls(
5481
some_file=some_file,
5582
some_optional_file=some_optional_file,
5683
some_string=some_string,
84+
some_number=some_number,
85+
some_array=some_array,
86+
some_object=some_object,
5787
)
5888

5989
return body_upload_file_tests_upload_post
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from typing import Any, Dict, List, Type, TypeVar
2+
3+
import attr
4+
5+
T = TypeVar("T", bound="BodyUploadFileTestsUploadPostSomeObject")
6+
7+
8+
@attr.s(auto_attribs=True)
9+
class BodyUploadFileTestsUploadPostSomeObject:
10+
""" """
11+
12+
num: float
13+
text: str
14+
additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
15+
16+
def to_dict(self) -> Dict[str, Any]:
17+
num = self.num
18+
text = self.text
19+
20+
field_dict: Dict[str, Any] = {}
21+
field_dict.update(self.additional_properties)
22+
field_dict.update(
23+
{
24+
"num": num,
25+
"text": text,
26+
}
27+
)
28+
29+
return field_dict
30+
31+
@classmethod
32+
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
33+
d = src_dict.copy()
34+
num = d.pop("num")
35+
36+
text = d.pop("text")
37+
38+
body_upload_file_tests_upload_post_some_object = cls(
39+
num=num,
40+
text=text,
41+
)
42+
43+
body_upload_file_tests_upload_post_some_object.additional_properties = d
44+
return body_upload_file_tests_upload_post_some_object
45+
46+
@property
47+
def additional_keys(self) -> List[str]:
48+
return list(self.additional_properties.keys())
49+
50+
def __getitem__(self, key: str) -> Any:
51+
return self.additional_properties[key]
52+
53+
def __setitem__(self, key: str, value: Any) -> None:
54+
self.additional_properties[key] = value
55+
56+
def __delitem__(self, key: str) -> None:
57+
del self.additional_properties[key]
58+
59+
def __contains__(self, key: str) -> bool:
60+
return key in self.additional_properties

end_to_end_tests/openapi.json

+24
Original file line numberDiff line numberDiff line change
@@ -943,6 +943,30 @@
943943
"title": "Some String",
944944
"type": "string",
945945
"default": "some_default_string"
946+
},
947+
"some_number": {
948+
"title": "Some Number",
949+
"type": "number"
950+
},
951+
"some_array": {
952+
"title": "Some Array",
953+
"type": "array",
954+
"items": {
955+
"type": "number"
956+
}
957+
},
958+
"some_object": {
959+
"title": "Some Object",
960+
"type": "object",
961+
"required": ["num", "text"],
962+
"properties": {
963+
"num": {
964+
"type": "number"
965+
},
966+
"text": {
967+
"type": "string"
968+
}
969+
}
946970
}
947971
},
948972
"additionalProperties": false

end_to_end_tests/test_end_to_end.py

+17-3
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,21 @@ def test_file_upload(mocker):
8686
# Arrange
8787
from io import StringIO
8888

89-
from end_to_end_tests.golden_record.my_test_api_client.models import BodyUploadFileTestsUploadPost
89+
from end_to_end_tests.golden_record.my_test_api_client.models import (
90+
BodyUploadFileTestsUploadPost,
91+
BodyUploadFileTestsUploadPostSomeObject,
92+
)
9093
from end_to_end_tests.golden_record.my_test_api_client.types import File
9194

9295
payload = StringIO("my payload")
9396
file = File(payload=payload, file_name="my_file_name", mime_type="foo/bar")
94-
multipart_data = BodyUploadFileTestsUploadPost(some_file=file, some_string="my_string")
97+
multipart_data = BodyUploadFileTestsUploadPost(
98+
some_file=file,
99+
some_string="my_string",
100+
some_number=123,
101+
some_array=[1, 4, 9],
102+
some_object=BodyUploadFileTestsUploadPostSomeObject(num=999, text="foo"),
103+
)
95104

96105
base_url = "http://my.base.url"
97106
client = mocker.MagicMock(base_url=base_url)
@@ -109,5 +118,10 @@ def test_file_upload(mocker):
109118
cookies=ANY,
110119
timeout=ANY,
111120
files={"some_file": ("my_file_name", payload, "foo/bar")},
112-
data={"some_string": "my_string"},
121+
data={
122+
"some_string": "my_string",
123+
"some_number": "123",
124+
"some_array": "[1, 4, 9]",
125+
"some_object": '{"num": 999, "text": "foo"}',
126+
},
113127
)

openapi_python_client/templates/endpoint_module.py.jinja

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
from typing import Any, Dict, List, Optional, Union, cast
22

33
import httpx
4+
{% if endpoint.multipart_body_class %}
5+
import json
6+
{% endif %}
7+
48
from attr import asdict
59

610
from ...client import AuthenticatedClient, Client
@@ -42,8 +46,10 @@ def _get_kwargs(
4246
for key, value in multipart_data.to_dict().items():
4347
if is_file(value):
4448
files[key] = value
45-
else:
49+
elif isinstance(value, str):
4650
data[key] = value
51+
else:
52+
data[key] = json.dumps(value)
4753
{% endif %}
4854

4955
return {

0 commit comments

Comments
 (0)