Skip to content

Commit c38d825

Browse files
etremblayEric Durand-Tremblay
andauthored
[BUG] Issue 10792 Kotlin generator produces invalid code when allOf is used (#12594)
* Step to reproduces * Fix isMap detection for kotlin codegen Co-authored-by: Eric Durand-Tremblay <etremblay@kronostechnologies.com>
1 parent 75b883c commit c38d825

40 files changed

Lines changed: 1435 additions & 0 deletions
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
generatorName: kotlin
2+
outputDir: samples/client/petstore/kotlin-allOff-discriminator
3+
inputSpec: modules/openapi-generator/src/test/resources/3_0/issue_10792.yaml
4+
templateDir: modules/openapi-generator/src/main/resources/kotlin-client
5+
additionalProperties:
6+
artifactId: kotlin-allOff-discriminator
7+
serializableModel: "false"
8+
dateLibrary: java8
9+
enumUnknownDefaultCase: true

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import com.fasterxml.jackson.databind.node.ArrayNode;
2121
import io.swagger.v3.oas.models.media.ArraySchema;
22+
import io.swagger.v3.oas.models.media.ComposedSchema;
2223
import io.swagger.v3.oas.models.media.Schema;
2324
import io.swagger.v3.oas.models.media.StringSchema;
2425
import org.apache.commons.io.FilenameUtils;
@@ -1045,4 +1046,30 @@ public String toDefaultValue(Schema schema) {
10451046
public GeneratorLanguage generatorLanguage() {
10461047
return GeneratorLanguage.KOTLIN;
10471048
}
1049+
1050+
@Override
1051+
protected void updateModelForObject(CodegenModel m, Schema schema) {
1052+
/**
1053+
* we have a custom version of this function so we only set isMap to true if
1054+
* ModelUtils.isMapSchema
1055+
* In other generators, isMap is true for all type object schemas
1056+
*/
1057+
if (schema.getProperties() != null || schema.getRequired() != null && !(schema instanceof ComposedSchema)) {
1058+
// passing null to allProperties and allRequired as there's no parent
1059+
addVars(m, unaliasPropertySchema(schema.getProperties()), schema.getRequired(), null, null);
1060+
}
1061+
if (ModelUtils.isMapSchema(schema)) {
1062+
// an object or anyType composed schema that has additionalProperties set
1063+
addAdditionPropertiesToCodeGenModel(m, schema);
1064+
} else {
1065+
m.setIsMap(false);
1066+
if (ModelUtils.isFreeFormObject(openAPI, schema)) {
1067+
// non-composed object type with no properties + additionalProperties
1068+
// additionalProperties must be null, ObjectSchema, or empty Schema
1069+
addAdditionPropertiesToCodeGenModel(m, schema);
1070+
}
1071+
}
1072+
// process 'additionalProperties'
1073+
setAddProps(schema, m);
1074+
}
10481075
}

modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/AbstractKotlinCodegenTest.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,4 +272,44 @@ public void testEnumPropertyWithDefaultValue() {
272272
Assert.assertEquals(cp1.getEnumName(), "PropertyName");
273273
Assert.assertEquals(cp1.getDefaultValue(), "PropertyName.vALUE");
274274
}
275+
276+
@Test(description = "Issue #10792")
277+
public void handleInheritanceWithObjectTypeShouldNotBeAMap() {
278+
Schema parent = new ObjectSchema()
279+
.addProperties("a", new StringSchema())
280+
.addProperties("b", new StringSchema())
281+
.addRequiredItem("a")
282+
.name("Parent");
283+
Schema child = new ComposedSchema()
284+
.addAllOfItem(new Schema().$ref("Parent"))
285+
.addAllOfItem(new ObjectSchema()
286+
.addProperties("c", new StringSchema())
287+
.addProperties("d", new StringSchema())
288+
.addRequiredItem("c"))
289+
.name("Child")
290+
.type("object"); // Without the object type it is not wrongly recognized as map
291+
Schema mapSchema = new ObjectSchema()
292+
.addProperties("a", new StringSchema())
293+
.additionalProperties(Boolean.TRUE)
294+
.name("MapSchema")
295+
.type("object");
296+
297+
OpenAPI openAPI = TestUtils.createOpenAPI();
298+
openAPI.getComponents().addSchemas(parent.getName(), parent);
299+
openAPI.getComponents().addSchemas(child.getName(), child);
300+
openAPI.getComponents().addSchemas(mapSchema.getName(), mapSchema);
301+
302+
final DefaultCodegen codegen = new P_AbstractKotlinCodegen();
303+
codegen.setOpenAPI(openAPI);
304+
305+
final CodegenModel pm = codegen
306+
.fromModel("Child", child);
307+
308+
Assert.assertFalse(pm.isMap);
309+
310+
// Make sure a real map is still flagged as map
311+
final CodegenModel mapSchemaModel = codegen
312+
.fromModel("MapSchema", mapSchema);
313+
Assert.assertTrue(mapSchemaModel.isMap);
314+
}
275315
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
openapi: 3.0.1
2+
info:
3+
title: Example
4+
description: An example
5+
version: '0.1'
6+
contact:
7+
email: contact@example.org
8+
url: 'https://example.org'
9+
servers:
10+
- url: http://example.org
11+
tags:
12+
- name: bird
13+
paths:
14+
'/v1/bird/{id}':
15+
get:
16+
tags:
17+
- bird
18+
responses:
19+
'200':
20+
description: OK
21+
content:
22+
application/json:
23+
schema:
24+
$ref: '#/components/schemas/bird'
25+
operationId: get-bird
26+
parameters:
27+
- schema:
28+
type: string
29+
format: uuid
30+
name: id
31+
in: path
32+
required: true
33+
components:
34+
schemas:
35+
animal:
36+
title: An animal
37+
type: object
38+
properties:
39+
id:
40+
type: string
41+
format: uuid
42+
required:
43+
- id
44+
discriminator:
45+
propertyName: type
46+
mapping:
47+
BIRD: '#/components/schemas/bird'
48+
bird:
49+
title: A bird
50+
type: object
51+
allOf:
52+
- $ref: '#/components/schemas/animal'
53+
- properties:
54+
featherType:
55+
type: string
56+
required:
57+
- featherType
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# OpenAPI Generator Ignore
2+
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
3+
4+
# Use this file to prevent files from being overwritten by the generator.
5+
# The patterns follow closely to .gitignore or .dockerignore.
6+
7+
# As an example, the C# client generator defines ApiClient.cs.
8+
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
9+
#ApiClient.cs
10+
11+
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
12+
#foo/*/qux
13+
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
14+
15+
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
16+
#foo/**/qux
17+
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
18+
19+
# You can also negate patterns with an exclamation (!).
20+
# For example, you can ignore all files in a docs folder with the file extension .md:
21+
#docs/*.md
22+
# Then explicitly reverse the ignore rule for a single file:
23+
#!docs/README.md
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
README.md
2+
build.gradle
3+
docs/Animal.md
4+
docs/Bird.md
5+
docs/BirdAllOf.md
6+
docs/BirdApi.md
7+
gradle/wrapper/gradle-wrapper.jar
8+
gradle/wrapper/gradle-wrapper.properties
9+
gradlew
10+
gradlew.bat
11+
settings.gradle
12+
src/main/kotlin/org/openapitools/client/apis/BirdApi.kt
13+
src/main/kotlin/org/openapitools/client/infrastructure/ApiAbstractions.kt
14+
src/main/kotlin/org/openapitools/client/infrastructure/ApiClient.kt
15+
src/main/kotlin/org/openapitools/client/infrastructure/ApiResponse.kt
16+
src/main/kotlin/org/openapitools/client/infrastructure/BigDecimalAdapter.kt
17+
src/main/kotlin/org/openapitools/client/infrastructure/BigIntegerAdapter.kt
18+
src/main/kotlin/org/openapitools/client/infrastructure/ByteArrayAdapter.kt
19+
src/main/kotlin/org/openapitools/client/infrastructure/Errors.kt
20+
src/main/kotlin/org/openapitools/client/infrastructure/LocalDateAdapter.kt
21+
src/main/kotlin/org/openapitools/client/infrastructure/LocalDateTimeAdapter.kt
22+
src/main/kotlin/org/openapitools/client/infrastructure/OffsetDateTimeAdapter.kt
23+
src/main/kotlin/org/openapitools/client/infrastructure/PartConfig.kt
24+
src/main/kotlin/org/openapitools/client/infrastructure/RequestConfig.kt
25+
src/main/kotlin/org/openapitools/client/infrastructure/RequestMethod.kt
26+
src/main/kotlin/org/openapitools/client/infrastructure/ResponseExtensions.kt
27+
src/main/kotlin/org/openapitools/client/infrastructure/Serializer.kt
28+
src/main/kotlin/org/openapitools/client/infrastructure/SerializerHelper.kt
29+
src/main/kotlin/org/openapitools/client/infrastructure/URIAdapter.kt
30+
src/main/kotlin/org/openapitools/client/infrastructure/UUIDAdapter.kt
31+
src/main/kotlin/org/openapitools/client/models/Animal.kt
32+
src/main/kotlin/org/openapitools/client/models/Bird.kt
33+
src/main/kotlin/org/openapitools/client/models/BirdAllOf.kt
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
6.0.1-SNAPSHOT
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# org.openapitools.client - Kotlin client library for Example
2+
3+
## Requires
4+
5+
* Kotlin 1.4.30
6+
* Gradle 6.8.3
7+
8+
## Build
9+
10+
First, create the gradle wrapper script:
11+
12+
```
13+
gradle wrapper
14+
```
15+
16+
Then, run:
17+
18+
```
19+
./gradlew check assemble
20+
```
21+
22+
This runs all tests and packages the library.
23+
24+
## Features/Implementation Notes
25+
26+
* Supports JSON inputs/outputs, File inputs, and Form inputs.
27+
* Supports collection formats for query parameters: csv, tsv, ssv, pipes.
28+
* Some Kotlin and Java types are fully qualified to avoid conflicts with types defined in OpenAPI definitions.
29+
* Implementation of ApiClient is intended to reduce method counts, specifically to benefit Android targets.
30+
31+
<a name="documentation-for-api-endpoints"></a>
32+
## Documentation for API Endpoints
33+
34+
All URIs are relative to *http://example.org*
35+
36+
Class | Method | HTTP request | Description
37+
------------ | ------------- | ------------- | -------------
38+
*BirdApi* | [**getBird**](docs/BirdApi.md#getbird) | **GET** /v1/bird/{id} |
39+
40+
41+
<a name="documentation-for-models"></a>
42+
## Documentation for Models
43+
44+
- [org.openapitools.client.models.Animal](docs/Animal.md)
45+
- [org.openapitools.client.models.Bird](docs/Bird.md)
46+
- [org.openapitools.client.models.BirdAllOf](docs/BirdAllOf.md)
47+
48+
49+
<a name="documentation-for-authorization"></a>
50+
## Documentation for Authorization
51+
52+
All endpoints do not require authorization.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
group 'org.openapitools'
2+
version '1.0.0'
3+
4+
wrapper {
5+
gradleVersion = '6.8.3'
6+
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
7+
}
8+
9+
buildscript {
10+
ext.kotlin_version = '1.5.10'
11+
12+
repositories {
13+
maven { url "https://repo1.maven.org/maven2" }
14+
}
15+
dependencies {
16+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
17+
}
18+
}
19+
20+
apply plugin: 'kotlin'
21+
22+
repositories {
23+
maven { url "https://repo1.maven.org/maven2" }
24+
}
25+
26+
test {
27+
useJUnitPlatform()
28+
}
29+
30+
dependencies {
31+
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
32+
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
33+
implementation "com.squareup.moshi:moshi-kotlin:1.12.0"
34+
implementation "com.squareup.moshi:moshi-adapters:1.12.0"
35+
implementation "com.squareup.okhttp3:okhttp:4.9.1"
36+
testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2"
37+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
# Animal
3+
4+
## Properties
5+
Name | Type | Description | Notes
6+
------------ | ------------- | ------------- | -------------
7+
**id** | [**java.util.UUID**](java.util.UUID.md) | |
8+
9+
10+

0 commit comments

Comments
 (0)