Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/samples-kotlin-server-jdk17.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
# server
- samples/server/petstore/kotlin-server-required-and-nullable-properties
- samples/server/petstore/kotlin-springboot-3
- samples/server/petstore/kotlin-springboot-3-no-response-entity
- samples/server/petstore/kotlin-springboot-additionalproperties
- samples/server/petstore/kotlin-springboot-delegate-nodefaults
- samples/server/petstore/kotlin-springboot-request-cookie
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/samples-kotlin-server.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ jobs:
sample:
# server
- samples/server/petstore/kotlin-springboot
- samples/server/petstore/kotlin-springboot-no-response-entity
- samples/server/petstore/kotlin-springboot-no-response-entity-delegate
- samples/server/petstore/kotlin-springboot-multipart-request-model
- samples/server/petstore/kotlin-springboot-bigdecimal-default
- samples/server/petstore/kotlin-springboot-delegate
Expand Down
14 changes: 14 additions & 0 deletions bin/configs/kotlin-spring-boot-3-no-response-entity.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
generatorName: kotlin-spring
outputDir: samples/server/petstore/kotlin-springboot-3-no-response-entity
library: spring-boot
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/kotlin-spring
additionalProperties:
documentationProvider: none
annotationLibrary: none
useSwaggerUI: "false"
serviceImplementation: "true"
serializableModel: "true"
beanValidations: "true"
useSpringBoot3: "true"
requestMappingMode: api_interface
15 changes: 15 additions & 0 deletions bin/configs/kotlin-spring-boot-no-response-entity-delegate.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
generatorName: kotlin-spring
outputDir: samples/server/petstore/kotlin-springboot-no-response-entity-delegate
library: spring-boot
inputSpec: modules/openapi-generator/src/test/resources/2_0/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/kotlin-spring
additionalProperties:
documentationProvider: none
annotationLibrary: none
useSwaggerUI: "false"
serviceImplementation: "true"
serializableModel: "true"
beanValidations: "true"
useResponseEntity: "false"
delegatePattern: true
requestMappingMode: controller
14 changes: 14 additions & 0 deletions bin/configs/kotlin-spring-boot-no-response-entity.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
generatorName: kotlin-spring
outputDir: samples/server/petstore/kotlin-springboot-no-response-entity
library: spring-boot
inputSpec: modules/openapi-generator/src/test/resources/2_0/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/kotlin-spring
additionalProperties:
documentationProvider: none
annotationLibrary: none
useSwaggerUI: "false"
serviceImplementation: "true"
serializableModel: "true"
beanValidations: "true"
useResponseEntity: "false"
requestMappingMode: controller
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ additionalProperties:
beanValidations: "true"
interfaceOnly: true
reactive: true
declarativeInterfaceWrapResponses: false
useResponseEntity: false
useFlowForArrayReturnType: false
declarativeInterfaceReactiveMode: "coroutines"
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ additionalProperties:
beanValidations: "true"
interfaceOnly: true
reactive: true
declarativeInterfaceWrapResponses: true
useResponseEntity: true
useFlowForArrayReturnType: false
declarativeInterfaceReactiveMode: "reactor"
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ additionalProperties:
beanValidations: "true"
interfaceOnly: true
reactive: false
declarativeInterfaceWrapResponses: true
useResponseEntity: true
useFlowForArrayReturnType: false
2 changes: 1 addition & 1 deletion bin/configs/kotlin-spring-declarative-interface.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ additionalProperties:
beanValidations: "true"
interfaceOnly: true
reactive: false
declarativeInterfaceWrapResponses: true
useResponseEntity: true
useFlowForArrayReturnType: false
2 changes: 1 addition & 1 deletion docs/generators/kotlin-spring.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|beanQualifiers|Whether to add fully-qualifier class names as bean qualifiers in @Component and @RestController annotations. May be used to prevent bean names clash if multiple generated libraries (contexts) added to single project.| |false|
|configPackage|configuration package for generated code| |org.openapitools.configuration|
|declarativeInterfaceReactiveMode|What type of reactive style to use in Spring Http declarative interface|<dl><dt>**coroutines**</dt><dd>Use kotlin-idiomatic 'suspend' functions</dd><dt>**reactor**</dt><dd>Use reactor return wrappers 'Mono' and 'Flux'</dd></dl>|coroutines|
|declarativeInterfaceWrapResponses|Whether (when false) to return actual type (e.g. List&lt;Fruit&gt;) and handle non 2xx responses via exceptions or (when true) return entire ResponseEntity (e.g. ResponseEntity&lt;List&lt;Fruit&gt;&gt;)| |false|
|delegatePattern|Whether to generate the server files using the delegate pattern| |false|
|documentationProvider|Select the OpenAPI documentation provider.|<dl><dt>**none**</dt><dd>Do not publish an OpenAPI specification.</dd><dt>**source**</dt><dd>Publish the original input OpenAPI specification.</dd><dt>**springfox**</dt><dd>Generate an OpenAPI 2 (fka Swagger RESTful API Documentation Specification) specification using SpringFox 2.x. Deprecated (for removal); use springdoc instead.</dd><dt>**springdoc**</dt><dd>Generate an OpenAPI 3 specification using SpringDoc.</dd></dl>|springdoc|
|enumPropertyNaming|Naming convention for enum properties: 'camelCase', 'PascalCase', 'snake_case', 'UPPERCASE', and 'original'| |original|
Expand Down Expand Up @@ -56,6 +55,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|useBeanValidation|Use BeanValidation API annotations to validate data types| |true|
|useFeignClientUrl|Whether to generate Feign client with url parameter.| |true|
|useFlowForArrayReturnType|Whether to use Flow for array/collection return types when reactive is enabled. If false, will use List instead.| |true|
|useResponseEntity|Whether (when false) to return actual type (e.g. List&lt;Fruit&gt;) and handle non-happy path responses via exceptions flow or (when true) return entire ResponseEntity (e.g. ResponseEntity&lt;List&lt;Fruit&gt;&gt;). If disabled, method are annotated using a @ResponseStatus annotation, which has the status of the first response declared in the Api definition| |true|
|useSpringBoot3|Generate code and provide dependencies for use with Spring Boot 3.x. (Use jakarta instead of javax in imports). Enabling this option will also enable `useJakartaEe`.| |false|
|useSwaggerUI|Open the OpenApi specification in swagger-ui. Will also import and configure needed dependencies| |true|
|useTags|Whether to use tags for creating interface and controller class names| |false|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.openapitools.codegen.model.ModelsMap;
import org.openapitools.codegen.model.OperationMap;
import org.openapitools.codegen.model.OperationsMap;
import org.openapitools.codegen.templating.mustache.SpringHttpStatusLambda;
import org.openapitools.codegen.utils.ModelUtils;
import org.openapitools.codegen.utils.URLPathUtils;
import org.slf4j.Logger;
Expand Down Expand Up @@ -85,7 +86,7 @@ public class KotlinSpringServerCodegen extends AbstractKotlinCodegen
public static final String DELEGATE_PATTERN = "delegatePattern";
public static final String USE_TAGS = "useTags";
public static final String BEAN_QUALIFIERS = "beanQualifiers";
public static final String DECLARATIVE_INTERFACE_WRAP_RESPONSES = "declarativeInterfaceWrapResponses";
public static final String USE_RESPONSE_ENTITY = "useResponseEntity";
public static final String DECLARATIVE_INTERFACE_REACTIVE_MODE = "declarativeInterfaceReactiveMode";

public static final String USE_SPRING_BOOT3 = "useSpringBoot3";
Expand All @@ -109,6 +110,7 @@ public enum DeclarativeInterfaceReactiveMode {
}
}


public enum RequestMappingMode {
api_interface("Generate the @RequestMapping annotation on the generated Api Interface."),
controller("Generate the @RequestMapping annotation on the generated Api Controller Implementation."),
Expand Down Expand Up @@ -155,7 +157,7 @@ public String getDescription() {
@Setter protected boolean useTags = false;
@Setter private boolean beanQualifiers = false;
@Setter private DeclarativeInterfaceReactiveMode declarativeInterfaceReactiveMode = DeclarativeInterfaceReactiveMode.coroutines;
@Setter private boolean declarativeInterfaceWrapResponses = false;
@Setter private boolean useResponseEntity = true;

@Getter @Setter
protected boolean useSpringBoot3 = false;
Expand Down Expand Up @@ -242,9 +244,9 @@ public KotlinSpringServerCodegen() {
addSwitch(USE_SPRING_BOOT3, "Generate code and provide dependencies for use with Spring Boot 3.x. (Use jakarta instead of javax in imports). Enabling this option will also enable `useJakartaEe`.", useSpringBoot3);
addSwitch(USE_FLOW_FOR_ARRAY_RETURN_TYPE, "Whether to use Flow for array/collection return types when reactive is enabled. If false, will use List instead.", useFlowForArrayReturnType);
addSwitch(INCLUDE_HTTP_REQUEST_CONTEXT, "Whether to include HttpServletRequest (blocking) or ServerWebExchange (reactive) as additional parameter in generated methods.", includeHttpRequestContext);
addSwitch(DECLARATIVE_INTERFACE_WRAP_RESPONSES,
"Whether (when false) to return actual type (e.g. List<Fruit>) and handle non 2xx responses via exceptions or (when true) return entire ResponseEntity (e.g. ResponseEntity<List<Fruit>>)",
declarativeInterfaceWrapResponses);
addSwitch(USE_RESPONSE_ENTITY,
"Whether (when false) to return actual type (e.g. List<Fruit>) and handle non-happy path responses via exceptions flow or (when true) return entire ResponseEntity (e.g. ResponseEntity<List<Fruit>>). If disabled, method are annotated using a @ResponseStatus annotation, which has the status of the first response declared in the Api definition",
useResponseEntity);
supportedLibraries.put(SPRING_BOOT, "Spring-boot Server application.");
supportedLibraries.put(SPRING_CLOUD_LIBRARY,
"Spring-Cloud-Feign client with Spring-Boot auto-configured settings.");
Expand Down Expand Up @@ -468,6 +470,12 @@ public void processOpts() {
additionalProperties.put(CodegenConstants.LIBRARY, library);
}

if(additionalProperties.containsKey(USE_RESPONSE_ENTITY)) {
this.setUseResponseEntity(Boolean.parseBoolean(additionalProperties.get(USE_RESPONSE_ENTITY).toString()));
}
writePropertyBack(USE_RESPONSE_ENTITY, useResponseEntity);
additionalProperties.put("springHttpStatus", new SpringHttpStatusLambda());

// Set basePackage from invokerPackage
if (!additionalProperties.containsKey(BASE_PACKAGE)
&& additionalProperties.containsKey(CodegenConstants.INVOKER_PACKAGE)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,19 @@ import io.swagger.annotations.AuthorizationScope
{{/swagger1AnnotationLibrary}}
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
{{#useResponseEntity}}
import org.springframework.http.ResponseEntity
{{/useResponseEntity}}

import org.springframework.web.bind.annotation.*
{{#useBeanValidation}}
import org.springframework.validation.annotation.Validated
{{/useBeanValidation}}
import org.springframework.web.context.request.NativeWebRequest
import org.springframework.beans.factory.annotation.Autowired
{{#useRequestMappingOnController}}
import {{#apiPackage}}{{.}}.{{/apiPackage}}{{classname}}Controller.Companion.BASE_PATH
{{/useRequestMappingOnController}}

{{#useBeanValidation}}
import {{javaxPackage}}.validation.Valid
Expand Down Expand Up @@ -56,15 +61,18 @@ import kotlin.collections.Map
{{/swagger1AnnotationLibrary}}
{{#useRequestMappingOnController}}
{{=<% %>=}}
@RequestMapping("\${api.base-path:<%contextPath%>}")
@RequestMapping("\${api.<%title%>.base-path:api.base-path:$BASE_PATH}")
<%={{ }}=%>
{{/useRequestMappingOnController}}
{{#operations}}
class {{classname}}Controller({{#serviceInterface}}@Autowired(required = true) val service: {{classname}}Service{{/serviceInterface}}) {
{{#operation}}

{{#swagger2AnnotationLibrary}}
@Operation(
{{#operation}}
{{^useResponseEntity}}
@ResponseStatus({{#springHttpStatus}}{{#responses.0}}{{{code}}}{{/responses.0}}{{/springHttpStatus}}){{!
}}{{/useResponseEntity}}{{!
}}{{#swagger2AnnotationLibrary}}{{!
}} @Operation(
summary = "{{{summary}}}",
operationId = "{{{operationId}}}",
description = """{{{unescapedNotes}}}""",
Expand All @@ -83,15 +91,28 @@ class {{classname}}Controller({{#serviceInterface}}@Autowired(required = true) v
value = [{{#responses}}ApiResponse(code = {{{code}}}, message = "{{{message}}}"{{#baseType}}, response = {{{.}}}::class{{/baseType}}{{#containerType}}, responseContainer = "{{{.}}}"{{/containerType}}){{^-last}},{{/-last}}{{/responses}}]){{/swagger1AnnotationLibrary}}
@RequestMapping(
method = [RequestMethod.{{httpMethod}}],
value = ["{{#lambdaEscapeInNormalString}}{{{path}}}{{/lambdaEscapeInNormalString}}"]{{#singleContentTypes}}{{#hasProduces}},
value = [PATH_{{#lambda.uppercase}}{{#lambda.snakecase}}{{{operationId}}}{{/lambda.snakecase}}{{/lambda.uppercase}} /* "{{#lambdaEscapeInNormalString}}{{{path}}}{{/lambdaEscapeInNormalString}}" */]{{#singleContentTypes}}{{#hasProduces}},
produces = [{{#vendorExtensions.x-accepts}}"{{{.}}}"{{^-last}}, {{/-last}}{{/vendorExtensions.x-accepts}}]{{/hasProduces}}{{#hasConsumes}},
consumes = "{{{vendorExtensions.x-content-type}}}"{{/hasConsumes}}{{/singleContentTypes}}{{^singleContentTypes}}{{#hasProduces}},
produces = [{{#produces}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/produces}}]{{/hasProduces}}{{#hasConsumes}},
consumes = [{{#consumes}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/consumes}}]{{/hasConsumes}}{{/singleContentTypes}}
)
{{#reactive}}{{^isArray}}suspend {{/isArray}}{{#isArray}}{{^useFlowForArrayReturnType}}suspend {{/useFlowForArrayReturnType}}{{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>cookieParams}}{{>bodyParams}}{{>formParams}}{{^-last}},{{/-last}}{{/allParams}}{{#includeHttpRequestContext}}{{#hasParams}}, {{/hasParams}}{{#swagger1AnnotationLibrary}}@ApiParam(hidden = true) {{/swagger1AnnotationLibrary}}{{#swagger2AnnotationLibrary}}@Parameter(hidden = true) {{/swagger2AnnotationLibrary}}{{#reactive}}exchange: org.springframework.web.server.ServerWebExchange{{/reactive}}{{^reactive}}request: {{javaxPackage}}.servlet.http.HttpServletRequest{{/reactive}}{{/includeHttpRequestContext}}): ResponseEntity<{{>returnTypes}}> {
{{#reactive}}{{^isArray}}suspend {{/isArray}}{{#isArray}}{{^useFlowForArrayReturnType}}suspend {{/useFlowForArrayReturnType}}{{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}
{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>cookieParams}}{{>bodyParams}}{{>formParams}}{{^-last}},{{/-last}}{{/allParams}}{{#includeHttpRequestContext}}{{#hasParams}},
{{/hasParams}}{{#swagger1AnnotationLibrary}}@ApiParam(hidden = true) {{/swagger1AnnotationLibrary}}{{#swagger2AnnotationLibrary}}@Parameter(hidden = true) {{/swagger2AnnotationLibrary}}{{#reactive}}exchange: org.springframework.web.server.ServerWebExchange{{/reactive}}{{^reactive}}request: {{javaxPackage}}.servlet.http.HttpServletRequest{{/reactive}}{{/includeHttpRequestContext}}{{#hasParams}}
{{/hasParams}}): {{#useResponseEntity}}ResponseEntity<{{/useResponseEntity}}{{>returnTypes}}{{#useResponseEntity}}>{{/useResponseEntity}} {
return {{>returnValue}}
}

{{/operation}}
companion object {
//for your own safety never directly reuse these path definitions in tests
{{#useRequestMappingOnController}}
const val BASE_PATH: String = "{{=<% %>=}}<%contextPath%><%={{ }}=%>"
{{/useRequestMappingOnController}}
{{#operation}}
const val PATH_{{#lambda.uppercase}}{{#lambda.snakecase}}{{{operationId}}}{{/lambda.snakecase}}{{/lambda.uppercase}}: String = "{{{path}}}"
{{/operation}}
}
}
{{/operations}}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ package {{package}}
{{/imports}}
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
{{#useResponseEntity}}
import org.springframework.http.ResponseEntity
{{/useResponseEntity}}
import org.springframework.web.context.request.NativeWebRequest
{{#appendRequestToHandler}}
import org.springframework.http.server.reactive.ServerHttpRequest
Expand All @@ -14,9 +16,6 @@ import kotlinx.coroutines.flow.Flow
{{/reactive}}

import java.util.Optional
{{#async}}
import java.util.concurrent.CompletableFuture
{{/async}}

{{#operations}}
/**
Expand All @@ -35,7 +34,7 @@ interface {{classname}}Delegate {
*/
{{#reactive}}{{^isArray}}suspend {{/isArray}}{{#isArray}}{{^useFlowForArrayReturnType}}suspend {{/useFlowForArrayReturnType}}{{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}{{#isBodyParam}}Flow<{{{baseType}}}>{{/isBodyParam}}{{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{/isArray}}{{/reactive}}{{^-last}},
{{/-last}}{{/allParams}}{{#includeHttpRequestContext}}{{#hasParams}},
{{/hasParams}}{{#reactive}}exchange: org.springframework.web.server.ServerWebExchange{{/reactive}}{{^reactive}}request: {{javaxPackage}}.servlet.http.HttpServletRequest{{/reactive}}{{/includeHttpRequestContext}}): {{#responseWrapper}}{{.}}<{{/responseWrapper}}ResponseEntity<{{>returnTypes}}>{{#responseWrapper}}>{{/responseWrapper}}{{^skipDefaultDelegateInterface}} {
{{/hasParams}}{{#reactive}}exchange: org.springframework.web.server.ServerWebExchange{{/reactive}}{{^reactive}}request: {{javaxPackage}}.servlet.http.HttpServletRequest{{/reactive}}{{/includeHttpRequestContext}}): {{#useResponseEntity}}ResponseEntity<{{/useResponseEntity}}{{>returnTypes}}{{#useResponseEntity}}>{{/useResponseEntity}}{{^skipDefaultDelegateInterface}} {
{{>methodBody}}{{! prevent indent}}
}{{/skipDefaultDelegateInterface}}

Expand Down
Loading
Loading