Skip to content

[JAVA-SPRING,KOTLIN-SPRING] - Enhance pageable validation by adding minSize and minPage constraints; Search also allOf references for constraints; fix PagedModel<T> substitution with 'modelNameSuffix' or 'modelNamePrefix'; add ValuedEnum<T> interface#23694

Merged
wing328 merged 37 commits into
OpenAPITools:masterfrom
Picazsoo:bugfix/pageable-scan-resolve-allof-constraints
Jun 3, 2026

Conversation

@Picazsoo

@Picazsoo Picazsoo commented May 5, 2026

Copy link
Copy Markdown
Contributor

Functionality implemented in #23575 is a bit incomplete (it is fully functional, but can miss some edge cases). In cases when the minimum/maximum/default values live on a schema referenced via allOf, the validations/defaults fail to generate.This PR fixes the behavior to properly discover the validation minimum/maximum constraints as well as the default.

  • for default, the inline default wins (overrides any value defined deeper in the schema - there if multiple allOf entries have a default defined, then the latest wins)
  • for maximum, the smallest maximum wins (so if maximum in allOf referenced schema is 75 and inline maximum is 50, then the resulting maximum is 50)
  • for minimum, the biggest minimum wins (so if minimum in allOf referenced schema is 100 and inline minimum is 10, then the resulting minimum is 100)

And for PagedModel<T> substitution the detection of models to supress did not take into account the existence of modelNameSuffix or modelNamePrefix

The purpose of the (substitutable) ValuedEnum<T> is to make it possible to work with the actual values in the generated enums in a standardized way in the codebase. As of now it is not possible to reliably access the actual value of the enum without resorting to function type parameters or reflection

I have decided to strip any changes related to the nested default resolution. i would rather have the functionality partly working than to risk breaking the general way the defaults are now resolved in open api generator. I still aim to provide a PR with a configurable default resolution strategy later, but for now sticking to the status quo seems like the safe way forward.

PR checklist

  • Read the contribution guidelines.
  • Pull Request title clearly describes the work in the pull request and Pull Request description provides details about how to validate the work. Missing information here may result in delayed response from the community.
  • 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.
  • File the PR against the correct branch: master (upcoming 7.x.0 minor release - breaking changes with fallbacks), 8.0.x (breaking changes without fallbacks)
  • If your PR solves a reported issue, reference it using GitHub's linking syntax (e.g., having "fixes #123" present in the PR description)
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request. - tagging @karismann (2019/03) @Zomzog (2019/04) @andrewemery (2019/10) @4brunu (2019/11) @yutaka0m (2020/03) @stefankoppier (2022/06) @e5l (2024/10) @dennisameling (2026/02), @cachescrubber (2022/02) @welshm (2022/02) @MelleD (2022/02) @atextor (2022/02) @manedev79 (2022/02) @javisst (2022/02) @borsch (2022/02) @banlevente (2022/02) @Zomzog (2022/09) @martin-mfg (2023/08), @wing328

Summary by cubic

Improves Spring Pageable validation with minSize/minPage and resolves page/size bounds from $ref and nested allOf (including exclusive). Adds optional useEnumValueInterface to generate a shared ValuedEnum<T> and implement it on enums in Kotlin/Java.

  • New Features

    • @ValidPageable: adds minSize and minPage; resolves max/min from $ref and nested allOf, including exclusive bounds.
    • useEnumValueInterface: generate ValuedEnum<T> in the config package (supports javax/jakarta) and implement on enums in Java/Kotlin (Spring and java-camel); override via importMappings.ValuedEnum.
  • Bug Fixes

    • Strip x-spring-paginated for non-spring-boot libraries (e.g., spring-cloud) even when set; keep explicit page/size/sort params.
    • Fix PagedModel<T> suppression/substitution when modelNameSuffix/modelNamePrefix or mappings are used.
    • Kotlin: do not emit override val value for container inline enums when useEnumValueInterface is enabled.
    • Update sample controllers and validation tests for allOf-resolved pageable constraints.

Written for commit 328d83f. Summary will update on new commits.

Review in cubic

@Picazsoo Picazsoo changed the title Enhance pageable validation by adding minSize and minPage constraints Search also allOf references for constraints and defaults [JAVA-SPRING,KOTLIN-SPRING] - Enhance pageable validation by adding minSize and minPage constraints; Search also allOf references for constraints and defaults May 6, 2026
@Picazsoo Picazsoo marked this pull request as ready for review May 6, 2026 12:14

@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 17 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringPageableScanUtils.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringPageableScanUtils.java:165">
P2: Array schemas without `items` can now NPE here because `ModelUtils.isArraySchema()` does not guarantee `schema.getItems()` is non-null.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@Picazsoo Picazsoo marked this pull request as draft May 6, 2026 14:43

@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 6 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java">

<violation number="1" location="modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java:5961">
P2: Removing @Test orphaned this regression test, so JUnit will no longer execute it.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

@Mattias-Sehlstedt

Copy link
Copy Markdown
Contributor

Should this also cover exclusiveMinimum and exclusiveMaximum? Also note that those fields have different representations between OAS 3.0 and OAS 3.1, where 3.0 is a boolean while 3.1. allows for a value (so for OAS 3.1 it might be that minimum is entirely omitted and that only exclusiveMinimum is specificed).

Do note that it might be that the project represent them the same internally, as they do for some OAS 3.1 concepts.

@Picazsoo

Picazsoo commented May 6, 2026

Copy link
Copy Markdown
Contributor Author

Should this also cover exclusiveMinimum and exclusiveMaximum? Also note that those fields have different representations between OAS 3.0 and OAS 3.1, where 3.0 is a boolean while 3.1. allows for a value (so for OAS 3.1 it might be that minimum is entirely omitted and that only exclusiveMinimum is specificed).

Do note that it might be that the project represent them the same internally, as they do for some OAS 3.1 concepts.

Hmm, good point. I would expect that such things should be ideally already handled by the normalizer? Or is it problematic because the values are not integers?

But thanks for raising this. I see my blind attempt to centralize the logic in the ModelUtils.resolveMaximum and ModelUtils.resolveMinimum might be a bit premature. As I was considering it mostly in the context of the page/size use. That was definitely short-sighted.

@Mattias-Sehlstedt

Copy link
Copy Markdown
Contributor

I am unable to provide comments in code, since I get an external error (so might be that you are flooded with the same comment over and over later).

What I am wondering is if schema.getAllOf() != null can be done with the utils function hasAllOf.

I would also suggest that some shared concepts be broken out into methods to improve readability and reduce duplication. For example that

if (schema == null) return null;
if (schema.get$ref() != null) {
    schema = getReferencedSchema(openAPI, schema);
    if (schema == null) return null;
}

Is broken out into a "getRefSchema()" (might be that it already exists?). In the same vein also maybe the same for the if-cases.

E.g.,
resolved != null && resolved.getMinimum() != null to hasMinimum
and
result == null || resolved.getMinimum().compareTo(result) > 0 to stricterMinimum.

This would change the code from "null, null, ..., null" to
"If there is a schema (referenced or direct), that has one or more allOfs. Then go through all allOfs and take the minimum value if there is one. Keep the most strict value".

Heavily opinionated of course and goes against how everything else is done. But personally I find most of the code to be extremely hard to read when it really should not be give the trivial things that are done.

@Mattias-Sehlstedt

Copy link
Copy Markdown
Contributor

Should this also cover exclusiveMinimum and exclusiveMaximum? Also note that those fields have different representations between OAS 3.0 and OAS 3.1, where 3.0 is a boolean while 3.1. allows for a value (so for OAS 3.1 it might be that minimum is entirely omitted and that only exclusiveMinimum is specificed).
Do note that it might be that the project represent them the same internally, as they do for some OAS 3.1 concepts.

Hmm, good point. I would expect that such things should be ideally already handled by the normalizer? Or is it problematic because the values are not integers?

But thanks for raising this. I see my blind attempt to centralize the logic in the ModelUtils.resolveMaximum and ModelUtils.resolveMinimum might be a bit premature. As I was considering it mostly in the context of the page/size use. That was definitely short-sighted.

One could also reason that it is out-of-scope and that someone that utilizes exclusiveMaximum will have to extend your initial logic to support their use-case. I believe the current structure and separation of responsibility should make it at least semi-straightforward. And as always, if one aim to support everything with the first version, then one might never get a first version. 😄

@Picazsoo

Picazsoo commented May 6, 2026

Copy link
Copy Markdown
Contributor Author

No, I think this is a really good point. I will try to fix it. This might bite someone, when they decide to rely on the method later. It is called *Utils after all (-:

@Picazsoo

Picazsoo commented May 7, 2026

Copy link
Copy Markdown
Contributor Author

Hello @Mattias-Sehlstedt , if you want to take another look. I feel like I am moving in the right direction hopefully.

@Picazsoo Picazsoo marked this pull request as ready for review May 7, 2026 12:34

@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 20 files

@Picazsoo Picazsoo changed the title [JAVA-SPRING,KOTLIN-SPRING] - Enhance pageable validation by adding minSize and minPage constraints; Search also allOf references for constraints and defaults [JAVA-SPRING,KOTLIN-SPRING] - Enhance pageable validation by adding minSize and minPage constraints; Search also allOf references for constraints and defaults; fix PagedModel<T> substitution when 'modelNameSuffix' or 'modelNamePrefix' are used May 7, 2026
@Picazsoo

Copy link
Copy Markdown
Contributor Author

Hello @Mattias-Sehlstedt, I have tried to do some more changes on the PR. I do not aim for perfect though.

Hi @wing328, how do you feel about the default handling? I now take the view that "inline" default has preference and when there are multiple conflicting defaults in allOf, then the later always overwrites the former. Basically my default resolution is like this now:

components:
  schemas:
    Base1:
      type: string
      default: "base_1"       # Step 1: Parser reads this first. (Memory = "base_1")

    Base2:
      type: string
      default: "base_2"       # Step 2: Read next. Overwrites Step 1. (Memory = "base_2")

    Intermediate:
      allOf:
        - $ref: '#/components/schemas/Base1'  
        - $ref: '#/components/schemas/Base2'  

    Final:
      # Step 4: The Root. Because this sits outside the allOf array entirely, 
      # it gets applied last and overwrites EVERYTHING below it.
      default: "absolute_winner" 
      
      allOf:
        - $ref: '#/components/schemas/Intermediate' # Pulls in "base_2" from Step 2
        - default: "final"                          # Step 3: Overwrites Step 2. (Memory = "final")

Ok - this is most probably not ok. Hence moving the PR back to draft. I will iterate some more - I have some ideas

@cubic-dev-ai

cubic-dev-ai Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment @cubic-dev-ai review.

@Picazsoo

Copy link
Copy Markdown
Contributor Author

You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment @cubic-dev-ai review.

@cubic-dev-ai review

@cubic-dev-ai

cubic-dev-ai Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment @cubic-dev-ai review.

@cubic-dev-ai review

@Picazsoo I have started the AI code review. It will take a few minutes to complete.

@Picazsoo

Picazsoo commented May 20, 2026

Copy link
Copy Markdown
Contributor Author

Hi @wing328, @Mattias-Sehlstedt
I have decided to strip any changes related to the nested default resolution. i would rather have the functionality partly working than to risk breaking the general way the defaults are now resolved in open api generator. I still aim to provide a PR with a configurable default resolution strategy later, but for now sticking to the status quo seems like the safe way forward.

If you would feel more comfortable if the multiple bug fixes/features were handled in separate merge requests, please let me know. In the order of criticality:

  1. fix PagedModel<T> substitution with 'modelNameSuffix' or 'modelNamePrefix' - this is not working correctly in the current release and is a real bummer as it limits the usability of this feature dramatically
  2. Enhance pageable validation by adding minSize and minPage constraints; Search also allOf references for constraints - this is a small improvement to properly extract the min/max constraint from more complex schemas.
  3. add ValuedEnum<T> interface - this is a really small feature allowing to interact with the values in the generated enum dtos cleanly.

@Picazsoo Picazsoo changed the title [JAVA-SPRING,KOTLIN-SPRING] - Enhance pageable validation by adding minSize and minPage constraints; Search also allOf references for constraints and defaults; fix PagedModel<T> substitution when 'modelNameSuffix' or 'modelNamePrefix' are used [JAVA-SPRING,KOTLIN-SPRING] - Enhance pageable validation by adding minSize and minPage constraints; Search also allOf references for constraints; fix PagedModel<T> substitution when 'modelNameSuffix' or 'modelNamePrefix' are used May 20, 2026
@Picazsoo Picazsoo changed the title [JAVA-SPRING,KOTLIN-SPRING] - Enhance pageable validation by adding minSize and minPage constraints; Search also allOf references for constraints; fix PagedModel<T> substitution when 'modelNameSuffix' or 'modelNamePrefix' are used [JAVA-SPRING,KOTLIN-SPRING] - Enhance pageable validation by adding minSize and minPage constraints; Search also allOf references for constraints; fix PagedModel<T> substitution with 'modelNameSuffix' or 'modelNamePrefix'; add ValuedEnum<T> interface May 20, 2026
@Picazsoo Picazsoo marked this pull request as ready for review May 20, 2026 13:02

@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 33 files

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Re-trigger cubic

…ints

# Conflicts:
#	modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java
@Picazsoo

Copy link
Copy Markdown
Contributor Author

I have merged latest master into the branch and resolved conflicts

Picazsoo and others added 2 commits May 30, 2026 16:13
… value`

EnumValueInterfaceUtils.injectInPostProcessModelsEnum() adds
ValuedEnum<...> to x-kotlin-implements only for `var.isEnum &&
!var.isContainer`, but dataClass.mustache emitted the `override` keyword
for every enum whenever useEnumValueInterface=true. Container inline
enums therefore produced uncompilable Kotlin (`overrides nothing`) since
the enum class did not actually implement any interface to override.

Gating the `override` token on `^isContainer` keeps the template in sync
with the util.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@wing328

wing328 commented Jun 3, 2026

Copy link
Copy Markdown
Member

I did a test with

java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -g spring -i https://raw.githubusercontent.com/Picazsoo/openapi-generator/2bdcb2fddd43f0e8d22951e33cd18f609d7f6205/modules/openapi-generator/src/test/resources/3_0/spring/enum-value-interface.yaml -o /tmp/klsp2 --additional-properties useEnumValueInterface=true

but the output won't compile

[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] /C:/Users/User/AppData/Local/Temp/klsp2/src/main/java/org/openapitools/configuration/ValuedEnum.java:[3,2] cannot find symbol
  symbol: class Generated
[INFO] 1 error
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  2.106 s
[INFO] Finished at: 2026-06-03T14:58:59+08:00
[INFO] ------------------------------------------------------------------------

Am I testing the change correctly?

(same test with kotlin-spring and the output compiles without issues)

Picazsoo added 3 commits June 3, 2026 09:54
…ate related configurations to verify compilation success for both javax and jakarta setups in kotlin and java spring
@Picazsoo

Picazsoo commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

I did a test with

java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -g spring -i https://raw.githubusercontent.com/Picazsoo/openapi-generator/2bdcb2fddd43f0e8d22951e33cd18f609d7f6205/modules/openapi-generator/src/test/resources/3_0/spring/enum-value-interface.yaml -o /tmp/klsp2 --additional-properties useEnumValueInterface=true

but the output won't compile

[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] /C:/Users/User/AppData/Local/Temp/klsp2/src/main/java/org/openapitools/configuration/ValuedEnum.java:[3,2] cannot find symbol
  symbol: class Generated
[INFO] 1 error
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  2.106 s
[INFO] Finished at: 2026-06-03T14:58:59+08:00
[INFO] ------------------------------------------------------------------------

Am I testing the change correctly?

(same test with kotlin-spring and the output compiles without issues)

Hi @wing328 , good catch! I missed imports for the @generated annotation. I fixed it now and added the useEnumValueInterface: "true" to a mix of spring/kotlin-spring javax/jakarta samples to check that everything compiles correctly.

So now the pipelines should finish ok.

Comment thread samples/server/petstore/springboot/pom.xml
@wing328 wing328 merged commit 91fa74a into OpenAPITools:master Jun 3, 2026
139 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants