diff --git a/springdoc-openapi-kotlin/src/main/java/org/springdoc/kotlin/SpringDocKotlinConfiguration.kt b/springdoc-openapi-kotlin/src/main/java/org/springdoc/kotlin/SpringDocKotlinConfiguration.kt index 7bb6db55b..fe9a6c43c 100644 --- a/springdoc-openapi-kotlin/src/main/java/org/springdoc/kotlin/SpringDocKotlinConfiguration.kt +++ b/springdoc-openapi-kotlin/src/main/java/org/springdoc/kotlin/SpringDocKotlinConfiguration.kt @@ -16,6 +16,8 @@ import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Lazy import org.springframework.core.MethodParameter import org.springframework.core.annotation.AnnotatedElementUtils +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.ValueConstants import kotlin.coroutines.Continuation import kotlin.reflect.KParameter import kotlin.reflect.jvm.kotlinFunction @@ -23,6 +25,7 @@ import kotlin.reflect.jvm.kotlinFunction /** * The type Spring doc kotlin configuration. * @author bnasslahsen + * @author tobiberger */ @Lazy(false) @Configuration(proxyBeanMethods = false) @@ -77,9 +80,16 @@ class SpringDocKotlinConfiguration(objectMapperProvider: ObjectMapperProvider) { AnnotatedElementUtils.forAnnotations(*methodParameter.parameterAnnotations), Parameter::class.java ) + val requestParam = AnnotatedElementUtils.findMergedAnnotation( + AnnotatedElementUtils.forAnnotations(*methodParameter.parameterAnnotations), + RequestParam::class.java + ) // Swagger @Parameter annotation takes precedence if (parameterDoc != null && parameterDoc.required) parameterModel.required = parameterDoc.required + // parameter is not required if a default value is provided in @RequestParam + else if (requestParam != null && requestParam.defaultValue != ValueConstants.DEFAULT_NONE) + parameterModel.required = false else parameterModel.required = kParameter.type.isMarkedNullable == false } diff --git a/springdoc-openapi-kotlin/src/test/kotlin/test/org/springdoc/api/app10/ExampleController.kt b/springdoc-openapi-kotlin/src/test/kotlin/test/org/springdoc/api/app10/ExampleController.kt new file mode 100644 index 000000000..e88e0a6c1 --- /dev/null +++ b/springdoc-openapi-kotlin/src/test/kotlin/test/org/springdoc/api/app10/ExampleController.kt @@ -0,0 +1,20 @@ +package test.org.springdoc.api.app10 + +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController +import test.org.springdoc.api.app8.Greeting + +data class Greeting(val greeting: String) + +@RestController +class ExampleController { + @GetMapping("/") + fun greet(@RequestParam name: String?) = Greeting("Hello ${name ?: "world"}") + + @GetMapping("/test") + fun test(@RequestParam name: String) = Greeting("Hello $name") + + @GetMapping("/test-with-default") + fun testWithDefault(@RequestParam(defaultValue = "world") name: String) = Greeting("Hello $name") +} \ No newline at end of file diff --git a/springdoc-openapi-kotlin/src/test/kotlin/test/org/springdoc/api/app10/SpringDocApp10Test.kt b/springdoc-openapi-kotlin/src/test/kotlin/test/org/springdoc/api/app10/SpringDocApp10Test.kt new file mode 100644 index 000000000..50d51183b --- /dev/null +++ b/springdoc-openapi-kotlin/src/test/kotlin/test/org/springdoc/api/app10/SpringDocApp10Test.kt @@ -0,0 +1,31 @@ +/* + * + * * Copyright 2019-2023 the original author or authors. + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * https://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package test.org.springdoc.api.app10 + +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.context.annotation.ComponentScan +import test.org.springdoc.api.AbstractKotlinSpringDocTest + +class SpringDocApp10Test : AbstractKotlinSpringDocTest() { + + @SpringBootApplication + @ComponentScan(basePackages = ["org.springdoc", "test.org.springdoc.api.app10"]) + open class DemoApplication + +} \ No newline at end of file diff --git a/springdoc-openapi-kotlin/src/test/resources/results/app10.json b/springdoc-openapi-kotlin/src/test/resources/results/app10.json new file mode 100644 index 000000000..502c1c68d --- /dev/null +++ b/springdoc-openapi-kotlin/src/test/resources/results/app10.json @@ -0,0 +1,121 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "", + "description": "Generated server url" + } + ], + "paths": { + "/": { + "get": { + "tags": [ + "example-controller" + ], + "operationId": "greet", + "parameters": [ + { + "name": "name", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/Greeting" + } + } + } + } + } + } + }, + "/test": { + "get": { + "tags": [ + "example-controller" + ], + "operationId": "test", + "parameters": [ + { + "name": "name", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/Greeting" + } + } + } + } + } + } + }, + "/test-with-default": { + "get": { + "tags": [ + "example-controller" + ], + "operationId": "testWithDefault", + "parameters": [ + { + "name": "name", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "world" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/Greeting" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Greeting": { + "required": [ + "greeting" + ], + "type": "object", + "properties": { + "greeting": { + "type": "string" + } + } + } + } + } +}