OpenAPITools / OpenAPITools/openapi-generator
[BUG][Java] Content-Type input header not honored
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 26.8k
- Forks
- 7.7k
- PR merge metrics
- PR metrics pending
Description
Description
We have a REST endpoint where users can upload files of various content types. The user supplies the content type in the header. This supplied header value is not being passed correctly through the generated code (opting to set it to application/json instead).
openapi-generator version
java -jar openapi-generator-cli.jar version
4.0.1
We are creating Python clients with an advanced form of the following spec (which demonstrates the issue):
OpenAPI declaration file content or url
Open API spec (json):
{
"openapi": "3.0.1",
"info": {
"title": "Object",
"version": "2019-06-11T02:09:00.000Z"
},
"servers": [
{
"url": "https://me.com/objects/v1"
}
],
"paths": {
"/": {
"put": {
"description": "CreateObject: Creates an object within the collection specified; object-id is returned in response",
"parameters": [
{
"name": "Content-Type",
"in": "header",
"required": true,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ObjectRequest"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/ObjectRequest"
}
},
"image/png": {
"schema": {
"$ref": "#/components/schemas/ObjectRequest"
}
},
"application/octet-stream": {
"schema": {
"$ref": "#/components/schemas/ObjectRequest"
}
},
"application/pdf": {
"schema": {
"$ref": "#/components/schemas/ObjectRequest"
}
},
"image/jpeg": {
"schema": {
"$ref": "#/components/schemas/ObjectRequest"
}
},
"application/atom+xml": {
"schema": {
"$ref": "#/components/schemas/ObjectRequest"
}
},
"image/bmp": {
"schema": {
"$ref": "#/components/schemas/ObjectRequest"
}
},
"image/gif": {
"schema": {
"$ref": "#/components/schemas/ObjectRequest"
}
},
"application/xhtml+xml": {
"schema": {
"$ref": "#/components/schemas/ObjectRequest"
}
},
"application/rtf": {
"schema": {
"$ref": "#/components/schemas/ObjectRequest"
}
},
"text/html": {
"schema": {
"$ref": "#/components/schemas/ObjectRequest"
}
},
"image/tiff": {
"schema": {
"$ref": "#/components/schemas/ObjectRequest"
}
},
"text/csv": {
"schema": {
"$ref": "#/components/schemas/ObjectRequest"
}
},
"application/zip": {
"schema": {
"$ref": "#/components/schemas/ObjectRequest"
}
},
"application/vnd.ms-excel": {
"schema": {
"$ref": "#/components/schemas/ObjectRequest"
}
},
"text/plain": {
"schema": {
"$ref": "#/components/schemas/ObjectRequest"
}
}
},
"required": true
},
"responses": {
"202": {
"description": "202 response",
"headers": {
"Access-Control-Allow-Origin": {
"schema": {
"type": "string"
}
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ObjectResponse"
}
}
}
},
"400": {
"description": "400 response",
"headers": {
"Access-Control-Allow-Origin": {
"schema": {
"type": "string"
}
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"403": {
"description": "403 response",
"headers": {
"Access-Control-Allow-Origin": {
"schema": {
"type": "string"
}
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"404": {
"description": "404 response",
"headers": {
"Access-Control-Allow-Origin": {
"schema": {
"type": "string"
}
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"405": {
"description": "405 response",
"headers": {
"Access-Control-Allow-Origin": {
"schema": {
"type": "string"
}
},
"Allow": {
"schema": {
"type": "string"
}
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"409": {
"description": "409 response",
"headers": {
"Access-Control-Allow-Origin": {
"schema": {
"type": "string"
}
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"422": {
"description": "422 response",
"headers": {
"Access-Control-Allow-Origin": {
"schema": {
"type": "string"
}
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"500": {
"description": "500 response",
"headers": {
"Access-Control-Allow-Origin": {
"schema": {
"type": "string"
}
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
},
"security": [
{
"api_key": []
}
]
}
}
},
"components": {
"schemas": {
"Error": {
"title": "Error Response schema",
"required": [
"context",
"error"
],
"type": "object",
"properties": {
"context": {
"$ref": "#/components/schemas/ContextProperties"
},
"error": {
"$ref": "#/components/schemas/ErrorProperties"
}
}
},
"ErrorProperties": {
"title": "Error properties schema",
"required": [
"message",
"type"
],
"type": "object",
"properties": {
"stack-trace": {
"type": "array",
"items": {
"type": "string"
}
},
"corrective-action": {
"type": "string"
},
"message": {
"type": "string"
},
"type": {
"type": "string"
}
}
},
"ObjectProperties": {
"title": "Object properties schema",
"required": [
"asset-id",
"collection-id",
"collection-url",
"object-id",
"object-state",
"object-url",
"owner-id"
],
"type": "object",
"properties": {
"object-expiration-date": {
"type": "string"
},
"object-state": {
"type": "string",
"enum": [
"Created",
"Pending",
"Failed",
"Removed"
]
},
"object-url": {
"type": "string"
},
"owner-id": {
"type": "integer"
},
"collection-id": {
"type": "string"
},
"collection-url": {
"type": "string"
},
"asset-id": {
"type": "integer"
},
"object-id": {
"type": "string"
}
}
},
"ObjectRequest": {
"title": "Object body schema",
"type": "string",
"format": "binary"
},
"ObjectResponse": {
"title": "Object response schema",
"required": [
"context",
"object"
],
"type": "object",
"properties": {
"object": {
"$ref": "#/components/schemas/ObjectProperties"
}
}
}
},
"securitySchemes": {
"api_key": {
"type": "apiKey",
"name": "x-api-key",
"in": "header"
}
}
}
}
Command line used for generation
java -jar openapi-generator-cli.jar generate -i content-type-bug.json -g python -o client\object --package-name content_type_bug
Steps to reproduce
The generated code creates the following (snippit) in default_api.py, which shows that the Content-Type header is set correctly but then (almost immediately) overwritten by the result of self.api_client.select_header_content_type().
def root_put_with_http_info(self, content_type, body, **kwargs): # noqa: E501
"""root_put # noqa: E501
CreateObject: Creates an object within the collection specified; object-id is returned in response # noqa: E501
This method makes a synchronous HTTP request by default. To make an
asynchronous HTTP request, please pass async_req=True
>>> thread = api.root_put_with_http_info(content_type, body, async_req=True)
>>> result = thread.get()
:param async_req bool: execute request asynchronously
:param str content_type: (required)
:param file body: (required)
:param _return_http_data_only: response data without head status code
and headers
:param _preload_content: if False, the urllib3.HTTPResponse object will
be returned without reading/decoding response
data. Default is True.
:param _request_timeout: timeout setting for this request. If one
number provided, it will be total request
timeout. It can also be a pair (tuple) of
(connection, read) timeouts.
:return: tuple(ObjectResponse, status_code(int), headers(HTTPHeaderDict))
If the method is called asynchronously,
returns the request thread.
"""
local_var_params = locals()
all_params = ['content_type', 'body'] # noqa: E501
all_params.append('async_req')
all_params.append('_return_http_data_only')
all_params.append('_preload_content')
all_params.append('_request_timeout')
for key, val in six.iteritems(local_var_params['kwargs']):
if key not in all_params:
raise ApiTypeError(
"Got an unexpected keyword argument '%s'"
" to method root_put" % key
)
local_var_params[key] = val
del local_var_params['kwargs']
# verify the required parameter 'content_type' is set
if ('content_type' not in local_var_params or
local_var_params['content_type'] is None):
raise ApiValueError("Missing the required parameter `content_type` when calling `root_put`") # noqa: E501
# verify the required parameter 'body' is set
if ('body' not in local_var_params or
local_var_params['body'] is None):
raise ApiValueError("Missing the required parameter `body` when calling `root_put`") # noqa: E501
collection_formats = {}
path_params = {}
query_params = []
header_params = {}
if 'content_type' in local_var_params:
header_params['Content-Type'] = local_var_params['content_type'] # noqa: E501
form_params = []
local_var_files = {}
body_params = None
if 'body' in local_var_params:
body_params = local_var_params['body']
# HTTP header `Accept`
header_params['Accept'] = self.api_client.select_header_accept(
['application/json']) # noqa: E501
# HTTP header `Content-Type`
header_params['Content-Type'] = self.api_client.select_header_content_type( # noqa: E501
['application/json', 'application/xml', 'image/png', 'application/octet-stream', 'application/pdf', 'image/jpeg', 'application/atom+xml', 'image/bmp', 'image/gif', 'application/xhtml+xml', 'application/rtf', 'text/html', 'image/tiff', 'text/csv', 'application/zip', 'application/vnd.ms-excel', 'text/plain']) # noqa: E501
# Authentication setting
auth_settings = ['api_key'] # noqa: E501
return self.api_client.call_api(
'/', 'PUT',
path_params,
query_params,
header_params,
body=body_params,
post_params=form_params,
files=local_var_files,
response_type='ObjectResponse', # noqa: E501
auth_settings=auth_settings,
async_req=local_var_params.get('async_req'),
_return_http_data_only=local_var_params.get('_return_http_data_only'), # noqa: E501
_preload_content=local_var_params.get('_preload_content', True),
_request_timeout=local_var_params.get('_request_timeout'),
collection_formats=collection_formats)
Suggest a fix
The call to self.api_client.select_header_content_type() should only be made if the Content-Type header hasn't already been set.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the issue with the provided OpenAPI document and Python generation command, then inspect the generated default_api.py method root_put_with_http_info. Trace where the supplied content_type header is replaced by select_header_content_type(); done means the generated client preserves the caller-provided Content-Type for the request.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, python
- Domain
- api, tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100