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
20.59k stars 6.29k forks source link

[BUG][KOTLIN] Kotlin client generation with generateOneOfAnyOfWrappers=true produces Java code in .kt file that does not compile in a Kotlin project #18890

Closed martinLeht closed 3 weeks ago

martinLeht commented 3 weeks ago

Bug Report Checklist

Description

I tested the latest openapi-generator latest (7.7.0-SNAPSHOT) that contains a fix related to better oneOf support for generated Kotlin client code generation. The latest Kotlin client generator generates code that does not compile in a Kotlin project when using new config-option generateOneOfAnyOfWrappers=true. Kotlin generator produces Java code among the Kotlin code which results in a .kt file that does not compile. This problem persists when generation is performed against an OpenAPI schema that uses oneOf and discriminator for polymorphism.

By simply looking at the mustache template files, one can see that there are variables declared in Java syntax and that some exceptions are thrown with "new" keyword that does not exist in Kotlin and thus does not compile. Below are some snippets and references to the templates:

openapi-generator version

This issue currently on latest 7.7.0-SNAPSHOT version.

OpenAPI declaration file content or url
openapi: 3.0.3
info:
  title: test
  version: 1.0.0
  description: test
  license:
    name: Apache 2.0
    url: https://www.apache.org/licenses/LICENSE-2.0.html
servers:
  - url: https://petstore.swagger.io/v2
paths:
  /pets:
    patch:
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '#/components/schemas/Cat'
                - $ref: '#/components/schemas/Dog'
              discriminator:
                propertyName: pet_type
      responses:
        '200':
          description: Updated
components:
  schemas:
    Pet:
      type: object
      required:
        - pet_type
      properties:
        pet_type:
          type: string
      discriminator:
        propertyName: pet_type
    Dog:     # "Dog" is a value for the pet_type property (the discriminator value)
      allOf: # Combines the main `Pet` schema with `Dog`-specific properties
        - $ref: '#/components/schemas/Pet'
        - type: object
          # all other properties specific to a `Dog`
          required:
            - names
          properties:
            names:
              type: array
              items:
                type: object
                properties:
                  firstname:
                    type: string
                  lastname:
                    type: string
            bark:
              type: boolean
            breed:
              type: string
              enum: [Dingo, Husky, Retriever, Shepherd]
    Cat:     # "Cat" is a value for the pet_type property (the discriminator value)
      allOf: # Combines the main `Pet` schema with `Cat`-specific properties
        - $ref: '#/components/schemas/Pet'
        - type: object
          # all other properties specific to a `Cat`
          properties:
            hunts:
              type: boolean
            age:
              type: integer
Generation Details

Use latest generator CLI via docker: openapitools/openapi-generator-cli:latest

Use above provided OpenAPI YAML to reproduce (see detailed below).

Configuration options (additional-properties) for Kotlin client generation:

Steps to reproduce

Run kotlin client generation command with latest openapi-generator-cli image against the OpenAPI YAML provided above with above additional properties described above: docker run --rm -v "${PWD}:/local" openapitools/openapi-generator-cli:latest generate -i /local/pets.yaml -g kotlin -o /local/generated/kotlin --additional-properties=generateOneOfAnyOfWrappers=true,enumPropertyNaming=original,serializationLibrary=gson

Related issues/PRs

Bug was a result of PR: https://github.com/OpenAPITools/openapi-generator/commit/353320cb04fdf84187e20ddfb0a3a8fe4de7da7a

Suggest a fix

Kotlin mustache templates should be fixed, so that there is only Kotlin syntax and no Java specific syntax. The above listed variable declarations and exception throwing should probably be fixed to Kotlin equivalent as follows:

JsonArray jsonArray{{name}} = jsonObj.getAsJsonArray("{{{baseName}}}") // Java -> val jsonArray{{name}} = jsonObj.getAsJsonArray("{{{baseName}}}") // Kotlin

throw new IllegalArgumentException(...) // Java -> throw IllegalArgumentException(...) // Kotlin

wing328 commented 3 weeks ago

@martinLeht thank for reporting the issue.

I've filed https://github.com/OpenAPITools/openapi-generator/pull/18891 to fix it

martinLeht commented 3 weeks ago

@martinLeht thank for reporting the issue.

I've filed #18891 to fix it

Thanks!

wing328 commented 3 weeks ago

PR merged. Please give it a try with the latest master. (snapshot version available in the project's readme)

martinLeht commented 3 weeks ago

@wing328 Nice, it seems to be working regarding the problems I mentioned earlier, thanks!

I noticed tho another problem (possibly related to encoding on generation?). In the template kotlin-client/data_class.mustache we have the following jsonArray variable declaration and for-loop:

val jsonArray{{name}} = jsonObj.getAsJsonArray("{{{baseName}}}")
// validate the required field `{{{baseName}}}` (array)
for (i in 0 until jsonArray{{name}}.size()) {
              {{{items.dataType}}}.validateJsonElement(jsonArray{{name}}.get(i))
}

The above template produces uncompilable code, when the variable {{name}} = "data" in the OpenAPI spec. The generated code would look like this (uncompilable):

val jsonArray`data` = jsonObj.getAsJsonArray("data")
// validate the required field `data` (array) 
for (i in 0 until jsonArray`data`.size()) {
             <SOME_MODEL>.validateJsonElement(jsonArray&#x60;data&#x60;.get(i))
}

But when the {{name}} = "entities" or something else than "data", it generates code that is compilable and good:

val jsonArrayentities = jsonObj.getAsJsonArray("entities")
// validate the required field `entities` (array)
for (i in 0 until jsonArrayentities.size()) {
              <SOME_MODEL>.validateJsonElement(jsonArrayentities.get(i))
}

Should I make a separate issue with more details or is this enough?

martinLeht commented 3 weeks ago

@wing328 made new issue related to the problem above: https://github.com/OpenAPITools/openapi-generator/issues/18908