Skip to content

Commit

Permalink
Fixed Generic types not creating proper models. Fixed creating separa…
Browse files Browse the repository at this point in the history
…te models for nullable object types
  • Loading branch information
Wicpar committed Mar 10, 2020
1 parent 37453e0 commit ef8daf7
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package com.papsign.ktor.openapigen.modules.schema

import com.papsign.kotlin.reflection.getKType
import com.papsign.kotlin.reflection.toInvariantFlexibleProjection
import com.papsign.kotlin.reflection.toKType
import com.papsign.ktor.openapigen.classLogger
import com.papsign.ktor.openapigen.openapi.Schema
import kotlin.reflect.KType
import kotlin.reflect.KTypeParameter
import kotlin.reflect.KVisibility
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.full.starProjectedType
import kotlin.reflect.full.withNullability
import kotlin.reflect.jvm.jvmErasure

open class SimpleSchemaRegistrar(val namer: SchemaNamer) : SchemaRegistrar {
Expand Down Expand Up @@ -52,17 +55,23 @@ open class SimpleSchemaRegistrar(val namer: SchemaNamer) : SchemaRegistrar {

private fun SchemaRegistrar.makeObjectSchema(type: KType): Schema<*> {
val erasure = type.jvmErasure
val typeParameters = erasure.typeParameters.zip(type.arguments).associate { Pair(it.first.name, it.second.type) }
if (erasure.isSealed) {
return Schema.OneSchemaOf(erasure.sealedSubclasses.map { get(it.starProjectedType).schema })
}
val props = erasure.declaredMemberProperties.filter { it.visibility == KVisibility.PUBLIC }
val properties = props.associate {
Pair(it.name, get(it.returnType).schema)
}
val props = erasure.declaredMemberProperties.filter { it.visibility == KVisibility.PUBLIC }.associateWith {
val retType = it.returnType
when(val classifier = retType.classifier) {
is KTypeParameter -> typeParameters[classifier.name] ?: it.returnType
else -> it.returnType
} }
val properties = props.map { (key, value) ->
Pair(key.name, get(value.withNullability(false)).schema)
}.associate { it }
if (properties.isEmpty()) log.warn("No public properties found in object $type")
return Schema.SchemaObj<Any>(
properties,
props.filter { !it.returnType.isMarkedNullable }.map { it.name })
props.filterValues { value -> !value.isMarkedNullable }.map { it.key.name })
}

private fun SchemaRegistrar.makeMapSchema(type: KType): Schema<*> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import io.ktor.util.toMap
import kotlin.reflect.KFunction
import kotlin.reflect.KParameter
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.withNullability

class ModularParameterHander<T>(val parsers: Map<KParameter, Builder<*>>, val constructor: KFunction<T>) :
ParameterHandler<T> {
Expand All @@ -30,7 +31,7 @@ class ModularParameterHander<T>(val parsers: Map<KParameter, Builder<*>>, val co
`in`,
!param.type.isMarkedNullable
).also {
it.schema = apiGen.schemaRegistrar[param.type].schema as Schema<Any>
it.schema = apiGen.schemaRegistrar[param.type.withNullability(false)].schema as Schema<Any>
config(it)
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/test/kotlin/Basic.kt
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ object Basic {
respond(body)
}
}

route("generic") {
post<Unit, GenericTest<A?>, GenericTest<A?>> { params, body ->
respond(body)
}
}
}
}.start(true)

Expand All @@ -108,6 +114,9 @@ object Basic {

data class A(val b: String)

@Request
data class GenericTest<T>(val value: T)

// A response can be any class, but a description will be generated from the annotation
@Response("A String Response")
data class StringResponse(val str: String)
Expand Down

0 comments on commit ef8daf7

Please sign in to comment.