OpenAPITools / OpenAPITools/openapi-generator
[BUG] [jaxrs-spec] [quarkus] Code Generation creates invalid structure for multipart/form-data
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 26.8k
- Forks
- 7.7k
- PR merge metrics
- PR metrics pending
Description
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?
- [ 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
Hello everyone,
I'm currently trying to get my file upload service running with Quarkus, but I ran into an issue with the generated code from the OpenAPI Generator for the jaxrs-spec.
The generator produces invalid code for multipart/form-data requests. When I start my Quarkus application, I receive the following error:
Caused by: java.lang.RuntimeException: RESTEASY003875: Unable to find a constructor that takes a String param or a valueOf() or fromString() method for jakarta.ws.rs.FormParam("documentMetaData") on public abstract jakarta.ws.rs.core.Response com.example.api.DocumentsApi.updateDocumentById(java.lang.String,com.example.api.model.DocumentDTO,java.io.InputStream,java.lang.String) for basetype: com.example.api.model.DocumentDTO
at org.jboss.resteasy.core.StringParameterInjector.initialize(StringParameterInjector.java:579)
at org.jboss.resteasy.core.StringParameterInjector.<init>(StringParameterInjector.java:515)
The OpenAPI Generator creates the following interface:
*/
@POST
@Consumes({ "multipart/form-data" })
@Produces({ "application/json" })
Response createDocument(@FormParam(value = "documentMetaData") DocumentDTO documentMetaData, @FormParam(value = "file") InputStream _fileInputStream,@FormParam(value = "filename") String filename);
@PUT
@Path("/{documentId}")
@Consumes({ "multipart/form-data" })
@Produces({ "application/json" })
Response updateDocumentById(@PathParam("documentId") String documentId,@FormParam(value = "documentMetaData") DocumentDTO documentMetaData, @FormParam(value = "file") InputStream _fileInputStream,@FormParam(value = "filename") String filename);
However, this approach doesn't work with Quarkus. When handling multipart/form-data, Quarkus expects a single @MultipartForm-annotated class that wraps all parts of the request. Inside that class, fields are annotated with @FormParam and @PartType, as described in the official guide: https://quarkus.io/guides/resteasy-client-multipart#setting-up-the-model
The current generator ignores this requirement and produces a flat method signature instead of using a proper wrapper class, which causes the runtime error shown above.
I've found a related issue here that seems to point in the same direction:
https://github.com/OpenAPITools/openapi-generator/issues/7994
To sum it up:
Does anyone know how to solve this issue? Has anyone faced a similar problem or found a workaround (e.g., custom template, generator option, or plugin)?
Any help would be greatly appreciated!
Thanks in advance!
openapi-generator version
7.14.0
OpenAPI declaration file content or url
openapi: 3.0.3
info:
title: Document Service API
version: 1.0.0
paths:
/documents:
post:
summary: Create a new document
description: Uploads a new document and returns the created document.
operationId: createDocument
tags:
- Documents
requestBody:
$ref: '#/components/requestBodies/CreateDocumentRequestBody'
responses:
'201':
$ref: '#/components/responses/DocumentResponse'
'400':
$ref: '#/components/responses/BadRequestErrorResponse'
'401':
$ref: '#/components/responses/UnauthorizedErrorResponse'
'403':
$ref: '#/components/responses/ForbiddenErrorResponse'
'422':
$ref: '#/components/responses/UnprocessableEntityErrorResponse'
5XX:
$ref: '#/components/responses/ServerErrorResponse'
/documents/{documentId}:
put:
summary: Update an existing document
description: Stores a new document and returns the updated document.
operationId: updateDocumentById
tags:
- Documents
parameters:
- name: documentId
in: path
required: true
schema:
type: string
description: The ID of the document to update
requestBody:
$ref: '#/components/requestBodies/CreateDocumentRequestBody'
responses:
'200':
$ref: '#/components/responses/DocumentResponse'
'400':
$ref: '#/components/responses/BadRequestErrorResponse'
'401':
$ref: '#/components/responses/UnauthorizedErrorResponse'
'403':
$ref: '#/components/responses/ForbiddenErrorResponse'
'404':
$ref: '#/components/responses/NotFoundErrorResponse'
'422':
$ref: '#/components/responses/UnprocessableEntityErrorResponse'
5XX:
$ref: '#/components/responses/ServerErrorResponse'
components:
requestBodies:
CreateDocumentRequestBody:
required: true
content:
multipart/form-data:
schema:
$ref: '#/components/schemas/CreateDocumentRequest'
schemas:
DocumentDTO:
type: object
properties:
title:
type: string
type:
type: string
createdAt:
type: string
format: date-time
createdBy:
type: string
required:
- title
- type
CreateDocumentRequest:
type: object
additionalProperties: false
properties:
documentMetaData:
$ref: '#/components/schemas/DocumentDTO'
file:
type: string
format: binary
description: The binary file content of the document
maxLength: 9999999999999
filename:
type: string
description: The name of the file
maxLength: 255
pattern: ^.*$
example: Trial.pdf
required:
- documentMetaData
- file
- filename
responses:
DocumentResponse:
description: Successful response containing document details
content:
application/json:
schema:
$ref: '#/components/schemas/DocumentDTO'
BadRequestErrorResponse:
description: The request is invalid
UnauthorizedErrorResponse:
description: Authentication is required
ForbiddenErrorResponse:
description: Access is forbidden
NotFoundErrorResponse:
description: Document not found
UnprocessableEntityErrorResponse:
description: Validation or semantic error
ServerErrorResponse:
description: Internal server error
Generation Details
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>7.14.0</version>
<executions>
<execution>
<id>generate-document</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/openapi.yaml</inputSpec>
<generatorName>jaxrs-spec</generatorName>
<output>${project.build.directory}/generated-sources/openapi</output>
<apiPackage>com.example.api</apiPackage>
<invokerPackage>com.example.api</invokerPackage>
<modelPackage>com.example.api.model</modelPackage>
<globalProperties>
<skipFormModel>false</skipFormModel>
</globalProperties>
<configOptions>
<useOneOfInterfaces>true</useOneOfInterfaces>
<useJakartaEe>true</useJakartaEe>
<library>quarkus</library>
<dateLibrary>java8</dateLibrary>
<useJakartaEe>true</useJakartaEe>
<useSwaggerAnnotations>false</useSwaggerAnnotations>
<openApiNullable>false</openApiNullable>
<interfaceOnly>true</interfaceOnly>
<generateRootResource>false</generateRootResource>
<returnResponse>true</returnResponse>
</configOptions>
<typeMappings>
<typeMapping>DateTime=LocalDateTime</typeMapping>
</typeMappings>
<importMappings>
<importMapping>LocalDateTime=java.time.LocalDateTime</importMapping>
</importMappings>
</configuration>
</execution>
</executions>
</plugin>
Steps to reproduce
Generate java code from the openapi.yml above using the jaxrs-spec generator and the quarkus library
FYI these are the other dependency from my pom.xml:
<properties>
<compiler-plugin.version>3.14.0</compiler-plugin.version>
<maven.compiler.release>21</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>3.24.5</quarkus.platform.version>
<skipITs>true</skipITs>
<surefire-plugin.version>3.5.3</surefire-plugin.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>${quarkus.platform.artifact-id}</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-openapi</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-multipart</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-client-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Related issues/PRs
https://github.com/OpenAPITools/openapi-generator/issues/7994
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
Start by reproducing the generated interface with the provided OpenAPI declaration, the jaxrs-spec generator, and the Quarkus library configuration. Compare the flat multipart/form-data parameters with Quarkus's documented @MultipartForm model; done should be generated code that starts successfully and handles the documented multipart request without the RESTEasy constructor error.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- api, backend-api-design
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100