SMILEY4 / ktor-swagger-ui

Kotlin Ktor plugin to generate OpenAPI and provide Swagger UI
Apache License 2.0
182 stars 33 forks source link

processKotlinxSerialization() doesn't seem to parse annotations #122

Closed vincentburesi-es closed 3 months ago

vincentburesi-es commented 3 months ago

When using a custom generator with type.processKotlinxSerialization(), it seems like the annotations (like @Description for example) are dropped. In schemakenerator's KotlinxSerializationTypeProcessingStep.kt the process function below uses KSerializer#descriptor

private fun process(type: KType, typeData: MutableList<BaseTypeData>): BaseTypeData {
    if (type.classifier is KClass<*>) {
        return (type.classifier as KClass<*>).serializerOrNull()
            ?.let { parse(it.descriptor, typeData, mutableMapOf()) } // <-- here we parse using KSerializer#descriptor
            ?: parseWildcard(typeData)
    } else {
        throw IllegalArgumentException("Type is not a class.")
    }
}

Later in the parsing, we have multiple specific parse functions like the one below, which don't transmit the annotations informations we would need for handleCoreAnnotations() and similar functions to work properly

private fun parseObject(descriptor: SerialDescriptor, typeData: MutableList<BaseTypeData>): BaseTypeData {
    val id = TypeId.build(descriptor.cleanSerialName())
    return typeData.find(id)
        ?: ObjectTypeData( // <-- here, we leave the annotations field blank
            id = id,
            simpleName = descriptor.simpleName(),
            qualifiedName = descriptor.qualifiedName(),
        ).also { result ->
            typeData.removeIf { it.id == result.id }
            typeData.add(result)
        }
}

To fix that, we would probably need a parseAnnotations step, similar to what is done for processReflection() in ReflectionTypeProcessingStep.kt parseClass() function, the parseAnnotations() function could even be reused as-is in both cases.

// collect annotation information
val annotations = parseAnnotations(clazz)
    // ...
return when (classType) {
    // ...
    TypeCategory.OBJECT -> ObjectTypeData(
        id = id,
        simpleName = clazz.simpleName!!,
        qualifiedName = clazz.qualifiedName!!,
        typeParameters = resolvedTypeParameters.toMutableMap(),
        subtypes = subtypes.toMutableList(),
        supertypes = supertypes.toMutableList(),
        members = members.toMutableList(),
        annotations = annotations.toMutableList() // <-- here
    )
    // ...
}
SMILEY4 commented 3 months ago

Hi, thanks for the suggestion.

Though i'm not sure i like the idea of mixing kotlinx-serialization with reflection since this would blur the lines between the two modules too much and may result too many differences between e.g. a generated jsonschema and the object serialized as json.

Is there a reason you need to use kotlinx-generator for generating the schemas instead of reflection-generator ?

SMILEY4 commented 3 months ago

schema-kenerator core annotations now also work with kotlinx