OpenAPITools / OpenAPITools/openapi-generator

[BUG][Java] Custom object wrapped in Optional won't result JsonNullable with OpenApi 3.1

Open
#22,812 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Issue: Bug
Dominant language
Java
Stars
26.8k
Forks
7.7k
PR merge metrics
PR metrics pending

Description

Bug Report Checklist
  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator?
  • Have you tested with the latest master to confirm the issue still exists?
  • Have you searched for related issues/PRs?
  • What's the actual output vs expected output?
  • [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description

When using OpenApi 3.1 I'm not able to generate a JsonNullable from Optional that wraps object of custom class. This is possible when using OpenApi 3.0. Built-in classes (Boolean, String etc.) and enums seem to work, but not more complex classes. See examples for more details.

Examples

####### Optional<String>
OpenApi 3.1 can behave like OpenApi 3.0.

With OpenApi 3.1 this

  @Schema(oneOf = {String.class, Null.class})
  private Optional<String> exampleStr;

generates this

  @jakarta.annotation.Nullable
  private JsonNullable<String> exampleStr = JsonNullable.<String>undefined();

which is what OpenApi 3.0 also looks like. With OpenApi 3.0 this

  @Schema(nullable = true)
  private Optional<String> exampleStr;

generates this

  @jakarta.annotation.Nullable
  private JsonNullable<String> exampleStr = JsonNullable.<String>undefined();

####### Optional<CustomEnum>
Enums seem to work in OpenApi 3.1 the same way they work in OpenApi 3.0.

With OpenApi 3.1 this

  @Schema(oneOf = {ExampleEnumm.class, Null.class})
  private Optional<ExampleEnumm> exampleEnumm;

  public enum ExampleEnumm {
    EXAMPLE_ONE,
    EXAMPLE_TWO
  }

generates this

  @jakarta.annotation.Nullable
  private JsonNullable<ExampleEnummEnum> exampleEnumm = JsonNullable.<ExampleEnummEnum>undefined();

  public enum ExampleEnummEnum {
    EXAMPLE_ONE(String.valueOf("EXAMPLE_ONE")),
    EXAMPLE_TWO(String.valueOf("EXAMPLE_TWO"));

    private String value;
    // rest is redacted, not relevant for this issue.
  }

which is identical result compared to OpenApi 3.0. With OpenApi 3.0 this

  @Schema(nullable = true)
  private Optional<ExampleEnumm> exampleEnumm;

  public enum ExampleEnumm {
    EXAMPLE_ONE,
    EXAMPLE_TWO
  }

generates this

  @jakarta.annotation.Nullable
  private JsonNullable<ExampleEnummEnum> exampleEnumm = JsonNullable.<ExampleEnummEnum>undefined();

  public enum ExampleEnummEnum {
    EXAMPLE_ONE(String.valueOf("EXAMPLE_ONE")),
    EXAMPLE_TWO(String.valueOf("EXAMPLE_TWO"));

    private String value;
    // rest is redacted, not relevant for this issue.
  }

####### Optional<CustomClazz>
With custom class the behavior differs very much between OpenApi 3.1 and 3.0.

With OpenApi 3.1 this

  @Schema(oneOf = {ExampleClazz.class, Null.class})
  private Optional<ExampleClazz> exampleClazz;

  @Data
  @Schema(name = "ExampleClazz")
  public class ExampleClazz {
    @Schema(oneOf = {String.class, Null.class})
    private Optional<@Size(min = 1) String> optionalStrOne;

    @Schema(oneOf = {String.class, Null.class})
    private Optional<@Size(min = 1) String> optionalStrTwo;
  }

generates (with warnings) this

  public static final String JSON_PROPERTY_EXAMPLE_CLAZZ = "exampleClazz";
  @jakarta.annotation.Nullable private MyDtoClazzExampleClazz exampleClazz;

which is different what OpenApi 3.0 produces. With OpenApi 3.0 this

  @Schema(nullable = true)
  private Optional<ExampleClazz> exampleClazz;

  @Data
  @Schema(name = "ExampleClazz")
  public class ExampleClazz {
    @Schema(nullable = true)
    private Optional<@Size(min = 1) String> optionalStrOne;

    @Schema(nullable = true)
    private Optional<@Size(min = 1) String> optionalStrTwo;
  }

generates this

  @jakarta.annotation.Nullable
  private JsonNullable<ExampleClazz> exampleClazz = JsonNullable.<ExampleClazz>undefined();
openapi-generator version

7.19.0, 7.18.0, 7.17.0

Generation Details

I'm using Gradle. The configs for generator:

    configOptions.set([
            dateLibrary                   : 'java8',
            useJakartaEe                  : 'true',
            hideGenerationTimestamp       : 'true',
            interfaceOnly                 : 'true',
            java8                         : 'true',
            library                       : 'webclient',
            useOptional                   : 'true',
            additionalModelTypeAnnotations: '@lombok.Data @lombok.Builder @lombok.AllArgsConstructor'
    ])
Steps to reproduce
Related issues/PRs
Suggest a fix

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

The report does not name a source file, test, or entry point. Reproduce the OpenAPI 3.1 and 3.0 Java generation examples with generator 7.19.0, then trace handling of Optional versus Optional and enums. Done when a custom class generates JsonNullable consistently with the OpenAPI 3.0 behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend-api-design, devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.