From c7545b10726865dc832be49e77ed801dc425e267 Mon Sep 17 00:00:00 2001 From: Ryan Brink <5607577+unredundant@users.noreply.github.com> Date: Wed, 17 Jan 2024 16:30:37 -0500 Subject: [PATCH] chore: yaml example (#564) --- playground/build.gradle.kts | 2 + .../kompendium/playground/YamlPlayground.kt | 137 ++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 playground/src/main/kotlin/io/bkbn/kompendium/playground/YamlPlayground.kt diff --git a/playground/build.gradle.kts b/playground/build.gradle.kts index ca7e69795..1ab3c2515 100644 --- a/playground/build.gradle.kts +++ b/playground/build.gradle.kts @@ -40,6 +40,8 @@ dependencies { implementation("org.slf4j:slf4j-api:2.0.11") implementation("org.slf4j:slf4j-simple:2.0.11") + // YAML + implementation("com.charleskorn.kaml:kaml:0.57.0") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0") diff --git a/playground/src/main/kotlin/io/bkbn/kompendium/playground/YamlPlayground.kt b/playground/src/main/kotlin/io/bkbn/kompendium/playground/YamlPlayground.kt new file mode 100644 index 000000000..1bac789a2 --- /dev/null +++ b/playground/src/main/kotlin/io/bkbn/kompendium/playground/YamlPlayground.kt @@ -0,0 +1,137 @@ +package io.bkbn.kompendium.playground + +import com.charleskorn.kaml.Yaml +import com.charleskorn.kaml.YamlConfiguration +import io.bkbn.kompendium.core.metadata.GetInfo +import io.bkbn.kompendium.core.plugin.NotarizedApplication +import io.bkbn.kompendium.core.plugin.NotarizedRoute +import io.bkbn.kompendium.core.routes.redoc +import io.bkbn.kompendium.core.routes.swagger +import io.bkbn.kompendium.json.schema.KotlinXSchemaConfigurator +import io.bkbn.kompendium.json.schema.definition.TypeDefinition +import io.bkbn.kompendium.oas.OpenApiSpec +import io.bkbn.kompendium.oas.payload.Parameter +import io.bkbn.kompendium.oas.serialization.KompendiumSerializersModule +import io.bkbn.kompendium.playground.util.ExampleResponse +import io.bkbn.kompendium.playground.util.ExceptionResponse +import io.bkbn.kompendium.playground.util.Util.baseSpec +import io.ktor.http.HttpStatusCode +import io.ktor.serialization.kotlinx.json.json +import io.ktor.server.application.Application +import io.ktor.server.application.call +import io.ktor.server.application.install +import io.ktor.server.cio.CIO +import io.ktor.server.engine.embeddedServer +import io.ktor.server.plugins.contentnegotiation.ContentNegotiation +import io.ktor.server.response.respond +import io.ktor.server.response.respondText +import io.ktor.server.routing.Route +import io.ktor.server.routing.get +import io.ktor.server.routing.route +import io.ktor.server.routing.routing +import kotlinx.serialization.json.Json +import kotlinx.serialization.modules.serializersModuleOf +import kotlinx.serialization.serializer + +fun main() { + embeddedServer( + CIO, + port = 8081, + module = Application::mainModule + ).start(wait = true) +} + +private val yaml = Yaml(configuration = YamlConfiguration( + encodeDefaults = true, + // TODO: How to do stuff like serializers module, explicit nulls, etc? +)) + +private fun Application.mainModule() { + install(ContentNegotiation) { + json(Json { + serializersModule = KompendiumSerializersModule.module + encodeDefaults = true + explicitNulls = false + }) + } + install(NotarizedApplication()) { + spec = { baseSpec } + specRoute = { spec, routing -> + routing.route("/openapi.yml") { + get { + call.respondText { yaml.encodeToString(OpenApiSpec.serializer(), spec) } + } + } + } + schemaConfigurator = KotlinXSchemaConfigurator() + } + routing { + swagger(pageTitle = "Simple API Docs") + redoc(pageTitle = "Simple API Docs") + route("/{id}") { + idDocumentation() + get { + call.respond(HttpStatusCode.OK, ExampleResponse(true)) + } + route("/profile") { + profileDocumentation() + get { + call.respond(HttpStatusCode.OK, ExampleResponse(true)) + } + } + } + } +} + +private fun Route.idDocumentation() { + install(NotarizedRoute()) { + parameters = listOf( + Parameter( + name = "id", + `in` = Parameter.Location.path, + schema = TypeDefinition.STRING + ) + ) + get = GetInfo.builder { + summary("Get user by id") + description("A very neat endpoint!") + response { + responseCode(HttpStatusCode.OK) + responseType() + description("Will return whether or not the user is real 😱") + } + + canRespond { + responseType() + responseCode(HttpStatusCode.NotFound) + description("Indicates that a user with this id does not exist") + } + } + } +} + +private fun Route.profileDocumentation() { + install(NotarizedRoute()) { + parameters = listOf( + Parameter( + name = "id", + `in` = Parameter.Location.path, + schema = TypeDefinition.STRING + ) + ) + get = GetInfo.builder { + summary("Get a users profile") + description("A cool endpoint!") + response { + responseCode(HttpStatusCode.OK) + responseType() + description("Returns user profile information") + } + canRespond { + responseType() + responseCode(HttpStatusCode.NotFound) + description("Indicates that a user with this id does not exist") + } + } + } +}