redoc and openapi routes standardized (#28)

This commit is contained in:
Ryan Brink
2021-04-19 09:11:32 -04:00
committed by GitHub
parent ae4999483b
commit dd978276d2
7 changed files with 188 additions and 141 deletions

View File

@ -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)
}
}
}

View File

@ -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"
}
}
}
}
}
}

View File

@ -11,7 +11,6 @@ import io.ktor.http.HttpStatusCode
import io.ktor.jackson.jackson
import io.ktor.response.respond
import io.ktor.response.respondText
import io.ktor.routing.get
import io.ktor.routing.route
import io.ktor.routing.routing
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.OpenApiSpecInfoLicense
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.KompendiumHttpCodes
import org.leafygreens.kompendium.util.TestCreatedResponse
import org.leafygreens.kompendium.util.TestData
import org.leafygreens.kompendium.util.TestDeleteResponse
import org.leafygreens.kompendium.util.TestParams
import org.leafygreens.kompendium.util.TestRequest
import org.leafygreens.kompendium.util.TestResponse
@ -56,7 +56,7 @@ internal class KompendiumTest {
fun `Notarized Get records all expected information`() {
withTestApplication({
configModule()
openApiModule()
docs()
notarizedGetModule()
}) {
// do
@ -72,7 +72,7 @@ internal class KompendiumTest {
fun `Notarized Get does not interrupt the pipeline`() {
withTestApplication({
configModule()
openApiModule()
docs()
notarizedGetModule()
}) {
// do
@ -88,7 +88,7 @@ internal class KompendiumTest {
fun `Notarized Post records all expected information`() {
withTestApplication({
configModule()
openApiModule()
docs()
notarizedPostModule()
}) {
// do
@ -104,7 +104,7 @@ internal class KompendiumTest {
fun `Notarized post does not interrupt the pipeline`() {
withTestApplication({
configModule()
openApiModule()
docs()
notarizedPostModule()
}) {
// do
@ -120,7 +120,7 @@ internal class KompendiumTest {
fun `Notarized Put records all expected information`() {
withTestApplication({
configModule()
openApiModule()
docs()
notarizedPutModule()
}) {
// do
@ -137,7 +137,7 @@ internal class KompendiumTest {
fun `Notarized put does not interrupt the pipeline`() {
withTestApplication({
configModule()
openApiModule()
docs()
notarizedPutModule()
}) {
// do
@ -153,7 +153,7 @@ internal class KompendiumTest {
fun `Notarized delete records all expected information`() {
withTestApplication({
configModule()
openApiModule()
docs()
notarizedDeleteModule()
}) {
// do
@ -169,7 +169,7 @@ internal class KompendiumTest {
fun `Notarized delete does not interrupt the pipeline`() {
withTestApplication({
configModule()
openApiModule()
docs()
notarizedDeleteModule()
}) {
// do
@ -184,7 +184,7 @@ internal class KompendiumTest {
fun `Path parser stores the expected path`() {
withTestApplication({
configModule()
openApiModule()
docs()
pathParsingTestModule()
}) {
// do
@ -200,7 +200,7 @@ internal class KompendiumTest {
fun `Can notarize the root route`() {
withTestApplication({
configModule()
openApiModule()
docs()
rootModule()
}) {
// do
@ -216,7 +216,7 @@ internal class KompendiumTest {
fun `Can call the root route`() {
withTestApplication({
configModule()
openApiModule()
docs()
rootModule()
}) {
// do
@ -232,7 +232,7 @@ internal class KompendiumTest {
fun `Can notarize a trailing slash route`() {
withTestApplication({
configModule()
openApiModule()
docs()
trailingSlash()
}) {
// do
@ -248,7 +248,7 @@ internal class KompendiumTest {
fun `Can call a trailing slash route`() {
withTestApplication({
configModule()
openApiModule()
docs()
trailingSlash()
}) {
// do
@ -264,7 +264,7 @@ internal class KompendiumTest {
fun `Can notarize a complex type`() {
withTestApplication({
configModule()
openApiModule()
docs()
complexType()
}) {
// do
@ -280,7 +280,7 @@ internal class KompendiumTest {
fun `Can notarize primitives`() {
withTestApplication({
configModule()
openApiModule()
docs()
primitives()
}) {
// do
@ -296,7 +296,7 @@ internal class KompendiumTest {
fun `Can notarize a top level list response`() {
withTestApplication({
configModule()
openApiModule()
docs()
returnsList()
}) {
// 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 {
val testGetResponse = ResponseInfo(KompendiumHttpCodes.OK, "A Successful Endeavor")
val testPostResponse = ResponseInfo(KompendiumHttpCodes.CREATED, "A Successful Endeavor")
@ -440,41 +457,38 @@ internal class KompendiumTest {
}
}
private fun Application.openApiModule() {
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"
)
)
)
private fun Application.docs() {
routing {
route("/openapi.json") {
get {
call.respond(
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"
)
)
)
)
}
}
openApi(oas)
redoc(oas)
}
}

View 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>