fix: swagger ui regression from dependency bump
This commit is contained in:

committed by
GitHub

parent
7a1e57f50c
commit
26c481e1a4
@ -3,8 +3,10 @@
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
- Added tests for Swagger UI module that verify that plugin generates correct responses for Swagger UI WEB resources (tests should detect future incompatible changes in new versions of `org.webjars.swagger-ui`)
|
||||
|
||||
### Changed
|
||||
- Fixed broken Swagger UI plugin (`org.webjars.swagger-ui` WEB resources structure changed in version 4.9.X). Issue: https://github.com/bkbnio/kompendium/issues/236
|
||||
|
||||
### Remove
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
package io.bkbn.kompendium.core
|
||||
|
||||
import io.bkbn.kompendium.annotations.constraint.Format
|
||||
import io.bkbn.kompendium.core.handler.CollectionHandler
|
||||
import io.bkbn.kompendium.core.handler.EnumHandler
|
||||
import io.bkbn.kompendium.core.handler.MapHandler
|
||||
@ -12,7 +11,6 @@ import io.bkbn.kompendium.oas.schema.SimpleSchema
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.full.createType
|
||||
import kotlin.reflect.full.findAnnotation
|
||||
import kotlin.reflect.full.isSubclassOf
|
||||
import kotlin.reflect.typeOf
|
||||
import org.slf4j.LoggerFactory
|
||||
|
@ -72,13 +72,13 @@ private fun Application.mainModule() {
|
||||
// Example is prepared according to this documentation: https://swagger.io/docs/open-source-tools/swagger-ui/usage/oauth2/
|
||||
jsInit = {
|
||||
"""
|
||||
ui.initOAuth({
|
||||
clientId: 'CLIENT_ID',
|
||||
clientSecret: 'CLIENT_SECRET',
|
||||
realm: 'MY REALM',
|
||||
appName: 'TEST APP',
|
||||
useBasicAuthenticationWithAccessCodeGrant: true
|
||||
});
|
||||
window.ui.initOAuth({
|
||||
clientId: 'CLIENT_ID',
|
||||
clientSecret: 'CLIENT_SECRET',
|
||||
realm: 'MY REALM',
|
||||
appName: 'TEST APP',
|
||||
useBasicAuthenticationWithAccessCodeGrant: true
|
||||
});
|
||||
"""
|
||||
}
|
||||
)
|
||||
|
@ -7,6 +7,7 @@ plugins {
|
||||
id("maven-publish")
|
||||
id("java-library")
|
||||
id("signing")
|
||||
id("java-test-fixtures")
|
||||
}
|
||||
|
||||
sourdough {
|
||||
@ -16,9 +17,13 @@ sourdough {
|
||||
|
||||
dependencies {
|
||||
val ktorVersion: String by project
|
||||
|
||||
implementation(projects.kompendiumCore)
|
||||
implementation(group = "io.ktor", name = "ktor-server-core", version = ktorVersion)
|
||||
implementation(group = "org.webjars", name = "webjars-locator-core", version = "0.50")
|
||||
implementation(group = "org.webjars", name = "swagger-ui", version = "4.9.1")
|
||||
|
||||
testImplementation(testFixtures(projects.kompendiumCore))
|
||||
}
|
||||
|
||||
testing {
|
||||
|
@ -23,7 +23,7 @@ data class JsConfig(
|
||||
internal fun JsConfig.toJsProps(): String = asMap()
|
||||
.filterKeys { !setOf("specs", "jsInit").contains(it) }
|
||||
.map{ "${it.key}: ${it.value.toJs()}" }
|
||||
.joinToString(separator = ",\n\t\t")
|
||||
.joinToString(separator = ",\n ")
|
||||
|
||||
internal fun JsConfig.getSpecUrlsProps(): String =
|
||||
if (specs.isEmpty()) "[]" else specs.map { "{url: ${it.value.toJs()}, name: ${it.key.toJs()}}" }.toString()
|
||||
|
@ -56,8 +56,8 @@ class SwaggerUI(val config: Configuration) {
|
||||
get("${config.swaggerBaseUrl}/{filename}") {
|
||||
call.parameters["filename"]!!.let { filename ->
|
||||
when(filename) {
|
||||
"index.html" ->
|
||||
locator.getSwaggerIndexContent(jsConfig = config.jsConfig)
|
||||
"swagger-initializer.js" ->
|
||||
locator.getSwaggerInitializerContent(jsConfig = config.jsConfig)
|
||||
else ->
|
||||
locator.getSwaggerResourceContent(path = filename)
|
||||
}
|
||||
|
@ -12,13 +12,13 @@ internal fun WebJarAssetLocator.getSwaggerResource(path: String): URL =
|
||||
internal fun WebJarAssetLocator.getSwaggerResourceContent(path: String): ByteArrayContent =
|
||||
ByteArrayContent(getSwaggerResource(path).readBytes())
|
||||
|
||||
internal fun WebJarAssetLocator.getSwaggerIndexContent(jsConfig: JsConfig): ByteArrayContent = ByteArrayContent(
|
||||
getSwaggerResource(path = "index.html").readText()
|
||||
internal fun WebJarAssetLocator.getSwaggerInitializerContent(jsConfig: JsConfig): ByteArrayContent = ByteArrayContent(
|
||||
getSwaggerResource(path = "swagger-initializer.js").readText()
|
||||
.replaceFirst("url: \"https://petstore.swagger.io/v2/swagger.json\",", "urls: ${jsConfig.getSpecUrlsProps()},")
|
||||
.replaceFirst("deepLinking: true", jsConfig.toJsProps())
|
||||
.let { content ->
|
||||
jsConfig.jsInit()?.let {
|
||||
content.replaceFirst("window.ui = ui", "$it\n\twindow.ui = ui")
|
||||
content.replaceFirst("});", "});\n$it")
|
||||
} ?: content
|
||||
}.toByteArray()
|
||||
)
|
||||
|
@ -0,0 +1,54 @@
|
||||
package io.bkbn.kompendium.swagger
|
||||
|
||||
import io.bkbn.kompendium.swagger.TestHelpers.TEST_SWAGGER_UI_INDEX
|
||||
import io.bkbn.kompendium.swagger.TestHelpers.TEST_SWAGGER_UI_INIT_JS
|
||||
import io.bkbn.kompendium.swagger.TestHelpers.TEST_SWAGGER_UI_ROOT
|
||||
import io.bkbn.kompendium.swagger.TestHelpers.compareRedirect
|
||||
import io.bkbn.kompendium.swagger.TestHelpers.compareResource
|
||||
import io.bkbn.kompendium.swagger.TestHelpers.withSwaggerApplication
|
||||
import io.kotest.core.spec.style.DescribeSpec
|
||||
|
||||
class SwaggerUiTest: DescribeSpec ({
|
||||
|
||||
describe("Swagger UI resources") {
|
||||
|
||||
it ("Redirects /swagger-ui -> index.html") {
|
||||
withSwaggerApplication {
|
||||
compareRedirect(TEST_SWAGGER_UI_ROOT, TEST_SWAGGER_UI_INDEX)
|
||||
}
|
||||
}
|
||||
|
||||
it ("Can return original: index.html") {
|
||||
withSwaggerApplication {
|
||||
compareResource(TEST_SWAGGER_UI_INDEX, listOf(
|
||||
"<title>Swagger UI</title>",
|
||||
"<div id=\"swagger-ui\"></div>",
|
||||
"src=\"./swagger-initializer.js\""
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
it("Can return generated: swagger-initializer.js") {
|
||||
withSwaggerApplication {
|
||||
compareResource(TEST_SWAGGER_UI_INIT_JS, listOf(
|
||||
"url: '/openapi.json', name: 'My API v1'",
|
||||
"url: '/openapi.json', name: 'My API v2'",
|
||||
"defaultModelExpandDepth: 4",
|
||||
"defaultModelsExpandDepth: 4",
|
||||
"displayOperationId: true",
|
||||
"displayRequestDuration: true",
|
||||
"operationsSorter: 'alpha'",
|
||||
"persistAuthorization: true",
|
||||
"tagsSorter: 'alpha'",
|
||||
"window.ui.initOAuth",
|
||||
"clientId: 'CLIENT_ID'",
|
||||
"clientSecret: 'CLIENT_SECRET'",
|
||||
"realm: 'MY REALM'",
|
||||
"appName: 'TEST APP'",
|
||||
"useBasicAuthenticationWithAccessCodeGrant: true"
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
@ -0,0 +1,79 @@
|
||||
package io.bkbn.kompendium.swagger
|
||||
|
||||
import io.bkbn.kompendium.core.Kompendium
|
||||
import io.bkbn.kompendium.core.fixtures.kompendium
|
||||
import io.kotest.assertions.ktor.shouldHaveStatus
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.application.install
|
||||
import io.ktor.http.HttpHeaders
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.testing.TestApplicationEngine
|
||||
import io.ktor.server.testing.createTestEnvironment
|
||||
import io.ktor.server.testing.handleRequest
|
||||
import io.ktor.server.testing.withApplication
|
||||
import java.net.URI
|
||||
|
||||
object TestHelpers {
|
||||
|
||||
const val TEST_SWAGGER_UI_ROOT = "/swagger-ui"
|
||||
const val TEST_SWAGGER_UI_INDEX = "/webjars/swagger-ui/index.html"
|
||||
const val TEST_SWAGGER_UI_INIT_JS = "/webjars/swagger-ui/swagger-initializer.js"
|
||||
|
||||
// The same config the same as in Playground
|
||||
private val basicSwaggerUiConfig: SwaggerUI.Configuration.() -> Unit = {
|
||||
swaggerUrl = "/swagger-ui"
|
||||
jsConfig = JsConfig(
|
||||
specs = mapOf(
|
||||
"My API v1" to URI("/openapi.json"),
|
||||
"My API v2" to URI("/openapi.json")
|
||||
),
|
||||
jsInit = {
|
||||
"""
|
||||
window.ui.initOAuth({
|
||||
clientId: 'CLIENT_ID',
|
||||
clientSecret: 'CLIENT_SECRET',
|
||||
realm: 'MY REALM',
|
||||
appName: 'TEST APP',
|
||||
useBasicAuthenticationWithAccessCodeGrant: true
|
||||
});
|
||||
"""
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun TestApplicationEngine.compareRedirect(resourceName: String, targetResource: String) {
|
||||
handleRequest(HttpMethod.Get, resourceName).apply {
|
||||
response shouldHaveStatus HttpStatusCode.Found
|
||||
response.headers[HttpHeaders.Location] shouldBe targetResource
|
||||
}
|
||||
}
|
||||
|
||||
fun TestApplicationEngine.compareResource(resourceName: String, mustContain: List<String>) {
|
||||
handleRequest(HttpMethod.Get, resourceName).apply {
|
||||
response shouldHaveStatus HttpStatusCode.OK
|
||||
response.content shouldNotBe null
|
||||
mustContain.forEach {
|
||||
response.content!! shouldContain it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <R> withSwaggerApplication(
|
||||
moduleFunction: Application.() -> Unit = {},
|
||||
kompendiumConfigurer: Kompendium.Configuration.() -> Unit = {},
|
||||
swaggerUIConfigurer: SwaggerUI.Configuration.() -> Unit = basicSwaggerUiConfig,
|
||||
test: TestApplicationEngine.() -> R
|
||||
) {
|
||||
withApplication(createTestEnvironment()) {
|
||||
moduleFunction(application.apply {
|
||||
kompendium(kompendiumConfigurer)
|
||||
install(SwaggerUI, swaggerUIConfigurer)
|
||||
})
|
||||
test()
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user