Skip to content

Commit b7edad5

Browse files
irishshaguadr4ke616
authored andcommitted
Issue 1025: Kotlin generator doesn't support inheritance (#1026)
* allVars is duplicating child preoperties when models are inherited. Filter out these duplicates in the KotlinSpringServerCodegen * isInherited property was not being populated in the CodegenModel, re-parse the models in the KotlinSpringServerCodegen class and populate the property here * Add template support for Kotlin models which require inheritance from a base class to support oneOf declarations in the api yaml * Change optional for parameters to use Kotlins nullable * Update petstore api with Optional -> Nullable
1 parent 78fae0e commit b7edad5

File tree

14 files changed

+60
-21
lines changed

14 files changed

+60
-21
lines changed

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.samskivert.mustache.Mustache;
2121
import com.samskivert.mustache.Template;
2222
import io.swagger.v3.oas.models.OpenAPI;
23+
import io.swagger.v3.oas.models.media.Schema;
2324
import org.openapitools.codegen.*;
2425
import org.openapitools.codegen.languages.features.BeanValidationFeatures;
2526
import org.openapitools.codegen.utils.URLPathUtils;
@@ -32,6 +33,7 @@
3233
import java.net.URL;
3334
import java.util.*;
3435
import java.util.regex.Matcher;
36+
import java.util.stream.Collectors;
3537

3638

3739
public class KotlinSpringServerCodegen extends AbstractKotlinCodegen
@@ -386,14 +388,16 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert
386388
if (Boolean.TRUE.equals(model.hasEnums)) {
387389
model.imports.add("JsonValue");
388390
}
389-
390391
} else {
391392
//Needed imports for Jackson's JsonCreator
392393
if (additionalProperties.containsKey("jackson")) {
393394
model.imports.add("JsonCreator");
394395
}
395396
}
396397

398+
if (model.discriminator != null && additionalProperties.containsKey("jackson")) {
399+
model.imports.addAll(Arrays.asList("JsonSubTypes", "JsonTypeInfo"));
400+
}
397401
}
398402

399403
@Override
@@ -513,4 +517,16 @@ public void execute(Template.Fragment fragment, Writer writer) throws IOExceptio
513517
writer.write(fragment.execute().replaceAll(from, to));
514518
}
515519
}
520+
521+
// Can't figure out the logic in DefaultCodegen but optional vars are getting duplicated when there's
522+
// inheritance involved. Also, isInherited doesn't seem to be getting set properly ¯\_(ツ)_/¯
523+
@Override
524+
public CodegenModel fromModel(String name, Schema schema, Map<String, Schema> allDefinitions) {
525+
CodegenModel m = super.fromModel(name, schema, allDefinitions);
526+
527+
m.optionalVars = m.optionalVars.stream().distinct().collect(Collectors.toList());
528+
m.allVars.stream().filter(p -> !m.vars.contains(p)).forEach(p -> p.isInherited = true);
529+
530+
return m;
531+
}
516532
}

modules/openapi-generator/src/main/resources/kotlin-spring/dataClass.mustache

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,23 @@
33
{{#vars}}
44
* @param {{name}} {{{description}}}
55
{{/vars}}
6-
*/
7-
data class {{classname}} (
6+
*/{{#discriminator}}
7+
{{>typeInfoAnnotation}}{{/discriminator}}
8+
{{#discriminator}}interface {{classname}}{{/discriminator}}{{^discriminator}}data class {{classname}} (
89
{{#requiredVars}}
910
{{>dataClassReqVar}}{{^-last}},
1011
{{/-last}}{{/requiredVars}}{{#hasRequired}}{{#hasOptional}},
1112
{{/hasOptional}}{{/hasRequired}}{{#optionalVars}}{{>dataClassOptVar}}{{^-last}},
1213
{{/-last}}{{/optionalVars}}
13-
) {
14+
) {{/discriminator}}{{#parent}}: {{{parent}}}{{/parent}}{
15+
{{#discriminator}}
16+
{{#requiredVars}}
17+
{{>interfaceReqVar}}
18+
{{/requiredVars}}
19+
{{#optionalVars}}
20+
{{>interfaceOptVar}}
21+
{{/optionalVars}}
22+
{{/discriminator}}
1423
{{#hasEnums}}{{#vars}}{{#isEnum}}
1524
/**
1625
* {{{description}}}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{{#useBeanValidation}}{{#required}}
22
@get:NotNull {{/required}}{{>beanValidationModel}}{{/useBeanValidation}}{{#swaggerAnnotations}}
33
@ApiModelProperty({{#example}}example = "{{{example}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}"){{/swaggerAnnotations}}
4-
@JsonProperty("{{{baseName}}}") val {{{name}}}: {{#isEnum}}{{classname}}.{{nameInCamelCase}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}? = {{#defaultvalue}}{{defaultvalue}}{{/defaultvalue}}{{^defaultvalue}}null{{/defaultvalue}}
4+
@JsonProperty("{{{baseName}}}"){{#isInherited}} override{{/isInherited}} val {{{name}}}: {{#isEnum}}{{classname}}.{{nameInCamelCase}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}? = {{#defaultvalue}}{{defaultvalue}}{{/defaultvalue}}{{^defaultvalue}}null{{/defaultvalue}}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{{#useBeanValidation}}{{#required}}
22
@get:NotNull {{/required}}{{>beanValidationModel}}{{/useBeanValidation}}{{#swaggerAnnotations}}
33
@ApiModelProperty({{#example}}example = "{{{example}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}"){{/swaggerAnnotations}}
4-
@JsonProperty("{{{baseName}}}") val {{{name}}}: {{#isEnum}}{{classname}}.{{nameInCamelCase}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}
4+
@JsonProperty("{{{baseName}}}"){{#isInherited}} override{{/isInherited}} val {{{name}}}: {{#isEnum}}{{classname}}.{{nameInCamelCase}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{{#swaggerAnnotations}}
2+
@ApiModelProperty({{#example}}example = "{{{example}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}"){{/swaggerAnnotations}}
3+
val {{{name}}}: {{#isEnum}}{{classname}}.{{nameInCamelCase}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}? {{^discriminator}}= {{#defaultvalue}}{{defaultvalue}}{{/defaultvalue}}{{^defaultvalue}}null{{/defaultvalue}}{{/discriminator}}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{{#swaggerAnnotations}}
2+
@ApiModelProperty({{#example}}example = "{{{example}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}"){{/swaggerAnnotations}}
3+
val {{{name}}}: {{#isEnum}}{{classname}}.{{nameInCamelCase}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{{#useOptional}}{{#required}}{{{dataType}}}{{/required}}{{^required}}Optional<{{{dataType}}}>{{/required}}{{/useOptional}}{{^useOptional}}{{{dataType}}}{{/useOptional}}
1+
{{#required}}{{{dataType}}}{{/required}}{{^required}}{{{dataType}}}?{{/required}}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{{#jackson}}
2+
3+
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "{{{discriminatorName}}}", visible = true)
4+
@JsonSubTypes(
5+
{{#discriminator.mappedModels}}
6+
JsonSubTypes.Type(value = {{modelName}}::class, name = "{{^vendorExtensions.x-discriminator-value}}{{mappingName}}{{/vendorExtensions.x-discriminator-value}}{{#vendorExtensions.x-discriminator-value}}{{{vendorExtensions.x-discriminator-value}}}{{/vendorExtensions.x-discriminator-value}}"){{^-last}},{{/-last}}
7+
{{/discriminator.mappedModels}}
8+
){{/jackson}}

samples/server/openapi3/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApi.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class PetApiController(@Autowired(required = true) val service: PetApiService) {
5656
@RequestMapping(
5757
value = ["/pet/{petId}"],
5858
method = [RequestMethod.DELETE])
59-
fun deletePet(@ApiParam(value = "Pet id to delete", required=true) @PathVariable("petId") petId: kotlin.Long,@ApiParam(value = "" ) @RequestHeader(value="api_key", required=false) apiKey: kotlin.String): ResponseEntity<Unit> {
59+
fun deletePet(@ApiParam(value = "Pet id to delete", required=true) @PathVariable("petId") petId: kotlin.Long,@ApiParam(value = "" ) @RequestHeader(value="api_key", required=false) apiKey: kotlin.String?): ResponseEntity<Unit> {
6060
return ResponseEntity(service.deletePet(petId, apiKey), HttpStatus.OK)
6161
}
6262

samples/server/openapi3/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApiService.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ interface PetApiService {
77

88
fun addPet(pet: Pet): Unit
99

10-
fun deletePet(petId: kotlin.Long,apiKey: kotlin.String): Unit
10+
fun deletePet(petId: kotlin.Long,apiKey: kotlin.String?): Unit
1111

1212
fun findPetsByStatus(status: kotlin.Array<kotlin.String>): List<Pet>
1313

@@ -17,7 +17,7 @@ interface PetApiService {
1717

1818
fun updatePet(pet: Pet): Unit
1919

20-
fun updatePetWithForm(petId: kotlin.Long,name: kotlin.String,status: kotlin.String): Unit
20+
fun updatePetWithForm(petId: kotlin.Long,name: kotlin.String?,status: kotlin.String?): Unit
2121

22-
fun uploadFile(petId: kotlin.Long,additionalMetadata: kotlin.String,file: org.springframework.web.multipart.MultipartFile): ModelApiResponse
22+
fun uploadFile(petId: kotlin.Long,additionalMetadata: kotlin.String?,file: org.springframework.web.multipart.MultipartFile): ModelApiResponse
2323
}

samples/server/openapi3/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApiServiceImpl.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class PetApiServiceImpl : PetApiService {
1111
TODO("Implement me")
1212
}
1313

14-
override fun deletePet(petId: kotlin.Long,apiKey: kotlin.String): Unit {
14+
override fun deletePet(petId: kotlin.Long,apiKey: kotlin.String?): Unit {
1515
TODO("Implement me")
1616
}
1717

@@ -31,11 +31,11 @@ class PetApiServiceImpl : PetApiService {
3131
TODO("Implement me")
3232
}
3333

34-
override fun updatePetWithForm(petId: kotlin.Long,name: kotlin.String,status: kotlin.String): Unit {
34+
override fun updatePetWithForm(petId: kotlin.Long,name: kotlin.String?,status: kotlin.String?): Unit {
3535
TODO("Implement me")
3636
}
3737

38-
override fun uploadFile(petId: kotlin.Long,additionalMetadata: kotlin.String,file: org.springframework.web.multipart.MultipartFile): ModelApiResponse {
38+
override fun uploadFile(petId: kotlin.Long,additionalMetadata: kotlin.String?,file: org.springframework.web.multipart.MultipartFile): ModelApiResponse {
3939
TODO("Implement me")
4040
}
4141
}

samples/server/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApi.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class PetApiController(@Autowired(required = true) val service: PetApiService) {
5656
@RequestMapping(
5757
value = ["/pet/{petId}"],
5858
method = [RequestMethod.DELETE])
59-
fun deletePet(@ApiParam(value = "Pet id to delete", required=true) @PathVariable("petId") petId: kotlin.Long,@ApiParam(value = "" ) @RequestHeader(value="api_key", required=false) apiKey: kotlin.String): ResponseEntity<Unit> {
59+
fun deletePet(@ApiParam(value = "Pet id to delete", required=true) @PathVariable("petId") petId: kotlin.Long,@ApiParam(value = "" ) @RequestHeader(value="api_key", required=false) apiKey: kotlin.String?): ResponseEntity<Unit> {
6060
return ResponseEntity(service.deletePet(petId, apiKey), HttpStatus.OK)
6161
}
6262

samples/server/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApiService.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ interface PetApiService {
77

88
fun addPet(pet: Pet): Unit
99

10-
fun deletePet(petId: kotlin.Long,apiKey: kotlin.String): Unit
10+
fun deletePet(petId: kotlin.Long,apiKey: kotlin.String?): Unit
1111

1212
fun findPetsByStatus(status: kotlin.Array<kotlin.String>): List<Pet>
1313

@@ -17,7 +17,7 @@ interface PetApiService {
1717

1818
fun updatePet(pet: Pet): Unit
1919

20-
fun updatePetWithForm(petId: kotlin.Long,name: kotlin.String,status: kotlin.String): Unit
20+
fun updatePetWithForm(petId: kotlin.Long,name: kotlin.String?,status: kotlin.String?): Unit
2121

22-
fun uploadFile(petId: kotlin.Long,additionalMetadata: kotlin.String,file: org.springframework.web.multipart.MultipartFile): ModelApiResponse
22+
fun uploadFile(petId: kotlin.Long,additionalMetadata: kotlin.String?,file: org.springframework.web.multipart.MultipartFile): ModelApiResponse
2323
}

samples/server/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApiServiceImpl.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class PetApiServiceImpl : PetApiService {
1111
TODO("Implement me")
1212
}
1313

14-
override fun deletePet(petId: kotlin.Long,apiKey: kotlin.String): Unit {
14+
override fun deletePet(petId: kotlin.Long,apiKey: kotlin.String?): Unit {
1515
TODO("Implement me")
1616
}
1717

@@ -31,11 +31,11 @@ class PetApiServiceImpl : PetApiService {
3131
TODO("Implement me")
3232
}
3333

34-
override fun updatePetWithForm(petId: kotlin.Long,name: kotlin.String,status: kotlin.String): Unit {
34+
override fun updatePetWithForm(petId: kotlin.Long,name: kotlin.String?,status: kotlin.String?): Unit {
3535
TODO("Implement me")
3636
}
3737

38-
override fun uploadFile(petId: kotlin.Long,additionalMetadata: kotlin.String,file: org.springframework.web.multipart.MultipartFile): ModelApiResponse {
38+
override fun uploadFile(petId: kotlin.Long,additionalMetadata: kotlin.String?,file: org.springframework.web.multipart.MultipartFile): ModelApiResponse {
3939
TODO("Implement me")
4040
}
4141
}

0 commit comments

Comments
 (0)