[KOTLIN-CLIENT] fix Parent interface class having incorrect collection property type List instead of Set (#22850)

* fix issue #22696

* reduce unnecessary repetition in mustache schema

* add unit tests & regenerate files

* regenerate files
This commit is contained in:
Jachym Metlicka
2026-02-01 08:11:56 +01:00
committed by GitHub
parent ba8327386c
commit 95e8360c19
13 changed files with 57 additions and 6 deletions

View File

@@ -21,4 +21,4 @@
{{#deprecated}}
@Deprecated(message = "This property is deprecated.")
{{/deprecated}}
{{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") {{/multiplatform}}{{#isInherited}}override {{/isInherited}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}{{#uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}Set{{/uniqueItems}}{{^uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}List{{/uniqueItems}}{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}? = {{^defaultValue}}null{{/defaultValue}}{{#defaultValue}}{{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{^multiplatform}}{{{dataType}}}("{{{defaultValue}}}"){{/multiplatform}}{{#multiplatform}}({{{defaultValue}}}).toDouble(){{/multiplatform}}{{/isNumber}}{{/defaultValue}}
{{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") {{/multiplatform}}{{#isInherited}}override {{/isInherited}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}{{#uniqueItems}}Set{{/uniqueItems}}{{^uniqueItems}}List{{/uniqueItems}}{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}? = {{^defaultValue}}null{{/defaultValue}}{{#defaultValue}}{{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{^multiplatform}}{{{dataType}}}("{{{defaultValue}}}"){{/multiplatform}}{{#multiplatform}}({{{defaultValue}}}).toDouble(){{/multiplatform}}{{/isNumber}}{{/defaultValue}}

View File

@@ -21,4 +21,4 @@
{{#deprecated}}
@Deprecated(message = "This property is deprecated.")
{{/deprecated}}
{{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") @Required {{/multiplatform}}{{#isInherited}}override {{/isInherited}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}{{#uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}Set{{/uniqueItems}}{{^uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}List{{/uniqueItems}}{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}{{#isNullable}}?{{/isNullable}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{^multiplatform}}{{{dataType}}}("{{{defaultValue}}}"){{/multiplatform}}{{#multiplatform}}({{{defaultValue}}}).toDouble(){{/multiplatform}}{{/isNumber}}{{/defaultValue}}
{{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") @Required {{/multiplatform}}{{#isInherited}}override {{/isInherited}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}{{#uniqueItems}}Set{{/uniqueItems}}{{^uniqueItems}}List{{/uniqueItems}}{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}{{#isNullable}}?{{/isNullable}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{^multiplatform}}{{{dataType}}}("{{{defaultValue}}}"){{/multiplatform}}{{#multiplatform}}({{{defaultValue}}}).toDouble(){{/multiplatform}}{{/isNumber}}{{/defaultValue}}

View File

@@ -15,4 +15,4 @@
{{^isEnum}}{{^isArray}}{{^isPrimitiveType}}{{^isModel}}@Contextual {{/isModel}}{{/isPrimitiveType}}{{/isArray}}{{/isEnum}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}")
{{/kotlinx_serialization}}
{{/multiplatform}}
{{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") abstract {{/multiplatform}}{{#kotlinx_serialization}}abstract {{/kotlinx_serialization}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}List{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}?
{{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") abstract {{/multiplatform}}{{#kotlinx_serialization}}abstract {{/kotlinx_serialization}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}{{#uniqueItems}}Set{{/uniqueItems}}{{^uniqueItems}}List{{/uniqueItems}}{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}?

View File

@@ -15,4 +15,4 @@
{{^isEnum}}{{^isArray}}{{^isPrimitiveType}}{{^isModel}}@Contextual {{/isModel}}{{/isPrimitiveType}}{{/isArray}}{{/isEnum}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}")
{{/kotlinx_serialization}}
{{/multiplatform}}
{{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") @Required abstract {{/multiplatform}}{{#kotlinx_serialization}}abstract {{/kotlinx_serialization}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}List{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}{{#isNullable}}?{{/isNullable}}
{{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") @Required abstract {{/multiplatform}}{{#kotlinx_serialization}}abstract {{/kotlinx_serialization}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}{{#uniqueItems}}Set{{/uniqueItems}}{{^uniqueItems}}List{{/uniqueItems}}{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}{{#isNullable}}?{{/isNullable}}

View File

@@ -649,6 +649,10 @@ public class KotlinClientCodegenModelTest {
// base properties are present
TestUtils.assertFileContains(animalKt, "val id");
TestUtils.assertFileContains(animalKt, "val optionalProperty");
// base array with unique items = false is correctly handled as List
TestUtils.assertFileContains(animalKt, "val stringArray: kotlin.collections.List<kotlin.String>");
// base array with unique items = true is correctly handled as Set
TestUtils.assertFileContains(animalKt, "val stringSet: kotlin.collections.Set<kotlin.String>");
// base doesn't contain discriminator
TestUtils.assertFileNotContains(animalKt, "val discriminator");
@@ -658,6 +662,10 @@ public class KotlinClientCodegenModelTest {
// derived properties are overridden
TestUtils.assertFileContains(birdKt, "override val id");
TestUtils.assertFileContains(birdKt, "override val optionalProperty");
// derived array with unique items = false is correctly handled as List and with override
TestUtils.assertFileContains(birdKt, "override val stringArray: kotlin.collections.List<kotlin.String>");
// derived array with unique items = true is correctly handled as Set and with override
TestUtils.assertFileContains(birdKt, "override val stringSet: kotlin.collections.Set<kotlin.String>");
// derived doesn't contain disciminator
TestUtils.assertFileNotContains(birdKt, "val discriminator");
}

View File

@@ -89,6 +89,8 @@ public class KotlinSpringServerCodegenTest {
// base properties are present
TestUtils.assertFileContains(animalKt, "val id");
TestUtils.assertFileContains(animalKt, "val optionalProperty");
TestUtils.assertFileContains(animalKt, "val stringArray: kotlin.collections.List<kotlin.String>");
TestUtils.assertFileContains(animalKt, "val stringSet: kotlin.collections.Set<kotlin.String>");
// base doesn't contain discriminator
TestUtils.assertFileNotContains(animalKt, "val discriminator");
@@ -98,6 +100,8 @@ public class KotlinSpringServerCodegenTest {
// derived properties are overridden
TestUtils.assertFileContains(birdKt, "override val id");
TestUtils.assertFileContains(birdKt, "override val optionalProperty");
TestUtils.assertFileContains(birdKt, "override val stringArray: kotlin.collections.List<kotlin.String>");
TestUtils.assertFileContains(birdKt, "override val stringSet: kotlin.collections.Set<kotlin.String>");
// derived doesn't contain disciminator
TestUtils.assertFileNotContains(birdKt, "val discriminator");
}

View File

@@ -41,8 +41,19 @@ components:
format: uuid
optional_property:
type: number
stringSet:
type: array
items:
type: string
uniqueItems: true
stringArray:
type: array
items:
type: string
required:
- id
- string_set
- string_array
discriminator:
propertyName: discriminator
mapping:

View File

@@ -6,6 +6,8 @@
| ------------ | ------------- | ------------- | ------------- |
| **id** | [**java.util.UUID**](java.util.UUID.md) | | |
| **optionalProperty** | [**java.math.BigDecimal**](java.math.BigDecimal.md) | | [optional] |
| **stringSet** | **kotlin.collections.Set&lt;kotlin.String&gt;** | | [optional] |
| **stringArray** | **kotlin.collections.List&lt;kotlin.String&gt;** | | [optional] |

View File

@@ -32,6 +32,8 @@ import kotlinx.serialization.json.JsonClassDiscriminator
*
* @param id
* @param optionalProperty
* @param stringSet
* @param stringArray
*/
@Serializable
@@ -44,6 +46,10 @@ sealed class Animal {
abstract val id: java.util.UUID
@Contextual @SerialName(value = "optional_property")
abstract val optionalProperty: java.math.BigDecimal?
@SerialName(value = "stringSet")
abstract val stringSet: kotlin.collections.Set<kotlin.String>?
@SerialName(value = "stringArray")
abstract val stringArray: kotlin.collections.List<kotlin.String>?
}

View File

@@ -32,6 +32,8 @@ import kotlinx.serialization.encoding.Encoder
* @param id
* @param featherType
* @param optionalProperty
* @param stringSet
* @param stringArray
*/
@Serializable
@@ -45,7 +47,13 @@ data class Bird (
val featherType: kotlin.String,
@Contextual @SerialName(value = "optional_property")
override val optionalProperty: java.math.BigDecimal? = null
override val optionalProperty: java.math.BigDecimal? = null,
@SerialName(value = "stringSet")
override val stringSet: kotlin.collections.Set<kotlin.String>? = null,
@SerialName(value = "stringArray")
override val stringArray: kotlin.collections.List<kotlin.String>? = null
) : Animal() {

View File

@@ -6,6 +6,8 @@
| ------------ | ------------- | ------------- | ------------- |
| **id** | **kotlin.String** | | |
| **optionalProperty** | **kotlin.Double** | | [optional] |
| **stringSet** | **kotlin.collections.Set&lt;kotlin.String&gt;** | | [optional] |
| **stringArray** | **kotlin.collections.List&lt;kotlin.String&gt;** | | [optional] |

View File

@@ -26,6 +26,8 @@ import kotlinx.serialization.json.JsonClassDiscriminator
*
* @param id
* @param optionalProperty
* @param stringSet
* @param stringArray
*/
@Serializable
@@ -36,6 +38,8 @@ sealed class Animal {
@SerialName(value = "id") @Required abstract val id: kotlin.String
@SerialName(value = "optional_property") abstract val optionalProperty: kotlin.Double?
@SerialName(value = "stringSet") abstract val stringSet: kotlin.collections.Set<kotlin.String>?
@SerialName(value = "stringArray") abstract val stringArray: kotlin.collections.List<kotlin.String>?
}

View File

@@ -27,6 +27,8 @@ import kotlinx.serialization.encoding.*
* @param id
* @param featherType
* @param optionalProperty
* @param stringSet
* @param stringArray
*/
@Serializable
@@ -37,7 +39,11 @@ data class Bird (
@SerialName(value = "featherType") @Required val featherType: kotlin.String,
@SerialName(value = "optional_property") override val optionalProperty: kotlin.Double? = null
@SerialName(value = "optional_property") override val optionalProperty: kotlin.Double? = null,
@SerialName(value = "stringSet") override val stringSet: kotlin.collections.Set<kotlin.String>? = null,
@SerialName(value = "stringArray") override val stringArray: kotlin.collections.List<kotlin.String>? = null
) : Animal() {