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

Commit 183a575

Browse files
authored
Fixes multipart form content disposition bug (#67)
* Updates api_client template to set content disposition header with make_multipart * Sample regenerated * Fixes all but one test * Fixes tests * Samples have been regenerated
1 parent ffe148a commit 183a575

File tree

6 files changed

+124
-63
lines changed

6 files changed

+124
-63
lines changed

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

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1399,24 +1399,24 @@ class RequestBody(StyleFormSerializer, JSONDetector):
13991399

14001400
def __multipart_json_item(self, key: str, value: Schema) -> RequestField:
14011401
json_value = self.__json_encoder.default(value)
1402-
return RequestField(name=key, data=json.dumps(json_value), headers={'Content-Type': 'application/json'})
1402+
request_field = RequestField(name=key, data=json.dumps(json_value))
1403+
request_field.make_multipart(content_type='application/json')
1404+
return request_field
14031405

14041406
def __multipart_form_item(self, key: str, value: Schema) -> RequestField:
14051407
if isinstance(value, str):
1406-
return RequestField(name=key, data=str(value), headers={'Content-Type': 'text/plain'})
1408+
request_field = RequestField(name=key, data=str(value))
1409+
request_field.make_multipart(content_type='text/plain')
14071410
elif isinstance(value, bytes):
1408-
return RequestField(name=key, data=value, headers={'Content-Type': 'application/octet-stream'})
1411+
request_field = RequestField(name=key, data=value)
1412+
request_field.make_multipart(content_type='application/octet-stream')
14091413
elif isinstance(value, FileIO):
1410-
request_field = RequestField(
1411-
name=key,
1412-
data=value.read(),
1413-
filename=os.path.basename(value.name),
1414-
headers={'Content-Type': 'application/octet-stream'}
1415-
)
1414+
# TODO use content.encoding to limit allowed content types if they are present
1415+
request_field = RequestField.from_tuples(key, (os.path.basename(value.name), value.read()))
14161416
value.close()
1417-
return request_field
14181417
else:
1419-
return self.__multipart_json_item(key=key, value=value)
1418+
request_field = self.__multipart_json_item(key=key, value=value)
1419+
return request_field
14201420

14211421
def __serialize_multipart_form_data(
14221422
self, in_data: Schema

samples/openapi3/client/3_0_3_unit_test/python/unit_test_api/api_client.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1389,24 +1389,24 @@ def __serialize_text_plain(in_data: typing.Any) -> typing.Dict[str, str]:
13891389

13901390
def __multipart_json_item(self, key: str, value: Schema) -> RequestField:
13911391
json_value = self.__json_encoder.default(value)
1392-
return RequestField(name=key, data=json.dumps(json_value), headers={'Content-Type': 'application/json'})
1392+
request_field = RequestField(name=key, data=json.dumps(json_value))
1393+
request_field.make_multipart(content_type='application/json')
1394+
return request_field
13931395

13941396
def __multipart_form_item(self, key: str, value: Schema) -> RequestField:
13951397
if isinstance(value, str):
1396-
return RequestField(name=key, data=str(value), headers={'Content-Type': 'text/plain'})
1398+
request_field = RequestField(name=key, data=str(value))
1399+
request_field.make_multipart(content_type='text/plain')
13971400
elif isinstance(value, bytes):
1398-
return RequestField(name=key, data=value, headers={'Content-Type': 'application/octet-stream'})
1401+
request_field = RequestField(name=key, data=value)
1402+
request_field.make_multipart(content_type='application/octet-stream')
13991403
elif isinstance(value, FileIO):
1400-
request_field = RequestField(
1401-
name=key,
1402-
data=value.read(),
1403-
filename=os.path.basename(value.name),
1404-
headers={'Content-Type': 'application/octet-stream'}
1405-
)
1404+
# TODO use content.encoding to limit allowed content types if they are present
1405+
request_field = RequestField.from_tuples(key, (os.path.basename(value.name), value.read()))
14061406
value.close()
1407-
return request_field
14081407
else:
1409-
return self.__multipart_json_item(key=key, value=value)
1408+
request_field = self.__multipart_json_item(key=key, value=value)
1409+
return request_field
14101410

14111411
def __serialize_multipart_form_data(
14121412
self, in_data: Schema

samples/openapi3/client/features/nonCompliantUseDiscriminatorIfCompositionFails/python/this_package/api_client.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1389,24 +1389,24 @@ def __serialize_text_plain(in_data: typing.Any) -> typing.Dict[str, str]:
13891389

13901390
def __multipart_json_item(self, key: str, value: Schema) -> RequestField:
13911391
json_value = self.__json_encoder.default(value)
1392-
return RequestField(name=key, data=json.dumps(json_value), headers={'Content-Type': 'application/json'})
1392+
request_field = RequestField(name=key, data=json.dumps(json_value))
1393+
request_field.make_multipart(content_type='application/json')
1394+
return request_field
13931395

13941396
def __multipart_form_item(self, key: str, value: Schema) -> RequestField:
13951397
if isinstance(value, str):
1396-
return RequestField(name=key, data=str(value), headers={'Content-Type': 'text/plain'})
1398+
request_field = RequestField(name=key, data=str(value))
1399+
request_field.make_multipart(content_type='text/plain')
13971400
elif isinstance(value, bytes):
1398-
return RequestField(name=key, data=value, headers={'Content-Type': 'application/octet-stream'})
1401+
request_field = RequestField(name=key, data=value)
1402+
request_field.make_multipart(content_type='application/octet-stream')
13991403
elif isinstance(value, FileIO):
1400-
request_field = RequestField(
1401-
name=key,
1402-
data=value.read(),
1403-
filename=os.path.basename(value.name),
1404-
headers={'Content-Type': 'application/octet-stream'}
1405-
)
1404+
# TODO use content.encoding to limit allowed content types if they are present
1405+
request_field = RequestField.from_tuples(key, (os.path.basename(value.name), value.read()))
14061406
value.close()
1407-
return request_field
14081407
else:
1409-
return self.__multipart_json_item(key=key, value=value)
1408+
request_field = self.__multipart_json_item(key=key, value=value)
1409+
return request_field
14101410

14111411
def __serialize_multipart_form_data(
14121412
self, in_data: Schema

samples/openapi3/client/petstore/python/petstore_api/api_client.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,24 +1398,24 @@ def __serialize_text_plain(in_data: typing.Any) -> typing.Dict[str, str]:
13981398

13991399
def __multipart_json_item(self, key: str, value: Schema) -> RequestField:
14001400
json_value = self.__json_encoder.default(value)
1401-
return RequestField(name=key, data=json.dumps(json_value), headers={'Content-Type': 'application/json'})
1401+
request_field = RequestField(name=key, data=json.dumps(json_value))
1402+
request_field.make_multipart(content_type='application/json')
1403+
return request_field
14021404

14031405
def __multipart_form_item(self, key: str, value: Schema) -> RequestField:
14041406
if isinstance(value, str):
1405-
return RequestField(name=key, data=str(value), headers={'Content-Type': 'text/plain'})
1407+
request_field = RequestField(name=key, data=str(value))
1408+
request_field.make_multipart(content_type='text/plain')
14061409
elif isinstance(value, bytes):
1407-
return RequestField(name=key, data=value, headers={'Content-Type': 'application/octet-stream'})
1410+
request_field = RequestField(name=key, data=value)
1411+
request_field.make_multipart(content_type='application/octet-stream')
14081412
elif isinstance(value, FileIO):
1409-
request_field = RequestField(
1410-
name=key,
1411-
data=value.read(),
1412-
filename=os.path.basename(value.name),
1413-
headers={'Content-Type': 'application/octet-stream'}
1414-
)
1413+
# TODO use content.encoding to limit allowed content types if they are present
1414+
request_field = RequestField.from_tuples(key, (os.path.basename(value.name), value.read()))
14151415
value.close()
1416-
return request_field
14171416
else:
1418-
return self.__multipart_json_item(key=key, value=value)
1417+
request_field = self.__multipart_json_item(key=key, value=value)
1418+
return request_field
14191419

14201420
def __serialize_multipart_form_data(
14211421
self, in_data: Schema

samples/openapi3/client/petstore/python/tests_manual/test_fake_api.py

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,11 @@ def test_upload_file(self):
468468
name='file',
469469
data=file_bytes,
470470
filename=file_name,
471-
headers={'Content-Type': 'application/octet-stream'}
471+
headers={
472+
'Content-Location': None,
473+
'Content-Type': 'image/png',
474+
"Content-Disposition": "form-data; name=\"file\"; filename=\"1px_pic1.png\""
475+
}
472476
),
473477
),
474478
content_type='multipart/form-data'
@@ -492,7 +496,11 @@ def test_upload_file(self):
492496
api_client.RequestField(
493497
name='file',
494498
data=file_bytes,
495-
headers={'Content-Type': 'application/octet-stream'}
499+
headers={
500+
'Content-Type': 'application/octet-stream',
501+
"Content-Disposition": "form-data; name=\"file\"",
502+
"Content-Location": None
503+
}
496504
),
497505
),
498506
content_type='multipart/form-data'
@@ -547,13 +555,22 @@ def test_upload_files(self):
547555
name='files',
548556
data=file_bytes,
549557
filename=file_name,
550-
headers={'Content-Type': 'application/octet-stream'}
558+
headers={
559+
'Content-Type': 'image/png',
560+
"Content-Disposition": "form-data; name=\"files\"; filename=\"1px_pic1.png\"",
561+
"Content-Location": None
562+
}
551563
),
552564
api_client.RequestField(
553565
name='files',
554566
data=file_bytes,
555567
filename=file_name,
556-
headers={'Content-Type': 'application/octet-stream'}
568+
headers={
569+
'Content-Type': 'image/png',
570+
"Content-Disposition": "form-data; name=\"files\"; filename=\"1px_pic1.png\"",
571+
"Content-Location": None
572+
573+
}
557574
),
558575
),
559576
content_type='multipart/form-data'
@@ -578,12 +595,20 @@ def test_upload_files(self):
578595
api_client.RequestField(
579596
name='files',
580597
data=file_bytes,
581-
headers={'Content-Type': 'application/octet-stream'}
598+
headers={
599+
'Content-Type': 'application/octet-stream',
600+
"Content-Disposition": "form-data; name=\"files\"",
601+
"Content-Location": None
602+
}
582603
),
583604
api_client.RequestField(
584605
name='files',
585606
data=file_bytes,
586-
headers={'Content-Type': 'application/octet-stream'}
607+
headers={
608+
'Content-Type': 'application/octet-stream',
609+
"Content-Disposition": "form-data; name=\"files\"",
610+
"Content-Location": None
611+
}
587612
),
588613
),
589614
content_type='multipart/form-data'
@@ -656,11 +681,15 @@ def test_inline_composition(self, mock_request):
656681
accept_content_type=content_type,
657682
content_type=content_type,
658683
fields=(
659-
api_client.RequestField(
660-
name='someProp',
661-
data=single_char_str,
662-
headers={'Content-Type': 'text/plain'}
663-
),
684+
api_client.RequestField(
685+
name='someProp',
686+
data=single_char_str,
687+
headers={
688+
'Content-Type': 'text/plain',
689+
"Content-Disposition": "form-data; name=\"someProp\"",
690+
"Content-Location": None
691+
}
692+
),
664693
),
665694
)
666695
self.assertEqual(api_response.body, {'someProp': single_char_str})

samples/openapi3/client/petstore/python/tests_manual/test_request_body.py

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,21 +71,53 @@ def test_content_multipart_form_data_serialization(self):
7171
dict(
7272
fields=(
7373
api_client.RequestField(
74-
name='some_null', data='null', headers={'Content-Type': 'application/json'}),
74+
name='some_null', data='null', headers={
75+
'Content-Type': 'application/json',
76+
"Content-Disposition": "form-data; name=\"some_null\"",
77+
"Content-Location": None
78+
}),
7579
api_client.RequestField(
76-
name='some_bool', data='true', headers={'Content-Type': 'application/json'}),
80+
name='some_bool', data='true', headers={
81+
'Content-Type': 'application/json',
82+
"Content-Disposition": "form-data; name=\"some_bool\"",
83+
"Content-Location": None
84+
}),
7785
api_client.RequestField(
78-
name='some_str', data='a', headers={'Content-Type': 'text/plain'}),
86+
name='some_str', data='a', headers={
87+
'Content-Type': 'text/plain',
88+
"Content-Disposition": "form-data; name=\"some_str\"",
89+
"Content-Location": None
90+
}),
7991
api_client.RequestField(
80-
name='some_int', data='1', headers={'Content-Type': 'application/json'}),
92+
name='some_int', data='1', headers={
93+
'Content-Type': 'application/json',
94+
"Content-Disposition": "form-data; name=\"some_int\"",
95+
"Content-Location": None
96+
}),
8197
api_client.RequestField(
82-
name='some_float', data='3.14', headers={'Content-Type': 'application/json'}),
98+
name='some_float', data='3.14', headers={
99+
'Content-Type': 'application/json',
100+
"Content-Disposition": "form-data; name=\"some_float\"",
101+
"Content-Location": None
102+
}),
83103
api_client.RequestField(
84-
name='some_list', data='[]', headers={'Content-Type': 'application/json'}),
104+
name='some_list', data='[]', headers={
105+
'Content-Type': 'application/json',
106+
"Content-Disposition": "form-data; name=\"some_list\"",
107+
"Content-Location": None
108+
}),
85109
api_client.RequestField(
86-
name='some_dict', data='{}', headers={'Content-Type': 'application/json'}),
110+
name='some_dict', data='{}', headers={
111+
'Content-Type': 'application/json',
112+
"Content-Disposition": "form-data; name=\"some_dict\"",
113+
"Content-Location": None
114+
}),
87115
api_client.RequestField(
88-
name='some_bytes', data=b'abc', headers={'Content-Type': 'application/octet-stream'})
116+
name='some_bytes', data=b'abc', headers={
117+
'Content-Type': 'application/octet-stream',
118+
"Content-Disposition": "form-data; name=\"some_bytes\"",
119+
"Content-Location": None
120+
})
89121
)
90122
)
91123
)

0 commit comments

Comments
 (0)