org.http4k.contract.openapi.v3.model.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of http4k-contract Show documentation
Show all versions of http4k-contract Show documentation
http4k typesafe HTTP contracts and OpenApi support
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)
}