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.98k stars 6.6k forks source link

[BUG][Kotlin][retrofit2] Enums in request body represented as String parameters, default value is wrong, resulting code does not compile #20020

Open lukas1 opened 3 weeks ago

lukas1 commented 3 weeks ago

Bug Report Checklist

Description

There's an issue generating models for API spec that contains enums in request body for Kotlin and Retrofit 2. Enums are generated, but the request method then uses Strings as parameters. If a default value is provided, the result is even code that doesn't compile at all, because the default value doesn't have quotation marks for String.

openapi-generator version

7.9.0

OpenAPI declaration file content or url
openapi: 3.0.3
info:
  title: enum_api
  version: '1.0'
  description: |-
    This specification demonstrates an issue in Kotlin code generator for Retrofit
servers:
  - description: Server
    url: 'https://test.com'
paths:
  /test:
    post:
      summary: Test
      responses:
        '200':
          description: Test
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Response'
              examples: {}
      description: Test
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/Request'
        description: Test
      security: []
components:
  schemas:
    Response:
      title: Response
      type: object
      properties:
        property:
          type: string
          example: eyJhbGciOiJIUzI1NiIs...
          description: Test
    Request:
      title: Request
      type: object
      properties:
        enum_prop_type:
          type: string
          enum:
            - 'test1'
            - 'test2'
          example: 'test1'
          default: 'test1'
          description: Test
      required:
        - enum_prop_type
Generation Details

Setup of the gradle task that fails to generate proper Kotlin code:

task openApiGenerateTestApi(type: GenerateTask) {
    globalProperties = [
            supportingFiles: "",
            apis           : "",
            models         : "",
    ]
    inputSpec = "$rootDir/app/api-specs/openapi_enum_service.yml"
    generatorName = "kotlin"
    outputDir = "$rootDir/app"
    modelPackage = "com.example.openapienumbug.openapi.entities"
    apiPackage = "com.example.openapienumbug.openapi.api"
    modelNameSuffix = "Entity"
    additionalProperties = [
            useCoroutines: "true",
            library      : "jvm-retrofit2",
            sourceFolder : "src/main/java",
            "enumPropertyNaming": "UPPERCASE"
    ]
    ignoreFileOverride = "$rootDir/.openapi-generator-ignore"
}
Steps to reproduce

Run the openApiGenerateTestApi task in the demo app provided here:

OpenApiEnumBug.zip

Observe the generated Kotlin code of the file DefaultApi.kt

Here's the generated file:

package com.example.openapienumbug.openapi.api

import org.openapitools.client.infrastructure.CollectionFormats.*
import retrofit2.http.*
import retrofit2.Response
import okhttp3.RequestBody
import com.squareup.moshi.Json

import com.example.openapienumbug.openapi.entities.ResponseEntity

interface DefaultApi {

    /**
    * enum for parameter enumPropType
    */
    enum class EnumPropTypeTestPost(val value: kotlin.String) {
        @Json(name = "test1") TEST1("test1"),
        @Json(name = "test2") TEST2("test2")
    }

    /**
     * Test
     * Test
     * Responses:
     *  - 200: Test
     *
     * @param enumPropType Test (default to test1)
     * @return [ResponseEntity]
     */
    @FormUrlEncoded
    @POST("test")
    suspend fun testPost(@Field("enum_prop_type") enumPropType: kotlin.String = test1): Response<ResponseEntity>

}

Notice how the EnumPropTypeTestPost is not used inside the testPost method, for the enumPropType. A String is used as the type. On top of that, the default value test1 is not wrapped by quotation marks for a String parameter.

This would be the expected result:

package com.example.openapienumbug.openapi.api

import org.openapitools.client.infrastructure.CollectionFormats.*
import retrofit2.http.*
import retrofit2.Response
import okhttp3.RequestBody
import com.squareup.moshi.Json

import com.example.openapienumbug.openapi.entities.ResponseEntity

interface DefaultApi {

    /**
    * enum for parameter enumPropType
    */
    enum class EnumPropTypeTestPost(val value: kotlin.String) {
        @Json(name = "test1") TEST1("test1"),
        @Json(name = "test2") TEST2("test2")
    }

    /**
     * Test
     * Test
     * Responses:
     *  - 200: Test
     *
     * @param enumPropType Test (default to test1)
     * @return [ResponseEntity]
     */
    @FormUrlEncoded
    @POST("test")
    suspend fun testPost(@Field("enum_prop_type") enumPropType: EnumPropTypeTestPost = EnumPropTypeTestPost.TEST1): Response<ResponseEntity>

}
Related issues/PRs

I did not find anything related

Suggest a fix

No suggestion on my side

lukas1 commented 2 weeks ago

The fact that Strings and not Enums are used in the parameters may not necessarily be a bug. Retrofit does not support enums for @FormUrlEncoded properly, one would need to provide also some converter for them. Maybe it was intentional for @FormUrlEncoded to use strings.

But the fact that when default value is provided the resulting code does not compile is a clear bug.