OpenAPITools / openapi-generator

OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (v2, v3)
https://openapi-generator.tech
Apache License 2.0
21.66k stars 6.54k forks source link

[BUG][Java] emitting non-compilable code with map&&!container #13551

Open dmivankov opened 2 years ago

dmivankov commented 2 years ago

Bug Report Checklist

Description

Don't have a minimized reproduction example yet, but it is triggered on recursive syntax tree schema on 5.x+ openapi-generator (also latest 6.2.0). Following part of template in java pojo.mustache

  {{#isMap}}

  public {{classname}} put{{nameInCamelCase}}Item(String key, {{{items.datatypeWithEnum}}} {{name}}Item) {
    {{#vendorExtensions.x-is-jackson-optional-nullable}}
    if (this.{{name}} == null || !this.{{name}}.isPresent()) {
      this.{{name}} = JsonNullable.<{{{datatypeWithEnum}}}>of({{{defaultValue}}});
    }

emits code like

  public SyntaxTree putNotItem(String key,  notItem) {
    this.not.put(key, notItem);
    return this;
  }

notice that items.datatypeWithEnum is empty and notItem breaks compilation. Adding {{#isContainer}} in addition to {{#isMap}} fixes it by not emitting a wrong method (setNot method is the only one needed and it is still emitted)

openapi-generator version

6.2.0, seems to have started since 5.x

OpenAPI declaration file content or url
Generation Details
Steps to reproduce
Related issues/PRs
Suggest a fix

Not sure if isMap&&!isContainer a valid combination. Fix could either to update template as mentioned above or fixing isMap detection

borsch commented 2 years ago

@dmivankov could you pleae attach you spec yaml/json so that we can reproduce this issue and provide more information on which exactly generator you are using and how you configure it

dmivankov commented 2 years ago

That was actually very insightful to look into.

Here's the minimized spec:

openapi: 3.0.1
info:
  version: 0.1
  title: test
servers: []
paths: {}
components:
  schemas:
    SyntaxTree:
      type: object
      oneOf:
        - type: object
          properties:
            not:
              type: object
              allOf: # this allOf wrapping is crucial to reproduce the issue
                - $ref: '#/components/schemas/SyntaxTree'
        - type: object
          properties:
            equals:
              type: object
              properties:
                selector:
                  type: string
                value:
                  type: integer

generating via

java -DmodelTests=false -DmodelDocs=false -Dmodels -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -i spec.yml -g java -o output  --library feign

which produces this malformed method in output/src/main/java/org/openapitools/client/model/SyntaxTree.java

  public SyntaxTree putNotItem(String key,  notItem) {
    if (this.not == null) {
      this.not = ;
    }
    this.not.put(key, notItem);
    return this;
  }

AllOf hack can be used to add a new description to a shared schema for documentation purposes while avoiding to copy-paste whole schema. --library feign is not crucial, just leads to smaller generated class than okhttp-gson default generator. For okhttp-gson the method is still there for each oneof class

  public SyntaxTreeOneOf putNotItem(String key,  notItem) {
    if (this.not == null) {
      this.not = ;
    }
    this.not.put(key, notItem);
    return this;
  }