Skip to content

[java][spring] Add @RequestParam for oneOf/allOf-wrapped model query params - #25211

Closed
cmagowan wants to merge 2 commits into
OpenAPITools:masterfrom
cmagowan:fix/spring-requestparam-oneof-model-query-param
Closed

cmagowan wants to merge 2 commits into
OpenAPITools:masterfrom
cmagowan:fix/spring-requestparam-oneof-model-query-param

Conversation

@cmagowan

@cmagowan cmagowan commented Oct 10, 2026 •

Copy link
Copy Markdown

Fix #25210

Problem

When a query parameter's schema wraps a $ref to a named model in oneOf or allOf, e.g.

- name: MaxPrice
  in: query
  schema:
    nullable: true
    oneOf:
      - $ref: '#/components/schemas/Price'

the spring generator leaves out @RequestParam on the generated parameter. The parameter resolves to isModel=true, and JavaSpring/queryParams.mustache only emits @RequestParam inside a {{^isModel}} guard. That guard was added in #2710 (for #2655) so that inline object query params are bound as a model attribute. It also catches these wrapped model refs.

Fix

Keep the existing {{^isModel}} branch and add an identical @RequestParam branch for models without vars:

{{#isModel}}{{^hasVars}} @RequestParam(...){{/hasVars}}{{/isModel}}{{^isModel}} @RequestParam(...){{/isModel}}

Behaviour by query param schema (all isModel=true):

Query param schema hasVars Before After
oneOf: [$ref: Price] false no @RequestParam @RequestParam
allOf: [$ref: Price] false no @RequestParam @RequestParam
direct $ref: Price true no @RequestParam unchanged
inline type: object (#2655) true no @RequestParam unchanged

Before:

@Parameter(name = "MaxPrice", description = "A model-typed query param via oneOf ref", in = ParameterIn.QUERY) @Valid Price maxPrice

After:

@Parameter(name = "MaxPrice", description = "A model-typed query param via oneOf ref", in = ParameterIn.QUERY) @Valid @RequestParam(value = "MaxPrice", required = false) Price maxPrice

Tests

  • Added SpringCodegenTest#shouldGenerateRequestParamForOneOfRefQueryParam with the spec 3_0/spring/issue-model-query-param-requestparam.yaml.
  • The existing tests doNotGenerateRequestParamForObjectQueryParam ([BUG] Query Parameter Serialization (Spring) #2655) and doGenerateRequestParamForSimpleParam still pass.
  • Regenerated the spring* and java-camel* samples: no changes, since no sample uses a oneOf/allOf-wrapped model query param.

kotlin-spring/queryParams.mustache has the same {{^isModel}} guard but is not changed in this PR.

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.

cc Java Spring technical committee: @cachescrubber @welshm @MelleD @atextor @manedev79 @javisst @borsch @banlevente @Zomzog @martin-mfg @KannaKim


Summary by cubic

Fixes @RequestParam being dropped from query parameters whose schema wraps a model $ref inside oneOf or allOf (e.g. oneOf: [$ref: Price]), which prevented Spring from binding them.

  • Adds @RequestParam for model query params without variables; inline object params and direct $ref params keep their existing behavior.
  • Adds a regression test covering oneOf- and allOf-wrapped model query params, plus negative cases for direct $ref and inline object params.

Written for commit d02ff07. Summary will update on new commits.

View guided diff

…params

Query params whose schema wraps a $ref to a named model in oneOf/allOf
(e.g. oneOf: [$ref: Price]) resolve to isModel=true, so the {{^isModel}}
guard added in OpenAPITools#2710 (for OpenAPITools#2655) dropped their @RequestParam.
Add a {{#isModel}}{{^hasVars}} branch in JavaSpring/queryParams.mustache
so these params get @RequestParam again. Inline object query params
(hasVars=true) still get no @RequestParam, as OpenAPITools#2655 requires.
Fixes OpenAPITools#25210

@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.

1 issue found across 3 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/resources/JavaSpring/queryParams.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/JavaSpring/queryParams.mustache:1">
P1: `hasVars` is never populated for query parameters, so this branch also adds `@RequestParam` to ordinary model `$ref`s and changes their existing binding behavior. Gate this annotation on the wrapped `oneOf`/`allOf` case instead.</violation>
</file>

Reply to a comment to ask cubic a question or push back. It learns from your replies.

View guided diff | Re-trigger cubic

@@ -1 +1 @@
{{#isQueryParam}}{{#vendorExtensions.x-field-extra-annotation}}{{{.}}} {{/vendorExtensions.x-field-extra-annotation}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{>paramDoc}}{{#useBeanValidation}}{{^isContainer}}{{#isModel}} @Valid{{/isModel}}{{^isModel}}{{^useOptional}} @Valid{{/useOptional}}{{#useOptional}}{{#required}} @Valid{{/required}}{{/useOptional}}{{/isModel}}{{/isContainer}}{{/useBeanValidation}}{{^isModel}} @RequestParam(value = {{#isMap}}""{{/isMap}}{{^isMap}}"{{baseName}}"{{/isMap}}{{#required}}, required = true{{/required}}{{^required}}, required = false{{/required}}{{#defaultValue}}, defaultValue = "{{{.}}}"{{/defaultValue}}){{/isModel}}{{>dateTimeParam}}{{#isDeprecated}} @Deprecated{{/isDeprecated}} {{>nullableAnnotation}}{{>optionalDataType}} {{paramName}}{{/isQueryParam}} No newline at end of file
{{#isQueryParam}}{{#vendorExtensions.x-field-extra-annotation}}{{{.}}} {{/vendorExtensions.x-field-extra-annotation}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{>paramDoc}}{{#useBeanValidation}}{{^isContainer}}{{#isModel}} @Valid{{/isModel}}{{^isModel}}{{^useOptional}} @Valid{{/useOptional}}{{#useOptional}}{{#required}} @Valid{{/required}}{{/useOptional}}{{/isModel}}{{/isContainer}}{{/useBeanValidation}}{{#isModel}}{{^hasVars}} @RequestParam(value = {{#isMap}}""{{/isMap}}{{^isMap}}"{{baseName}}"{{/isMap}}{{#required}}, required = true{{/required}}{{^required}}, required = false{{/required}}{{#defaultValue}}, defaultValue = "{{{.}}}"{{/defaultValue}}){{/hasVars}}{{/isModel}}{{^isModel}} @RequestParam(value = {{#isMap}}""{{/isMap}}{{^isMap}}"{{baseName}}"{{/isMap}}{{#required}}, required = true{{/required}}{{^required}}, required = false{{/required}}{{#defaultValue}}, defaultValue = "{{{.}}}"{{/defaultValue}}){{/isModel}}{{>dateTimeParam}}{{#isDeprecated}} @Deprecated{{/isDeprecated}} {{>nullableAnnotation}}{{>optionalDataType}} {{paramName}}{{/isQueryParam}} No newline at end of file

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.

P1: hasVars is never populated for query parameters, so this branch also adds @RequestParam to ordinary model $refs and changes their existing binding behavior. Gate this annotation on the wrapped oneOf/allOf case instead.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At modules/openapi-generator/src/main/resources/JavaSpring/queryParams.mustache, line 1:

<comment>`hasVars` is never populated for query parameters, so this branch also adds `@RequestParam` to ordinary model `$ref`s and changes their existing binding behavior. Gate this annotation on the wrapped `oneOf`/`allOf` case instead.</comment>

<file context>
@@ -1 +1 @@
-{{#isQueryParam}}{{#vendorExtensions.x-field-extra-annotation}}{{{.}}} {{/vendorExtensions.x-field-extra-annotation}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{>paramDoc}}{{#useBeanValidation}}{{^isContainer}}{{#isModel}} @Valid{{/isModel}}{{^isModel}}{{^useOptional}} @Valid{{/useOptional}}{{#useOptional}}{{#required}} @Valid{{/required}}{{/useOptional}}{{/isModel}}{{/isContainer}}{{/useBeanValidation}}{{^isModel}} @RequestParam(value = {{#isMap}}""{{/isMap}}{{^isMap}}"{{baseName}}"{{/isMap}}{{#required}}, required = true{{/required}}{{^required}}, required = false{{/required}}{{#defaultValue}}, defaultValue = "{{{.}}}"{{/defaultValue}}){{/isModel}}{{>dateTimeParam}}{{#isDeprecated}} @Deprecated{{/isDeprecated}} {{>nullableAnnotation}}{{>optionalDataType}} {{paramName}}{{/isQueryParam}}
\ No newline at end of file
+{{#isQueryParam}}{{#vendorExtensions.x-field-extra-annotation}}{{{.}}} {{/vendorExtensions.x-field-extra-annotation}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{>paramDoc}}{{#useBeanValidation}}{{^isContainer}}{{#isModel}} @Valid{{/isModel}}{{^isModel}}{{^useOptional}} @Valid{{/useOptional}}{{#useOptional}}{{#required}} @Valid{{/required}}{{/useOptional}}{{/isModel}}{{/isContainer}}{{/useBeanValidation}}{{#isModel}}{{^hasVars}} @RequestParam(value = {{#isMap}}""{{/isMap}}{{^isMap}}"{{baseName}}"{{/isMap}}{{#required}}, required = true{{/required}}{{^required}}, required = false{{/required}}{{#defaultValue}}, defaultValue = "{{{.}}}"{{/defaultValue}}){{/hasVars}}{{/isModel}}{{^isModel}} @RequestParam(value = {{#isMap}}""{{/isMap}}{{^isMap}}"{{baseName}}"{{/isMap}}{{#required}}, required = true{{/required}}{{^required}}, required = false{{/required}}{{#defaultValue}}, defaultValue = "{{{.}}}"{{/defaultValue}}){{/isModel}}{{>dateTimeParam}}{{#isDeprecated}} @Deprecated{{/isDeprecated}} {{>nullableAnnotation}}{{>optionalDataType}} {{paramName}}{{/isQueryParam}}
\ No newline at end of file
</file context>

…s in test

Extend the regression test for OpenAPITools#25210 to assert that allOf-wrapped model
query params also get @RequestParam, while direct $ref and inline object
query params keep their existing behaviour (no @RequestParam). Rename the
test to reflect its wider coverage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cmagowan cmagowan closed this Oct 10, 2026
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.

[BUG][Java][Spring] @RequestParam annotation missing for query parameters using oneOf with $ref to a named model

1 participant