Skip to content

[jaxrs-spec] Bind an array of binary form properties to a list - #24452

Closed
Ignacio-Vidal wants to merge 1 commit into
OpenAPITools:masterfrom
Ignacio-Vidal:jaxrs-spec-files-upload
Closed

[jaxrs-spec] Bind an array of binary form properties to a list#24452
Ignacio-Vidal wants to merge 1 commit into
OpenAPITools:masterfrom
Ignacio-Vidal:jaxrs-spec-files-upload

Conversation

@Ignacio-Vidal

@Ignacio-Vidal Ignacio-Vidal commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

A type: array of format: binary form properties was collapsed onto the scalar file type, so a multi-file upload was generated with the same signature as a single-file one — only the array dimension was silently dropped, and only one file could ever be bound.

Given:

files:
  type: array
  items:
    type: string
    format: binary

Before (identical to the single-file signature):

public Response uploadMultiple(@FormParam(value = "files") InputStream filesInputStream) {

After — default library:

public Response uploadMultiple(@FormParam(value = "files") List<InputStream> filesInputStream) {

After — quarkus library:

public Response uploadMultiple(@RestForm(value = "files") List<FileUpload> files) {

The array dimension is now preserved. isArray was already populated on file parameters — this mirrors what JavaJaxRS/libraries/jersey3 already does with List<FormDataBodyPart> — so no DefaultCodegen change was needed.

The quarkus library additionally moves from InputStream to the RESTEasy Reactive multipart types via a new library-scoped formParams.mustache. Per the Quarkus REST guide, the supported multipart types are FileUpload, Path, File, byte[] and Buffer; InputStream is not among them, and multiple files sharing one part name require List<FileUpload>. The other libraries (thorntail, helidon, openliberty, kumuluzee, default) keep plain java.io/java.util types so nothing outside Quarkus gains a dependency.

The org.jboss.resteasy.reactive imports are emitted only for API files that actually declare a file form parameter, via a new hasFileFormParams flag — verified against petstore, where only PetApi gets them and StoreApi/UserApi stay unchanged.

Note this changes the generated signature for existing quarkus users with a single-file upload: InputStream _fileInputStream becomes FileUpload _file. That is the type Quarkus actually supports, but it is a breaking change for code implementing the generated interfaces.

Testing

  • 3 new tests in JavaJAXRSSpecServerCodegenTest (array-as-list for the default library, List<FileUpload> for quarkus, and an import-gating regression test); reuses the existing 3_0/form-multipart-binary-array.yaml fixture.
  • 151 tests in JavaJAXRSSpecServerCodegenTest and 1084 across org.openapitools.codegen.java.** pass.
  • The regenerated samples/server/petstore/jaxrs-spec-quarkus-mutiny sample compiles against Quarkus (mvn compile), and a live Quarkus app built from these templates bound 3 files posted under one part name.

PR checklist

  • Read the contribution guidelines.
  • Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    (For Windows users, please run the script in WSL)
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.

Summary by cubic

Fixes multi-file form uploads in jaxrs-spec by preserving array dimensions for format: binary. Arrays of files now bind to @FormParam List<InputStream> instead of collapsing to a single InputStream.

  • Bug Fixes
    • Arrays of binary form parts now generate @FormParam("...") List<InputStream> ...InputStream.
    • Single-file params stay as InputStream; existing signatures are unchanged.
    • Applies to all libraries, including quarkus.

Written for commit 8e6ee81. Summary will update on new commits.

Review in cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 8 files

Re-trigger cubic

@Ignacio-Vidal
Ignacio-Vidal marked this pull request as draft July 26, 2026 02:10
A `type: array` of `format: binary` form properties was collapsed onto the
scalar file type, so a multi-file upload was generated with the same signature
as a single-file one and only one file could ever be bound.

The array dimension is now preserved and such a parameter is generated as
`List<InputStream>`. A single binary property is still bound to a scalar
`InputStream`, so existing specs are unaffected.
@Ignacio-Vidal

Copy link
Copy Markdown
Contributor Author

Converting this to a draft — I verified the generated signature against a real runtime and it does not work.

List<InputStream> cannot be injected via @FormParam. Deploying the generated resource on RESTEasy Classic (Quarkus 3.27.4.1, quarkus-resteasy + quarkus-resteasy-multipart) fails at startup:

RESTEASY003875: Unable to find a constructor that takes a String param or a valueOf()
or fromString() method for jakarta.ws.rs.FormParam("files")
... for basetype: java.io.InputStream

@FormParam is a string-based parameter annotation: for List<T>, JAX-RS requires T to be constructible from a String (constructor, valueOf(), or fromString()). InputStream has none of those. The scalar case only works because RESTEasy special-cases InputStream as the raw entity body, and that special case does not extend to collections.

So this change would turn a silent data-loss bug (only one file bound) into a hard deployment failure, which is worse. My earlier verification only checked that the generated code compiles — it does — and never checked that it deploys. That was not sufficient for an annotation-driven injection contract.

Worth noting the other JAX-RS generators avoid this by using @FormDataParam with a provider-specific type (List<FormDataBodyPart> in jersey, List<Attachment> in CXF) rather than @FormParam with a raw type — the annotation, not just the type, is what makes those work.

Options I can see:

  1. Close this. There is no portable multi-file binding via @FormParam on RESTEasy Classic; a correct fix needs @MultipartForm with a POJO, which is a much larger change.
  2. Narrow the fix to the quarkus/Jakarta path, where @RestForm List<FileUpload> is a supported binding (verified end-to-end: the server receives all N files). That would fold into [jaxrs-spec][quarkus] Migrate the Jakarta path to Quarkus REST and fix file upload types #24455.
  3. Implement a per-library binding (List<FormDataBodyPart>, List<Attachment>, …), a substantially bigger piece of work.

I'd lean towards option 2. Happy to follow whichever direction maintainers prefer — flagging now so nobody spends review time on the current diff.

@Ignacio-Vidal

Copy link
Copy Markdown
Contributor Author

Closing in favour of #24455.

As above, @FormParam List<InputStream> is not a valid JAX-RS binding and breaks the application at startup, so there is no portable multi-file fix for the shared template. The array fix is being folded into #24455, where the quarkus/Jakarta path binds file parts with @RestForm + FileUpload/List<FileUpload> — a supported multipart binding, verified end-to-end against a running server.

The equivalent client-side fix for java/microprofile remains open as #24453, where List<File> is correct because the MicroProfile REST Client serialises the parts rather than injecting them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant