[BUG] [Java Native] client incorrect generation of `application/octet-stream`
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
When generating a native Java client using application/octet-stream it incorrectly tries to decode it to JSON using ObjectMapper.
openapi-generator version
7.2.0
OpenAPI declaration file content or url
"/datasetrw/snapshot/{snapshotId}/file/raw": {
"get": {
"operationId": "getFileRaw",
"parameters": [
{
"description": "snapshot ID",
"in": "path",
"name": "snapshotId",
"required": true,
"schema": {
"pattern": "^[0-9a-f]{24}$",
"type": "string"
}
},
{
"description": "path of file to get raw content for",
"in": "query",
"name": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"description": "whether endpoint is used for download",
"in": "query",
"name": "download",
"required": false,
"schema": {
"nullable": true,
"type": "boolean"
}
}
],
"responses": {
"200": {
"content": {
"application/octet-stream": {
"schema": {
"format": "binary",
"type": "string"
}
}
},
"description": "success"
},
"400": {
"$ref": "#/components/responses/BadRequest"
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"403": {
"$ref": "#/components/responses/Forbidden"
},
"500": {
"$ref": "#/components/responses/InternalError"
}
},
"summary": "Get snapshot file raw content at specified path",
"tags": [
"DatasetRw"
]
}
},
Generation Details
It then generates this Java code trying to convert it to java.io.File...
public ApiResponse<File> getFileRawWithHttpInfo(String snapshotId, String path, Boolean download) throws ApiException {
HttpRequest.Builder localVarRequestBuilder = getFileRawRequestBuilder(snapshotId, path, download);
try {
HttpResponse<InputStream> localVarResponse = memberVarHttpClient.send(
localVarRequestBuilder.build(),
HttpResponse.BodyHandlers.ofInputStream());
if (memberVarResponseInterceptor != null) {
memberVarResponseInterceptor.accept(localVarResponse);
}
try {
if (localVarResponse.statusCode()/ 100 != 2) {
throw getApiException("getFileRaw", localVarResponse);
}
return new ApiResponse<File>(
localVarResponse.statusCode(),
localVarResponse.headers().map(),
localVarResponse.body() == null ? null : memberVarObjectMapper.readValue(localVarResponse.body(), new TypeReference<File>() {}) // closes the InputStream
);
} finally {
}
} catch (IOException e) {
throw new ApiException(e);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new ApiException(e);
}
}
You can see this line is incorrect.
localVarResponse.body() == null ? null : memberVarObjectMapper.readValue(localVarResponse.body(), new TypeReference<File>() {}) // closes the InputStream
Its using the JSON Mapper to try and convert to a File.
I encountered this same issue with version 7.17.0 using the java generator from the openapi-generator-maven-plugin. The specific error is:
org.springframework.web.client.UnknownContentTypeException: Could not extract response: no suitable HttpMessageConverter found for response type [class java.io.File] and content type [application/octet-stream]
at org.springframework.web.client.DefaultRestClient.readWithMessageConverters(DefaultRestClient.java:251) ~[spring-web-6.2.10.jar:6.2.10]
at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.readBody(DefaultRestClient.java:826) ~[spring-web-6.2.10.jar:6.2.10]
at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.lambda$body$1(DefaultRestClient.java:765) ~[spring-web-6.2.10.jar:6.2.10]
at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.exchangeInternal(DefaultRestClient.java:586) ~[spring-web-6.2.10.jar:6.2.10]
at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.exchange(DefaultRestClient.java:540) ~[spring-web-6.2.10.jar:6.2.10]
at org.springframework.web.client.RestClient$RequestHeadersSpec.exchange(RestClient.java:680) ~[spring-web-6.2.10.jar:6.2.10]
at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.executeAndExtract(DefaultRestClient.java:821) ~[spring-web-6.2.10.jar:6.2.10]
at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.body(DefaultRestClient.java:765) ~[spring-web-6.2.10.jar:6.2.10]
...
My OpenAPI spec is the following:
paths:
...
/files/{id}/download:
get:
tags:
- file-controller
summary: Download a file by id
description: Download a file by id
operationId: downloadFile
parameters:
- name: id
in: path
description: UUID of the file
required: true
schema:
type: string
format: uuid
responses:
"200":
description: The actual contents of the file, as byte stream
content:
application/octet-stream:
schema:
type: string
format: binary
With the following configuration of the maven plugin:
<configuration>
<inputSpec>${project.basedir}/src/main/resources/swagger/client/files-service.yaml</inputSpec>
<generatorName>java</generatorName>
<library>restclient</library>
<apiPackage>com.xxx.files_service.api</apiPackage>
<modelPackage>com.xxx.files_service.model.api</modelPackage>
<generateApiTests>false</generateApiTests>
<generateModelTests>false</generateModelTests>
<generateApiDocumentation>false</generateApiDocumentation>
<strictSpec>true</strictSpec>
<modelNamePrefix>Fs</modelNamePrefix>
<configOptions>
<annotationLibrary>none</annotationLibrary>
<delegatePattern>true</delegatePattern>
<documentationProvider>none</documentationProvider>
<generateClientAsBean>true</generateClientAsBean>
<openApiNullable>false</openApiNullable>
<serializableModel>true</serializableModel>
<useBeanValidation>true</useBeanValidation>
<useOptional>true</useOptional>
<useJakartaEe>true</useJakartaEe>
<performBeanValidation>true</performBeanValidation>
</configOptions>
</configuration>
In the meantime, I opted for returning a JSON object with the binary file data as String, not the best solution (especially with large files) but it works.