Skip to content

Commit ca1f46d

Browse files
committed
replaced use of KClass with KType for preserving generics that would otherwise be erased without reified, change access of several routing methods with potential of incorrect use to internal
1 parent 98a6ad1 commit ca1f46d

File tree

19 files changed

+317
-137
lines changed

19 files changed

+317
-137
lines changed

src/main/kotlin/com/papsign/ktor/openapigen/KTypeUtil.kt

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,24 @@
11
package com.papsign.ktor.openapigen
22

3-
import com.papsign.ktor.openapigen.annotations.mapping.openAPIName
4-
import java.lang.reflect.Field
53
import kotlin.reflect.*
64
import kotlin.reflect.full.createType
7-
import kotlin.reflect.full.declaredMemberProperties
85
import kotlin.reflect.full.memberProperties
9-
import kotlin.reflect.jvm.javaField
106
import kotlin.reflect.jvm.jvmErasure
117

128
val unitKType = getKType<Unit>()
139

14-
inline fun <reified T> isNullable(): Boolean {
10+
internal inline fun <reified T> isNullable(): Boolean {
1511
return null is T
1612
}
1713

18-
inline fun <reified T> getKType() = typeOf<T>()
14+
@PublishedApi
15+
internal inline fun <reified T> getKType() = typeOf<T>()
1916

20-
fun KType.strip(nullable: Boolean = isMarkedNullable): KType {
17+
internal fun KType.strip(nullable: Boolean = isMarkedNullable): KType {
2118
return jvmErasure.createType(arguments, nullable)
2219
}
2320

24-
fun KType.deepStrip(nullable: Boolean = isMarkedNullable): KType {
21+
internal fun KType.deepStrip(nullable: Boolean = isMarkedNullable): KType {
2522
return jvmErasure.createType(arguments.map { it.copy(type = it.type?.deepStrip()) }, nullable)
2623
}
2724

@@ -44,4 +41,4 @@ val KType.memberProperties: List<KTypeProperty>
4441
}
4542
}
4643

47-
val KClass<*>.isInterface get() = java.isInterface
44+
internal val KClass<*>.isInterface get() = java.isInterface

src/main/kotlin/com/papsign/ktor/openapigen/content/type/BodyParser.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@ package com.papsign.ktor.openapigen.content.type
33
import io.ktor.application.ApplicationCall
44
import io.ktor.http.ContentType
55
import io.ktor.util.pipeline.PipelineContext
6-
import kotlin.reflect.KClass
76
import kotlin.reflect.KType
87

98
interface BodyParser: ContentTypeProvider {
10-
fun <T: Any> getParseableContentTypes(clazz: KClass<T>): List<ContentType>
9+
fun <T: Any> getParseableContentTypes(type: KType): List<ContentType>
1110
suspend fun <T: Any> parseBody(clazz: KType, request: PipelineContext<Unit, ApplicationCall>): T
1211
}

src/main/kotlin/com/papsign/ktor/openapigen/content/type/ResponseSerializer.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ import io.ktor.application.ApplicationCall
44
import io.ktor.http.ContentType
55
import io.ktor.http.HttpStatusCode
66
import io.ktor.util.pipeline.PipelineContext
7-
import kotlin.reflect.KClass
7+
import kotlin.reflect.KType
88

99
interface ResponseSerializer: ContentTypeProvider {
1010
/**
1111
* used to determine which registered response serializer is used, based on the accept header
1212
*/
13-
fun <T: Any> getSerializableContentTypes(clazz: KClass<T>): List<ContentType>
13+
fun <T: Any> getSerializableContentTypes(type: KType): List<ContentType>
1414
suspend fun <T: Any> respond(response: T, request: PipelineContext<Unit, ApplicationCall>, contentType: ContentType)
1515
suspend fun <T: Any> respond(statusCode: HttpStatusCode, response: T, request: PipelineContext<Unit, ApplicationCall>, contentType: ContentType)
1616
}

src/main/kotlin/com/papsign/ktor/openapigen/content/type/binary/BinaryContentTypeParser.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ object BinaryContentTypeParser: BodyParser, ResponseSerializer, OpenAPIGenModule
2929
return constructors.first { it.parameters.size == 1 && acceptedTypes.contains(it.parameters[0].type) }
3030
}
3131

32-
override fun <T : Any> getParseableContentTypes(clazz: KClass<T>): List<ContentType> {
33-
return clazz.findAnnotation<BinaryRequest>()?.contentTypes?.map(ContentType.Companion::parse) ?: listOf()
32+
override fun <T : Any> getParseableContentTypes(type: KType): List<ContentType> {
33+
return type.jvmErasure.findAnnotation<BinaryRequest>()?.contentTypes?.map(ContentType.Companion::parse) ?: listOf()
3434
}
3535

36-
override fun <T: Any> getSerializableContentTypes(clazz: KClass<T>): List<ContentType> {
37-
return clazz.findAnnotation<BinaryResponse>()?.contentTypes?.map(ContentType.Companion::parse) ?: listOf()
36+
override fun <T: Any> getSerializableContentTypes(type: KType): List<ContentType> {
37+
return type.jvmErasure.findAnnotation<BinaryResponse>()?.contentTypes?.map(ContentType.Companion::parse) ?: listOf()
3838
}
3939

4040
override suspend fun <T : Any> respond(response: T, request: PipelineContext<Unit, ApplicationCall>, contentType: ContentType) {

src/main/kotlin/com/papsign/ktor/openapigen/content/type/ktor/KtorContentProvider.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import io.ktor.http.HttpStatusCode
2222
import io.ktor.request.receive
2323
import io.ktor.response.respond
2424
import io.ktor.util.pipeline.PipelineContext
25-
import kotlin.reflect.KClass
2625
import kotlin.reflect.KType
2726
import kotlin.reflect.full.findAnnotation
2827
import kotlin.reflect.jvm.jvmErasure
@@ -63,15 +62,15 @@ object KtorContentProvider : ContentTypeProvider, BodyParser, ResponseSerializer
6362
return contentTypes.associateWith { media.copy() }
6463
}
6564

66-
override fun <T : Any> getParseableContentTypes(clazz: KClass<T>): List<ContentType> {
65+
override fun <T : Any> getParseableContentTypes(type: KType): List<ContentType> {
6766
return contentTypes!!.toList()
6867
}
6968

7069
override suspend fun <T: Any> parseBody(clazz: KType, request: PipelineContext<Unit, ApplicationCall>): T {
7170
return request.call.receive(clazz)
7271
}
7372

74-
override fun <T: Any> getSerializableContentTypes(clazz: KClass<T>): List<ContentType> {
73+
override fun <T: Any> getSerializableContentTypes(type: KType): List<ContentType> {
7574
return contentTypes!!.toList()
7675
}
7776

src/main/kotlin/com/papsign/ktor/openapigen/content/type/multipart/MultipartFormDataContentProvider.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import kotlin.reflect.jvm.jvmErasure
3131

3232
object MultipartFormDataContentProvider : BodyParser, OpenAPIGenModuleExtension {
3333

34-
override fun <T : Any> getParseableContentTypes(clazz: KClass<T>): List<ContentType> {
34+
override fun <T : Any> getParseableContentTypes(type: KType): List<ContentType> {
3535
return listOf(ContentType.MultiPart.FormData)
3636
}
3737

@@ -68,7 +68,7 @@ object MultipartFormDataContentProvider : BodyParser, OpenAPIGenModuleExtension
6868
private val typeContentTypes = HashMap<KType, Map<String, MediaTypeEncodingModel>>()
6969

7070

71-
override suspend fun <T : Any> parseBody(clazz: KType, request: PipelineContext<Unit, ApplicationCall>): T {
71+
override suspend fun <T : Any> parseBody(type: KType, request: PipelineContext<Unit, ApplicationCall>): T {
7272
val objectMap = HashMap<String, Any>()
7373
request.context.receiveMultipart().forEachPart {
7474
val name = it.name
@@ -86,7 +86,8 @@ object MultipartFormDataContentProvider : BodyParser, OpenAPIGenModuleExtension
8686
}
8787
}
8888
}
89-
val ctor = (clazz.classifier as KClass<T>).primaryConstructor!!
89+
@Suppress("UNCHECKED_CAST")
90+
val ctor = (type.classifier as KClass<T>).primaryConstructor!!
9091
return ctor.callBy(ctor.parameters.associateWith {
9192
val raw = objectMap[it.openAPIName]
9293
if ((raw == null || (raw !is InputStream && streamTypes.contains(it.type))) && it.type.isMarkedNullable) {

src/main/kotlin/com/papsign/ktor/openapigen/modules/handlers/RequestHandlerModule.kt

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.papsign.ktor.openapigen.modules.handlers
22

3-
import com.papsign.ktor.openapigen.getKType
43
import com.papsign.ktor.openapigen.OpenAPIGen
54
import com.papsign.ktor.openapigen.annotations.Request
65
import com.papsign.ktor.openapigen.classLogger
@@ -14,13 +13,11 @@ import com.papsign.ktor.openapigen.modules.ofType
1413
import com.papsign.ktor.openapigen.modules.openapi.OperationModule
1514
import com.papsign.ktor.openapigen.modules.providers.ParameterProvider
1615
import com.papsign.ktor.openapigen.modules.registerModule
17-
import kotlin.reflect.KClass
1816
import kotlin.reflect.KType
1917
import kotlin.reflect.full.findAnnotation
20-
import kotlin.reflect.full.starProjectedType
18+
import kotlin.reflect.jvm.jvmErasure
2119

2220
class RequestHandlerModule<T : Any>(
23-
val requestClass: KClass<T>,
2421
val requestType: KType,
2522
val requestExample: T? = null
2623
) : OperationModule {
@@ -35,7 +32,7 @@ class RequestHandlerModule<T : Any>(
3532
mediaType.map { Pair(it.key.toString(), it.value) }
3633
}.flatten().associate { it }
3734

38-
val requestMeta = requestClass.findAnnotation<Request>()
35+
val requestMeta = requestType.jvmErasure.findAnnotation<Request>()
3936

4037
val parameters = provider.ofType<ParameterProvider>().flatMap { it.getParameters(apiGen, provider) }
4138
operation.parameters = operation.parameters?.let { (it + parameters).distinct() } ?: parameters
@@ -52,7 +49,6 @@ class RequestHandlerModule<T : Any>(
5249
}
5350

5451
companion object {
55-
inline fun <reified T : Any> create(requestExample: T? = null) = RequestHandlerModule(T::class, getKType<T>(), requestExample)
56-
fun <T : Any> create(tClass: KClass<T>, requestExample: T? = null) = RequestHandlerModule(tClass, tClass.starProjectedType, requestExample)
52+
fun <T : Any> create(tType: KType, requestExample: T? = null) = RequestHandlerModule(tType, requestExample)
5753
}
5854
}

src/main/kotlin/com/papsign/ktor/openapigen/modules/handlers/ResponseHandlerModule.kt

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.papsign.ktor.openapigen.modules.handlers
22

3-
import com.papsign.ktor.openapigen.getKType
43
import com.papsign.ktor.openapigen.OpenAPIGen
54
import com.papsign.ktor.openapigen.annotations.Response
65
import com.papsign.ktor.openapigen.classLogger
@@ -16,10 +15,8 @@ import com.papsign.ktor.openapigen.modules.providers.StatusProvider
1615
import com.papsign.ktor.openapigen.modules.registerModule
1716
import io.ktor.http.HttpStatusCode
1817
import kotlin.reflect.KAnnotatedElement
19-
import kotlin.reflect.KClass
2018
import kotlin.reflect.KType
2119
import kotlin.reflect.full.findAnnotation
22-
import kotlin.reflect.full.starProjectedType
2320

2421
class ResponseHandlerModule<T>(val responseType: KType, val responseExample: T? = null) : OperationModule {
2522
private val log = classLogger()
@@ -48,7 +45,6 @@ class ResponseHandlerModule<T>(val responseType: KType, val responseExample: T?
4845
}
4946

5047
companion object {
51-
inline fun <reified T : Any> create(responseExample: T? = null) = ResponseHandlerModule(getKType<T>(), responseExample)
52-
fun <T : Any> create(tClass: KClass<T>, responseExample: T? = null) = ResponseHandlerModule(tClass.starProjectedType, responseExample)
48+
fun <T : Any> create(tType: KType, responseExample: T? = null) = ResponseHandlerModule(tType, responseExample)
5349
}
5450
}

src/main/kotlin/com/papsign/ktor/openapigen/modules/handlers/ThrowOperationHandler.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@ object ThrowOperationHandler : OperationModule {
2121
private val log = classLogger()
2222
override fun configure(apiGen: OpenAPIGen, provider: ModuleProvider<*>, operation: OperationModel) {
2323

24-
val exceptions = provider.ofType<ThrowInfoProvider>().flatMap { it.exceptions }
25-
exceptions.groupBy { it.status }.forEach { exceptions ->
24+
provider
25+
.ofType<ThrowInfoProvider>()
26+
.flatMap { it.exceptions }
27+
.groupBy { it.status }
28+
.forEach { exceptions ->
2629
val map: MutableMap<String, MediaTypeModel<*>> = exceptions.value.flatMap { ex ->
2730
provider.ofType<ResponseSerializer>().mapNotNull {
2831
if (ex.contentType == unitKType) return@mapNotNull null

src/main/kotlin/com/papsign/ktor/openapigen/parameters/util/Util.kt

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,17 @@ import com.papsign.ktor.openapigen.parameters.handlers.ModularParameterHandler
77
import com.papsign.ktor.openapigen.parameters.handlers.ParameterHandler
88
import com.papsign.ktor.openapigen.parameters.handlers.UnitParameterHandler
99
import com.papsign.ktor.openapigen.parameters.parsers.builders.Builder
10-
import kotlin.reflect.KClass
10+
import kotlin.reflect.KFunction
1111
import kotlin.reflect.KParameter
12+
import kotlin.reflect.KType
1213
import kotlin.reflect.full.findAnnotation
1314
import kotlin.reflect.full.primaryConstructor
15+
import kotlin.reflect.jvm.jvmErasure
1416

15-
16-
inline fun <T : Any> buildParameterHandler(tClass: KClass<T>): ParameterHandler<T> {
17-
if (tClass == Unit::class) return UnitParameterHandler as ParameterHandler<T>
17+
fun <T : Any> buildParameterHandler(tType: KType): ParameterHandler<T> {
18+
@Suppress("UNCHECKED_CAST")
19+
if (tType.classifier == Unit::class) return UnitParameterHandler as ParameterHandler<T>
20+
val tClass = tType.jvmErasure
1821
assert(tClass.isData) { "API route with ${tClass.simpleName} must be a data class." }
1922
val constructor = tClass.primaryConstructor ?: error("API routes with ${tClass.simpleName} must have a primary constructor.")
2023
val parsers: Map<KParameter, Builder<*>> = constructor.parameters.associateWith { param ->
@@ -24,8 +27,9 @@ inline fun <T : Any> buildParameterHandler(tClass: KClass<T>): ParameterHandler<
2427
param.findAnnotation<QueryParam>()?.let { a -> a.style.factory.buildBuilderForced(type, a.explode) } ?:
2528
error("Parameters must be annotated with @PathParam or @QueryParam")
2629
}
30+
@Suppress("UNCHECKED_CAST")
2731
return ModularParameterHandler(
2832
parsers,
29-
constructor
33+
constructor as KFunction<T>
3034
)
3135
}

0 commit comments

Comments
 (0)