SMILEY4 / ktor-swagger-ui

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

Example for common schema generation like Instant, LocalDate #155

Closed anderssonw closed 6 days ago

anderssonw commented 3 weeks ago

The example which is currently present only shows how to handle it for a top-level LocalDateTime. I currently have the following setup:

generator = { type ->
    type
        .processReflection {
            redirect<Instant, String>()
            redirect<LocalDate, String>()
        }
        .connectSubTypes()
        .generateSwaggerSchema {}
        .withTitle(TitleType.SIMPLE)
        .compileReferencingRoot()
}

Which is fine, but I would love to be able to specify the schema type such that this (registreringstidspunkt is an Instant):

image

becomes this:

image

Which was something I was able to do using an older library, Kompendium, but I'm not entirely sure how to setup using ktor-swagger-ui and schema-kenerator

s-frei commented 3 weeks ago

Have a look at: https://github.com/SMILEY4/ktor-swagger-ui/issues/135

anderssonw commented 3 weeks ago

I've gave it a little shot now, but I have an issue. Just dumping the current setup and the result here:


// Instant Serializer
object InstantSerializer : KSerializer<Instant> {
    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Instant", PrimitiveKind.STRING)

    override fun deserialize(decoder: Decoder): Instant {
        return Instant.parse(decoder.decodeString())
    }

    override fun serialize(encoder: Encoder, value: Instant) {
        encoder.encodeString(value.toString())
    }
}

// Class to be serialized and shown in the schemas
@Serializable
data class RegisterMetadataResponse(
    @Serializable(with = InstantSerializer::class)
    val registreringstidspunkt: Instant,
    val registrertAv: String,
)

private inline fun <reified T> createDefaultPrimitiveTypeData(type: String, format: String): PrimitiveTypeData {
    return PrimitiveTypeData(
        id = TypeId.build(T::class.qualifiedName!!),
        simpleName = T::class.simpleName!!,
        qualifiedName = T::class.qualifiedName!!,
        annotations = mutableListOf(
            AnnotationData(
                name = "type_format_annotation",
                values = mutableMapOf(
                    "type" to type,
                    "format" to format,
                ),
                annotation = null,
            ),
        ),
    )
}

schemas {
    generator = { type ->
        type
            .processKotlinxSerialization {
                customProcessor("Instant") {
                    createDefaultPrimitiveTypeData<Instant>(
                        type = "string",
                        format = "date-time"
                    )
                }
            }
            .connectSubTypes()
            .generateSwaggerSchema()
            .withTitle(TitleType.SIMPLE)
            .handleCoreAnnotations()
            .customizeTypes { typeData, typeSchema ->
                typeData.annotations.find { it.name == "type_format_annotation" }?.also { annotation ->
                    typeSchema.format = annotation.values["format"]?.toString()
                    typeSchema.type = annotation.values["type"]?.toString()
                }
            }
            .handleSchemaAnnotations()
            .compileReferencingRoot()
    }
  }

This however results in the type seemingly being null? See:

image

Also, for some particular reason, using .processKotlinxSerialization() turns a different schema into this:

image

Using .processReflection() shows the correct class name, as shown:

image
SMILEY4 commented 1 week ago

Hi, sorry for the late response.

Regarding the property type "null": with openapi3.1 the "types" field is used instead of "type". So

typeSchema.types = setOf(annotation.values["type"]?.toString())

should fix the issue. Im thinking to maybe make it so that "type" also works with the next version :thinking:

Regarding the random string after the class-name in the schema: That comes from a limitiation of kotlinx that i'm facing when generating the schemas of classes where i dont know if generics are involved. You can find a explanation and solution here: https://github.com/SMILEY4/schema-kenerator/issues/37#issuecomment-2416326579.

anderssonw commented 1 week ago

Cheers, @SMILEY4 ! I'll check it out with version 4.1.0 if that comes with some QoL stuff :)