diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java index 3693f398074..02fdb2f248b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java @@ -20,6 +20,7 @@ import com.google.common.collect.ImmutableMap; import com.samskivert.mustache.Mustache; import com.samskivert.mustache.Template; import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Schema; import org.openapitools.codegen.*; import org.openapitools.codegen.languages.features.BeanValidationFeatures; import org.openapitools.codegen.utils.URLPathUtils; @@ -32,6 +33,7 @@ import java.io.Writer; import java.net.URL; import java.util.*; import java.util.regex.Matcher; +import java.util.stream.Collectors; public class KotlinSpringServerCodegen extends AbstractKotlinCodegen @@ -386,7 +388,6 @@ public class KotlinSpringServerCodegen extends AbstractKotlinCodegen if (Boolean.TRUE.equals(model.hasEnums)) { model.imports.add("JsonValue"); } - } else { //Needed imports for Jackson's JsonCreator if (additionalProperties.containsKey("jackson")) { @@ -394,6 +395,9 @@ public class KotlinSpringServerCodegen extends AbstractKotlinCodegen } } + if (model.discriminator != null && additionalProperties.containsKey("jackson")) { + model.imports.addAll(Arrays.asList("JsonSubTypes", "JsonTypeInfo")); + } } @Override @@ -513,4 +517,16 @@ public class KotlinSpringServerCodegen extends AbstractKotlinCodegen writer.write(fragment.execute().replaceAll(from, to)); } } + + // Can't figure out the logic in DefaultCodegen but optional vars are getting duplicated when there's + // inheritance involved. Also, isInherited doesn't seem to be getting set properly ¯\_(ツ)_/¯ + @Override + public CodegenModel fromModel(String name, Schema schema, Map allDefinitions) { + CodegenModel m = super.fromModel(name, schema, allDefinitions); + + m.optionalVars = m.optionalVars.stream().distinct().collect(Collectors.toList()); + m.allVars.stream().filter(p -> !m.vars.contains(p)).forEach(p -> p.isInherited = true); + + return m; + } } diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/dataClass.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/dataClass.mustache index aead2978801..5a8079786c5 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/dataClass.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/dataClass.mustache @@ -3,14 +3,23 @@ {{#vars}} * @param {{name}} {{{description}}} {{/vars}} - */ -data class {{classname}} ( + */{{#discriminator}} +{{>typeInfoAnnotation}}{{/discriminator}} +{{#discriminator}}interface {{classname}}{{/discriminator}}{{^discriminator}}data class {{classname}} ( {{#requiredVars}} {{>dataClassReqVar}}{{^-last}}, {{/-last}}{{/requiredVars}}{{#hasRequired}}{{#hasOptional}}, {{/hasOptional}}{{/hasRequired}}{{#optionalVars}}{{>dataClassOptVar}}{{^-last}}, {{/-last}}{{/optionalVars}} -) { +) {{/discriminator}}{{#parent}}: {{{parent}}}{{/parent}}{ +{{#discriminator}} + {{#requiredVars}} + {{>interfaceReqVar}} + {{/requiredVars}} + {{#optionalVars}} + {{>interfaceOptVar}} + {{/optionalVars}} +{{/discriminator}} {{#hasEnums}}{{#vars}}{{#isEnum}} /** * {{{description}}} diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/dataClassOptVar.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/dataClassOptVar.mustache index 52b6ae1157d..c09e021fa49 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/dataClassOptVar.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/dataClassOptVar.mustache @@ -1,4 +1,4 @@ {{#useBeanValidation}}{{#required}} @get:NotNull {{/required}}{{>beanValidationModel}}{{/useBeanValidation}}{{#swaggerAnnotations}} @ApiModelProperty({{#example}}example = "{{{example}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}"){{/swaggerAnnotations}} - @JsonProperty("{{{baseName}}}") val {{{name}}}: {{#isEnum}}{{classname}}.{{nameInCamelCase}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}? = {{#defaultvalue}}{{defaultvalue}}{{/defaultvalue}}{{^defaultvalue}}null{{/defaultvalue}} \ No newline at end of file + @JsonProperty("{{{baseName}}}"){{#isInherited}} override{{/isInherited}} val {{{name}}}: {{#isEnum}}{{classname}}.{{nameInCamelCase}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}? = {{#defaultvalue}}{{defaultvalue}}{{/defaultvalue}}{{^defaultvalue}}null{{/defaultvalue}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/dataClassReqVar.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/dataClassReqVar.mustache index e09ea971bea..80d863d3d27 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/dataClassReqVar.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/dataClassReqVar.mustache @@ -1,4 +1,4 @@ {{#useBeanValidation}}{{#required}} @get:NotNull {{/required}}{{>beanValidationModel}}{{/useBeanValidation}}{{#swaggerAnnotations}} @ApiModelProperty({{#example}}example = "{{{example}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}"){{/swaggerAnnotations}} - @JsonProperty("{{{baseName}}}") val {{{name}}}: {{#isEnum}}{{classname}}.{{nameInCamelCase}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}} \ No newline at end of file + @JsonProperty("{{{baseName}}}"){{#isInherited}} override{{/isInherited}} val {{{name}}}: {{#isEnum}}{{classname}}.{{nameInCamelCase}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/interfaceOptVar.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/interfaceOptVar.mustache new file mode 100644 index 00000000000..8db4ba7698f --- /dev/null +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/interfaceOptVar.mustache @@ -0,0 +1,3 @@ +{{#swaggerAnnotations}} + @ApiModelProperty({{#example}}example = "{{{example}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}"){{/swaggerAnnotations}} + val {{{name}}}: {{#isEnum}}{{classname}}.{{nameInCamelCase}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}? {{^discriminator}}= {{#defaultvalue}}{{defaultvalue}}{{/defaultvalue}}{{^defaultvalue}}null{{/defaultvalue}}{{/discriminator}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/interfaceReqVar.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/interfaceReqVar.mustache new file mode 100644 index 00000000000..5f6eea31139 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/interfaceReqVar.mustache @@ -0,0 +1,3 @@ +{{#swaggerAnnotations}} + @ApiModelProperty({{#example}}example = "{{{example}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}"){{/swaggerAnnotations}} + val {{{name}}}: {{#isEnum}}{{classname}}.{{nameInCamelCase}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/optionalDataType.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/optionalDataType.mustache index 976950e27e8..de3cbd0d9cf 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/optionalDataType.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/optionalDataType.mustache @@ -1 +1 @@ -{{#useOptional}}{{#required}}{{{dataType}}}{{/required}}{{^required}}Optional<{{{dataType}}}>{{/required}}{{/useOptional}}{{^useOptional}}{{{dataType}}}{{/useOptional}} \ No newline at end of file +{{#required}}{{{dataType}}}{{/required}}{{^required}}{{{dataType}}}?{{/required}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/typeInfoAnnotation.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/typeInfoAnnotation.mustache new file mode 100644 index 00000000000..a110d9ea9cf --- /dev/null +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/typeInfoAnnotation.mustache @@ -0,0 +1,8 @@ +{{#jackson}} + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "{{{discriminatorName}}}", visible = true) +@JsonSubTypes( + {{#discriminator.mappedModels}} + 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}} + {{/discriminator.mappedModels}} +){{/jackson}} diff --git a/samples/server/openapi3/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApi.kt b/samples/server/openapi3/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApi.kt index d33c5efd3e9..696dc563e00 100644 --- a/samples/server/openapi3/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApi.kt +++ b/samples/server/openapi3/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApi.kt @@ -56,7 +56,7 @@ class PetApiController(@Autowired(required = true) val service: PetApiService) { @RequestMapping( value = ["/pet/{petId}"], method = [RequestMethod.DELETE]) - 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 { + 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 { return ResponseEntity(service.deletePet(petId, apiKey), HttpStatus.OK) } diff --git a/samples/server/openapi3/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApiService.kt b/samples/server/openapi3/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApiService.kt index e7bd7fae34e..ff856a8a069 100644 --- a/samples/server/openapi3/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApiService.kt +++ b/samples/server/openapi3/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApiService.kt @@ -7,7 +7,7 @@ interface PetApiService { fun addPet(pet: Pet): Unit - fun deletePet(petId: kotlin.Long,apiKey: kotlin.String): Unit + fun deletePet(petId: kotlin.Long,apiKey: kotlin.String?): Unit fun findPetsByStatus(status: kotlin.Array): List @@ -17,7 +17,7 @@ interface PetApiService { fun updatePet(pet: Pet): Unit - fun updatePetWithForm(petId: kotlin.Long,name: kotlin.String,status: kotlin.String): Unit + fun updatePetWithForm(petId: kotlin.Long,name: kotlin.String?,status: kotlin.String?): Unit - fun uploadFile(petId: kotlin.Long,additionalMetadata: kotlin.String,file: org.springframework.web.multipart.MultipartFile): ModelApiResponse + fun uploadFile(petId: kotlin.Long,additionalMetadata: kotlin.String?,file: org.springframework.web.multipart.MultipartFile): ModelApiResponse } diff --git a/samples/server/openapi3/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApiServiceImpl.kt b/samples/server/openapi3/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApiServiceImpl.kt index e898ab98921..fc3f8dcf9dc 100644 --- a/samples/server/openapi3/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApiServiceImpl.kt +++ b/samples/server/openapi3/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApiServiceImpl.kt @@ -11,7 +11,7 @@ class PetApiServiceImpl : PetApiService { TODO("Implement me") } - override fun deletePet(petId: kotlin.Long,apiKey: kotlin.String): Unit { + override fun deletePet(petId: kotlin.Long,apiKey: kotlin.String?): Unit { TODO("Implement me") } @@ -31,11 +31,11 @@ class PetApiServiceImpl : PetApiService { TODO("Implement me") } - override fun updatePetWithForm(petId: kotlin.Long,name: kotlin.String,status: kotlin.String): Unit { + override fun updatePetWithForm(petId: kotlin.Long,name: kotlin.String?,status: kotlin.String?): Unit { TODO("Implement me") } - override fun uploadFile(petId: kotlin.Long,additionalMetadata: kotlin.String,file: org.springframework.web.multipart.MultipartFile): ModelApiResponse { + override fun uploadFile(petId: kotlin.Long,additionalMetadata: kotlin.String?,file: org.springframework.web.multipart.MultipartFile): ModelApiResponse { TODO("Implement me") } } diff --git a/samples/server/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApi.kt b/samples/server/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApi.kt index d33c5efd3e9..696dc563e00 100644 --- a/samples/server/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApi.kt +++ b/samples/server/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApi.kt @@ -56,7 +56,7 @@ class PetApiController(@Autowired(required = true) val service: PetApiService) { @RequestMapping( value = ["/pet/{petId}"], method = [RequestMethod.DELETE]) - 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 { + 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 { return ResponseEntity(service.deletePet(petId, apiKey), HttpStatus.OK) } diff --git a/samples/server/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApiService.kt b/samples/server/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApiService.kt index e7bd7fae34e..ff856a8a069 100644 --- a/samples/server/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApiService.kt +++ b/samples/server/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApiService.kt @@ -7,7 +7,7 @@ interface PetApiService { fun addPet(pet: Pet): Unit - fun deletePet(petId: kotlin.Long,apiKey: kotlin.String): Unit + fun deletePet(petId: kotlin.Long,apiKey: kotlin.String?): Unit fun findPetsByStatus(status: kotlin.Array): List @@ -17,7 +17,7 @@ interface PetApiService { fun updatePet(pet: Pet): Unit - fun updatePetWithForm(petId: kotlin.Long,name: kotlin.String,status: kotlin.String): Unit + fun updatePetWithForm(petId: kotlin.Long,name: kotlin.String?,status: kotlin.String?): Unit - fun uploadFile(petId: kotlin.Long,additionalMetadata: kotlin.String,file: org.springframework.web.multipart.MultipartFile): ModelApiResponse + fun uploadFile(petId: kotlin.Long,additionalMetadata: kotlin.String?,file: org.springframework.web.multipart.MultipartFile): ModelApiResponse } diff --git a/samples/server/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApiServiceImpl.kt b/samples/server/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApiServiceImpl.kt index e898ab98921..fc3f8dcf9dc 100644 --- a/samples/server/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApiServiceImpl.kt +++ b/samples/server/petstore/kotlin-springboot/src/main/kotlin/org/openapitools/api/PetApiServiceImpl.kt @@ -11,7 +11,7 @@ class PetApiServiceImpl : PetApiService { TODO("Implement me") } - override fun deletePet(petId: kotlin.Long,apiKey: kotlin.String): Unit { + override fun deletePet(petId: kotlin.Long,apiKey: kotlin.String?): Unit { TODO("Implement me") } @@ -31,11 +31,11 @@ class PetApiServiceImpl : PetApiService { TODO("Implement me") } - override fun updatePetWithForm(petId: kotlin.Long,name: kotlin.String,status: kotlin.String): Unit { + override fun updatePetWithForm(petId: kotlin.Long,name: kotlin.String?,status: kotlin.String?): Unit { TODO("Implement me") } - override fun uploadFile(petId: kotlin.Long,additionalMetadata: kotlin.String,file: org.springframework.web.multipart.MultipartFile): ModelApiResponse { + override fun uploadFile(petId: kotlin.Long,additionalMetadata: kotlin.String?,file: org.springframework.web.multipart.MultipartFile): ModelApiResponse { TODO("Implement me") } }