Repository navigation
[kotlin-spring] Move cascading @Valid off containers and kotlin.Any properties to element types (HV000271/HV000272) - #25668
Open
Inf1mum wants to merge 2 commits into
Conversation
…ement types (HV000271/HV000272) What: - kotlin-spring model properties no longer get the deprecated container-level `@field:Valid`: containers (List/Set/Map) cascade through element-type annotations instead (`List<@Valid T>`, `Map<String, @Valid V>`), and free-form/ Any-typed properties (rendered as kotlin.Any) carry no `@Valid` at all. Bean-typed properties keep cascading via `@field:Valid`. Why: Hibernate Validator 9.1 (Spring Boot 4.1) deprecates `@Valid` on containers and logs HV000271/HV000272 while building bean metadata; since the bean metadata cache uses soft references, the warnings repeat every time a rarely used's model metadata is evicted, producing a steady stream of warnings in production logs. Fixes OpenAPITools#25347. The Java generators fixed the same problem in OpenAPITools#24176 (issue OpenAPITools#23614) by moving `@Valid` to the type argument. The closed attempt OpenAPITools#24029 dropped `@Valid` from map properties entirely, silently losing cascading validation of map values; here cascading is preserved through the type argument instead. The `-Xemit-jvm-type-annotations` flag needed for Kotlin type-use annotations to be visible to Hibernate Validator is already emitted into the generated Gradle/Maven builds when useBeanValidation is enabled. How: - beanValidation.mustache: remove the container branch; guard the remaining non-container branch with isFreeFormObject/isAnyType so kotlin.Any fields are left unannotated. - KotlinSpringServerCodegen: opt into map value type annotations by returning useBeanValidation from useBeanValidationOnMapValueType(), reusing the element annotation infrastructure (getAnnotatedItemsTypeDeclaration). - tests: update the collection element annotation expectations for map values and add fieldValidOnlyOnBeanTypedProperties covering bean-typed, free-form, AnyType and free-form map properties; regenerate all kotlin-spring samples (35 deprecated annotations removed across 34 model files).
Contributor
There was a problem hiding this comment.
2 issues found across 38 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/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:8482">
P3: The comment claims exactly one `@field:Valid` remains, but this test never counts them. Either assert the count (e.g. with `countOccurrences`) or reword the comment to describe only the negative checks.</violation>
</file>
<file name="modules/openapi-generator/src/main/resources/kotlin-spring/beanValidation.mustache">
<violation number="1" location="modules/openapi-generator/src/main/resources/kotlin-spring/beanValidation.mustache:2">
P2: The new `isAnyType` guard also matches inline `oneOf`/`anyOf`/`allOf` bean properties, so those properties lose `@field:Valid` and their nested constraints are no longer cascaded. Exclude only free-form/arbitrary `kotlin.Any` properties, or add a composed-model exception.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
| }}{{^isContainer}}{{^isPrimitiveType}}{{^isNumber}}{{^isUuid}}{{^isDateTime}} | ||
| @field:Valid{{/isDateTime}}{{/isUuid}}{{/isNumber}}{{/isPrimitiveType}}{{/isContainer}} No newline at end of file | ||
| {{! @field:Valid cascades only into bean-typed properties. Containers (List/Set/Map) cascade via element-type annotations instead (e.g. List<@Valid T>, Map<String, @Valid V>) - a container-level @Valid is deprecated by Hibernate Validator 9.1+ and logs HV000271, and free-form / Any-typed properties render as kotlin.Any with nothing to cascade into (HV000272). }}{{! | ||
| }}{{^isContainer}}{{^isPrimitiveType}}{{^isNumber}}{{^isUuid}}{{^isDateTime}}{{^isFreeFormObject}}{{^isAnyType}} |
Contributor
There was a problem hiding this comment.
P2: The new isAnyType guard also matches inline oneOf/anyOf/allOf bean properties, so those properties lose @field:Valid and their nested constraints are no longer cascaded. Exclude only free-form/arbitrary kotlin.Any properties, or add a composed-model exception.
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/kotlin-spring/beanValidation.mustache, line 2:
<comment>The new `isAnyType` guard also matches inline `oneOf`/`anyOf`/`allOf` bean properties, so those properties lose `@field:Valid` and their nested constraints are no longer cascaded. Exclude only free-form/arbitrary `kotlin.Any` properties, or add a composed-model exception.</comment>
<file context>
@@ -1,4 +1,3 @@
- @field:Valid{{/isDateTime}}{{/isUuid}}{{/isNumber}}{{/isPrimitiveType}}{{/isContainer}}
\ No newline at end of file
+{{! @field:Valid cascades only into bean-typed properties. Containers (List/Set/Map) cascade via element-type annotations instead (e.g. List<@Valid T>, Map<String, @Valid V>) - a container-level @Valid is deprecated by Hibernate Validator 9.1+ and logs HV000271, and free-form / Any-typed properties render as kotlin.Any with nothing to cascade into (HV000272). }}{{!
+}}{{^isContainer}}{{^isPrimitiveType}}{{^isNumber}}{{^isUuid}}{{^isDateTime}}{{^isFreeFormObject}}{{^isAnyType}}
+ @field:Valid{{/isAnyType}}{{/isFreeFormObject}}{{/isDateTime}}{{/isUuid}}{{/isNumber}}{{/isPrimitiveType}}{{/isContainer}}
\ No newline at end of file
</file context>
| "val anything: kotlin.Any? = null"); | ||
| // free-form map values render as kotlin.Any without any @Valid | ||
| assertFileContains(holder, "val anyMap: kotlin.collections.Map<kotlin.String, kotlin.Any>? = null"); | ||
| // exactly one @field:Valid remains (singlePet); containers and Any-typed fields carry none |
Contributor
There was a problem hiding this comment.
P3: The comment claims exactly one @field:Valid remains, but this test never counts them. Either assert the count (e.g. with countOccurrences) or reword the comment to describe only the negative checks.
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/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java, line 8482:
<comment>The comment claims exactly one `@field:Valid` remains, but this test never counts them. Either assert the count (e.g. with `countOccurrences`) or reword the comment to describe only the negative checks.</comment>
<file context>
@@ -8455,10 +8455,36 @@ public void collectionItemsGetBeanValidationOnElementType() throws IOException {
+ "val anything: kotlin.Any? = null");
+ // free-form map values render as kotlin.Any without any @Valid
+ assertFileContains(holder, "val anyMap: kotlin.collections.Map<kotlin.String, kotlin.Any>? = null");
+ // exactly one @field:Valid remains (singlePet); containers and Any-typed fields carry none
+ assertFileNotContains(holder,
+ "@field:Valid\n @Schema(description = \"\")\n @param:JsonProperty(\"pets\")",
</file context>
Suggested change
| // exactly one @field:Valid remains (singlePet); containers and Any-typed fields carry none | |
| // containers and Any-typed fields carry no @field:Valid; singlePet keeps it (checked above) |
… Central 429 The "Build Kotlin server (jdk21) (samples/server/others/kotlin-springboot/oneOf-discriminator)" check (run 43515494934, job 132196316812) failed without compiling anything: the "Install Gradle wrapper" step died during Gradle configuration while resolving kotlin-gradle-plugin:1.9.25 / spring-boot-gradle-plugin:3.3.13, with repeated "Received status code 429 from server: Too Many Requests" from repo.maven.apache.org / plugins.gradle.org (5 occurrences). No code defect behind the red check: - the same sample is green on jdk17 for the same head SHA (job 132196323194); - sibling oneOf-discriminator samples pass on jdk21; - -Xemit-jvm-type-annotations is present in the sample's generated build.gradle.kts and pom.xml; - regenerating the sample produces zero diff (Samples up-to-date is green); - a local Gradle 8.14.4 + JDK 21 build of the sample succeeds. This empty commit only re-runs the CI suite on a fresh head SHA; no changes to templates, codegen or samples. Signed-off-by: Inf1mum <vadimlazuk5@gmail.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
kotlin-springwithuseBeanValidation(the default) puts@field:Validon container properties (List,Set,Map) and on free-formkotlin.Anyproperties. Hibernate Validator 9.1 (shipped with Spring Boot 4.1) deprecated container-level@Validand logs HV000271/HV000272 while building bean metadata. Because the bean metadata cache is backed by soft references, the warnings are not only logged at startup: they come back every time the metadata of a rarely used model is evicted and rebuilt, so production logs end up with a steady stream of them.This PR applies to the Kotlin templates what the Java generators got in #24176 (issue #23614, released in 7.26.0):
beanValidation.mustacheno longer emits@field:Validfor containers. Cascading goes through the element type instead (List<@Valid T>,Set<@Valid T>), which the element annotation infrastructure added in [kotlin-spring] Bean Validation and custom annotations on collection element types #25592 already renders for arrays/sets; the deprecated duplicate on the container itself is gone.Map<String, @Valid V>(the generator opts intouseBeanValidationOnMapValueType()). This deliberately preserves cascading validation of map values, which the earlier attempt fix(kotlin-spring): skip @field:Valid on Map container properties (HV000271) #24027/fix(kotlin-spring): skip @field:Valid on Map container properties (HV000271) #24029 dropped along with the annotation, and which was the reason that PR was closed.type: objectand{}properties (rendered askotlin.Any) no longer get@field:Valid- there is nothing to cascade into (HV000272).@field:Valid, so cascading into a single$refproperty is unchanged.Since the Kotlin compiler only writes type-use annotations into bytecode when compiled with
-Xemit-jvm-type-annotations, note that the generated Gradle and Maven builds already set this flag whenuseBeanValidationis enabled (added in #25592). If you compile the generated sources with your own build, keep setting that flag - without it, cascading into elements silently stops (same situation as the array/set element annotations introduced in #25592).Generated output change
which matches the expected output from the issue.
Testing
KotlinSpringServerCodegenTest: updated the collection element annotation tests for the new map value rendering, and addedfieldValidOnlyOnBeanTypedProperties, which asserts@field:Validis present on a$ref-typed property and absent on free-form object,{}and free-form map properties.mvn -pl modules/openapi-generator test: green (5756 tests, 0 failures).bin/generate-samples.sh bin/configs/kotlin-spring-*.yaml: 35 deprecated@field:Validannotations removed across 34 model files, no other diff.kotlin-springbootsample with Gradle and checked the compiled model withjavap: the field carriesjavax.validation.ValidinRuntimeVisibleTypeAnnotationsatTYPE_ARGUMENT(0), which is exactly what Hibernate Validator 9.x reads for cascading into elements, and which no longer logs HV000271.PR checklist
./bin/to update the kotlin-spring Petstore samples so that CIs can verify the change.master.Fixes #25347
Summary by cubic
Fixes #25347.
kotlin-springno longer emits the deprecated container-level@field:ValidonList/Set/Mapproperties, which Hibernate Validator 9.1+ (Spring Boot 4.1) warns about with HV000271/HV000272. Cascading is preserved through element-type annotations instead (List<@Valid T>,Map<String, @Valid V>), and bean-typed properties keep@field:Valid.Free-form
object/{}properties rendered askotlin.Anylose the annotation since there is nothing to cascade into. Map value cascading is preserved via the type argument, unlike the closed attempt #24029 which dropped it entirely.Type-use annotations are only visible to Hibernate Validator when compiled with
-Xemit-jvm-type-annotations; the generated Gradle/Maven builds already set this flag whenuseBeanValidationis enabled.Written for commit ca40fce. Summary will update on new commits.