Skip to content

Commit a68b42e

Browse files
committed
List of value classes in Kotlin. Fixes #2971
1 parent 95fa3bb commit a68b42e

File tree

7 files changed

+195
-12
lines changed

7 files changed

+195
-12
lines changed

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

+8
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
package org.springdoc.core.configuration
2828

2929
import io.swagger.v3.oas.annotations.Parameter
30+
import org.springdoc.core.converters.KotlinInlineClassUnwrappingConverter
3031
import org.springdoc.core.customizers.KotlinDeprecatedPropertyCustomizer
3132
import org.springdoc.core.customizers.ParameterCustomizer
3233
import org.springdoc.core.providers.ObjectMapperProvider
@@ -136,4 +137,11 @@ class SpringDocKotlinConfiguration() {
136137
}
137138
}
138139

140+
@Bean
141+
@Lazy(false)
142+
@ConditionalOnMissingBean
143+
fun kotlinModelConverter(objectMapperProvider: ObjectMapperProvider): KotlinInlineClassUnwrappingConverter {
144+
return KotlinInlineClassUnwrappingConverter(objectMapperProvider)
145+
}
146+
139147
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package org.springdoc.core.converters
2+
3+
import com.fasterxml.jackson.databind.JavaType
4+
import io.swagger.v3.core.converter.AnnotatedType
5+
import io.swagger.v3.core.converter.ModelConverter
6+
import io.swagger.v3.core.converter.ModelConverterContext
7+
import io.swagger.v3.oas.models.media.Schema
8+
import org.springdoc.core.providers.ObjectMapperProvider
9+
import kotlin.reflect.full.findAnnotation
10+
import kotlin.reflect.full.primaryConstructor
11+
import kotlin.reflect.jvm.jvmErasure
12+
13+
class KotlinInlineClassUnwrappingConverter(
14+
private val objectMapperProvider: ObjectMapperProvider
15+
) : ModelConverter {
16+
17+
override fun resolve(
18+
type: AnnotatedType?,
19+
context: ModelConverterContext?,
20+
chain: Iterator<ModelConverter>
21+
): Schema<*>? {
22+
if (type?.type == null || context == null || !chain.hasNext()) {
23+
return null
24+
}
25+
val javaType: JavaType = objectMapperProvider.jsonMapper().constructType(type.type)
26+
val kClass = javaType.rawClass.kotlin
27+
if (kClass.findAnnotation<JvmInline>() != null) {
28+
val constructor = kClass.primaryConstructor
29+
val param = constructor?.parameters?.firstOrNull()
30+
val unwrappedClass = param?.type?.jvmErasure?.java
31+
if (unwrappedClass != null) {
32+
val unwrappedType = AnnotatedType()
33+
.type(unwrappedClass)
34+
.ctxAnnotations(type.ctxAnnotations)
35+
.jsonViewAnnotation(type.jsonViewAnnotation)
36+
.resolveAsRef(false)
37+
38+
return chain.next().resolve(unwrappedType, context, chain)
39+
}
40+
}
41+
return chain.next().resolve(type, context, chain)
42+
}
43+
}

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/AbstractRequestService.java

+1-11
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@
7777
import org.springdoc.core.models.RequestBodyInfo;
7878
import org.springdoc.core.properties.SpringDocConfigProperties.ApiDocs.OpenApiVersion;
7979
import org.springdoc.core.providers.JavadocProvider;
80+
import org.springdoc.core.utils.SchemaUtils;
8081
import org.springdoc.core.utils.SpringDocAnnotationsUtils;
8182

82-
import org.springdoc.core.utils.SchemaUtils;
8383
import org.springframework.core.MethodParameter;
8484
import org.springframework.core.annotation.AnnotatedElementUtils;
8585
import org.springframework.http.HttpMethod;
@@ -118,16 +118,6 @@ public abstract class AbstractRequestService {
118118
*/
119119
private static final List<Class<?>> PARAM_TYPES_TO_IGNORE = Collections.synchronizedList(new ArrayList<>());
120120

121-
/**
122-
* The constant POSITIVE_OR_ZERO.
123-
*/
124-
private static final String POSITIVE_OR_ZERO = "PositiveOrZero";
125-
126-
/**
127-
* The constant NEGATIVE_OR_ZERO.
128-
*/
129-
private static final String NEGATIVE_OR_ZERO = "NegativeOrZero";
130-
131121
static {
132122
PARAM_TYPES_TO_IGNORE.add(WebRequest.class);
133123
PARAM_TYPES_TO_IGNORE.add(NativeWebRequest.class);

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ private SchemaUtils() {
5050
/**
5151
* The constant JAVA_FIELD_NULLABLE_DEFAULT.
5252
*/
53-
public static Boolean JAVA_FIELD_NULLABLE_DEFAULT = true;
53+
public static final Boolean JAVA_FIELD_NULLABLE_DEFAULT = true;
5454

5555
/**
5656
* The constant OPTIONAL_TYPES.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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.app18
20+
21+
import org.springframework.web.bind.annotation.GetMapping
22+
import org.springframework.web.bind.annotation.RequestMapping
23+
import org.springframework.web.bind.annotation.RestController
24+
import java.util.*
25+
26+
27+
@JvmInline
28+
value class CarId(val value: UUID)
29+
30+
data class CarIdsDTO(
31+
val singleId: CarId,
32+
val ids: List<CarId>
33+
)
34+
35+
@RestController
36+
@RequestMapping("/test")
37+
class HelloController {
38+
@GetMapping
39+
suspend fun index(s: CarIdsDTO) = s
40+
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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.app18
20+
21+
import org.springframework.boot.autoconfigure.SpringBootApplication
22+
import org.springframework.test.context.TestPropertySource
23+
import test.org.springdoc.api.v31.AbstractKotlinSpringDocMVCTest
24+
25+
@TestPropertySource(properties = ["springdoc.api-docs.version=openapi_3_1"])
26+
class SpringDocApp18Test : AbstractKotlinSpringDocMVCTest() {
27+
28+
@SpringBootApplication
29+
class DemoApplication {
30+
}
31+
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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+
"/test": {
15+
"get": {
16+
"tags": [
17+
"hello-controller"
18+
],
19+
"operationId": "index",
20+
"parameters": [
21+
{
22+
"name": "s",
23+
"in": "query",
24+
"required": true,
25+
"schema": {
26+
"$ref": "#/components/schemas/CarIdsDTO"
27+
}
28+
}
29+
],
30+
"responses": {
31+
"200": {
32+
"description": "OK",
33+
"content": {
34+
"*/*": {
35+
"schema": {
36+
"$ref": "#/components/schemas/CarIdsDTO"
37+
}
38+
}
39+
}
40+
}
41+
}
42+
}
43+
}
44+
},
45+
"components": {
46+
"schemas": {
47+
"CarIdsDTO": {
48+
"type": "object",
49+
"properties": {
50+
"singleId": {
51+
"type": "string",
52+
"format": "uuid"
53+
},
54+
"ids": {
55+
"type": "array",
56+
"items": {
57+
"type": "string",
58+
"format": "uuid"
59+
}
60+
}
61+
},
62+
"required": [
63+
"ids",
64+
"singleId"
65+
]
66+
}
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)