Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
@NotNull
{{/isReadOnly}}
{{/required}}
{{#isContainer}}
{{! Arrays and sets already carry @Valid on their type argument (e.g. List<@Valid Pet>), }}
{{! so annotating the container itself is redundant and deprecated (HV000271). }}
{{! Map values get no such type argument, so maps still need the container-level @Valid. }}
{{#isMap}}
{{^isPrimitiveType}}
{{^isEnum}}
@Valid
{{/isEnum}}
{{/isPrimitiveType}}
{{/isContainer}}
{{/isMap}}
{{^isContainer}}
{{^isPrimitiveType}}
@Valid
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{{#required}}{{^isReadOnly}}@NotNull {{/isReadOnly}}{{/required}}{{#isContainer}}{{^isPrimitiveType}}{{^isEnum}}@Valid {{/isEnum}}{{/isPrimitiveType}}{{/isContainer}}{{^isContainer}}{{^isPrimitiveType}}@Valid {{/isPrimitiveType}}{{/isContainer}}{{^openApiNullable}}{{>beanValidationCore}}{{/openApiNullable}}{{#openApiNullable}}{{^useOptional}}{{>beanValidationCore}}{{/useOptional}}{{/openApiNullable}}{{#useOptional}}{{#openApiNullable}}{{#isContainer}}{{^required}}{{>beanValidationCore}}{{/required}}{{/isContainer}}{{/openApiNullable}}{{#openApiNullable}}{{#required}}{{>beanValidationCore}}{{/required}}{{/openApiNullable}}{{/useOptional}}
{{#required}}{{^isReadOnly}}@NotNull {{/isReadOnly}}{{/required}}{{! Arrays/sets carry @Valid on the type argument (List<@Valid Pet>); a container-level @Valid is redundant and deprecated (HV000271). Map values do not, so maps keep it. }}{{#isMap}}{{^isPrimitiveType}}{{^isEnum}}@Valid {{/isEnum}}{{/isPrimitiveType}}{{/isMap}}{{^isContainer}}{{^isPrimitiveType}}@Valid {{/isPrimitiveType}}{{/isContainer}}{{^openApiNullable}}{{>beanValidationCore}}{{/openApiNullable}}{{#openApiNullable}}{{^useOptional}}{{>beanValidationCore}}{{/useOptional}}{{/openApiNullable}}{{#useOptional}}{{#openApiNullable}}{{#isContainer}}{{^required}}{{>beanValidationCore}}{{/required}}{{/isContainer}}{{/openApiNullable}}{{#openApiNullable}}{{#required}}{{>beanValidationCore}}{{/required}}{{/openApiNullable}}{{/useOptional}}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ public {{>sealed}}class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}
@Deprecated
{{/deprecated}}
{{#isContainer}}
{{! Arrays/sets carry @Valid on the type argument; only map values need a container-level @Valid (HV000271). }}
{{#isMap}}
{{#useBeanValidation}}@Valid{{/useBeanValidation}}
{{/isMap}}
{{#openApiNullable}}
private {{>nullableAnnotation}}{{#isNullable}}{{>nullableDataTypeBeanValidation}} {{name}} = JsonNullable.<{{{datatypeWithEnum}}}>undefined();{{/isNullable}}{{^required}}{{^isNullable}}{{>nullableDataTypeBeanValidation}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};{{/isNullable}}{{/required}}{{#required}}{{^isNullable}}{{>nullableDataTypeBeanValidation}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};{{/isNullable}}{{/required}}
{{/openApiNullable}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{{#required}}
@NotNull
{{/required}}{{#isContainer}}{{^isPrimitiveType}}{{^isEnum}}
@Valid{{/isEnum}}{{/isPrimitiveType}}{{/isContainer}}{{^isContainer}}{{^isPrimitiveType}}
{{/required}}{{#isMap}}{{^isPrimitiveType}}{{^isEnum}}
@Valid{{/isEnum}}{{/isPrimitiveType}}{{/isMap}}{{^isContainer}}{{^isPrimitiveType}}
@Valid{{/isPrimitiveType}}{{/isContainer}}
{{>beanValidationCore}}

Original file line number Diff line number Diff line change
Expand Up @@ -2300,6 +2300,37 @@ public void testRestTemplateWithUseBeanValidationEnabled() {
assertFileContains(output.resolve("src/main/java/org/openapitools/client/model/Pet.java"), "@Valid");
}

@Test
public void testUseBeanValidationDoesNotAnnotateArrayContainersWithValid_issue24927() {
final Path output = newTempFolder();
final CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName(JAVA_GENERATOR)
.setLibrary(JavaClientCodegen.NATIVE)
.addAdditionalProperty(JavaClientCodegen.USE_BEANVALIDATION, true)
.setInputSpec("src/test/resources/3_0/petstore-with-fake-endpoints-models-for-testing.yaml")
.setOutputDir(output.toString().replace("\\", "/"));

List<File> files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate();

validateJavaSourceFiles(files);
// Hibernate Validator deprecated @Valid on containers (HV000271). Arrays already carry it
// on their type argument, so the getter must not repeat it.
JavaFileAssert.assertThat(output.resolve("src/main/java/org/openapitools/client/model/Pet.java"))
.fileContains("private List<@Valid Tag> tags")
.assertMethod("getTags").assertMethodAnnotations().doesNotContainWithName("Valid")
.toMethod().toFileAssert()
.assertMethod("getPhotoUrls").assertMethodAnnotations().doesNotContainWithName("Valid")
.toMethod().toFileAssert()
// A plain object property is not a container, so it keeps @Valid.
.assertMethod("getCategory").assertMethodAnnotations().containsWithName("Valid");

// Map values get no type argument annotation, so maps keep the container-level @Valid.
JavaFileAssert.assertThat(output.resolve(
"src/main/java/org/openapitools/client/model/MixedPropertiesAndAdditionalPropertiesClass.java"))
.fileContains("private Map<String, Animal> map")
.assertMethod("getMap").assertMethodAnnotations().containsWithName("Valid");
}

@Test
public void testRestTemplateWithUseBeanValidationDisabled() {
final Path output = newTempFolder();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5365,6 +5365,50 @@ public void testCollectionTypesWithDefaults_issue_18102() throws IOException {
.fileDoesNotContain("private Set<String> stringSet = new LinkedHashSet<>()");
}

@Test
public void shouldNotAnnotateArrayContainersWithValid_issue24927() throws IOException {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
output.deleteOnExit();

OpenAPI openAPI = new OpenAPIParser()
.readLocation("src/test/resources/3_0/spring/petstore-with-fake-endpoints-models-for-testing.yaml",
null, new ParseOptions())
.getOpenAPI();
SpringCodegen codegen = new SpringCodegen();
codegen.setOutputDir(output.getAbsolutePath());
codegen.setUseBeanValidation(true);

ClientOptInput input = new ClientOptInput()
.openAPI(openAPI)
.config(codegen);

DefaultGenerator generator = new DefaultGenerator();
generator.setGenerateMetadata(false);
generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "true");

Map<String, File> files = generator.opts(input).generate().stream()
.collect(Collectors.toMap(File::getName, Function.identity()));

// Hibernate Validator deprecated @Valid on containers (HV000271). Arrays and sets already
// carry it on their type argument, so neither the field nor the getter may repeat it.
JavaFileAssert.assertThat(files.get("Pet.java"))
.fileContains("private List<@Valid Tag> tags")
.assertProperty("tags").doesNotHaveAnnotation("Valid").toProperty().toType()
.assertProperty("photoUrls").doesNotHaveAnnotation("Valid").toProperty().toType()
.assertMethod("getTags").assertMethodAnnotations().doesNotContainWithName("Valid")
.toMethod().toFileAssert()
.assertMethod("getPhotoUrls").assertMethodAnnotations().doesNotContainWithName("Valid")
.toMethod().toFileAssert()
// A plain object property is not a container, so it keeps @Valid.
.assertMethod("getCategory").assertMethodAnnotations().containsWithName("Valid");

// Map values get no type argument annotation, so maps keep the container-level @Valid.
JavaFileAssert.assertThat(files.get("MixedPropertiesAndAdditionalPropertiesClass.java"))
.fileContains("private Map<String, Animal> map")
.assertProperty("map").hasAnnotation("Valid").toProperty().toType()
.assertMethod("getMap").assertMethodAnnotations().containsWithName("Valid");
}

@Test
public void shouldGenerateOptionalParameterTypesWhenUsingOptionalAndDelegate_issue17768() throws IOException {
Map<String, Object> additionalProperties = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ public ArrayOfArrayOfNumberOnly addArrayArrayNumberItem(List<BigDecimal> arrayAr
* @return arrayArrayNumber
*/
@jakarta.annotation.Nullable
@Valid

@JsonProperty(value = JSON_PROPERTY_ARRAY_ARRAY_NUMBER, required = false)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ public ArrayOfNumberOnly addArrayNumberItem(BigDecimal arrayNumberItem) {
* @return arrayNumber
*/
@jakarta.annotation.Nullable
@Valid

@JsonProperty(value = JSON_PROPERTY_ARRAY_NUMBER, required = false)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ public ArrayTest addArrayArrayOfIntegerItem(List<Long> arrayArrayOfIntegerItem)
* @return arrayArrayOfInteger
*/
@jakarta.annotation.Nullable
@Valid

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.

P2: For nested containers (List<List<@Valid ReadOnlyFirst>>), removing the getter-level @Valid drops validation of the inner model entirely. The container-level @Valid is only redundant when the container's immediate type argument itself carries @Valid (e.g. List<@Valid Pet>); here the outer List's type argument (List<@Valid ReadOnlyFirst>) has no @Valid, so HV no longer cascades, and the inner ReadOnlyFirst elements are never validated. This needs a template fix, not just the sample: for a container whose item type is itself a container (isContainer && items.isContainer), keep @Valid on the outer getter or emit @Valid on the nested type argument.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/petstore/java/jersey3/src/main/java/org/openapitools/client/model/ArrayTest.java, line 113:

<comment>For nested containers (List<List<@Valid ReadOnlyFirst>>), removing the getter-level @Valid drops validation of the inner model entirely. The container-level @Valid is only redundant when the container's immediate type argument itself carries @Valid (e.g. List<@Valid Pet>); here the outer List's type argument (List<@Valid ReadOnlyFirst>) has no @Valid, so HV no longer cascades, and the inner ReadOnlyFirst elements are never validated. This needs a template fix, not just the sample: for a container whose item type is itself a container (isContainer && items.isContainer), keep @Valid on the outer getter or emit @Valid on the nested type argument.</comment>

<file context>
@@ -110,7 +110,6 @@ public ArrayTest addArrayArrayOfIntegerItem(List<Long> arrayArrayOfIntegerItem)
    */
   @jakarta.annotation.Nullable
-  @Valid
 
   @JsonProperty(value = JSON_PROPERTY_ARRAY_ARRAY_OF_INTEGER, required = false)
   @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
</file context>


@JsonProperty(value = JSON_PROPERTY_ARRAY_ARRAY_OF_INTEGER, required = false)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
Expand Down Expand Up @@ -145,7 +144,6 @@ public ArrayTest addArrayArrayOfModelItem(List<@Valid ReadOnlyFirst> arrayArrayO
* @return arrayArrayOfModel
*/
@jakarta.annotation.Nullable
@Valid

@JsonProperty(value = JSON_PROPERTY_ARRAY_ARRAY_OF_MODEL, required = false)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,6 @@ public Drawing addShapesItem(Shape shapesItem) {
* @return shapes
*/
@jakarta.annotation.Nullable
@Valid

@JsonProperty(value = JSON_PROPERTY_SHAPES, required = false)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ public FileSchemaTestClass addFilesItem(ModelFile filesItem) {
* @return files
*/
@jakarta.annotation.Nullable
@Valid

@JsonProperty(value = JSON_PROPERTY_FILES, required = false)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,6 @@ public Pet addTagsItem(Tag tagsItem) {
* @return tags
*/
@jakarta.annotation.Nullable
@Valid

@JsonProperty(value = JSON_PROPERTY_TAGS, required = false)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ public ArrayOfArrayOfNumberOnly addArrayArrayNumberItem(List<BigDecimal> arrayAr
* @return arrayArrayNumber
*/
@javax.annotation.Nullable
@Valid

@JsonProperty(value = JSON_PROPERTY_ARRAY_ARRAY_NUMBER, required = false)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ public ArrayOfNumberOnly addArrayNumberItem(BigDecimal arrayNumberItem) {
* @return arrayNumber
*/
@javax.annotation.Nullable
@Valid

@JsonProperty(value = JSON_PROPERTY_ARRAY_NUMBER, required = false)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ public ArrayTest addArrayArrayOfIntegerItem(List<Long> arrayArrayOfIntegerItem)
* @return arrayArrayOfInteger
*/
@javax.annotation.Nullable
@Valid

@JsonProperty(value = JSON_PROPERTY_ARRAY_ARRAY_OF_INTEGER, required = false)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
Expand Down Expand Up @@ -143,7 +142,6 @@ public ArrayTest addArrayArrayOfModelItem(List<@Valid ReadOnlyFirst> arrayArrayO
* @return arrayArrayOfModel
*/
@javax.annotation.Nullable
@Valid

@JsonProperty(value = JSON_PROPERTY_ARRAY_ARRAY_OF_MODEL, required = false)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ public FileSchemaTestClass addFilesItem(ModelFile filesItem) {
* @return files
*/
@javax.annotation.Nullable
@Valid

@JsonProperty(value = JSON_PROPERTY_FILES, required = false)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,6 @@ public Pet addTagsItem(Tag tagsItem) {
* @return tags
*/
@javax.annotation.Nullable
@Valid

@JsonProperty(value = JSON_PROPERTY_TAGS, required = false)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ public ArrayOfArrayOfNumberOnly addArrayArrayNumberItem(List<BigDecimal> arrayAr
* @return arrayArrayNumber
*/
@javax.annotation.Nullable
@Valid


public List<List<BigDecimal>> getArrayArrayNumber() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ public ArrayOfNumberOnly addArrayNumberItem(BigDecimal arrayNumberItem) {
* @return arrayNumber
*/
@javax.annotation.Nullable
@Valid


public List<BigDecimal> getArrayNumber() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ public ArrayTest addArrayArrayOfIntegerItem(List<Long> arrayArrayOfIntegerItem)
* @return arrayArrayOfInteger
*/
@javax.annotation.Nullable
@Valid


public List<List<Long>> getArrayArrayOfInteger() {
Expand Down Expand Up @@ -132,7 +131,6 @@ public ArrayTest addArrayArrayOfModelItem(List<@Valid ReadOnlyFirst> arrayArrayO
* @return arrayArrayOfModel
*/
@javax.annotation.Nullable
@Valid


public List<List<@Valid ReadOnlyFirst>> getArrayArrayOfModel() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ public FileSchemaTestClass addFilesItem(ModelFile filesItem) {
* @return files
*/
@javax.annotation.Nullable
@Valid


public List<@Valid ModelFile> getFiles() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,6 @@ public Pet addTagsItem(Tag tagsItem) {
* @return tags
*/
@javax.annotation.Nullable
@Valid


public List<@Valid Tag> getTags() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ public ArrayOfArrayOfNumberOnly addArrayArrayNumberItem(List<BigDecimal> arrayAr
* @return arrayArrayNumber
*/
@jakarta.annotation.Nullable
@Valid

@JsonProperty(value = JSON_PROPERTY_ARRAY_ARRAY_NUMBER, required = false)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ public ArrayOfNumberOnly addArrayNumberItem(BigDecimal arrayNumberItem) {
* @return arrayNumber
*/
@jakarta.annotation.Nullable
@Valid

@JsonProperty(value = JSON_PROPERTY_ARRAY_NUMBER, required = false)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ public ArrayTest addArrayArrayOfIntegerItem(List<Long> arrayArrayOfIntegerItem)
* @return arrayArrayOfInteger
*/
@jakarta.annotation.Nullable
@Valid

@JsonProperty(value = JSON_PROPERTY_ARRAY_ARRAY_OF_INTEGER, required = false)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
Expand Down Expand Up @@ -142,7 +141,6 @@ public ArrayTest addArrayArrayOfModelItem(List<@Valid ReadOnlyFirst> arrayArrayO
* @return arrayArrayOfModel
*/
@jakarta.annotation.Nullable
@Valid

@JsonProperty(value = JSON_PROPERTY_ARRAY_ARRAY_OF_MODEL, required = false)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ public FileSchemaTestClass addFilesItem(ModelFile filesItem) {
* @return files
*/
@jakarta.annotation.Nullable
@Valid

@JsonProperty(value = JSON_PROPERTY_FILES, required = false)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,6 @@ public Pet addTagsItem(Tag tagsItem) {
* @return tags
*/
@jakarta.annotation.Nullable
@Valid

@JsonProperty(value = JSON_PROPERTY_TAGS, required = false)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,8 @@ public class Pet {
private JsonNullable<String> name = JsonNullable.<String>undefined();

@Deprecated
@Valid
private List<String> photoUrls = new ArrayList<>();

@Valid
private List<@Valid Tag> tags = new ArrayList<>();

/**
Expand Down Expand Up @@ -208,7 +206,7 @@ public Pet addTagsItem(Tag tagsItem) {
* Get tags
* @return tags
*/
@Valid

@Schema(name = "tags", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@JsonProperty("tags")
public List<@Valid Tag> getTags() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,8 @@ public class Pet {

private String name;

@Valid
private List<String> photoUrls = new ArrayList<>();

@Valid
private List<@Valid Tag> tags = new ArrayList<>();

/**
Expand Down Expand Up @@ -202,7 +200,7 @@ public Pet addTagsItem(Tag tagsItem) {
* Get tags
* @return tags
*/
@Valid

@Schema(name = "tags", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@JsonProperty("tags")
public List<@Valid Tag> getTags() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,8 @@ public class Pet {

private String name;

@Valid
private List<String> photoUrls = new ArrayList<>();

@Valid
private List<@Valid Tag> tags = new ArrayList<>();

/**
Expand Down Expand Up @@ -201,7 +199,7 @@ public Pet addTagsItem(Tag tagsItem) {
* Get tags
* @return tags
*/
@Valid

@Schema(name = "tags", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
@JsonProperty("tags")
public List<@Valid Tag> getTags() {
Expand Down
Loading