Don't cast list to tuple in python-prior binding (#14014)

* Add test for python-prior type conversion error

In the spirit of test driven development, this test intentionally fails.
A following commit will fix the code to comply with the test.

See: https://github.com/OpenAPITools/openapi-generator/issues/14012

* Don't cast list to tuple in python-prior binding

Tweak the python-prior API bindings, so that they no longer cast lists
to tuples when making a POST request with a multipart/form-data
content-type. This fixes an interaction with
`urllib3.request_encode_body`, whose `fields` parameter expects tuples,
not lists.

cc @spacether

See: https://urllib3.readthedocs.io/en/stable/reference/urllib3.request.html

Fix: https://github.com/OpenAPITools/openapi-generator/issues/14012
This commit is contained in:
Jeremy Audet
2022-11-15 20:40:28 -05:00
committed by GitHub
parent 77226981b6
commit 92ecee8c27
6 changed files with 60 additions and 5 deletions

View File

@@ -298,8 +298,10 @@ class ApiClient(object):
return obj.isoformat() return obj.isoformat()
elif isinstance(obj, ModelSimple): elif isinstance(obj, ModelSimple):
return cls.sanitize_for_serialization(obj.value) return cls.sanitize_for_serialization(obj.value)
elif isinstance(obj, (list, tuple)): elif isinstance(obj, list):
return [cls.sanitize_for_serialization(item) for item in obj] return [cls.sanitize_for_serialization(item) for item in obj]
elif isinstance(obj, tuple):
return tuple(cls.sanitize_for_serialization(item) for item in obj)
if isinstance(obj, dict): if isinstance(obj, dict):
return {key: cls.sanitize_for_serialization(val) for key, val in obj.items()} return {key: cls.sanitize_for_serialization(val) for key, val in obj.items()}
raise ApiValueError( raise ApiValueError(

View File

@@ -286,8 +286,10 @@ class ApiClient(object):
return obj.isoformat() return obj.isoformat()
elif isinstance(obj, ModelSimple): elif isinstance(obj, ModelSimple):
return cls.sanitize_for_serialization(obj.value) return cls.sanitize_for_serialization(obj.value)
elif isinstance(obj, (list, tuple)): elif isinstance(obj, list):
return [cls.sanitize_for_serialization(item) for item in obj] return [cls.sanitize_for_serialization(item) for item in obj]
elif isinstance(obj, tuple):
return tuple(cls.sanitize_for_serialization(item) for item in obj)
if isinstance(obj, dict): if isinstance(obj, dict):
return {key: cls.sanitize_for_serialization(val) for key, val in obj.items()} return {key: cls.sanitize_for_serialization(val) for key, val in obj.items()}
raise ApiValueError( raise ApiValueError(

View File

@@ -286,8 +286,10 @@ class ApiClient(object):
return obj.isoformat() return obj.isoformat()
elif isinstance(obj, ModelSimple): elif isinstance(obj, ModelSimple):
return cls.sanitize_for_serialization(obj.value) return cls.sanitize_for_serialization(obj.value)
elif isinstance(obj, (list, tuple)): elif isinstance(obj, list):
return [cls.sanitize_for_serialization(item) for item in obj] return [cls.sanitize_for_serialization(item) for item in obj]
elif isinstance(obj, tuple):
return tuple(cls.sanitize_for_serialization(item) for item in obj)
if isinstance(obj, dict): if isinstance(obj, dict):
return {key: cls.sanitize_for_serialization(val) for key, val in obj.items()} return {key: cls.sanitize_for_serialization(val) for key, val in obj.items()}
raise ApiValueError( raise ApiValueError(

View File

@@ -286,8 +286,10 @@ class ApiClient(object):
return obj.isoformat() return obj.isoformat()
elif isinstance(obj, ModelSimple): elif isinstance(obj, ModelSimple):
return cls.sanitize_for_serialization(obj.value) return cls.sanitize_for_serialization(obj.value)
elif isinstance(obj, (list, tuple)): elif isinstance(obj, list):
return [cls.sanitize_for_serialization(item) for item in obj] return [cls.sanitize_for_serialization(item) for item in obj]
elif isinstance(obj, tuple):
return tuple(cls.sanitize_for_serialization(item) for item in obj)
if isinstance(obj, dict): if isinstance(obj, dict):
return {key: cls.sanitize_for_serialization(val) for key, val in obj.items()} return {key: cls.sanitize_for_serialization(val) for key, val in obj.items()}
raise ApiValueError( raise ApiValueError(

View File

@@ -286,8 +286,10 @@ class ApiClient(object):
return obj.isoformat() return obj.isoformat()
elif isinstance(obj, ModelSimple): elif isinstance(obj, ModelSimple):
return cls.sanitize_for_serialization(obj.value) return cls.sanitize_for_serialization(obj.value)
elif isinstance(obj, (list, tuple)): elif isinstance(obj, list):
return [cls.sanitize_for_serialization(item) for item in obj] return [cls.sanitize_for_serialization(item) for item in obj]
elif isinstance(obj, tuple):
return tuple(cls.sanitize_for_serialization(item) for item in obj)
if isinstance(obj, dict): if isinstance(obj, dict):
return {key: cls.sanitize_for_serialization(val) for key, val in obj.items()} return {key: cls.sanitize_for_serialization(val) for key, val in obj.items()}
raise ApiValueError( raise ApiValueError(

View File

@@ -14,8 +14,11 @@ from collections import namedtuple
import os import os
import json import json
import unittest import unittest
from pathlib import Path
from unittest.mock import patch from unittest.mock import patch
from urllib3.request import RequestMethods
import petstore_api import petstore_api
from petstore_api.api.fake_api import FakeApi # noqa: E501 from petstore_api.api.fake_api import FakeApi # noqa: E501
from petstore_api.rest import RESTClientObject, RESTResponse from petstore_api.rest import RESTClientObject, RESTResponse
@@ -490,6 +493,48 @@ class TestFakeApi(unittest.TestCase):
finally: finally:
file.close() file.close()
def test_upload_with_mime_type(self):
"""Upload a file, while setting a MIME type for that file.
Verify that:
* ``post_params`` may contain a three-tuple in the form ``(file_name, file_handle,
file_mime_type)``
* This three-tuple is passed to urllib3 as a tuple, without being converted to a list.
See:
* https://github.com/OpenAPITools/openapi-generator/issues/14012
* https://urllib3.readthedocs.io/en/stable/reference/urllib3.request.html
"""
file_path = Path(__file__, "..", "..", "testfiles", "1px_pic1.png").resolve()
file_mime_type = "image/png"
with patch.object(RequestMethods, 'request') as request:
with open(file_path, mode="rb") as file_handle:
request.return_value.status = 200
resp = self.api.api_client.call_api(
resource_path="/fake/uploadFile",
method="POST",
header_params={"Content-Type": "multipart/form-data"},
post_params={"file": (file_path.name, file_handle, file_mime_type)},
)
# a single multipart/form-data POST call was made, with a single form field
request.assert_called_once()
fields = request.call_args.kwargs['fields']
self.assertEqual(len(fields), 1, fields)
field = fields[0]
# it is in the form (form_field_name, (filename, filedata, mimetype))
self.assertEqual(field[0], "file")
self.assertEqual(field[1][0], file_path.name)
with open(file_path, mode="rb") as file_handle:
self.assertEqual(field[1][1], file_handle.read())
self.assertEqual(field[1][2], file_mime_type)
# the form field value wasn't cast to a list
self.assertIsInstance(fields[0][1], tuple)
def test_download_attachment(self): def test_download_attachment(self):
"""Ensures that file deserialization works""" """Ensures that file deserialization works"""