All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
commonMain.community.flock.wirespec.openapi.v2.OpenApiV2Emitter.kt Maven / Gradle / Ivy
package community.flock.wirespec.openapi.v2
import community.flock.kotlinx.openapi.bindings.v2.HeaderObject
import community.flock.kotlinx.openapi.bindings.v2.InfoObject
import community.flock.kotlinx.openapi.bindings.v2.OperationObject
import community.flock.kotlinx.openapi.bindings.v2.ParameterLocation
import community.flock.kotlinx.openapi.bindings.v2.ParameterObject
import community.flock.kotlinx.openapi.bindings.v2.Path
import community.flock.kotlinx.openapi.bindings.v2.PathItemObject
import community.flock.kotlinx.openapi.bindings.v2.Ref
import community.flock.kotlinx.openapi.bindings.v2.ReferenceObject
import community.flock.kotlinx.openapi.bindings.v2.ResponseObject
import community.flock.kotlinx.openapi.bindings.v2.SchemaObject
import community.flock.kotlinx.openapi.bindings.v2.SchemaOrReferenceObject
import community.flock.kotlinx.openapi.bindings.v2.StatusCode
import community.flock.kotlinx.openapi.bindings.v2.SwaggerObject
import community.flock.wirespec.compiler.core.parse.AST
import community.flock.wirespec.compiler.core.parse.Endpoint
import community.flock.wirespec.compiler.core.parse.Enum
import community.flock.wirespec.compiler.core.parse.Field
import community.flock.wirespec.compiler.core.parse.Reference
import community.flock.wirespec.compiler.core.parse.Refined
import community.flock.wirespec.compiler.core.parse.Type
import kotlinx.serialization.json.JsonPrimitive
import community.flock.kotlinx.openapi.bindings.v2.Type as OpenApiType
object OpenApiV2Emitter {
fun emit(ast: AST): SwaggerObject =
SwaggerObject(
swagger = "2.0",
info = InfoObject(
title = "Wirespec",
version = "0.0.0"
),
consumes = listOf("application/json"),
produces = listOf("application/json"),
paths = ast.filterIsInstance().groupBy { it.path }.map { (segments, endpoints) ->
Path(segments.emitSegment()) to PathItemObject(
parameters = segments.filterIsInstance().map {
ParameterObject(
`in` = ParameterLocation.PATH,
name = it.identifier.value,
type = it.reference.emitType()
)
},
get = endpoints.emit(Endpoint.Method.GET),
post = endpoints.emit(Endpoint.Method.POST),
put = endpoints.emit(Endpoint.Method.PUT),
delete = endpoints.emit(Endpoint.Method.DELETE),
patch = endpoints.emit(Endpoint.Method.PATCH),
options = endpoints.emit(Endpoint.Method.OPTIONS),
trace = endpoints.emit(Endpoint.Method.TRACE),
head = endpoints.emit(Endpoint.Method.HEAD),
)
}.toMap(),
definitions = ast
.filterIsInstance().associate { type ->
type.identifier.value to SchemaObject(
type = OpenApiType.STRING,
pattern = type.validator.value
)
} + ast
.filterIsInstance().associate { type ->
type.identifier.value to SchemaObject(
properties = type.shape.value.associate { it.emit() },
required = type.shape.value
.filter { !it.isNullable }
.map { it.identifier.value }
.takeIf { it.isNotEmpty() }
)
} + ast
.filterIsInstance().associate { enum ->
enum.identifier.value to SchemaObject(
type = OpenApiType.STRING,
enum = enum.entries.map { JsonPrimitive(it) }
)
}
)
private fun List.emit(method: Endpoint.Method): OperationObject? =
filter { it.method == method }.map { it.emit() }.firstOrNull()
private fun Endpoint.emit() = OperationObject(
operationId = identifier.value,
consumes = requests.mapNotNull { it.content?.type }.distinct().ifEmpty { null },
produces = responses.mapNotNull { it.content?.type }.distinct().ifEmpty { null },
parameters = requests
.mapNotNull { it.content }
.take(1)
.map {
ParameterObject(
`in` = ParameterLocation.BODY,
name = "RequestBody",
schema = it.reference.emit(),
required = !it.isNullable,
)
} + queries.map { it.emitParameter(ParameterLocation.QUERY) } + headers.map {
it.emitParameter(
ParameterLocation.HEADER
)
},
responses = responses
.associate { response ->
StatusCode(response.status) to ResponseObject(
description = "$identifier ${response.status} response",
headers = response.headers.associate {
it.identifier.value to HeaderObject(
type = it.reference.value,
items = if (it.reference.isIterable) it.reference.emit() else null
)
},
schema = response.content
?.takeIf { content -> content.reference !is Reference.Unit }
?.let { content ->
when (content.reference.isIterable) {
false -> content.reference.emit()
true -> SchemaObject(
type = OpenApiType.ARRAY,
items = content.reference.emit()
)
}
}
)
}
)
private fun List.emitSegment() = "/" + joinToString("/") {
when (it) {
is Endpoint.Segment.Param -> "{${it.identifier.value}}"
is Endpoint.Segment.Literal -> it.value
}
}
fun Field.emit(): Pair = identifier.value to reference.emit()
private fun Field.emitParameter(location: ParameterLocation) =
ParameterObject(
`in` = location,
name = identifier.value,
type = reference.emitType(),
items = when (reference.isIterable) {
true -> when (val emit = reference.emit()) {
is ReferenceObject -> emit
is SchemaObject -> emit.items
}
false -> null
},
required = !isNullable
)
fun Reference.emit(): SchemaOrReferenceObject = when (this) {
is Reference.Custom -> ReferenceObject(ref = Ref("#/definitions/${value}"))
is Reference.Primitive -> SchemaObject(type = type.emitType())
is Reference.Any -> error("Cannot map Any")
is Reference.Unit -> error("Cannot map Unit")
}.let { if (isIterable) SchemaObject(type = OpenApiType.ARRAY, items = it) else it }
private fun Reference.Primitive.Type.emitType(): OpenApiType = when (this) {
Reference.Primitive.Type.String -> OpenApiType.STRING
Reference.Primitive.Type.Integer -> OpenApiType.INTEGER
Reference.Primitive.Type.Number -> OpenApiType.NUMBER
Reference.Primitive.Type.Boolean -> OpenApiType.BOOLEAN
}
private fun Reference.emitType() =
if (isIterable) OpenApiType.ARRAY else when (this) {
is Reference.Primitive -> type.emitType()
is Reference.Custom -> OpenApiType.OBJECT
is Reference.Any -> OpenApiType.OBJECT
is Reference.Unit -> OpenApiType.OBJECT
}
}