diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f0ede238..0e00785b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Added +- Add support for swagger documentation + ### Changed ### Remove diff --git a/core/src/main/kotlin/io/bkbn/kompendium/core/routes/Redoc.kt b/core/src/main/kotlin/io/bkbn/kompendium/core/routes/Redoc.kt index ca6bcd3d5..2523d6a89 100644 --- a/core/src/main/kotlin/io/bkbn/kompendium/core/routes/Redoc.kt +++ b/core/src/main/kotlin/io/bkbn/kompendium/core/routes/Redoc.kt @@ -18,7 +18,7 @@ import kotlinx.html.unsafe /** * Provides an out-of-the-box route to view docs using ReDoc on the specified [path]. * @param pageTitle Webpage title you wish to be displayed on your docs - * @param route path to docs resource + * @param path path to docs resource * @param specUrl url to point ReDoc to the OpenAPI json document */ fun Route.redoc(pageTitle: String = "Docs", path: String = "/docs", specUrl: String = "/openapi.json") { diff --git a/core/src/main/kotlin/io/bkbn/kompendium/core/routes/Swagger.kt b/core/src/main/kotlin/io/bkbn/kompendium/core/routes/Swagger.kt new file mode 100644 index 000000000..70a1ec94c --- /dev/null +++ b/core/src/main/kotlin/io/bkbn/kompendium/core/routes/Swagger.kt @@ -0,0 +1,91 @@ +package io.bkbn.kompendium.core.routes + +import io.ktor.server.application.call +import io.ktor.server.html.respondHtml +import io.ktor.server.routing.Route +import io.ktor.server.routing.get +import io.ktor.server.routing.route +import kotlinx.html.body +import kotlinx.html.div +import kotlinx.html.head +import kotlinx.html.id +import kotlinx.html.link +import kotlinx.html.meta +import kotlinx.html.script +import kotlinx.html.title +import kotlinx.html.unsafe + +/** + * Provides an out-of-the-box route to view docs using Swagger + * @see Swagger OpenApi Specification + * for the latest supported open api version. + * @param pageTitle Webpage title you wish to be displayed on your docs + * @param path path to docs resource + * @param specUrl url to point Swagger to the OpenAPI json document + * @param swaggerVersion version of swagger-ui distribution + */ +fun Route.swagger( + pageTitle: String = "Docs", + path: String = "/swagger-ui", + specUrl: String = "/openapi.json", + swaggerVersion: String? = null +) { + val swaggerVersionSuffix = if (swaggerVersion == null) "" else "@$swaggerVersion" + + route(path) { + get { + call.respondHtml { + head { + title { + +pageTitle + } + meta { + charset = "utf-8" + } + meta { + name = "viewport" + content = "width=device-width, initial-scale=1" + } + link { + href = "https://unpkg.com/swagger-ui-dist$swaggerVersionSuffix/swagger-ui.css" + rel = "stylesheet" + } + } + body { + div { + id = "swagger-ui" + } + script { + src = "https://unpkg.com/swagger-ui-dist$swaggerVersionSuffix/swagger-ui-standalone-preset.js" + } + script { + src = "https://unpkg.com/swagger-ui-dist$swaggerVersionSuffix/swagger-ui-bundle.js" + } + unsafe { + +""" + + """.trimIndent() + } + } + } + } + } +} diff --git a/core/src/testFixtures/kotlin/io/bkbn/kompendium/core/fixtures/TestHelpers.kt b/core/src/testFixtures/kotlin/io/bkbn/kompendium/core/fixtures/TestHelpers.kt index 30ab58f6a..e59c1ee73 100644 --- a/core/src/testFixtures/kotlin/io/bkbn/kompendium/core/fixtures/TestHelpers.kt +++ b/core/src/testFixtures/kotlin/io/bkbn/kompendium/core/fixtures/TestHelpers.kt @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.SerializationFeature import io.bkbn.kompendium.core.fixtures.TestSpecs.defaultSpec import io.bkbn.kompendium.core.plugin.NotarizedApplication 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.JsonSchema import io.bkbn.kompendium.oas.OpenApiSpec @@ -130,6 +131,7 @@ object TestHelpers { } application(applicationSetup) routing { + swagger() redoc() routeUnderTest() } diff --git a/playground/src/main/kotlin/io/bkbn/kompendium/playground/AuthPlayground.kt b/playground/src/main/kotlin/io/bkbn/kompendium/playground/AuthPlayground.kt index 83784e32f..96fe6d72a 100644 --- a/playground/src/main/kotlin/io/bkbn/kompendium/playground/AuthPlayground.kt +++ b/playground/src/main/kotlin/io/bkbn/kompendium/playground/AuthPlayground.kt @@ -4,6 +4,7 @@ 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.definition.TypeDefinition import io.bkbn.kompendium.oas.component.Components import io.bkbn.kompendium.oas.payload.Parameter @@ -68,6 +69,7 @@ private fun Application.mainModule() { ) } routing { + swagger(pageTitle = "Simple API Docs") redoc(pageTitle = "Simple API Docs") authenticate("basic") { route("/{id}") { diff --git a/playground/src/main/kotlin/io/bkbn/kompendium/playground/BasicPlayground.kt b/playground/src/main/kotlin/io/bkbn/kompendium/playground/BasicPlayground.kt index 78fa65414..ed1d240d5 100644 --- a/playground/src/main/kotlin/io/bkbn/kompendium/playground/BasicPlayground.kt +++ b/playground/src/main/kotlin/io/bkbn/kompendium/playground/BasicPlayground.kt @@ -4,6 +4,7 @@ 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.payload.Parameter @@ -49,6 +50,7 @@ private fun Application.mainModule() { schemaConfigurator = KotlinXSchemaConfigurator() } routing { + swagger(pageTitle = "Simple API Docs") redoc(pageTitle = "Simple API Docs") route("/{id}") { idDocumentation() diff --git a/playground/src/main/kotlin/io/bkbn/kompendium/playground/CustomTypePlayground.kt b/playground/src/main/kotlin/io/bkbn/kompendium/playground/CustomTypePlayground.kt index 4e53b9654..c122d6f23 100644 --- a/playground/src/main/kotlin/io/bkbn/kompendium/playground/CustomTypePlayground.kt +++ b/playground/src/main/kotlin/io/bkbn/kompendium/playground/CustomTypePlayground.kt @@ -4,6 +4,7 @@ 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.definition.TypeDefinition import io.bkbn.kompendium.oas.payload.Parameter import io.bkbn.kompendium.oas.serialization.KompendiumSerializersModule @@ -51,6 +52,7 @@ private fun Application.mainModule() { ) } routing { + swagger(pageTitle = "Simple API Docs") redoc(pageTitle = "Simple API Docs") route("/{id}") { diff --git a/playground/src/main/kotlin/io/bkbn/kompendium/playground/EnrichmentPlayground.kt b/playground/src/main/kotlin/io/bkbn/kompendium/playground/EnrichmentPlayground.kt index fb9021890..c8aee894b 100644 --- a/playground/src/main/kotlin/io/bkbn/kompendium/playground/EnrichmentPlayground.kt +++ b/playground/src/main/kotlin/io/bkbn/kompendium/playground/EnrichmentPlayground.kt @@ -5,6 +5,7 @@ import io.bkbn.kompendium.core.metadata.PostInfo 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.enrichment.TypeEnrichment import io.bkbn.kompendium.json.schema.KotlinXSchemaConfigurator import io.bkbn.kompendium.json.schema.definition.TypeDefinition @@ -50,6 +51,7 @@ private fun Application.mainModule() { schemaConfigurator = KotlinXSchemaConfigurator() } routing { + swagger(pageTitle = "Simple API Docs") redoc(pageTitle = "Simple API Docs") enrichedDocumentation() post { diff --git a/playground/src/main/kotlin/io/bkbn/kompendium/playground/ExceptionPlayground.kt b/playground/src/main/kotlin/io/bkbn/kompendium/playground/ExceptionPlayground.kt index 2ba34c939..599133e2b 100644 --- a/playground/src/main/kotlin/io/bkbn/kompendium/playground/ExceptionPlayground.kt +++ b/playground/src/main/kotlin/io/bkbn/kompendium/playground/ExceptionPlayground.kt @@ -4,6 +4,7 @@ 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.definition.TypeDefinition import io.bkbn.kompendium.oas.payload.Parameter import io.bkbn.kompendium.oas.serialization.KompendiumSerializersModule @@ -50,6 +51,7 @@ private fun Application.mainModule() { } } routing { + swagger(pageTitle = "Simple API Docs") redoc(pageTitle = "Simple API Docs") route("/{id}") { diff --git a/playground/src/main/kotlin/io/bkbn/kompendium/playground/GsonSerializationPlayground.kt b/playground/src/main/kotlin/io/bkbn/kompendium/playground/GsonSerializationPlayground.kt index e88ff5d66..54dfae6f3 100644 --- a/playground/src/main/kotlin/io/bkbn/kompendium/playground/GsonSerializationPlayground.kt +++ b/playground/src/main/kotlin/io/bkbn/kompendium/playground/GsonSerializationPlayground.kt @@ -6,6 +6,7 @@ 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.SchemaConfigurator import io.bkbn.kompendium.json.schema.definition.TypeDefinition import io.bkbn.kompendium.oas.payload.Parameter @@ -48,6 +49,7 @@ private fun Application.mainModule() { schemaConfigurator = GsonSchemaConfigurator() } routing { + swagger(pageTitle = "Simple API Docs") redoc(pageTitle = "Simple API Docs") route("/{id}") { diff --git a/playground/src/main/kotlin/io/bkbn/kompendium/playground/HiddenDocsPlayground.kt b/playground/src/main/kotlin/io/bkbn/kompendium/playground/HiddenDocsPlayground.kt index 9bab93fcd..b2de57d23 100644 --- a/playground/src/main/kotlin/io/bkbn/kompendium/playground/HiddenDocsPlayground.kt +++ b/playground/src/main/kotlin/io/bkbn/kompendium/playground/HiddenDocsPlayground.kt @@ -5,6 +5,7 @@ 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.definition.TypeDefinition import io.bkbn.kompendium.oas.component.Components import io.bkbn.kompendium.oas.payload.Parameter @@ -80,6 +81,7 @@ private fun Application.mainModule() { } routing { authenticate("basic") { + swagger(pageTitle = "Simple API Docs") redoc(pageTitle = "Simple API Docs") route("/{id}") { locationDocumentation() diff --git a/playground/src/main/kotlin/io/bkbn/kompendium/playground/JacksonSerializationPlayground.kt b/playground/src/main/kotlin/io/bkbn/kompendium/playground/JacksonSerializationPlayground.kt index b93d3c29f..ffb847eb3 100644 --- a/playground/src/main/kotlin/io/bkbn/kompendium/playground/JacksonSerializationPlayground.kt +++ b/playground/src/main/kotlin/io/bkbn/kompendium/playground/JacksonSerializationPlayground.kt @@ -8,6 +8,7 @@ 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.SchemaConfigurator import io.bkbn.kompendium.json.schema.definition.TypeDefinition import io.bkbn.kompendium.oas.payload.Parameter @@ -51,6 +52,7 @@ private fun Application.mainModule() { schemaConfigurator = JacksonSchemaConfigurator() } routing { + swagger(pageTitle = "Simple API Docs") redoc(pageTitle = "Simple API Docs") route("/{id}") { diff --git a/playground/src/main/kotlin/io/bkbn/kompendium/playground/LocationPlayground.kt b/playground/src/main/kotlin/io/bkbn/kompendium/playground/LocationPlayground.kt index a6e59ba2b..8eb0838f8 100644 --- a/playground/src/main/kotlin/io/bkbn/kompendium/playground/LocationPlayground.kt +++ b/playground/src/main/kotlin/io/bkbn/kompendium/playground/LocationPlayground.kt @@ -3,6 +3,7 @@ package io.bkbn.kompendium.playground import io.bkbn.kompendium.core.metadata.GetInfo import io.bkbn.kompendium.core.plugin.NotarizedApplication import io.bkbn.kompendium.core.routes.redoc +import io.bkbn.kompendium.core.routes.swagger import io.bkbn.kompendium.json.schema.definition.TypeDefinition import io.bkbn.kompendium.locations.NotarizedLocations import io.bkbn.kompendium.oas.payload.Parameter @@ -72,6 +73,7 @@ private fun Application.mainModule() { ) } routing { + swagger(pageTitle = "Simple API Docs") redoc(pageTitle = "Simple API Docs") get { listing -> call.respondText("Listing ${listing.name}, page ${listing.page}") diff --git a/playground/src/main/kotlin/io/bkbn/kompendium/playground/ResourcesPlayground.kt b/playground/src/main/kotlin/io/bkbn/kompendium/playground/ResourcesPlayground.kt index 0108002b2..1119537d6 100644 --- a/playground/src/main/kotlin/io/bkbn/kompendium/playground/ResourcesPlayground.kt +++ b/playground/src/main/kotlin/io/bkbn/kompendium/playground/ResourcesPlayground.kt @@ -3,6 +3,7 @@ package io.bkbn.kompendium.playground import io.bkbn.kompendium.core.metadata.GetInfo import io.bkbn.kompendium.core.plugin.NotarizedApplication import io.bkbn.kompendium.core.routes.redoc +import io.bkbn.kompendium.core.routes.swagger import io.bkbn.kompendium.json.schema.definition.TypeDefinition import io.bkbn.kompendium.oas.payload.Parameter import io.bkbn.kompendium.oas.serialization.KompendiumSerializersModule @@ -73,6 +74,7 @@ private fun Application.mainModule() { ) } routing { + swagger(pageTitle = "Simple API Docs") redoc(pageTitle = "Simple API Docs") get { listing -> call.respondText("Listing ${listing.name}, page ${listing.page}")