Skip to content

[BUG] [Kotlin] [kotlin-server] [jaxrs-spec] additionalModelTypeAnnotations also added to generated enums #24978

Description

@rupert-jung-mw

Bug Report Checklist

Description

The Java-family generators (AbstractJavaCodegen, e.g. JavaJaxRS/JavaSpring) expose two separate CLI/config options:

  • additionalModelTypeAnnotations — applied to regular model (data) classes
  • additionalEnumTypeAnnotations — applied only to enum classes
  • AbstractKotlinCodegen (used by kotlin-server, kotlin-client, kotlin-spring, etc.) only implements additionalModelTypeAnnotations. Its enum_class.mustache template (e.g. libraries/jaxrs-spec/enum_class.mustache) unconditionally includes {{>additionalModelTypeAnnotations}}, so any annotation supplied via additionalModelTypeAnnotations gets applied to both data classes and enum classes, with no way to target one but not the other.
  • This breaks generation whenever the annotation is only valid on classes (not enums)
  • error: @ is only supported on classes, records, constructors, and methods.
openapi-generator version

7.25

Steps to reproduce
  1. OpenAPI spec with at least one enum-typed model property.
  2. Generate with: generatorName: kotlin-server, library: jaxrs-spec, configOptions.additionalModelTypeAnnotations: @<any annotation which does not support enums
  3. Generated enum .kt files (e.g. MyEnum.kt) contain specified enum at the top of the class
Suggest a fix

In enum classes, actually only additionalEnumTypeAnnotations should be included, see e.g. the documentation of JaxRs-Spec Generator

Image

The template could look like this:

enum_class.mustache

import com.fasterxml.jackson.annotation.JsonProperty
{{#kotlinx_serialization}}
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
{{/kotlinx_serialization}}

/**
 * {{{description}}}
 *
 * Values: {{#allowableValues}}{{#enumVars}}{{&name}}{{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}}
 */
{{#kotlinx_serialization}}@Serializable{{/kotlinx_serialization}}
{{>additionalEnumTypeAnnotations}}

{{#nonPublicApi}}internal {{/nonPublicApi}}enum class {{classname}}(val value: {{{dataType}}}) {
{{#allowableValues}}{{#enumVars}}

    {{#enumDescription}}
    /**
    * {{.}}
    */
    {{/enumDescription}}
    @JsonProperty(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}})
    {{#kotlinx_serialization}}
    @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}})
    {{/kotlinx_serialization}}
    {{#isArray}}
    {{#isList}}
    {{&name}}(listOf({{{value}}})){{^-last}},{{/-last}}{{#-last}};{{/-last}}
    {{/isList}}
    {{^isList}}
    {{&name}}(arrayOf({{{value}}})){{^-last}},{{/-last}}{{#-last}};{{/-last}}
    {{/isList}}
    {{/isArray}}
    {{^isArray}}
    {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}}
    {{/isArray}}
{{/enumVars}}{{/allowableValues}}
    /**
     * Override toString() to avoid using the enum variable name as the value, and instead use
     * the actual value defined in the API spec file.
     *
     * This solves a problem when the variable name and its value are different, and ensures that
     * the client sends the correct enum values to the server always.
     */
    override fun toString(): String = value{{^isString}}.toString(){{/isString}}

    companion object {
        /**
         * Converts the provided [data] to a [String] on success, null otherwise.
         */
        fun encode(data: Any?): kotlin.String? = if (data is {{classname}}) "$data" else null

        /**
         * Returns a valid [{{classname}}] for [data], null otherwise.
         */
        @OptIn(ExperimentalStdlibApi::class)
        fun decode(data: Any?): {{classname}}? = data?.let {
          val normalizedData = "$it".lowercase()
          values().firstOrNull { value ->
            it == value || normalizedData == "$value".lowercase()
          }
        }
    }
}

The acual change has been made in line 13 where

>{{>additionalModelTypeAnnotations}}

has been replaced with

{{>additionalEnumTypeAnnotations}}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions