I'm using the OpenAPI Generator with the Maven plugin to generate Java Spring code. In my OpenAPI spec, I have a Project schema with a skills array. I want non null items in this array.
The problem is that in the generated Java code, the @NotNull annotation for the array items is missing, which allows null values inside the skills array, which is not the expected behavior.
Here’s the relevant section from my api-spec.yml:
components:
schemas:
Project:
type: object
properties:
skills:
type: array
items:
type: string
nullable: false
minLength: 1
maxLength: 30
In the generated javacode the skills field does not include the @NotNull annotation for array items:
private List<@Size(min = 1, max = 30)String> skills;
I expected it to generate something as below:
private List<@NotNull @Size(min = 1, max = 30) String> skills;
I am using bean validation with Jakarta EE and it is all configured in maven plugin as below.
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>7.7.0</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/api/api-spec.yml</inputSpec>
<generatorName>spring</generatorName>
<output>${project.build.directory}/generated-sources/openapi</output>
<cleanupOutput>true</cleanupOutput>
<packageName>com.company.project.api</packageName>
<generateSupportingFiles>false</generateSupportingFiles>
<generateModels>true</generateModels>
<generateModelTests>false</generateModelTests>
<generateModelDocumentation>false</generateModelDocumentation>
<modelNamePrefix>Api</modelNamePrefix>
<modelPackage>com.company.project.api.model</modelPackage>
<generateApis>true</generateApis>
<generateApiTests>false</generateApiTests>
<generateApiDocumentation>false</generateApiDocumentation>
<apiPackage>com.company.project.api</apiPackage>
<configOptions>
<sourceFolder>/</sourceFolder>
<dateLibrary>java8</dateLibrary>
<openApiNullable>false</openApiNullable>
<useResponseEntity>true</useResponseEntity>
<interfaceOnly>true</interfaceOnly>
<useSpringBoot3>true</useSpringBoot3>
<skipDefaultInterface>true</skipDefaultInterface>
<annotationLibrary>none</annotationLibrary>
<documentationProvider>none</documentationProvider>
<useBeanValidation>true</useBeanValidation>
<performBeanValidation>true</performBeanValidation>
<useTags>true</useTags>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
How can I ensure that the generated code enforces the non-null constraint for array items?
One possible solution that you can try right now is to apply a custom annotation on your array field and then write a custom validation based on that that annotation.
components:
schemas:
Project:
type: object
properties:
skills:
type: array
x-field-extra-annotation: @bla.bla.CustomAnnotation
items:
type: string
nullable: false
minLength: 1
maxLength: 30
CustomAnnotation
package bla.bla;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import bla.bla.CustomValidator;
@Constraint(validatedBy = CustomValidator.class)
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnnotation {
String message() default "custom error message";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
CustomValidator
package bla.bla;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class CustomValidator implements ConstraintValidator<CustomAnnotation, List<String>> {
@Override
public void initialize(CustomAnnotation constraintAnnotation) {
// Initialization logic if necessary
}
@Override
public boolean isValid(List<String> value, ConstraintValidatorContext context) {
return true; //or false after performing validation.
}
}
:: Following is not an answer but an attempt to find a better solution by making changes in open api generator
Currently open api generator doesn't annotate generic parameter type as not null or use x-field-extra-annotation. I looked into open api generator and found that relevant code is being generated in org.openapitools.codegen.languages.AbstractJavaCodegen#getStringBeanValidation
so locally I added following lines in this method after pattern related code.
if (!Boolean.TRUE.equals(items.getNullable())) {
validations = String.join("",validations, "@NotNull ");
}
Map<String, Object> extensions = items.getExtensions() != null ? items.getExtensions() : Map.of();
if (extensions.containsKey("x-field-extra-annotation")) {
validations = String.join("",
validations,
(String) extensions.get("x-field-extra-annotation"),
" "
);
}
Then I tried it with following schema in spec.
components:
schemas:
Project:
type: object
properties:
skills:
type: array
items:
type: string
nullable: false
minLength: 1
maxLength: 30
x-field-extra-annotation: '@bla.bla.Dummy'
It generated following code which seems correct now.
private List<@NotNull @bla.bla.Dummy @Size(min = 1, max = 30)String> skills;
But this requires a change in open api generator project. I am not an opn api expert (in fact I just explored the generator code for the first time). So this better to be discussed with open api generator team whether this is feasible or not.