redoc and openapi routes standardized (#28)
This commit is contained in:
@ -1,5 +1,11 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [0.5.0] - April 19th, 2021
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Expose `/openapi.json` and `/docs` as opt-in pre-built Routes
|
||||||
|
|
||||||
## [0.4.0] - April 17th, 2021
|
## [0.4.0] - April 17th, 2021
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Kompendium
|
# Kompendium
|
||||||
project.version=0.4.0
|
project.version=0.5.0
|
||||||
# Kotlin
|
# Kotlin
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
# Gradle
|
# Gradle
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
package org.leafygreens.kompendium.routes
|
||||||
|
|
||||||
|
import io.ktor.application.call
|
||||||
|
import io.ktor.response.respond
|
||||||
|
import io.ktor.routing.Routing
|
||||||
|
import io.ktor.routing.get
|
||||||
|
import io.ktor.routing.route
|
||||||
|
import org.leafygreens.kompendium.models.oas.OpenApiSpec
|
||||||
|
|
||||||
|
fun Routing.openApi(oas: OpenApiSpec) {
|
||||||
|
route("/openapi.json") {
|
||||||
|
get {
|
||||||
|
call.respond(oas)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
package org.leafygreens.kompendium.routes
|
||||||
|
|
||||||
|
import io.ktor.application.call
|
||||||
|
import io.ktor.html.respondHtml
|
||||||
|
import io.ktor.routing.Routing
|
||||||
|
import io.ktor.routing.get
|
||||||
|
import io.ktor.routing.route
|
||||||
|
import kotlinx.html.body
|
||||||
|
import kotlinx.html.head
|
||||||
|
import kotlinx.html.link
|
||||||
|
import kotlinx.html.meta
|
||||||
|
import kotlinx.html.script
|
||||||
|
import kotlinx.html.style
|
||||||
|
import kotlinx.html.title
|
||||||
|
import kotlinx.html.unsafe
|
||||||
|
import org.leafygreens.kompendium.models.oas.OpenApiSpec
|
||||||
|
|
||||||
|
fun Routing.redoc(oas: OpenApiSpec) {
|
||||||
|
route("/docs") {
|
||||||
|
get {
|
||||||
|
call.respondHtml {
|
||||||
|
head {
|
||||||
|
title {
|
||||||
|
+"${oas.info.title}"
|
||||||
|
}
|
||||||
|
meta {
|
||||||
|
charset = "utf-8"
|
||||||
|
}
|
||||||
|
meta {
|
||||||
|
name = "viewport"
|
||||||
|
content = "width=device-width, initial-scale=1"
|
||||||
|
}
|
||||||
|
link {
|
||||||
|
href = "https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700"
|
||||||
|
rel = "stylesheet"
|
||||||
|
}
|
||||||
|
style {
|
||||||
|
unsafe {
|
||||||
|
raw("body { margin: 0; padding: 0; }")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
// TODO needs to mirror openApi route
|
||||||
|
unsafe { +"<redoc spec-url='/openapi.json'></redoc>" }
|
||||||
|
script {
|
||||||
|
src = "https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,6 @@ import io.ktor.http.HttpStatusCode
|
|||||||
import io.ktor.jackson.jackson
|
import io.ktor.jackson.jackson
|
||||||
import io.ktor.response.respond
|
import io.ktor.response.respond
|
||||||
import io.ktor.response.respondText
|
import io.ktor.response.respondText
|
||||||
import io.ktor.routing.get
|
|
||||||
import io.ktor.routing.route
|
import io.ktor.routing.route
|
||||||
import io.ktor.routing.routing
|
import io.ktor.routing.routing
|
||||||
import io.ktor.server.testing.handleRequest
|
import io.ktor.server.testing.handleRequest
|
||||||
@ -31,11 +30,12 @@ import org.leafygreens.kompendium.models.oas.OpenApiSpecInfo
|
|||||||
import org.leafygreens.kompendium.models.oas.OpenApiSpecInfoContact
|
import org.leafygreens.kompendium.models.oas.OpenApiSpecInfoContact
|
||||||
import org.leafygreens.kompendium.models.oas.OpenApiSpecInfoLicense
|
import org.leafygreens.kompendium.models.oas.OpenApiSpecInfoLicense
|
||||||
import org.leafygreens.kompendium.models.oas.OpenApiSpecServer
|
import org.leafygreens.kompendium.models.oas.OpenApiSpecServer
|
||||||
|
import org.leafygreens.kompendium.routes.openApi
|
||||||
|
import org.leafygreens.kompendium.routes.redoc
|
||||||
import org.leafygreens.kompendium.util.ComplexRequest
|
import org.leafygreens.kompendium.util.ComplexRequest
|
||||||
import org.leafygreens.kompendium.util.KompendiumHttpCodes
|
import org.leafygreens.kompendium.util.KompendiumHttpCodes
|
||||||
import org.leafygreens.kompendium.util.TestCreatedResponse
|
import org.leafygreens.kompendium.util.TestCreatedResponse
|
||||||
import org.leafygreens.kompendium.util.TestData
|
import org.leafygreens.kompendium.util.TestData
|
||||||
import org.leafygreens.kompendium.util.TestDeleteResponse
|
|
||||||
import org.leafygreens.kompendium.util.TestParams
|
import org.leafygreens.kompendium.util.TestParams
|
||||||
import org.leafygreens.kompendium.util.TestRequest
|
import org.leafygreens.kompendium.util.TestRequest
|
||||||
import org.leafygreens.kompendium.util.TestResponse
|
import org.leafygreens.kompendium.util.TestResponse
|
||||||
@ -56,7 +56,7 @@ internal class KompendiumTest {
|
|||||||
fun `Notarized Get records all expected information`() {
|
fun `Notarized Get records all expected information`() {
|
||||||
withTestApplication({
|
withTestApplication({
|
||||||
configModule()
|
configModule()
|
||||||
openApiModule()
|
docs()
|
||||||
notarizedGetModule()
|
notarizedGetModule()
|
||||||
}) {
|
}) {
|
||||||
// do
|
// do
|
||||||
@ -72,7 +72,7 @@ internal class KompendiumTest {
|
|||||||
fun `Notarized Get does not interrupt the pipeline`() {
|
fun `Notarized Get does not interrupt the pipeline`() {
|
||||||
withTestApplication({
|
withTestApplication({
|
||||||
configModule()
|
configModule()
|
||||||
openApiModule()
|
docs()
|
||||||
notarizedGetModule()
|
notarizedGetModule()
|
||||||
}) {
|
}) {
|
||||||
// do
|
// do
|
||||||
@ -88,7 +88,7 @@ internal class KompendiumTest {
|
|||||||
fun `Notarized Post records all expected information`() {
|
fun `Notarized Post records all expected information`() {
|
||||||
withTestApplication({
|
withTestApplication({
|
||||||
configModule()
|
configModule()
|
||||||
openApiModule()
|
docs()
|
||||||
notarizedPostModule()
|
notarizedPostModule()
|
||||||
}) {
|
}) {
|
||||||
// do
|
// do
|
||||||
@ -104,7 +104,7 @@ internal class KompendiumTest {
|
|||||||
fun `Notarized post does not interrupt the pipeline`() {
|
fun `Notarized post does not interrupt the pipeline`() {
|
||||||
withTestApplication({
|
withTestApplication({
|
||||||
configModule()
|
configModule()
|
||||||
openApiModule()
|
docs()
|
||||||
notarizedPostModule()
|
notarizedPostModule()
|
||||||
}) {
|
}) {
|
||||||
// do
|
// do
|
||||||
@ -120,7 +120,7 @@ internal class KompendiumTest {
|
|||||||
fun `Notarized Put records all expected information`() {
|
fun `Notarized Put records all expected information`() {
|
||||||
withTestApplication({
|
withTestApplication({
|
||||||
configModule()
|
configModule()
|
||||||
openApiModule()
|
docs()
|
||||||
notarizedPutModule()
|
notarizedPutModule()
|
||||||
}) {
|
}) {
|
||||||
// do
|
// do
|
||||||
@ -137,7 +137,7 @@ internal class KompendiumTest {
|
|||||||
fun `Notarized put does not interrupt the pipeline`() {
|
fun `Notarized put does not interrupt the pipeline`() {
|
||||||
withTestApplication({
|
withTestApplication({
|
||||||
configModule()
|
configModule()
|
||||||
openApiModule()
|
docs()
|
||||||
notarizedPutModule()
|
notarizedPutModule()
|
||||||
}) {
|
}) {
|
||||||
// do
|
// do
|
||||||
@ -153,7 +153,7 @@ internal class KompendiumTest {
|
|||||||
fun `Notarized delete records all expected information`() {
|
fun `Notarized delete records all expected information`() {
|
||||||
withTestApplication({
|
withTestApplication({
|
||||||
configModule()
|
configModule()
|
||||||
openApiModule()
|
docs()
|
||||||
notarizedDeleteModule()
|
notarizedDeleteModule()
|
||||||
}) {
|
}) {
|
||||||
// do
|
// do
|
||||||
@ -169,7 +169,7 @@ internal class KompendiumTest {
|
|||||||
fun `Notarized delete does not interrupt the pipeline`() {
|
fun `Notarized delete does not interrupt the pipeline`() {
|
||||||
withTestApplication({
|
withTestApplication({
|
||||||
configModule()
|
configModule()
|
||||||
openApiModule()
|
docs()
|
||||||
notarizedDeleteModule()
|
notarizedDeleteModule()
|
||||||
}) {
|
}) {
|
||||||
// do
|
// do
|
||||||
@ -184,7 +184,7 @@ internal class KompendiumTest {
|
|||||||
fun `Path parser stores the expected path`() {
|
fun `Path parser stores the expected path`() {
|
||||||
withTestApplication({
|
withTestApplication({
|
||||||
configModule()
|
configModule()
|
||||||
openApiModule()
|
docs()
|
||||||
pathParsingTestModule()
|
pathParsingTestModule()
|
||||||
}) {
|
}) {
|
||||||
// do
|
// do
|
||||||
@ -200,7 +200,7 @@ internal class KompendiumTest {
|
|||||||
fun `Can notarize the root route`() {
|
fun `Can notarize the root route`() {
|
||||||
withTestApplication({
|
withTestApplication({
|
||||||
configModule()
|
configModule()
|
||||||
openApiModule()
|
docs()
|
||||||
rootModule()
|
rootModule()
|
||||||
}) {
|
}) {
|
||||||
// do
|
// do
|
||||||
@ -216,7 +216,7 @@ internal class KompendiumTest {
|
|||||||
fun `Can call the root route`() {
|
fun `Can call the root route`() {
|
||||||
withTestApplication({
|
withTestApplication({
|
||||||
configModule()
|
configModule()
|
||||||
openApiModule()
|
docs()
|
||||||
rootModule()
|
rootModule()
|
||||||
}) {
|
}) {
|
||||||
// do
|
// do
|
||||||
@ -232,7 +232,7 @@ internal class KompendiumTest {
|
|||||||
fun `Can notarize a trailing slash route`() {
|
fun `Can notarize a trailing slash route`() {
|
||||||
withTestApplication({
|
withTestApplication({
|
||||||
configModule()
|
configModule()
|
||||||
openApiModule()
|
docs()
|
||||||
trailingSlash()
|
trailingSlash()
|
||||||
}) {
|
}) {
|
||||||
// do
|
// do
|
||||||
@ -248,7 +248,7 @@ internal class KompendiumTest {
|
|||||||
fun `Can call a trailing slash route`() {
|
fun `Can call a trailing slash route`() {
|
||||||
withTestApplication({
|
withTestApplication({
|
||||||
configModule()
|
configModule()
|
||||||
openApiModule()
|
docs()
|
||||||
trailingSlash()
|
trailingSlash()
|
||||||
}) {
|
}) {
|
||||||
// do
|
// do
|
||||||
@ -264,7 +264,7 @@ internal class KompendiumTest {
|
|||||||
fun `Can notarize a complex type`() {
|
fun `Can notarize a complex type`() {
|
||||||
withTestApplication({
|
withTestApplication({
|
||||||
configModule()
|
configModule()
|
||||||
openApiModule()
|
docs()
|
||||||
complexType()
|
complexType()
|
||||||
}) {
|
}) {
|
||||||
// do
|
// do
|
||||||
@ -280,7 +280,7 @@ internal class KompendiumTest {
|
|||||||
fun `Can notarize primitives`() {
|
fun `Can notarize primitives`() {
|
||||||
withTestApplication({
|
withTestApplication({
|
||||||
configModule()
|
configModule()
|
||||||
openApiModule()
|
docs()
|
||||||
primitives()
|
primitives()
|
||||||
}) {
|
}) {
|
||||||
// do
|
// do
|
||||||
@ -296,7 +296,7 @@ internal class KompendiumTest {
|
|||||||
fun `Can notarize a top level list response`() {
|
fun `Can notarize a top level list response`() {
|
||||||
withTestApplication({
|
withTestApplication({
|
||||||
configModule()
|
configModule()
|
||||||
openApiModule()
|
docs()
|
||||||
returnsList()
|
returnsList()
|
||||||
}) {
|
}) {
|
||||||
// do
|
// do
|
||||||
@ -308,6 +308,23 @@ internal class KompendiumTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Generates the expected redoc`() {
|
||||||
|
withTestApplication({
|
||||||
|
configModule()
|
||||||
|
docs()
|
||||||
|
returnsList()
|
||||||
|
}) {
|
||||||
|
|
||||||
|
// do
|
||||||
|
val html = handleRequest(HttpMethod.Get, "/docs").response.content
|
||||||
|
|
||||||
|
// expected
|
||||||
|
val expected = TestData.getFileSnapshot("redoc.html")
|
||||||
|
assertEquals(expected, html)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
val testGetResponse = ResponseInfo(KompendiumHttpCodes.OK, "A Successful Endeavor")
|
val testGetResponse = ResponseInfo(KompendiumHttpCodes.OK, "A Successful Endeavor")
|
||||||
val testPostResponse = ResponseInfo(KompendiumHttpCodes.CREATED, "A Successful Endeavor")
|
val testPostResponse = ResponseInfo(KompendiumHttpCodes.CREATED, "A Successful Endeavor")
|
||||||
@ -440,12 +457,7 @@ internal class KompendiumTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Application.openApiModule() {
|
private val oas = Kompendium.openApiSpec.copy(
|
||||||
routing {
|
|
||||||
route("/openapi.json") {
|
|
||||||
get {
|
|
||||||
call.respond(
|
|
||||||
Kompendium.openApiSpec.copy(
|
|
||||||
info = OpenApiSpecInfo(
|
info = OpenApiSpecInfo(
|
||||||
title = "Test API",
|
title = "Test API",
|
||||||
version = "1.33.7",
|
version = "1.33.7",
|
||||||
@ -472,9 +484,11 @@ internal class KompendiumTest {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
}
|
private fun Application.docs() {
|
||||||
}
|
routing {
|
||||||
|
openApi(oas)
|
||||||
|
redoc(oas)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
13
kompendium-core/src/test/resources/redoc.html
Normal file
13
kompendium-core/src/test/resources/redoc.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Test API</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
|
||||||
|
<style>body { margin: 0; padding: 0; }</style>
|
||||||
|
</head>
|
||||||
|
<body><redoc spec-url='/openapi.json'></redoc>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -6,30 +6,18 @@ import io.ktor.application.Application
|
|||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.application.install
|
import io.ktor.application.install
|
||||||
import io.ktor.features.ContentNegotiation
|
import io.ktor.features.ContentNegotiation
|
||||||
import io.ktor.html.respondHtml
|
|
||||||
import io.ktor.jackson.jackson
|
import io.ktor.jackson.jackson
|
||||||
import io.ktor.response.respond
|
|
||||||
import io.ktor.response.respondText
|
import io.ktor.response.respondText
|
||||||
import io.ktor.routing.Routing
|
|
||||||
import io.ktor.routing.get
|
|
||||||
import io.ktor.routing.route
|
import io.ktor.routing.route
|
||||||
import io.ktor.routing.routing
|
import io.ktor.routing.routing
|
||||||
import io.ktor.server.engine.embeddedServer
|
import io.ktor.server.engine.embeddedServer
|
||||||
import io.ktor.server.netty.Netty
|
import io.ktor.server.netty.Netty
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import kotlinx.html.body
|
import org.leafygreens.kompendium.Kompendium
|
||||||
import kotlinx.html.head
|
|
||||||
import kotlinx.html.link
|
|
||||||
import kotlinx.html.meta
|
|
||||||
import kotlinx.html.script
|
|
||||||
import kotlinx.html.style
|
|
||||||
import kotlinx.html.title
|
|
||||||
import kotlinx.html.unsafe
|
|
||||||
import org.leafygreens.kompendium.Kompendium.notarizedDelete
|
import org.leafygreens.kompendium.Kompendium.notarizedDelete
|
||||||
import org.leafygreens.kompendium.Kompendium.notarizedGet
|
import org.leafygreens.kompendium.Kompendium.notarizedGet
|
||||||
import org.leafygreens.kompendium.Kompendium.notarizedPost
|
import org.leafygreens.kompendium.Kompendium.notarizedPost
|
||||||
import org.leafygreens.kompendium.Kompendium.notarizedPut
|
import org.leafygreens.kompendium.Kompendium.notarizedPut
|
||||||
import org.leafygreens.kompendium.Kompendium.openApiSpec
|
|
||||||
import org.leafygreens.kompendium.annotations.KompendiumField
|
import org.leafygreens.kompendium.annotations.KompendiumField
|
||||||
import org.leafygreens.kompendium.annotations.PathParam
|
import org.leafygreens.kompendium.annotations.PathParam
|
||||||
import org.leafygreens.kompendium.annotations.QueryParam
|
import org.leafygreens.kompendium.annotations.QueryParam
|
||||||
@ -45,8 +33,38 @@ import org.leafygreens.kompendium.playground.KompendiumTOC.testSingleDeleteInfo
|
|||||||
import org.leafygreens.kompendium.playground.KompendiumTOC.testSingleGetInfo
|
import org.leafygreens.kompendium.playground.KompendiumTOC.testSingleGetInfo
|
||||||
import org.leafygreens.kompendium.playground.KompendiumTOC.testSinglePostInfo
|
import org.leafygreens.kompendium.playground.KompendiumTOC.testSinglePostInfo
|
||||||
import org.leafygreens.kompendium.playground.KompendiumTOC.testSinglePutInfo
|
import org.leafygreens.kompendium.playground.KompendiumTOC.testSinglePutInfo
|
||||||
|
import org.leafygreens.kompendium.routes.openApi
|
||||||
|
import org.leafygreens.kompendium.routes.redoc
|
||||||
import org.leafygreens.kompendium.util.KompendiumHttpCodes
|
import org.leafygreens.kompendium.util.KompendiumHttpCodes
|
||||||
|
|
||||||
|
private val oas = Kompendium.openApiSpec.copy(
|
||||||
|
info = OpenApiSpecInfo(
|
||||||
|
title = "Test API",
|
||||||
|
version = "1.33.7",
|
||||||
|
description = "An amazing, fully-ish 😉 generated API spec",
|
||||||
|
termsOfService = URI("https://example.com"),
|
||||||
|
contact = OpenApiSpecInfoContact(
|
||||||
|
name = "Homer Simpson",
|
||||||
|
email = "chunkylover53@aol.com",
|
||||||
|
url = URI("https://gph.is/1NPUDiM")
|
||||||
|
),
|
||||||
|
license = OpenApiSpecInfoLicense(
|
||||||
|
name = "MIT",
|
||||||
|
url = URI("https://github.com/lg-backbone/kompendium/blob/main/LICENSE")
|
||||||
|
)
|
||||||
|
),
|
||||||
|
servers = mutableListOf(
|
||||||
|
OpenApiSpecServer(
|
||||||
|
url = URI("https://myawesomeapi.com"),
|
||||||
|
description = "Production instance of my API"
|
||||||
|
),
|
||||||
|
OpenApiSpecServer(
|
||||||
|
url = URI("https://staging.myawesomeapi.com"),
|
||||||
|
description = "Where the fun stuff happens"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
embeddedServer(
|
embeddedServer(
|
||||||
Netty,
|
Netty,
|
||||||
@ -63,8 +81,8 @@ fun Application.mainModule() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
routing {
|
routing {
|
||||||
openApi()
|
openApi(oas)
|
||||||
redoc()
|
redoc(oas)
|
||||||
route("/test") {
|
route("/test") {
|
||||||
route("/{id}") {
|
route("/{id}") {
|
||||||
notarizedGet<ExampleParams, ExampleResponse>(testIdGetInfo) {
|
notarizedGet<ExampleParams, ExampleResponse>(testIdGetInfo) {
|
||||||
@ -163,76 +181,3 @@ object KompendiumTOC {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Routing.openApi() {
|
|
||||||
route("/openapi.json") {
|
|
||||||
get {
|
|
||||||
call.respond(
|
|
||||||
openApiSpec.copy(
|
|
||||||
info = OpenApiSpecInfo(
|
|
||||||
title = "Test API",
|
|
||||||
version = "1.33.7",
|
|
||||||
description = "An amazing, fully-ish 😉 generated API spec",
|
|
||||||
termsOfService = URI("https://example.com"),
|
|
||||||
contact = OpenApiSpecInfoContact(
|
|
||||||
name = "Homer Simpson",
|
|
||||||
email = "chunkylover53@aol.com",
|
|
||||||
url = URI("https://gph.is/1NPUDiM")
|
|
||||||
),
|
|
||||||
license = OpenApiSpecInfoLicense(
|
|
||||||
name = "MIT",
|
|
||||||
url = URI("https://github.com/lg-backbone/kompendium/blob/main/LICENSE")
|
|
||||||
)
|
|
||||||
),
|
|
||||||
servers = mutableListOf(
|
|
||||||
OpenApiSpecServer(
|
|
||||||
url = URI("https://myawesomeapi.com"),
|
|
||||||
description = "Production instance of my API"
|
|
||||||
),
|
|
||||||
OpenApiSpecServer(
|
|
||||||
url = URI("https://staging.myawesomeapi.com"),
|
|
||||||
description = "Where the fun stuff happens"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Routing.redoc() {
|
|
||||||
route("/docs") {
|
|
||||||
get {
|
|
||||||
call.respondHtml {
|
|
||||||
head {
|
|
||||||
title {
|
|
||||||
+"${openApiSpec.info.title}"
|
|
||||||
}
|
|
||||||
meta {
|
|
||||||
charset = "utf-8"
|
|
||||||
}
|
|
||||||
meta {
|
|
||||||
name = "viewport"
|
|
||||||
content = "width=device-width, initial-scale=1"
|
|
||||||
}
|
|
||||||
link {
|
|
||||||
href = "https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700"
|
|
||||||
rel = "stylesheet"
|
|
||||||
}
|
|
||||||
style {
|
|
||||||
unsafe {
|
|
||||||
raw("body { margin: 0; padding: 0; }")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
// TODO needs to mirror openApi route
|
|
||||||
unsafe { +"<redoc spec-url='/openapi.json'></redoc>" }
|
|
||||||
script {
|
|
||||||
src = "https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user