Skip to content

Commit 3520f36

Browse files
committed
Merge remote-tracking branch 'origin/master'
2 parents b524a99 + ec7d012 commit 3520f36

File tree

7 files changed

+159
-4
lines changed

7 files changed

+159
-4
lines changed

src/main/kotlin/com/papsign/ktor/openapigen/parameters/parsers/builders/query/deepobject/DeepBuilderFactory.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ object DeepBuilderFactory : BuilderSelectorFactory<Builder<QueryParamStyle>, Que
1010
ListDeepBuilder,
1111
ArrayDeepBuilder,
1212
MapDeepBuilder,
13+
OptionalDeepBuilder,
1314
ObjectDeepBuilder
1415
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.papsign.ktor.openapigen.parameters.parsers.builders.query.deepobject
2+
3+
import com.papsign.ktor.openapigen.parameters.parsers.builders.BuilderSelector
4+
import com.papsign.ktor.openapigen.parameters.parsers.converters.Converter
5+
import com.papsign.ktor.openapigen.parameters.parsers.converters.ConverterFactory
6+
import java.util.*
7+
import kotlin.reflect.KType
8+
import kotlin.reflect.full.isSubclassOf
9+
import kotlin.reflect.jvm.jvmErasure
10+
11+
class OptionalDeepBuilder(type: KType) : DeepBuilder {
12+
private val converter: Converter = ConverterFactory.buildConverterForced(type.arguments[0].type!!)
13+
14+
override fun build(key: String, parameters: Map<String, List<String>>): Any? {
15+
return parameters[key]?.let { it[0] }?.let { value ->
16+
when (value) {
17+
"" -> Optional.empty()
18+
"null" -> Optional.empty()
19+
else -> Optional.ofNullable(converter.convert(value))
20+
}
21+
}
22+
}
23+
24+
25+
companion object : BuilderSelector<OptionalDeepBuilder> {
26+
override fun canHandle(type: KType, explode: Boolean): Boolean {
27+
return type.jvmErasure.isSubclassOf(Optional::class)
28+
}
29+
30+
override fun create(type: KType, explode: Boolean): OptionalDeepBuilder {
31+
return OptionalDeepBuilder(type)
32+
}
33+
}
34+
}

src/main/kotlin/com/papsign/ktor/openapigen/parameters/parsers/converters/ConverterFactory.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.papsign.ktor.openapigen.parameters.parsers.converters
22

33
import com.papsign.ktor.openapigen.parameters.parsers.converters.`object`.MapConverter
44
import com.papsign.ktor.openapigen.parameters.parsers.converters.`object`.ObjectConverter
5+
import com.papsign.ktor.openapigen.parameters.parsers.converters.`object`.OptionalConverter
56
import com.papsign.ktor.openapigen.parameters.parsers.converters.collection.ArrayConverter
67
import com.papsign.ktor.openapigen.parameters.parsers.converters.collection.ListConverter
78
import com.papsign.ktor.openapigen.parameters.parsers.converters.primitive.EnumConverter
@@ -18,6 +19,7 @@ interface ConverterFactory {
1819
ListConverter,
1920
ArrayConverter,
2021
MapConverter,
22+
OptionalConverter,
2123
ObjectConverter
2224
)
2325
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.papsign.ktor.openapigen.parameters.parsers.converters.`object`
2+
3+
import com.papsign.ktor.openapigen.parameters.parsers.converters.Converter
4+
import com.papsign.ktor.openapigen.parameters.parsers.converters.ConverterFactory
5+
import com.papsign.ktor.openapigen.parameters.parsers.converters.ConverterSelector
6+
import java.util.*
7+
import kotlin.reflect.KType
8+
import kotlin.reflect.full.isSubclassOf
9+
import kotlin.reflect.jvm.jvmErasure
10+
11+
class OptionalConverter(type: KType) : Converter {
12+
private val converter: Converter = ConverterFactory.buildConverterForced(type.arguments[0].type!!)
13+
14+
override fun convert(value: String): Any? {
15+
return when (value) {
16+
"" -> Optional.empty()
17+
"null" -> Optional.empty()
18+
else -> Optional.ofNullable(converter.convert(value))
19+
}
20+
}
21+
22+
23+
companion object : ConverterSelector {
24+
override fun canHandle(type: KType): Boolean {
25+
return type.jvmErasure.isSubclassOf(Optional::class)
26+
}
27+
28+
override fun create(type: KType): OptionalConverter {
29+
return OptionalConverter(type)
30+
}
31+
}
32+
}

src/main/kotlin/com/papsign/ktor/openapigen/schema/builder/provider/FinalSchemaBuilderProvider.kt

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ import java.util.*
1414
import kotlin.Comparator
1515
import kotlin.reflect.KType
1616
import kotlin.reflect.full.findAnnotation
17+
import kotlin.reflect.full.isSubclassOf
1718
import kotlin.reflect.full.isSubtypeOf
19+
import kotlin.reflect.full.withNullability
1820
import kotlin.reflect.jvm.jvmErasure
1921

2022
object FinalSchemaBuilderProvider: FinalSchemaBuilderProviderModule, OpenAPIGenModuleExtension {
@@ -63,10 +65,17 @@ object FinalSchemaBuilderProvider: FinalSchemaBuilderProviderModule, OpenAPIGenM
6365
}
6466

6567
override fun build(type: KType, annotations: List<Annotation>): SchemaModel<*> {
66-
return map.getOrPut(type) {
67-
map.entries.firstOrNull { type.isSubtypeOf(it.key) }?.value
68-
?: error("Schema builder could not find declared builder for type $type, make sure it has a provider registered on the route")
69-
}.build(type, this) { it.applyAnnotations(type, type.jvmErasure.annotations).applyAnnotations(type, type.annotations).applyAnnotations(type, annotations) }
68+
type.let {
69+
when {
70+
type.jvmErasure.isSubclassOf(Optional::class) -> type.arguments[0].type!!.withNullability(true)
71+
else -> type
72+
}
73+
}.let { type ->
74+
return map.getOrPut(type) {
75+
map.entries.firstOrNull { type.isSubtypeOf(it.key) }?.value
76+
?: error("Schema builder could not find declared builder for type $type, make sure it has a provider registered on the route")
77+
}.build(type, this) { it.applyAnnotations(type, type.jvmErasure.annotations).applyAnnotations(type, type.annotations).applyAnnotations(type, annotations) }
78+
}
7079
}
7180
}
7281
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.papsign.ktor.openapigen.parameters.parsers.builder.query.form
2+
3+
import com.papsign.ktor.openapigen.parameters.parsers.builders.query.form.FormBuilderFactory
4+
import com.papsign.ktor.openapigen.parameters.parsers.testSelector
5+
import org.junit.Test
6+
import java.util.*
7+
8+
class OptionalBuilderTest {
9+
10+
@Test
11+
fun testFilledValue() {
12+
val key = "key"
13+
val expected = Optional.of(1)
14+
val parse = mapOf(
15+
key to listOf("1")
16+
)
17+
FormBuilderFactory.testSelector(expected, key, parse, true)
18+
}
19+
20+
@Test
21+
fun testEmptyValue() {
22+
val key = "key"
23+
val expected = Optional.empty<Int>()
24+
val parse = mapOf(
25+
key to listOf("")
26+
)
27+
FormBuilderFactory.testSelector(expected, key, parse, true)
28+
}
29+
30+
@Test
31+
fun testExplicitNullValue() {
32+
val key = "key"
33+
val expected = Optional.empty<Int>()
34+
val parse = mapOf(
35+
key to listOf("null")
36+
)
37+
FormBuilderFactory.testSelector(expected, key, parse, true)
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.papsign.ktor.openapigen.parameters.parsers.builders.query.deepobject
2+
3+
import com.papsign.ktor.openapigen.parameters.parsers.testSelector
4+
import org.junit.Test
5+
import java.util.*
6+
7+
class OptionalBuilderTest {
8+
9+
@Test
10+
fun testFilledValue() {
11+
val key = "key"
12+
val expected = Optional.of(1)
13+
val parse = mapOf(
14+
key to listOf("1")
15+
)
16+
DeepBuilderFactory.testSelector(expected, key, parse, true)
17+
}
18+
19+
@Test
20+
fun testEmptyValue() {
21+
val key = "key"
22+
val expected = Optional.empty<Int>()
23+
val parse = mapOf(
24+
key to listOf("")
25+
)
26+
DeepBuilderFactory.testSelector(expected, key, parse, true)
27+
}
28+
29+
@Test
30+
fun testExplicitNullValue() {
31+
val key = "key"
32+
val expected = Optional.empty<Int>()
33+
val parse = mapOf(
34+
key to listOf("null")
35+
)
36+
DeepBuilderFactory.testSelector(expected, key, parse, true)
37+
}
38+
}

0 commit comments

Comments
 (0)