[BUG] [Python] python/python-legacy generated client unable to serialize JSON object in multipart/form-data request
Bug Report Checklist
- [x] Have you provided a full/minimal spec to reproduce the issue?
- [x] Have you validated the input using an OpenAPI validator (example)?
- [x] Have you tested with the latest master to confirm the issue still exists?
- [x] Have you searched for related issues/PRs?
- [x] What's the actual output vs expected output?
- [ ] [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description
I have an endpoint in which I upload a file and a JSON request. Since I want both the JSON request, and the file to be part of the request body I have configured the endpoint to expect a multipart/form-data request.
When sending a request to the service via the generated python client, I get the following error: TypeError: Can not serialize value type: <class 'dict'>.
The generated Request python class is converted to a dictionary before being put into the params which are sent to the service. It appears the error originates from the aiohttp library because it fails to serialize a dictionary which is passed from rest.mustache.
Note: if you generate the client using the python generator or the python-legacy generator without the --library asyncio flag, a similar error occurs because the request dictionary cannot be serialized.
openapi-generator version
6.1.0
OpenAPI declaration file content or url
Example swagger.json file:
{
"openapi": "3.0.1",
"info": {
"title": "api",
"description": "An autogenerated client for the api.",
"version": "v1"
},
"paths": {
"/v1/create": {
"post": {
"operationId": "Upload",
"requestBody": {
"content": {
"multipart/form-data": {
"schema": {
"type": "object",
"properties": {
"uploadRequest": {
"required": [
"id"
],
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
}
},
"additionalProperties": false,
"description":"some descriptions"
},
"file": {
"type": "string",
"format": "binary"
}
}
}
}
}
},
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Response"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"Response": {
"type": "object",
"properties": {
"str_field": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false
},
"UploadRequest": {
"required": [
"id"
],
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
}
},
"additionalProperties": false
}
}
}
}
Generation Details
Using version 6.1.0 of openapi-generator-cli
Command to generate python client:
docker run --rm -v "${PWD}:/local" openapitools/openapi-generator-cli generate \
-i /local/swagger.json \
-g python-legacy \
-o /local/ \
--library asyncio
I am using the python-legacy generator so the generated client supports async/await semantics.
Steps to reproduce
- Generate the python client using the above command and swagger file.
- Make an upload request using the following sample code:
from openapi_client import api_client
from openapi_client import DefaultApi
from openapi_client.configuration import Configuration
from openapi_client.models import UploadRequest
import asyncio
config = Configuration(host="http://localhost:8000") # can be any URL, an error will be thrown before the request is made
base_client = api_client.ApiClient(configuration=config)
upload_api = DefaultApi(api_client=base_client)
upload_request = UploadRequest(id=12345)
path_to_file = "<path to file>"
loop = asyncio.get_event_loop()
loop.run_until_complete(upload_api.upload(file=path_to_file, upload_request=upload_request))
- Observe the error
TypeError: Can not serialize value type: <class 'dict'>
Related issues/PRs
Suggest a fix
One potential solution is to simply serialize the dictionary into a JSON string before a request is made to the service. We can update this segment in rest.mustache from this:
if isinstance(v, tuple) and len(v) == 3:
data.add_field(k,
value=v[1],
filename=v[0],
content_type=v[2])
else:
data.add_field(k, v)
to this:
if isinstance(v, tuple) and len(v) == 3:
data.add_field(k,
value=v[1],
filename=v[0],
content_type=v[2])
elif isinstance(v, dict):
data.add_field(k, json.dumps(v))
else:
data.add_field(k, v)
This way, the aiohttp library will be receiving a serialized JSON string instead of a dictionary. I have tested this change locally and have verified that it fixes this issue.