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.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.util.JsonSchema

data class Api private constructor(
    val info: ApiInfo,
    val tags: List,
    val servers: List,
    val paths: Map>>,
    val components: Components
) {
    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
    ) : this(info, tags, servers.ifEmpty { listOf(ApiServer(Uri.of("/"))) }, paths, components)

    val openapi = "3.0.0"
}

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
) {
    open fun definitions() = listOfNotNull(
        responses.flatMap { it.value.definitions() },
        parameters.filterIsInstance>().flatMap { it.definitions() }
    ).flatten()

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

    class WithBody(
        summary: String,
        description: String?,
        tags: List,
        parameters: List>,
        val requestBody: RequestContents,
        responses: Map>,
        security: NODE,
        operationId: String,
        deprecated: Boolean
    ) : ApiPath(summary, description, tags, parameters, responses, security, operationId, deprecated) {
        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 ?: emptySet()
    }

    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: List) {
            val type = "object"
            val properties = metas.associate {
                val paramMeta = it.paramMeta
                val listOfNotNull = listOfNotNull(
                    "type" to paramMeta.value,
                    paramMeta.takeIf { it == FileParam }?.let { "format" to "binary" },
                    it.description?.let { "description" to it },
                    if (paramMeta is ArrayParam) "items" to mapOf(
                        *listOfNotNull(
                            "type" to paramMeta.itemType().value,
                            paramMeta.itemType().takeIf { it == FileParam }
                                ?.let { "format" to "binary" }).toTypedArray()
                    )
                    else null
                )
                it.name to listOfNotNull.toMap()
            }
            val required = metas.filter(Meta::required).map { it.name }
        }
    }
}

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

    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 ?: emptySet()
    }

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy