3

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?

3
  • 1
    the lack of nullable is the same as nullable: false. the definition of type: string is the constraint applied to the value. It can't be any other type, such as: null, boolean, object, array, number Commented Sep 13, 2024 at 14:10
  • 2
    I agree Jeremy, by default it's non nullable. But OP concern is valid. The generated java code doesn't add @NotNull annotation with type specified in generic but it adds them with normal fields. This may be a bug in openapi spring generator. Commented Sep 13, 2024 at 21:18
  • I'm from a Typescript generator and see pretty much the same. On version 7.3.0 is fine, update to 7.8.0 magically shows all my string arrays as string|null while nullable default false. Commented Dec 19, 2024 at 14:53

1 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.