All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.http4k.contract.openapi.v3.jacksonExt.kt Maven / Gradle / Ivy

There is a newer version: 5.31.0.0
Show newest version
package org.http4k.contract.openapi.v3

import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.annotation.JsonPropertyDescription
import com.fasterxml.jackson.databind.PropertyNamingStrategies
import com.fasterxml.jackson.databind.PropertyNamingStrategy
import com.fasterxml.jackson.databind.annotation.JsonNaming
import org.http4k.contract.openapi.ApiInfo
import org.http4k.contract.openapi.ApiRenderer
import org.http4k.contract.openapi.OpenAPIJackson
import org.http4k.contract.openapi.OpenApiExtension
import org.http4k.format.ConfigurableJackson
import org.http4k.format.Jackson
import kotlin.reflect.KParameter
import kotlin.reflect.full.createInstance

/**
 * Defaults for configuring OpenApi3 with Jackson
 */
fun OpenApi3(
    apiInfo: ApiInfo,
    json: ConfigurableJackson = OpenAPIJackson,
    extensions: List = emptyList(),
    servers: List = emptyList()
) =
    OpenApi3(apiInfo, json, extensions, ApiRenderer.Auto(json, AutoJsonToJsonSchema(json)), servers = servers)

fun AutoJsonToJsonSchema(json: ConfigurableJackson) = AutoJsonToJsonSchema(
    json,
    FieldRetrieval.compose(
        SimpleLookup(metadataRetrievalStrategy =
        JacksonFieldMetadataRetrievalStrategy.then(PrimitivesFieldMetadataRetrievalStrategy)),
        FieldRetrieval.compose(JacksonJsonPropertyAnnotated, JacksonJsonNamingAnnotated(json))
    )
)

object JacksonJsonPropertyAnnotated : FieldRetrieval {
    override fun invoke(target: Any, name: String) =
        SimpleLookup(
            metadataRetrievalStrategy = JacksonFieldMetadataRetrievalStrategy
        )(target, target.javaClass.findName(name) ?: throw NoFieldFound(name, this))

    private fun Class.findName(name: String): String? =
        kotlin.constructors.first().parameters.firstNotNullOfOrNull { f ->
            f.annotations.filterIsInstance().find { it.value == name }
                ?.let { f.name }
        }
            ?: try {
                superclass?.findName(name)
            } catch (e: IllegalStateException) {
                throw NoFieldFound(name, this, e)
            }
}

class JacksonJsonNamingAnnotated(private val json: ConfigurableJackson = Jackson) : FieldRetrieval {
    override fun invoke(target: Any, name: String) = SimpleLookup(
        renamingStrategyIfRequired(target::class.java),
        JacksonFieldMetadataRetrievalStrategy
    )(target, name)

    private fun renamingStrategyIfRequired(clazz: Class<*>): (String) -> String {
        val namingStrategy = clazz.annotations
            .filterIsInstance()
            .map { it.value }.getOrNull(0)
            ?.let { it.createInstance() as PropertyNamingStrategy }
            ?: json.mapper.propertyNamingStrategy

        return if (namingStrategy is PropertyNamingStrategies.NamingBase) {
            { name: String -> namingStrategy.translate(name) }
        } else {
            { name -> name }
        }
    }
}

object JacksonFieldMetadataRetrievalStrategy : FieldMetadataRetrievalStrategy {
    override fun invoke(target: Any, fieldName: String): FieldMetadata =
        FieldMetadata(target.javaClass.findPropertyDescription(fieldName)?.let { mapOf("description" to it) }
            ?: emptyMap())

    private fun Class.findPropertyDescription(name: String): String? =
        kotlin.constructors.firstOrNull()?.let {
            it.parameters
                .firstOrNull { p -> p.kind == KParameter.Kind.VALUE && p.name == name }
                ?.let { p ->
                    p.annotations.filterIsInstance().firstOrNull()?.value
                } ?: superclass?.findPropertyDescription(name)
        } ?: superclass?.findPropertyDescription(name)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy