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

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

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

import org.http4k.contract.Tag
import org.http4k.contract.jsonschema.JsonSchema
import org.http4k.contract.jsonschema.v3.value
import org.http4k.contract.openapi.ApiInfo
import org.http4k.core.Uri
import org.http4k.lens.Meta
import org.http4k.lens.ParamMeta.ArrayParam
import org.http4k.lens.ParamMeta.FileParam
import org.http4k.lens.ParamMeta.ObjectParam

data class Api private constructor(
    val info: ApiInfo,
    val tags: List,
    val servers: List,
    val paths: Map>>,
    val webhooks: Map>>?,
    val components: Components,
    val openapi: String
) {
    init {
        require(servers.isNotEmpty())
        { "openAPI spec requires not-null and non-empty servers. See: https://swagger.io/specification/#openapi-object " }
    }

    constructor(
        info: ApiInfo,
        tags: List,
        paths: Map>>,
        components: Components,
        servers: List,
        webhooks: Map>>?,
        openapi: String
    ) : this(info, tags,
        servers.ifEmpty { listOf(ApiServer(Uri.of("/"))) },
        paths,
        webhooks,
        components,
        openapi
    )
}

data class Components(
    val schemas: NODE,
    val securitySchemes: NODE
)

data class ApiServer(
    val url: Uri,
    val description: String? = null
)

sealed class ApiPath(
    val summary: String,
    val description: String?,
    val tags: List?,
    val parameters: List>,
    val responses: Map>,
    val security: NODE?,
    val operationId: String?,
    val deprecated: Boolean?,
    val callbacks: Map>>>?
) {
    open fun definitions(): List> = listOfNotNull(
        responses.flatMap { it.value.definitions() },
        parameters.filterIsInstance>().flatMap { it.definitions() },
        callbacks?.flatMap { it.value.values.flatMap { it.values.flatMap { it.definitions() } } }
    ).flatten()

    class NoBody(
        summary: String,
        description: String?,
        tags: List?,
        parameters: List>,
        responses: Map>,
        security: NODE?,
        operationId: String?,
        deprecated: Boolean?,
        callbacks: Map>>>?
    ) : ApiPath(summary, description, tags, parameters, responses, security, operationId, deprecated, callbacks)

    class WithBody(
        summary: String,
        description: String?,
        tags: List?,
        parameters: List>,
        val requestBody: RequestContents,
        responses: Map>,
        security: NODE?,
        operationId: String?,
        deprecated: Boolean?,
        callbacks: Map>>>?
    ) : ApiPath(summary, description, tags, parameters, responses, security, operationId, deprecated, callbacks) {
        override fun definitions() = super.definitions() + requestBody.definitions().toList()
    }
}

fun interface HasSchema {
    fun definitions(): Iterable>
}

sealed class BodyContent {

    data class NoSchema(val schema: NODE, val example: String? = null) : BodyContent()

    class SchemaContent(private val jsonSchema: JsonSchema?, val example: NODE?) : BodyContent(),
        HasSchema {
        val schema = jsonSchema?.node
        override fun definitions() = jsonSchema?.definitions.orEmpty()
    }

    class OneOfSchemaContent(private val schemas: List) : BodyContent(), HasSchema {
        data class OneOf(val oneOf: List)

        val schema = OneOf(
            schemas.filterIsInstance>().map { it.schema } +
                schemas.filterIsInstance>().mapNotNull { it.schema }
        )

        override fun definitions() = schemas
            .filterIsInstance>()
            .flatMap { it.definitions() }
    }

    class FormContent(val schema: FormSchema) : BodyContent() {
        class FormSchema(metas: Map?>) {
            val type = "object"
            val properties = metas.keys.associate {
                val paramMeta = it.paramMeta
                it.name to listOfNotNull(
                    "type" to paramMeta.value,
                    paramMeta.takeIf { it == FileParam }?.let { "format" to "binary" },
                    it.description?.let { "description" to it },
                    when (paramMeta) {
                        is ArrayParam -> "items" to mapOf(
                            *listOfNotNull(
                                "type" to paramMeta.itemType().value,
                                paramMeta.itemType().takeIf { it == FileParam }
                                    ?.let { "format" to "binary" }).toTypedArray()
                        )

                        is ObjectParam -> null
                        else -> null
                    }
                ).toMap()
            }
            val required = metas.keys.filter(Meta::required).map { it.name }
        }
    }
}

class RequestContents(val content: Map? = null) : HasSchema {
    override fun definitions() = content?.values
        ?.filterIsInstance>()
        ?.flatMap { it.definitions() }
        .orEmpty()

    val required = content != null
}

class ResponseContents(val description: String?, val content: Map = emptyMap()) :
    HasSchema {
    override fun definitions() = content.values
        .filterIsInstance>()
        .flatMap { it.definitions() }.toSet()
}

sealed class RequestParameter(
    val `in`: String,
    val name: String,
    val required: Boolean,
    val description: String?
) {
    class SchemaParameter(meta: Meta, private val jsonSchema: JsonSchema?) :
        RequestParameter(meta.location, meta.name, meta.required, meta.description), HasSchema {
        val schema: NODE? = jsonSchema?.node
        override fun definitions() = jsonSchema?.definitions.orEmpty()
    }

    class PrimitiveParameter(meta: Meta, val schema: NODE) :
        RequestParameter(meta.location, meta.name, meta.required, meta.description)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy