Skip to content

Commit 1a23016

Browse files
committed
Parameter is no longer optional after upgrade to 2.8.8. Fixes #2978.
1 parent 347227a commit 1a23016

File tree

6 files changed

+150
-66
lines changed

6 files changed

+150
-66
lines changed

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocKotlinConfiguration.kt

Lines changed: 17 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,12 @@
2626

2727
package org.springdoc.core.configuration
2828

29-
import io.swagger.v3.oas.annotations.Parameter
3029
import org.springdoc.core.converters.KotlinInlineClassUnwrappingConverter
30+
import org.springdoc.core.customizers.DelegatingMethodParameterCustomizer
3131
import org.springdoc.core.customizers.KotlinDeprecatedPropertyCustomizer
32-
import org.springdoc.core.customizers.ParameterCustomizer
32+
import org.springdoc.core.extractor.DelegatingMethodParameter
3333
import org.springdoc.core.providers.ObjectMapperProvider
3434
import org.springdoc.core.utils.Constants
35-
import org.springdoc.core.utils.SchemaUtils
3635
import org.springdoc.core.utils.SpringDocUtils
3736
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
3837
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
@@ -43,14 +42,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
4342
import org.springframework.context.annotation.Bean
4443
import org.springframework.context.annotation.Configuration
4544
import org.springframework.context.annotation.Lazy
46-
import org.springframework.core.KotlinDetector
47-
import org.springframework.core.MethodParameter
48-
import org.springframework.core.annotation.AnnotatedElementUtils
49-
import org.springframework.web.bind.annotation.RequestParam
50-
import org.springframework.web.bind.annotation.ValueConstants
5145
import kotlin.coroutines.Continuation
52-
import kotlin.reflect.KParameter
53-
import kotlin.reflect.jvm.kotlinFunction
46+
import kotlin.reflect.full.primaryConstructor
5447

5548
/**
5649
* The type Spring doc kotlin configuration.
@@ -76,60 +69,21 @@ class SpringDocKotlinConfiguration() {
7669
.addDeprecatedType(Deprecated::class.java)
7770
}
7871

79-
/**
80-
* Kotlin springdoc-openapi ParameterCustomizer.
81-
* deprecated as not anymore required, use [SchemaUtils.fieldNullable]
82-
*
83-
* @return the nullable Kotlin Request Parameter Customizer
84-
* @see SchemaUtils.fieldNullable()
85-
*/
86-
@Deprecated("Deprecated since 2.8.7", level = DeprecationLevel.ERROR)
87-
fun nullableKotlinRequestParameterCustomizer(): ParameterCustomizer {
88-
return ParameterCustomizer { parameterModel, methodParameter ->
89-
if (parameterModel == null) return@ParameterCustomizer null
90-
if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(
91-
methodParameter.parameterType
92-
)
93-
) {
94-
val kParameter = methodParameter.toKParameter()
95-
if (kParameter != null) {
96-
val parameterDoc = AnnotatedElementUtils.findMergedAnnotation(
97-
AnnotatedElementUtils.forAnnotations(*methodParameter.parameterAnnotations),
98-
Parameter::class.java
99-
)
100-
val requestParam = AnnotatedElementUtils.findMergedAnnotation(
101-
AnnotatedElementUtils.forAnnotations(*methodParameter.parameterAnnotations),
102-
RequestParam::class.java
103-
)
104-
// Swagger @Parameter annotation takes precedence
105-
if (parameterDoc != null && parameterDoc.required)
106-
parameterModel.required = parameterDoc.required
107-
// parameter is not required if a default value is provided in @RequestParam
108-
else if (requestParam != null && requestParam.defaultValue != ValueConstants.DEFAULT_NONE)
109-
parameterModel.required = false
110-
else {
111-
val isJavaNullableAnnotationPresent =
112-
methodParameter.parameterAnnotations.any {
113-
it.annotationClass.qualifiedName == "jakarta.annotation.Nullable"
114-
}
115-
parameterModel.required =
116-
kParameter.type.isMarkedNullable == false && !isJavaNullableAnnotationPresent
117-
}
118-
}
119-
}
120-
return@ParameterCustomizer parameterModel
72+
@Bean
73+
@ConditionalOnProperty(
74+
name = [Constants.SPRINGDOC_NULLABLE_REQUEST_PARAMETER_ENABLED],
75+
matchIfMissing = true
76+
)
77+
@Lazy(false)
78+
fun kotlinDefaultsInParamObjects(): DelegatingMethodParameterCustomizer =
79+
DelegatingMethodParameterCustomizer { _, mp ->
80+
val kProp = mp.containingClass.kotlin.primaryConstructor
81+
?.parameters
82+
?.firstOrNull { it.name == mp.parameterName }
83+
if (kProp?.isOptional == true)
84+
(mp as DelegatingMethodParameter).isNotRequired = true
12185
}
122-
}
123-
124-
private fun MethodParameter.toKParameter(): KParameter? {
125-
// ignore return type, see org.springframework.core.MethodParameter.getParameterIndex
126-
if (parameterIndex == -1) return null
127-
val kotlinFunction = method?.kotlinFunction ?: return null
128-
// The first parameter of the kotlin function is the "this" reference and not needed here.
129-
// See also kotlin.reflect.KCallable.getParameters
130-
return kotlinFunction.parameters[parameterIndex + 1]
131-
}
132-
86+
13387
@ConditionalOnClass(name = ["kotlin.reflect.full.KClasses"])
13488
class KotlinReflectDependingConfiguration {
13589

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/Constants.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,6 @@ public final class Constants {
422422
/**
423423
* The constant SPRINGDOC_NULLABLE_REQUEST_PARAMETER_ENABLED.
424424
*/
425-
@Deprecated(since = "2.8.7")
426425
public static final String SPRINGDOC_NULLABLE_REQUEST_PARAMETER_ENABLED = "springdoc.nullable-request-parameter-enabled";
427426

428427
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
*
3+
* * Copyright 2019-2020 the original author or authors.
4+
* *
5+
* * Licensed under the Apache License, Version 2.0 (the "License");
6+
* * you may not use this file except in compliance with the License.
7+
* * You may obtain a copy of the License at
8+
* *
9+
* * https://www.apache.org/licenses/LICENSE-2.0
10+
* *
11+
* * Unless required by applicable law or agreed to in writing, software
12+
* * distributed under the License is distributed on an "AS IS" BASIS,
13+
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* * See the License for the specific language governing permissions and
15+
* * limitations under the License.
16+
*
17+
*/
18+
19+
package test.org.springdoc.api.v31.app20
20+
21+
import io.swagger.v3.oas.annotations.Parameter
22+
import io.swagger.v3.oas.annotations.media.Schema
23+
import jakarta.validation.Valid
24+
import jakarta.validation.constraints.Max
25+
import jakarta.validation.constraints.Min
26+
import jakarta.validation.constraints.Size
27+
import org.springdoc.core.annotations.ParameterObject
28+
import org.springframework.boot.autoconfigure.SpringBootApplication
29+
import org.springframework.http.ResponseEntity
30+
import org.springframework.web.bind.annotation.GetMapping
31+
import org.springframework.web.bind.annotation.PostMapping
32+
import org.springframework.web.bind.annotation.RequestMapping
33+
import org.springframework.web.bind.annotation.RequestParam
34+
import org.springframework.web.bind.annotation.RestController
35+
import test.org.springdoc.api.v31.AbstractKotlinSpringDocMVCTest
36+
37+
38+
class SpringDocApp20Test : AbstractKotlinSpringDocMVCTest() {
39+
@SpringBootApplication
40+
class DemoApplication
41+
}
42+
@RestController("/")
43+
class FooController {
44+
45+
@GetMapping("/foo")
46+
fun getFoo(@Valid fooParameters: FooParameters): String {
47+
return "Ok"
48+
}
49+
}
50+
51+
@ParameterObject
52+
data class FooParameters(
53+
@RequestParam(name = "bar", required = false, defaultValue = DEFAULT_BAR.toString())
54+
@field:Parameter(schema = Schema(
55+
minimum = "1",
56+
type = "integer",
57+
defaultValue = DEFAULT_BAR.toString()
58+
)
59+
)
60+
@field:Min(1)
61+
val bar: Int = DEFAULT_BAR,
62+
@RequestParam(name = "baz", required = false, defaultValue = DEFAULT_BAZ.toString())
63+
@field:Parameter(schema = Schema(minimum = "1", maximum = "200", type = "integer", defaultValue = DEFAULT_BAZ.toString()))
64+
@field:Min(1)
65+
@field:Max(200)
66+
val baz: Int = DEFAULT_BAZ
67+
) {
68+
companion object {
69+
const val DEFAULT_BAR = 1
70+
const val DEFAULT_BAZ = 20
71+
}
72+
}

springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/src/test/resources/results/3.0.1/app17.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
{
3838
"name": "barList",
3939
"in": "query",
40-
"required": true,
40+
"required": false,
4141
"schema": {
4242
"maxItems": 22,
4343
"minItems": 12,

springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/src/test/resources/results/3.1.0/app17.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
{
3838
"name": "barList",
3939
"in": "query",
40-
"required": true,
40+
"required": false,
4141
"schema": {
4242
"type": "array",
4343
"items": {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
{
2+
"openapi": "3.1.0",
3+
"info": {
4+
"title": "OpenAPI definition",
5+
"version": "v0"
6+
},
7+
"servers": [
8+
{
9+
"url": "http://localhost",
10+
"description": "Generated server url"
11+
}
12+
],
13+
"paths": {
14+
"/foo": {
15+
"get": {
16+
"tags": [
17+
"foo-controller"
18+
],
19+
"operationId": "getFoo",
20+
"parameters": [
21+
{
22+
"name": "bar",
23+
"in": "query",
24+
"required": false,
25+
"schema": {
26+
"type": "integer",
27+
"default": 1,
28+
"minimum": 1
29+
}
30+
},
31+
{
32+
"name": "baz",
33+
"in": "query",
34+
"required": false,
35+
"schema": {
36+
"type": "integer",
37+
"default": 20,
38+
"maximum": 200,
39+
"minimum": 1
40+
}
41+
}
42+
],
43+
"responses": {
44+
"200": {
45+
"description": "OK",
46+
"content": {
47+
"*/*": {
48+
"schema": {
49+
"type": "string"
50+
}
51+
}
52+
}
53+
}
54+
}
55+
}
56+
}
57+
},
58+
"components": {}
59+
}

0 commit comments

Comments
 (0)