From 42b2ab9f256d4fa6327629b489adfdf728d084c2 Mon Sep 17 00:00:00 2001 From: Lukas Ruegner Date: Sat, 16 Sep 2023 14:04:34 +0200 Subject: [PATCH 01/12] bump version to 2.6.0 --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 32b8f281..fdc75bee 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ import io.gitlab.arturbosch.detekt.Detekt object Meta { const val groupId = "io.github.smiley4" const val artifactId = "ktor-swagger-ui" - const val version = "2.5.0" + const val version = "2.6.0" const val name = "Ktor Swagger-UI" const val description = "Ktor plugin to document routes and provide Swagger UI" const val licenseName = "The Apache License, Version 2.0" From 605b3901f7bbed716fe2ec7f5c3c2fd6f38a517f Mon Sep 17 00:00:00 2001 From: Lukas Ruegner Date: Sat, 16 Sep 2023 14:08:51 +0200 Subject: [PATCH 02/12] add note with commands to publish to maven central --- HowToRelease.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 HowToRelease.md diff --git a/HowToRelease.md b/HowToRelease.md new file mode 100644 index 00000000..afa95656 --- /dev/null +++ b/HowToRelease.md @@ -0,0 +1,9 @@ +# How to release + +https://vanniktech.github.io/gradle-maven-publish-plugin/central/ + +1. Credentials should be configured in a gradle.properties file (in user home) + +2. `./gradlew publishAllPublicationsToMavenCentral --no-configuration-cache` + +3. `./gradlew closeAndReleaseRepository` \ No newline at end of file From f9cd6261b949cec9b8de1722706138e2a81bc522 Mon Sep 17 00:00:00 2001 From: Lukas Ruegner Date: Sat, 16 Sep 2023 14:22:04 +0200 Subject: [PATCH 03/12] add testcase for issue #60 --- .../tests/schema/SchemaBuilderTest.kt | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaBuilderTest.kt index 1c1fa311..29047be5 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaBuilderTest.kt @@ -30,6 +30,35 @@ import kotlin.reflect.jvm.javaType class SchemaBuilderTest : StringSpec({ + "victools field names".config(enabled = false) { + /** + * Test-case for https://github.com/SMILEY4/ktor-swagger-ui/issues/60. + * -> victools incorrectly ignores fields with is[A-Z] + */ + createSchemaVictools(false).also { defs -> + defs.root.also { schema -> + schema.`$ref` shouldBe null + schema.type shouldBe "object" + schema.properties.keys shouldContainExactly setOf("flag", "getAnotherText", "text", "isSomething", "isSomeText") + schema.properties["flag"]!!.also { prop -> + prop.type shouldBe "boolean" + } + schema.properties["isSomething"]!!.also { prop -> + prop.type shouldBe "boolean" + } + schema.properties["text"]!!.also { prop -> + prop.type shouldBe "string" + } + schema.properties["getAnotherText"]!!.also { prop -> + prop.type shouldBe "string" + } + schema.properties["isSomeText"]!!.also { prop -> + prop.type shouldBe "string" + } + } + } + } + "primitive (victools, all definitions)" { createSchemaVictools(true).also { defs -> defs.definitions.keys shouldContainExactly setOf("int") @@ -403,6 +432,14 @@ class SchemaBuilderTest : StringSpec({ ) + data class WithFieldNames( + val flag: Boolean, + val isSomething: Boolean, + val text: String, + val isSomeText: String, + val getAnotherText: String + ) + inline fun createSchemaVictools(definitions: Boolean) = createSchema("\$defs", serializerVictools(definitions)) From f2051f228b73c1e3dd74a5e9e4a1b209018b67bb Mon Sep 17 00:00:00 2001 From: Lukas Ruegner Date: Tue, 10 Oct 2023 22:38:26 +0200 Subject: [PATCH 04/12] split endpoint over multiple specs --- .../ktorswaggerui/SwaggerController.kt | 104 --------------- .../smiley4/ktorswaggerui/SwaggerPlugin.kt | 97 +++++++++++--- .../smiley4/ktorswaggerui/SwaggerRouting.kt | 85 ------------- .../ktorswaggerui/SwaggerUIPluginConfig.kt | 26 +++- .../ktorswaggerui/routing/ResourceContent.kt | 32 +++++ .../routing/SwaggerController.kt | 120 ++++++++++++++++++ .../examples/MultipleSpecsExample.kt | 38 ++++++ 7 files changed, 291 insertions(+), 211 deletions(-) delete mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerController.kt delete mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerRouting.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/ResourceContent.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/SwaggerController.kt create mode 100644 src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/MultipleSpecsExample.kt diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerController.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerController.kt deleted file mode 100644 index d5f4fcda..00000000 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerController.kt +++ /dev/null @@ -1,104 +0,0 @@ -package io.github.smiley4.ktorswaggerui - -import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIDsl -import io.github.smiley4.ktorswaggerui.dsl.SwaggerUiSort -import io.ktor.http.ContentType -import io.ktor.http.HttpStatusCode -import io.ktor.http.content.OutgoingContent -import io.ktor.http.withCharset -import io.ktor.server.application.ApplicationCall -import io.ktor.server.response.respond -import io.ktor.server.response.respondText -import java.net.URL - -class SwaggerController( - private val swaggerWebjarVersion: String, - private val apiSpecUrl: String, - private val jsonSpecProvider: () -> String, - private val swaggerUiConfig: SwaggerUIDsl -) { - - suspend fun serveOpenApiSpec(call: ApplicationCall) { - call.respondText(ContentType.Application.Json, HttpStatusCode.OK, jsonSpecProvider) - } - - suspend fun serverSwaggerUI(call: ApplicationCall) { - when (val filename = call.parameters["filename"]!!) { - "swagger-initializer.js" -> serveSwaggerInitializer(call) - else -> serveStaticResource(filename, call) - } - } - - private suspend fun serveSwaggerInitializer(call: ApplicationCall) { - val propValidatorUrl = swaggerUiConfig.getSpecValidatorUrl()?.let { "validatorUrl: \"$it\"" } ?: "validatorUrl: false" - val propDisplayOperationId = "displayOperationId: ${swaggerUiConfig.displayOperationId}" - val propFilter = "filter: ${swaggerUiConfig.showTagFilterInput}" - val propSort = "operationsSorter: " + if (swaggerUiConfig.sort == SwaggerUiSort.NONE) "undefined" else - "\"${swaggerUiConfig.sort.value}\"" - val propSyntaxHighlight = "syntaxHighlight: { theme: \"${swaggerUiConfig.syntaxHighlight.value}\" }" - // see https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/configuration.md for reference - val content = """ - window.onload = function() { - // - window.ui = SwaggerUIBundle({ - url: "$apiSpecUrl", - dom_id: '#swagger-ui', - deepLinking: true, - presets: [ - SwaggerUIBundle.presets.apis, - SwaggerUIStandalonePreset - ], - plugins: [ - SwaggerUIBundle.plugins.DownloadUrl - ], - layout: "StandaloneLayout", - $propValidatorUrl, - $propDisplayOperationId, - $propFilter, - $propSort, - $propSyntaxHighlight - }); - // - }; - """.trimIndent() - call.respondText(ContentType.Application.JavaScript, HttpStatusCode.OK) { content } - } - - - private suspend fun serveStaticResource(filename: String, call: ApplicationCall) { - val resource = this::class.java.getResource("/META-INF/resources/webjars/swagger-ui/$swaggerWebjarVersion/$filename") - if (resource == null) { - call.respond(HttpStatusCode.NotFound, "$filename could not be found") - } else { - call.respond(ResourceContent(resource)) - } - } - -} - - -private class ResourceContent(val resource: URL) : OutgoingContent.ByteArrayContent() { - private val bytes by lazy { resource.readBytes() } - - override val contentType: ContentType? by lazy { - val extension = resource.file.substring(resource.file.lastIndexOf('.') + 1) - contentTypes[extension] ?: ContentType.Text.Html - } - - override val contentLength: Long? by lazy { - bytes.size.toLong() - } - - override fun bytes(): ByteArray = bytes - - override fun toString() = "ResourceContent \"$resource\"" -} - - -private val contentTypes = mapOf( - "html" to ContentType.Text.Html, - "css" to ContentType.Text.CSS, - "js" to ContentType.Application.JavaScript, - "json" to ContentType.Application.Json.withCharset(Charsets.UTF_8), - "png" to ContentType.Image.PNG -) diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt index f019555b..caf6dee9 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt @@ -1,9 +1,32 @@ package io.github.smiley4.ktorswaggerui import com.fasterxml.jackson.databind.ObjectMapper +import io.github.smiley4.ktorswaggerui.routing.SwaggerController import io.github.smiley4.ktorswaggerui.spec.example.ExampleContext import io.github.smiley4.ktorswaggerui.spec.example.ExampleContextBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.* +import io.github.smiley4.ktorswaggerui.spec.openapi.ComponentsBuilder +import io.github.smiley4.ktorswaggerui.spec.openapi.ContactBuilder +import io.github.smiley4.ktorswaggerui.spec.openapi.ContentBuilder +import io.github.smiley4.ktorswaggerui.spec.openapi.ExampleBuilder +import io.github.smiley4.ktorswaggerui.spec.openapi.ExternalDocumentationBuilder +import io.github.smiley4.ktorswaggerui.spec.openapi.HeaderBuilder +import io.github.smiley4.ktorswaggerui.spec.openapi.InfoBuilder +import io.github.smiley4.ktorswaggerui.spec.openapi.LicenseBuilder +import io.github.smiley4.ktorswaggerui.spec.openapi.OAuthFlowsBuilder +import io.github.smiley4.ktorswaggerui.spec.openapi.OpenApiBuilder +import io.github.smiley4.ktorswaggerui.spec.openapi.OperationBuilder +import io.github.smiley4.ktorswaggerui.spec.openapi.OperationTagsBuilder +import io.github.smiley4.ktorswaggerui.spec.openapi.ParameterBuilder +import io.github.smiley4.ktorswaggerui.spec.openapi.PathBuilder +import io.github.smiley4.ktorswaggerui.spec.openapi.PathsBuilder +import io.github.smiley4.ktorswaggerui.spec.openapi.RequestBodyBuilder +import io.github.smiley4.ktorswaggerui.spec.openapi.ResponseBuilder +import io.github.smiley4.ktorswaggerui.spec.openapi.ResponsesBuilder +import io.github.smiley4.ktorswaggerui.spec.openapi.SecurityRequirementsBuilder +import io.github.smiley4.ktorswaggerui.spec.openapi.SecuritySchemesBuilder +import io.github.smiley4.ktorswaggerui.spec.openapi.ServerBuilder +import io.github.smiley4.ktorswaggerui.spec.openapi.TagBuilder +import io.github.smiley4.ktorswaggerui.spec.openapi.TagExternalDocumentationBuilder import io.github.smiley4.ktorswaggerui.spec.route.RouteCollector import io.github.smiley4.ktorswaggerui.spec.route.RouteDocumentationMerger import io.github.smiley4.ktorswaggerui.spec.route.RouteMeta @@ -11,10 +34,15 @@ import io.github.smiley4.ktorswaggerui.spec.schema.SchemaBuilder import io.github.smiley4.ktorswaggerui.spec.schema.SchemaContext import io.github.smiley4.ktorswaggerui.spec.schema.SchemaContextBuilder import io.github.smiley4.ktorswaggerui.spec.schema.TypeOverwrites -import io.ktor.server.application.* -import io.ktor.server.application.hooks.* -import io.ktor.server.routing.* -import io.ktor.server.webjars.* +import io.ktor.server.application.Application +import io.ktor.server.application.ApplicationStarted +import io.ktor.server.application.createApplicationPlugin +import io.ktor.server.application.hooks.MonitoringEvent +import io.ktor.server.application.install +import io.ktor.server.application.plugin +import io.ktor.server.application.pluginOrNull +import io.ktor.server.routing.Routing +import io.ktor.server.webjars.Webjars import io.swagger.v3.core.util.Json import mu.KotlinLogging @@ -26,26 +54,57 @@ internal const val SWAGGER_UI_WEBJARS_VERSION = "4.15.0" private val logger = KotlinLogging.logger {} val SwaggerUI = createApplicationPlugin(name = "SwaggerUI", createConfiguration = ::SwaggerUIPluginConfig) { - var apiSpecJson = "{}" on(MonitoringEvent(ApplicationStarted)) { application -> - if (application.pluginOrNull(Webjars) == null) { - application.install(Webjars) - } + + val apiSpecsJson = mutableMapOf() + try { + if (application.pluginOrNull(Webjars) == null) { + application.install(Webjars) + } val routes = routes(application, pluginConfig) - val schemaContext = schemaContext(pluginConfig, routes) - val exampleContext = exampleContext(pluginConfig, routes) - val openApi = builder(pluginConfig, schemaContext, exampleContext).build(routes) - apiSpecJson = Json.pretty(openApi) + apiSpecsJson.putAll(buildOpenApiSpecs(pluginConfig, routes)) } catch (e: Exception) { - logger.error("Error during openapi-generation", e) + logger.error("Error during application startup in swagger-ui-plugin", e) + } + + apiSpecsJson.forEach { (name, json) -> + SwaggerController( + applicationConfig!!, + pluginConfig.getSwaggerUI(), + SWAGGER_UI_WEBJARS_VERSION, + name, + json + ).setup(application) } + + } +} + +private fun buildOpenApiSpecs(pluginConfig: SwaggerUIPluginConfig, routes: List): Map { + val routesBySpec = buildMap> { + routes.forEach { route -> + val specName = pluginConfig.specAssigner(route.path, route.documentation.tags) + computeIfAbsent(specName) { mutableListOf() }.add(route) + } + } + return buildMap { + routesBySpec.forEach { (specName, routes) -> + this[specName] = buildOpenApiSpec(pluginConfig, routes) + } + } +} + +private fun buildOpenApiSpec(pluginConfig: SwaggerUIPluginConfig, routes: List): String { + return try { + val schemaContext = schemaContext(pluginConfig, routes) + val exampleContext = exampleContext(pluginConfig, routes) + val openApi = builder(pluginConfig, schemaContext, exampleContext).build(routes) + Json.pretty(openApi) + } catch (e: Exception) { + logger.error("Error during openapi-generation", e) + "{}" } - SwaggerRouting( - pluginConfig.getSwaggerUI(), - application.environment.config, - SWAGGER_UI_WEBJARS_VERSION, - ) { apiSpecJson }.setup(application) } private fun routes(application: Application, pluginConfig: SwaggerUIPluginConfig): List { diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerRouting.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerRouting.kt deleted file mode 100644 index a3ed89bd..00000000 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerRouting.kt +++ /dev/null @@ -1,85 +0,0 @@ -package io.github.smiley4.ktorswaggerui - -import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIDsl -import io.ktor.server.application.Application -import io.ktor.server.application.call -import io.ktor.server.auth.authenticate -import io.ktor.server.config.ApplicationConfig -import io.ktor.server.response.respondRedirect -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 mu.KotlinLogging - -/** - * Registers and handles routes required for the swagger-ui - */ -class SwaggerRouting( - private val swaggerUiConfig: SwaggerUIDsl, - appConfig: ApplicationConfig, - swaggerWebjarVersion: String, - jsonSpecProvider: () -> String -) { - - private val logger = KotlinLogging.logger {} - - private val controller = SwaggerController( - swaggerWebjarVersion = swaggerWebjarVersion, - apiSpecUrl = getApiSpecUrl(appConfig), - jsonSpecProvider = jsonSpecProvider, - swaggerUiConfig = swaggerUiConfig - ) - - private fun getApiSpecUrl(appConfig: ApplicationConfig): String { - val rootPath = appConfig.propertyOrNull("ktor.deployment.rootPath")?.getString()?.let { "/${dropSlashes(it)}" } ?: "" - return "$rootPath${swaggerUiConfig.rootHostPath}/${dropSlashes(swaggerUiConfig.swaggerUrl)}/api.json" - } - - private fun dropSlashes(str: String): String { - var value = str - value = if (value.startsWith("/")) value.substring(1) else value - value = if (value.endsWith("/")) value.substring(0, value.length - 1) else value - return value - } - - /** - * registers the required routes - */ - fun setup(app: Application) { - val swaggerUrl = swaggerUiConfig.swaggerUrl - val forwardRoot = swaggerUiConfig.forwardRoot - val authentication = swaggerUiConfig.authentication - logger.info("Registering routes for swagger-ui: $swaggerUrl (forwardRoot=$forwardRoot)") - app.routing { - if (forwardRoot) { - get("/") { - call.respondRedirect("${swaggerUiConfig.rootHostPath}/${dropSlashes(swaggerUrl)}/index.html") - } - } - if (authentication == null) { - setupSwaggerRoutes() - } else { - authenticate(authentication) { - setupSwaggerRoutes() - } - } - } - } - - private fun Route.setupSwaggerRoutes() { - val swaggerUrl = swaggerUiConfig.swaggerUrl - route(swaggerUrl) { - get { - call.respondRedirect("${swaggerUiConfig.rootHostPath}/${dropSlashes(swaggerUrl)}/index.html") - } - get("api.json") { - controller.serveOpenApiSpec(call) - } - get("{filename}") { - controller.serverSwaggerUI(call) - } - } - } - -} diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIPluginConfig.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIPluginConfig.kt index 9059928b..4f70cae0 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIPluginConfig.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIPluginConfig.kt @@ -1,8 +1,18 @@ package io.github.smiley4.ktorswaggerui -import io.github.smiley4.ktorswaggerui.dsl.* -import io.ktor.http.* -import io.ktor.server.routing.* +import io.github.smiley4.ktorswaggerui.dsl.CustomSchemas +import io.github.smiley4.ktorswaggerui.dsl.EncodingConfig +import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker +import io.github.smiley4.ktorswaggerui.dsl.OpenApiExternalDocs +import io.github.smiley4.ktorswaggerui.dsl.OpenApiInfo +import io.github.smiley4.ktorswaggerui.dsl.OpenApiResponse +import io.github.smiley4.ktorswaggerui.dsl.OpenApiSecurityScheme +import io.github.smiley4.ktorswaggerui.dsl.OpenApiServer +import io.github.smiley4.ktorswaggerui.dsl.OpenApiTag +import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIDsl +import io.ktor.http.HttpMethod +import io.ktor.http.HttpStatusCode +import io.ktor.server.routing.RouteSelector import kotlin.reflect.KClass /** @@ -11,6 +21,16 @@ import kotlin.reflect.KClass @OpenApiDslMarker class SwaggerUIPluginConfig { + + fun config(name: String, config: SwaggerUIPluginConfig.() -> Unit) { + // todo: temp + } + + + // todo: temp + var specAssigner: (url: String, tags: List) -> String = { _, _ -> "api" } + + /** * Default response to automatically add to each protected route for the "Unauthorized"-Response-Code. * Generated response can be overwritten with custom response. diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/ResourceContent.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/ResourceContent.kt new file mode 100644 index 00000000..9523187a --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/ResourceContent.kt @@ -0,0 +1,32 @@ +package io.github.smiley4.ktorswaggerui.routing + +import io.ktor.http.ContentType +import io.ktor.http.content.OutgoingContent +import io.ktor.http.withCharset +import java.net.URL + +class ResourceContent(private val resource: URL) : OutgoingContent.ByteArrayContent() { + + private val contentTypes = mapOf( + "html" to ContentType.Text.Html, + "css" to ContentType.Text.CSS, + "js" to ContentType.Application.JavaScript, + "json" to ContentType.Application.Json.withCharset(Charsets.UTF_8), + "png" to ContentType.Image.PNG + ) + + private val bytes by lazy { resource.readBytes() } + + override val contentType: ContentType? by lazy { + val extension = resource.file.substring(resource.file.lastIndexOf('.') + 1) + contentTypes[extension] ?: ContentType.Text.Html + } + + override val contentLength: Long? by lazy { + bytes.size.toLong() + } + + override fun bytes(): ByteArray = bytes + + override fun toString() = "ResourceContent \"$resource\"" +} diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/SwaggerController.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/SwaggerController.kt new file mode 100644 index 00000000..c2f4721a --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/SwaggerController.kt @@ -0,0 +1,120 @@ +package io.github.smiley4.ktorswaggerui.routing + +import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIDsl +import io.github.smiley4.ktorswaggerui.dsl.SwaggerUiSort +import io.ktor.http.ContentType +import io.ktor.http.HttpStatusCode +import io.ktor.server.application.Application +import io.ktor.server.application.ApplicationCall +import io.ktor.server.application.call +import io.ktor.server.auth.authenticate +import io.ktor.server.config.ApplicationConfig +import io.ktor.server.response.respond +import io.ktor.server.response.respondRedirect +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 + +class SwaggerController( + private val appConfig: ApplicationConfig, + private val swaggerUiConfig: SwaggerUIDsl, + private val swaggerWebjarVersion: String, + private val specName: String, + private val jsonSpec: String, +) { + + fun setup(app: Application) { + app.routing { + if (swaggerUiConfig.authentication == null) { + setup() + } else { + authenticate(swaggerUiConfig.authentication) { + setup() + } + } + } + } + + private fun Route.setup() { + route(getSubUrl()) { + get { + call.respondRedirect("${getSubUrl()}/index.html") + } + get("{filename}") { + serveStaticResource(call.parameters["filename"]!!, call) + } + get("swagger-initializer.js") { + serveSwaggerInitializer(call) + } + get("$specName.json") { + serveOpenApiSpec(call) + } + } + } + + private suspend fun serveSwaggerInitializer(call: ApplicationCall) { + // see https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/configuration.md for reference + val propValidatorUrl = swaggerUiConfig.getSpecValidatorUrl()?.let { "validatorUrl: \"$it\"" } ?: "validatorUrl: false" + val propDisplayOperationId = "displayOperationId: ${swaggerUiConfig.displayOperationId}" + val propFilter = "filter: ${swaggerUiConfig.showTagFilterInput}" + val propSort = "operationsSorter: " + + if (swaggerUiConfig.sort == SwaggerUiSort.NONE) "undefined" + else "\"${swaggerUiConfig.sort.value}\"" + val propSyntaxHighlight = "syntaxHighlight: { theme: \"${swaggerUiConfig.syntaxHighlight.value}\" }" + val content = """ + window.onload = function() { + window.ui = SwaggerUIBundle({ + url: "${getRootUrl(appConfig)}/$specName.json", + dom_id: '#swagger-ui', + deepLinking: true, + presets: [ + SwaggerUIBundle.presets.apis, + SwaggerUIStandalonePreset + ], + plugins: [ + SwaggerUIBundle.plugins.DownloadUrl + ], + layout: "StandaloneLayout", + $propValidatorUrl, + $propDisplayOperationId, + $propFilter, + $propSort, + $propSyntaxHighlight + }); + }; + """.trimIndent() + call.respondText(ContentType.Application.JavaScript, HttpStatusCode.OK) { content } + } + + private suspend fun serveOpenApiSpec(call: ApplicationCall) { + call.respondText(ContentType.Application.Json, HttpStatusCode.OK) { jsonSpec } + } + + private suspend fun serveStaticResource(filename: String, call: ApplicationCall) { + val resource = this::class.java.getResource("/META-INF/resources/webjars/swagger-ui/$swaggerWebjarVersion/$filename") + if (resource != null) { + call.respond(ResourceContent(resource)) + } else { + call.respond(HttpStatusCode.NotFound, "$filename could not be found") + } + } + + private fun getRootUrl(appConfig: ApplicationConfig): String { + val rootPath = appConfig.propertyOrNull("ktor.deployment.rootPath")?.getString()?.let { "/${dropSlashes(it)}" } ?: "" + return "$rootPath${swaggerUiConfig.rootHostPath}/${dropSlashes(swaggerUiConfig.swaggerUrl)}/$specName" + } + + private fun getSubUrl(): String { + return "${swaggerUiConfig.rootHostPath}/${dropSlashes(swaggerUiConfig.swaggerUrl)}/$specName" + } + + private fun dropSlashes(str: String): String { + var value = str + value = if (value.startsWith("/")) value.substring(1) else value + value = if (value.endsWith("/")) value.substring(0, value.length - 1) else value + return value + } + +} diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/MultipleSpecsExample.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/MultipleSpecsExample.kt new file mode 100644 index 00000000..9ccf12b9 --- /dev/null +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/MultipleSpecsExample.kt @@ -0,0 +1,38 @@ +package io.github.smiley4.ktorswaggerui.examples + +import io.github.smiley4.ktorswaggerui.SwaggerUI +import io.github.smiley4.ktorswaggerui.dsl.get +import io.ktor.server.application.Application +import io.ktor.server.application.call +import io.ktor.server.application.install +import io.ktor.server.engine.embeddedServer +import io.ktor.server.netty.Netty +import io.ktor.server.response.respondText +import io.ktor.server.routing.routing + +/** + * An example showcasing multiple openapi-specs in a single application + */ +fun main() { + embeddedServer(Netty, port = 8080, host = "localhost", module = Application::myModule).start(wait = true) +} + +private fun Application.myModule() { + install(SwaggerUI) { + specAssigner = { _, tags -> tags.firstOrNull() ?: "other" } + } + routing { + get("v1/hello", { + description = "Simple version 1 'Hello World'- Route" + tags = listOf("v1") + }) { + call.respondText("Hello World!") + } + get("v2/hello", { + description = "Simple version 2 'Hello World'- Route" + tags = listOf("v2") + }) { + call.respondText("Improved Hello World!") + } + } +} From 361cd5b597345b40de97c704c92ec0d45ab64a9a Mon Sep 17 00:00:00 2001 From: Lukas Ruegner Date: Mon, 16 Oct 2023 21:57:14 +0200 Subject: [PATCH 05/12] refine: assign routes to specs, serve multiple specs --- build.gradle.kts | 2 +- .../smiley4/ktorswaggerui/SwaggerPlugin.kt | 9 +- .../ktorswaggerui/SwaggerUIPluginConfig.kt | 16 +- .../smiley4/ktorswaggerui/dsl/OpenApiRoute.kt | 5 + .../ktorswaggerui/routing/ControllerUtils.kt | 18 + .../routing/ForwardRouteController.kt | 35 ++ .../routing/SwaggerController.kt | 31 +- .../spec/route/RouteCollector.kt | 11 - .../spec/route/RouteDocumentationMerger.kt | 1 + .../examples/MultipleSpecsExample.kt | 38 +- .../ktorswaggerui/tests/ApplicationTests.kt | 457 ++++++++++++++---- .../route/RouteDocumentationMergerTest.kt | 3 + 12 files changed, 488 insertions(+), 138 deletions(-) create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/ControllerUtils.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/ForwardRouteController.kt diff --git a/build.gradle.kts b/build.gradle.kts index fdc75bee..0bca512d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -68,7 +68,7 @@ dependencies { val versionMockk = "1.12.7" testImplementation("io.mockk:mockk:$versionMockk") - val versionKotest = "5.4.2" + val versionKotest = "5.7.2" testImplementation("io.kotest:kotest-runner-junit5:$versionKotest") testImplementation("io.kotest:kotest-assertions-core:$versionKotest") diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt index caf6dee9..00299805 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt @@ -1,6 +1,7 @@ package io.github.smiley4.ktorswaggerui import com.fasterxml.jackson.databind.ObjectMapper +import io.github.smiley4.ktorswaggerui.routing.ForwardRouteController import io.github.smiley4.ktorswaggerui.routing.SwaggerController import io.github.smiley4.ktorswaggerui.spec.example.ExampleContext import io.github.smiley4.ktorswaggerui.spec.example.ExampleContextBuilder @@ -73,18 +74,22 @@ val SwaggerUI = createApplicationPlugin(name = "SwaggerUI", createConfiguration applicationConfig!!, pluginConfig.getSwaggerUI(), SWAGGER_UI_WEBJARS_VERSION, - name, + if (apiSpecsJson.size > 1) name else null, json ).setup(application) } + if(apiSpecsJson.size == 1 && pluginConfig.getSwaggerUI().forwardRoot) { + ForwardRouteController(applicationConfig!!, pluginConfig.getSwaggerUI()).setup(application) + } + } } private fun buildOpenApiSpecs(pluginConfig: SwaggerUIPluginConfig, routes: List): Map { val routesBySpec = buildMap> { routes.forEach { route -> - val specName = pluginConfig.specAssigner(route.path, route.documentation.tags) + val specName = route.documentation.specId ?: pluginConfig.specAssigner(route.path, route.documentation.tags) computeIfAbsent(specName) { mutableListOf() }.add(route) } } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIPluginConfig.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIPluginConfig.kt index 4f70cae0..8940e3f3 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIPluginConfig.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIPluginConfig.kt @@ -21,16 +21,10 @@ import kotlin.reflect.KClass @OpenApiDslMarker class SwaggerUIPluginConfig { - - fun config(name: String, config: SwaggerUIPluginConfig.() -> Unit) { - // todo: temp + companion object { + const val DEFAULT_SPEC_ID = "api" } - - // todo: temp - var specAssigner: (url: String, tags: List) -> String = { _, _ -> "api" } - - /** * Default response to automatically add to each protected route for the "Unauthorized"-Response-Code. * Generated response can be overwritten with custom response. @@ -69,6 +63,12 @@ class SwaggerUIPluginConfig { fun getTagGenerator() = tagGenerator + /** + * Assigns routes without an [io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute.specId] to a specified openapi-spec. + */ + var specAssigner: (url: String, tags: List) -> String = { _, _ -> DEFAULT_SPEC_ID } + + /** * Filter to apply to all routes. Return 'false' for routes to not include them in the OpenApi-Spec and Swagger-UI. * The url of the paths are already split at '/'. diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiRoute.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiRoute.kt index e3781dfb..68dc0dbc 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiRoute.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiRoute.kt @@ -3,6 +3,11 @@ package io.github.smiley4.ktorswaggerui.dsl @OpenApiDslMarker class OpenApiRoute { + /** + * the id of the openapi-spec this route belongs to. 'Null' to use default spec. + */ + var specId: String? = null + /** * A list of tags for API documentation control. Tags can be used for logical grouping of operations by resources or any other qualifier */ diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/ControllerUtils.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/ControllerUtils.kt new file mode 100644 index 00000000..bb990937 --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/ControllerUtils.kt @@ -0,0 +1,18 @@ +package io.github.smiley4.ktorswaggerui.routing + +import io.ktor.server.config.ApplicationConfig + +object ControllerUtils { + + fun getRootPath(appConfig: ApplicationConfig): String { + return appConfig.propertyOrNull("ktor.deployment.rootPath")?.getString()?.let { "/${dropSlashes(it)}" } ?: "" + } + + fun dropSlashes(str: String): String { + var value = str + value = if (value.startsWith("/")) value.substring(1) else value + value = if (value.endsWith("/")) value.substring(0, value.length - 1) else value + return value + } + +} diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/ForwardRouteController.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/ForwardRouteController.kt new file mode 100644 index 00000000..33f2947c --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/ForwardRouteController.kt @@ -0,0 +1,35 @@ +package io.github.smiley4.ktorswaggerui.routing + +import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIDsl +import io.ktor.server.application.Application +import io.ktor.server.application.call +import io.ktor.server.config.ApplicationConfig +import io.ktor.server.response.respondRedirect +import io.ktor.server.routing.get +import io.ktor.server.routing.routing + +class ForwardRouteController( + private val appConfig: ApplicationConfig, + private val swaggerUiConfig: SwaggerUIDsl, +) { + + fun setup(app: Application) { + app.routing { + get { + call.respondRedirect("${getRootUrl()}/index.html") + } + } + } + + private fun getRootUrl(): String { + return "/" + listOf( + ControllerUtils.getRootPath(appConfig), + swaggerUiConfig.rootHostPath, + swaggerUiConfig.swaggerUrl, + ) + .filter { it.isNotBlank() } + .map { ControllerUtils.dropSlashes(it) } + .joinToString("/") + } + +} diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/SwaggerController.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/SwaggerController.kt index c2f4721a..6d09f193 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/SwaggerController.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/SwaggerController.kt @@ -9,6 +9,7 @@ import io.ktor.server.application.ApplicationCall import io.ktor.server.application.call import io.ktor.server.auth.authenticate import io.ktor.server.config.ApplicationConfig +import io.ktor.server.request.uri import io.ktor.server.response.respond import io.ktor.server.response.respondRedirect import io.ktor.server.response.respondText @@ -21,10 +22,14 @@ class SwaggerController( private val appConfig: ApplicationConfig, private val swaggerUiConfig: SwaggerUIDsl, private val swaggerWebjarVersion: String, - private val specName: String, + private val specName: String?, private val jsonSpec: String, ) { + companion object { + const val DEFAULT_SPEC_NAME: String = "api" + } + fun setup(app: Application) { app.routing { if (swaggerUiConfig.authentication == null) { @@ -40,7 +45,7 @@ class SwaggerController( private fun Route.setup() { route(getSubUrl()) { get { - call.respondRedirect("${getSubUrl()}/index.html") + call.respondRedirect("${call.request.uri}/index.html") } get("{filename}") { serveStaticResource(call.parameters["filename"]!!, call) @@ -48,7 +53,7 @@ class SwaggerController( get("swagger-initializer.js") { serveSwaggerInitializer(call) } - get("$specName.json") { + get("${specName ?: DEFAULT_SPEC_NAME}.json") { serveOpenApiSpec(call) } } @@ -66,7 +71,7 @@ class SwaggerController( val content = """ window.onload = function() { window.ui = SwaggerUIBundle({ - url: "${getRootUrl(appConfig)}/$specName.json", + url: "${getRootUrl(appConfig)}/${specName ?: DEFAULT_SPEC_NAME}.json", dom_id: '#swagger-ui', deepLinking: true, presets: [ @@ -102,19 +107,19 @@ class SwaggerController( } private fun getRootUrl(appConfig: ApplicationConfig): String { - val rootPath = appConfig.propertyOrNull("ktor.deployment.rootPath")?.getString()?.let { "/${dropSlashes(it)}" } ?: "" - return "$rootPath${swaggerUiConfig.rootHostPath}/${dropSlashes(swaggerUiConfig.swaggerUrl)}/$specName" + return "${ControllerUtils.getRootPath(appConfig)}${getSubUrl()}" } private fun getSubUrl(): String { - return "${swaggerUiConfig.rootHostPath}/${dropSlashes(swaggerUiConfig.swaggerUrl)}/$specName" + return "/" + listOf( + swaggerUiConfig.rootHostPath, + swaggerUiConfig.swaggerUrl, + specName + ) + .filter { !it.isNullOrBlank() } + .map { ControllerUtils.dropSlashes(it!!) } + .joinToString("/") } - private fun dropSlashes(str: String): String { - var value = str - value = if (value.startsWith("/")) value.substring(1) else value - value = if (value.endsWith("/")) value.substring(0, value.length - 1) else value - return value - } } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteCollector.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteCollector.kt index 4fac4256..607e2ea3 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteCollector.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteCollector.kt @@ -31,10 +31,6 @@ class RouteCollector( protected = documentation.protected ?: isProtected(route) ) } - .filter { removeLeadingSlash(it.path) != removeLeadingSlash(config.getSwaggerUI().swaggerUrl) } - .filter { removeLeadingSlash(it.path) != removeLeadingSlash("${config.getSwaggerUI().swaggerUrl}/api.json") } - .filter { removeLeadingSlash(it.path) != removeLeadingSlash("${config.getSwaggerUI().swaggerUrl}/{filename}") } - .filter { !config.getSwaggerUI().forwardRoot || it.path != "/" } .filter { !it.documentation.hidden } .filter { path -> config.pathFilter @@ -43,13 +39,6 @@ class RouteCollector( } } - private fun removeLeadingSlash(str: String): String = - if (str.startsWith("/")) { - str.substring(1) - } else { - str - } - private fun getDocumentation(route: Route, base: OpenApiRoute): OpenApiRoute { var documentation = base if (route.selector is DocumentedRouteSelector) { diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteDocumentationMerger.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteDocumentationMerger.kt index 6c4cf509..5e6439cb 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteDocumentationMerger.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteDocumentationMerger.kt @@ -6,6 +6,7 @@ class RouteDocumentationMerger { fun merge(a: OpenApiRoute, b: OpenApiRoute): OpenApiRoute { return OpenApiRoute().apply { + specId = a.specId ?: b.specId tags = mutableListOf().also { it.addAll(a.tags) it.addAll(b.tags) diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/MultipleSpecsExample.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/MultipleSpecsExample.kt index 9ccf12b9..a489ffdb 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/MultipleSpecsExample.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/MultipleSpecsExample.kt @@ -2,6 +2,7 @@ package io.github.smiley4.ktorswaggerui.examples import io.github.smiley4.ktorswaggerui.SwaggerUI import io.github.smiley4.ktorswaggerui.dsl.get +import io.github.smiley4.ktorswaggerui.dsl.route import io.ktor.server.application.Application import io.ktor.server.application.call import io.ktor.server.application.install @@ -12,6 +13,11 @@ import io.ktor.server.routing.routing /** * An example showcasing multiple openapi-specs in a single application + * - localhost:8080/swagger-ui/v1/index.html + * * /v1/hello + * - localhost:8080/swagger-ui/v2/index.html + * * /v2/hello + * * /hi */ fun main() { embeddedServer(Netty, port = 8080, host = "localhost", module = Application::myModule).start(wait = true) @@ -19,20 +25,34 @@ fun main() { private fun Application.myModule() { install(SwaggerUI) { - specAssigner = { _, tags -> tags.firstOrNull() ?: "other" } + specAssigner = { _, _ -> "v2" } } routing { - get("v1/hello", { - description = "Simple version 1 'Hello World'- Route" - tags = listOf("v1") + route("v1", { + specId = "v1" }) { - call.respondText("Hello World!") + get("hello", { + description = "Simple version 1 'Hello World'-Route" + }) { + call.respondText("Hello World!") + } } - get("v2/hello", { - description = "Simple version 2 'Hello World'- Route" - tags = listOf("v2") + + route("v2", { + specId = "v2" }) { - call.respondText("Improved Hello World!") + get("hello", { + description = "Simple version 2 'Hello World'-Route" + }) { + call.respondText("Improved Hello World!") + } } + + get("hi", { + description = "Alternative version of 'Hello World'-Route" + }) { + call.respondText("Alternative Hello World!") + } + } } diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/ApplicationTests.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/ApplicationTests.kt index 43197f74..1b6479ca 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/ApplicationTests.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/ApplicationTests.kt @@ -4,6 +4,7 @@ import io.github.smiley4.ktorswaggerui.SwaggerUI import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.dsl.get import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain import io.kotest.matchers.string.shouldNotBeEmpty import io.ktor.client.request.get import io.ktor.client.statement.bodyAsText @@ -24,141 +25,409 @@ import kotlin.test.Test class ApplicationTests { - private fun ApplicationTestBuilder.setupTestApplication(pluginConfig: SwaggerUIPluginConfig.() -> Unit) { - application { - install(Authentication) { - basic("my-auth") { - validate { credentials -> - if (credentials.name == "user" && credentials.password == "pass") { - UserIdPrincipal(credentials.name) - } else { - null - } - } - } - } - install(SwaggerUI) { pluginConfig() } - routing { - get("hello", { - description = "Simple 'Hello World'- Route" - response { - HttpStatusCode.OK to { - description = "Successful Response" - } - } - }) { - call.respondText("Hello Test") - } - } + @Test + fun minimal() = swaggerUITestApplication { + get("hello").also { + it.status shouldBe HttpStatusCode.OK + it.body shouldBe "Hello Test" + } + get("/").also { + it.status shouldBe HttpStatusCode.NotFound + } + get("/swagger-ui").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Text.Html + it.body.shouldNotBeEmpty() + } + get("/swagger-ui/index.html").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Text.Html + it.body.shouldNotBeEmpty() + } + get("/swagger-ui/swagger-initializer.js").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Application.JavaScript.withCharset(Charsets.UTF_8) + it.body shouldContain "url: \"/swagger-ui/api.json\"" + } + get("/swagger-ui/api.json").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Application.Json.withCharset(Charsets.UTF_8) + it.body.shouldNotBeEmpty() } } @Test - fun testDefaultSwaggerUi() = testApplication { - setupTestApplication {} - client.get("/hello").bodyAsText() shouldBe "Hello Test" - client.get("/").status shouldBe HttpStatusCode.NotFound - client.get("/swagger-ui").let { + fun customRootHost() = swaggerUITestApplication({ + swagger { + rootHostPath = "my-root" + } + }) { + get("hello").also { + it.status shouldBe HttpStatusCode.OK + it.body shouldBe "Hello Test" + } + get("/").also { + it.status shouldBe HttpStatusCode.NotFound + } + get("my-root/swagger-ui").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Text.Html + it.body.shouldNotBeEmpty() + } + get("my-root/swagger-ui/index.html").also { it.status shouldBe HttpStatusCode.OK - it.contentType() shouldBe ContentType.Text.Html - it.bodyAsText().shouldNotBeEmpty() + it.contentType shouldBe ContentType.Text.Html + it.body.shouldNotBeEmpty() } - client.get("/swagger-ui/api.json").let { + get("my-root/swagger-ui/swagger-initializer.js").also { it.status shouldBe HttpStatusCode.OK - it.contentType() shouldBe ContentType.Application.Json.withCharset(Charsets.UTF_8) - it.bodyAsText().shouldNotBeEmpty() + it.contentType shouldBe ContentType.Application.JavaScript.withCharset(Charsets.UTF_8) + it.body shouldContain "url: \"/my-root/swagger-ui/api.json\"" + + } + get("my-root/swagger-ui/api.json").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Application.Json.withCharset(Charsets.UTF_8) + it.body.shouldNotBeEmpty() } } + @Test - fun testSwaggerUiForwardRoot() = testApplication { - setupTestApplication { - swagger { - forwardRoot = true - } + fun forwardRoot() = swaggerUITestApplication({ + swagger { + forwardRoot = true + } + }) { + get("hello").also { + it.status shouldBe HttpStatusCode.OK + it.body shouldBe "Hello Test" + } + get("/").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Text.Html + it.body.shouldNotBeEmpty() + } + get("/swagger-ui").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Text.Html + it.body.shouldNotBeEmpty() } - client.get("/hello").bodyAsText() shouldBe "Hello Test" - client.get("/").let { + get("/swagger-ui/index.html").also { it.status shouldBe HttpStatusCode.OK - it.contentType() shouldBe ContentType.Text.Html - it.bodyAsText().shouldNotBeEmpty() + it.contentType shouldBe ContentType.Text.Html + it.body.shouldNotBeEmpty() } - client.get("/swagger-ui").let { + get("/swagger-ui/swagger-initializer.js").also { it.status shouldBe HttpStatusCode.OK - it.contentType() shouldBe ContentType.Text.Html - it.bodyAsText().shouldNotBeEmpty() + it.contentType shouldBe ContentType.Application.JavaScript.withCharset(Charsets.UTF_8) + it.body shouldContain "url: \"/swagger-ui/api.json\"" } - client.get("/swagger-ui/api.json").let { + get("/swagger-ui/api.json").also { it.status shouldBe HttpStatusCode.OK - it.contentType() shouldBe ContentType.Application.Json.withCharset(Charsets.UTF_8) - it.bodyAsText().shouldNotBeEmpty() + it.contentType shouldBe ContentType.Application.Json.withCharset(Charsets.UTF_8) + it.body.shouldNotBeEmpty() } } @Test - fun testSwaggerUiProtected() = testApplication { - setupTestApplication { - swagger { - authentication = "my-auth" - } + fun forwardRootWithCustomSwaggerUrl() = swaggerUITestApplication({ + swagger { + forwardRoot = true + swaggerUrl = "test-swagger" + } + }) { + get("/").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Text.Html + it.body.shouldNotBeEmpty() + } + get("/test-swagger").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Text.Html + it.body.shouldNotBeEmpty() + } + get("/test-swagger/index.html").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Text.Html + it.body.shouldNotBeEmpty() } - client.get("/hello").bodyAsText() shouldBe "Hello Test" - client.get("/").status shouldBe HttpStatusCode.NotFound - client.get("/swagger-ui").status shouldBe HttpStatusCode.Unauthorized - client.get("/swagger-ui/api.json").status shouldBe HttpStatusCode.Unauthorized } + @Test - fun testSwaggerUiProtectedAndForwardRoot() = testApplication { - setupTestApplication { - swagger { - authentication = "my-auth" - forwardRoot = true - } + fun protectedSwaggerUI() = swaggerUITestApplication({ + swagger { + authentication = "my-auth" + } + }) { + get("hello").also { + it.status shouldBe HttpStatusCode.OK + it.body shouldBe "Hello Test" + } + get("/").also { + it.status shouldBe HttpStatusCode.NotFound + } + get("/swagger-ui").also { + it.status shouldBe HttpStatusCode.Unauthorized + } + get("/swagger-ui/index.html").also { + it.status shouldBe HttpStatusCode.Unauthorized + } + get("/swagger-ui/swagger-initializer.js").also { + it.status shouldBe HttpStatusCode.Unauthorized + } + get("/swagger-ui/api.json").also { + it.status shouldBe HttpStatusCode.Unauthorized } - client.get("/hello").bodyAsText() shouldBe "Hello Test" - client.get("/").status shouldBe HttpStatusCode.Unauthorized - client.get("/swagger-ui").status shouldBe HttpStatusCode.Unauthorized - client.get("/swagger-ui/api.json").status shouldBe HttpStatusCode.Unauthorized } @Test - fun testSwaggerUiCustomRoute() = testApplication { - setupTestApplication { - swagger { - swaggerUrl = "test-swagger" - } + fun forwardRootAndProtectedSwaggerUI() = swaggerUITestApplication({ + swagger { + authentication = "my-auth" + forwardRoot = true + } + }) { + get("hello").also { + it.status shouldBe HttpStatusCode.OK + it.body shouldBe "Hello Test" + } + get("/").also { + it.status shouldBe HttpStatusCode.Unauthorized + } + get("/swagger-ui").also { + it.status shouldBe HttpStatusCode.Unauthorized + } + get("/swagger-ui/index.html").also { + it.status shouldBe HttpStatusCode.Unauthorized + } + get("/swagger-ui/swagger-initializer.js").also { + it.status shouldBe HttpStatusCode.Unauthorized + } + get("/swagger-ui/api.json").also { + it.status shouldBe HttpStatusCode.Unauthorized + } + } + + + @Test + fun customSwaggerUrl() = swaggerUITestApplication({ + swagger { + swaggerUrl = "test-swagger" + } + }) { + get("hello").also { + it.status shouldBe HttpStatusCode.OK + it.body shouldBe "Hello Test" + } + get("/").also { + it.status shouldBe HttpStatusCode.NotFound + } + get("/swagger-ui").also { + it.status shouldBe HttpStatusCode.NotFound + } + get("/swagger-ui/index.html").also { + it.status shouldBe HttpStatusCode.NotFound + } + get("/swagger-ui/swagger-initializer.js").also { + it.status shouldBe HttpStatusCode.NotFound } - client.get("/hello").bodyAsText() shouldBe "Hello Test" - client.get("/swagger-ui").status shouldBe HttpStatusCode.NotFound - client.get("/swagger-ui/api.json").status shouldBe HttpStatusCode.NotFound - client.get("/test-swagger").let { + get("/swagger-ui/api.json").also { + it.status shouldBe HttpStatusCode.NotFound + } + get("/test-swagger").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Text.Html + it.body.shouldNotBeEmpty() + } + get("/test-swagger/index.html").also { it.status shouldBe HttpStatusCode.OK - it.contentType() shouldBe ContentType.Text.Html - it.bodyAsText().shouldNotBeEmpty() + it.contentType shouldBe ContentType.Text.Html + it.body.shouldNotBeEmpty() } - client.get("/test-swagger/api.json").let { + get("/test-swagger/swagger-initializer.js").also { it.status shouldBe HttpStatusCode.OK - it.contentType() shouldBe ContentType.Application.Json.withCharset(Charsets.UTF_8) - it.bodyAsText().shouldNotBeEmpty() + it.contentType shouldBe ContentType.Application.JavaScript.withCharset(Charsets.UTF_8) + it.body shouldContain "url: \"/test-swagger/api.json\"" + + } + get("/test-swagger/api.json").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Application.Json.withCharset(Charsets.UTF_8) + it.body.shouldNotBeEmpty() } } + @Test - fun testSwaggerUiCustomRouteProtected() = testApplication { - setupTestApplication { - swagger { - authentication = "my-auth" - swaggerUrl = "test-swagger" + fun customSwaggerUrlAndProtected() = swaggerUITestApplication({ + swagger { + authentication = "my-auth" + swaggerUrl = "test-swagger" + } + }) { + get("hello").also { + it.status shouldBe HttpStatusCode.OK + it.body shouldBe "Hello Test" + } + get("/").also { + it.status shouldBe HttpStatusCode.NotFound + } + get("/swagger-ui").also { + it.status shouldBe HttpStatusCode.NotFound + } + get("/swagger-ui/index.html").also { + it.status shouldBe HttpStatusCode.NotFound + } + get("/swagger-ui/swagger-initializer.js").also { + it.status shouldBe HttpStatusCode.NotFound + } + get("/swagger-ui/api.json").also { + it.status shouldBe HttpStatusCode.NotFound + } + get("/test-swagger").also { + it.status shouldBe HttpStatusCode.Unauthorized + } + get("/test-swagger/index.html").also { + it.status shouldBe HttpStatusCode.Unauthorized + } + get("/test-swagger/swagger-initializer.js").also { + it.status shouldBe HttpStatusCode.Unauthorized + } + get("/test-swagger/api.json").also { + it.status shouldBe HttpStatusCode.Unauthorized + } + } + + + @Test + fun multipleSwaggerUI() = swaggerUITestApplication({ + specAssigner = {_, tags -> tags.firstOrNull() ?: "other"} + }) { + get("hello").also { + it.status shouldBe HttpStatusCode.OK + it.body shouldBe "Hello Test" + } + get("/").also { + it.status shouldBe HttpStatusCode.NotFound + } + get("/swagger-ui/hello").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Text.Html + it.body.shouldNotBeEmpty() + } + get("/swagger-ui/hello/index.html").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Text.Html + it.body.shouldNotBeEmpty() + } + get("/swagger-ui/hello/swagger-initializer.js").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Application.JavaScript.withCharset(Charsets.UTF_8) + it.body shouldContain "url: \"/swagger-ui/hello/hello.json\"" + } + get("/swagger-ui/hello/hello.json").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Application.Json.withCharset(Charsets.UTF_8) + it.body.shouldNotBeEmpty() + } + get("/swagger-ui/world").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Text.Html + it.body.shouldNotBeEmpty() + } + get("/swagger-ui/world/index.html").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Text.Html + it.body.shouldNotBeEmpty() + } + get("/swagger-ui/world/swagger-initializer.js").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Application.JavaScript.withCharset(Charsets.UTF_8) + it.body shouldContain "url: \"/swagger-ui/world/world.json\"" + } + get("/swagger-ui/world/world.json").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Application.Json.withCharset(Charsets.UTF_8) + it.body.shouldNotBeEmpty() + } + } + + + private fun swaggerUITestApplication(block: suspend ApplicationTestBuilder.() -> Unit) { + swaggerUITestApplication({}, block) + } + + private fun swaggerUITestApplication(pluginConfig: SwaggerUIPluginConfig.() -> Unit, block: suspend ApplicationTestBuilder.() -> Unit) { + testApplication { + application { + install(Authentication) { + basic("my-auth") { + validate { credentials -> + if (credentials.name == "user" && credentials.password == "pass") { + UserIdPrincipal(credentials.name) + } else { + null + } + } + } + } + install(SwaggerUI, pluginConfig) + routing { + get("hello", { + tags = listOf("hello") + description = "Simple 'Hello World'- Route" + response { + HttpStatusCode.OK to { + description = "Successful Response" + } + } + }) { + call.respondText("Hello Test") + } + get("world", { + tags = listOf("world") + description = "Another simple 'Hello World'- Route" + response { + HttpStatusCode.OK to { + description = "Successful Response" + } + } + }) { + call.respondText("Hello World") + } + } + Thread.sleep(500) } + block() } - client.get("/hello").bodyAsText() shouldBe "Hello Test" - client.get("/swagger-ui").status shouldBe HttpStatusCode.NotFound - client.get("/swagger-ui/api.json").status shouldBe HttpStatusCode.NotFound - client.get("/test-swagger").status shouldBe HttpStatusCode.Unauthorized - client.get("/test-swagger/api.json").status shouldBe HttpStatusCode.Unauthorized + } + + private suspend fun ApplicationTestBuilder.get(path: String): GetResult { + return client.get(path) + .let { + GetResult( + path = path, + status = it.status, + contentType = it.contentType(), + body = it.bodyAsText() + ) + } + .also { it.print() } + } + + private data class GetResult( + val path: String, + val status: HttpStatusCode, + val contentType: ContentType?, + val body: String, + ) + + private fun GetResult.print() { + println("GET ${this.path} => ${this.status} (${this.contentType}): ${this.body}") } } diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/route/RouteDocumentationMergerTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/route/RouteDocumentationMergerTest.kt index 9a62986c..6799788a 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/route/RouteDocumentationMergerTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/route/RouteDocumentationMergerTest.kt @@ -37,6 +37,7 @@ class RouteDocumentationMergerTest : StringSpec({ "merge complete routes" { merge( route { + specId = "test-spec-a" tags = listOf("a1", "a2") summary = "Summary A" description = "Description A" @@ -60,6 +61,7 @@ class RouteDocumentationMergerTest : StringSpec({ } }, route { + specId = "test-spec-b" tags = listOf("b1", "b2") summary = "Summary B" description = "Description B" @@ -83,6 +85,7 @@ class RouteDocumentationMergerTest : StringSpec({ } } ).also { route -> + route.specId shouldBe "test-spec-a" route.tags shouldContainExactlyInAnyOrder listOf("a1", "a2", "b1", "b2") route.summary shouldBe "Summary A" route.description shouldBe "Description A" From 7656845448582966e144db27307aa1ab222fc19c Mon Sep 17 00:00:00 2001 From: Lukas Ruegner Date: Fri, 20 Oct 2023 14:50:43 +0200 Subject: [PATCH 06/12] split dsl from data for plugin-config --- src/_todo | 5 + .../smiley4/ktorswaggerui/SwaggerPlugin.kt | 41 ++++---- .../ktorswaggerui/data/AuthKeyLocation.kt | 9 ++ .../smiley4/ktorswaggerui/data/AuthScheme.kt | 14 +++ .../smiley4/ktorswaggerui/data/AuthType.kt | 11 +++ .../ktorswaggerui/data/BaseCustomSchema.kt | 11 +++ .../smiley4/ktorswaggerui/data/ContactData.kt | 15 +++ .../smiley4/ktorswaggerui/data/DataUtils.kt | 11 +++ .../ktorswaggerui/data/EncodingData.kt | 99 +++++++++++++++++++ .../ktorswaggerui/data/ExternalDocsData.kt | 15 +++ .../smiley4/ktorswaggerui/data/InfoData.kt | 24 +++++ .../smiley4/ktorswaggerui/data/LicenseData.kt | 13 +++ .../ktorswaggerui/data/OpenIdOAuthFlowData.kt | 19 ++++ .../data/OpenIdOAuthFlowsData.kt | 19 ++++ .../smiley4/ktorswaggerui/data/PathFilter.kt | 6 ++ .../ktorswaggerui/data/PluginConfigData.kt | 45 +++++++++ .../ktorswaggerui/data/SecuritySchemeData.kt | 27 +++++ .../smiley4/ktorswaggerui/data/ServerData.kt | 15 +++ .../ktorswaggerui/data/SpecAssigned.kt | 7 ++ .../ktorswaggerui/data/SwaggerUIData.kt | 29 ++++++ .../ktorswaggerui/data/SwaggerUiSort.kt | 20 ++++ .../data/SwaggerUiSyntaxHighlight.kt | 10 ++ .../smiley4/ktorswaggerui/data/TagData.kt | 18 ++++ .../ktorswaggerui/data/TagGenerator.kt | 7 ++ .../ktorswaggerui/dsl/CustomSchemas.kt | 11 +-- .../ktorswaggerui/dsl/EncodingConfig.kt | 92 ++--------------- .../ktorswaggerui/dsl/OpenApiContact.kt | 16 ++- .../ktorswaggerui/dsl/OpenApiExternalDocs.kt | 10 ++ .../smiley4/ktorswaggerui/dsl/OpenApiInfo.kt | 20 +++- .../ktorswaggerui/dsl/OpenApiLicense.kt | 12 ++- .../dsl/OpenApiSecurityScheme.kt | 47 ++++----- .../ktorswaggerui/dsl/OpenApiServer.kt | 13 ++- .../smiley4/ktorswaggerui/dsl/OpenApiTag.kt | 11 +++ .../ktorswaggerui/dsl/OpenIdOAuthFlow.kt | 12 +++ .../ktorswaggerui/dsl/OpenIdOAuthFlows.kt | 10 ++ .../smiley4/ktorswaggerui/dsl/SwaggerUIDsl.kt | 72 +++++++------- .../{ => dsl}/SwaggerUIPluginConfig.kt | 84 +++++++++++----- .../routing/ForwardRouteController.kt | 8 +- .../routing/SwaggerController.kt | 17 ++-- .../spec/openapi/ComponentsBuilder.kt | 8 +- .../spec/openapi/ContactBuilder.kt | 4 +- .../spec/openapi/ExampleBuilder.kt | 6 +- .../openapi/ExternalDocumentationBuilder.kt | 4 +- .../ktorswaggerui/spec/openapi/InfoBuilder.kt | 8 +- .../spec/openapi/LicenseBuilder.kt | 4 +- .../spec/openapi/OAuthFlowsBuilder.kt | 18 ++-- .../spec/openapi/OpenApiBuilder.kt | 12 +-- .../spec/openapi/OperationTagsBuilder.kt | 6 +- .../spec/openapi/ResponsesBuilder.kt | 8 +- .../openapi/SecurityRequirementsBuilder.kt | 7 +- .../spec/openapi/SecuritySchemesBuilder.kt | 7 +- .../spec/openapi/ServerBuilder.kt | 4 +- .../ktorswaggerui/spec/openapi/TagBuilder.kt | 6 +- .../spec/route/RouteCollector.kt | 15 ++- .../spec/route/RouteDocumentationMerger.kt | 3 + .../spec/schema/SchemaContextBuilder.kt | 18 ++-- .../ktorswaggerui/examples/AuthExample.kt | 4 +- .../examples/CompletePluginConfigExample.kt | 11 ++- .../ktorswaggerui/tests/ApplicationTests.kt | 2 +- .../tests/example/ExampleTest.kt | 5 +- .../tests/openapi/ExternalDocsBuilderTest.kt | 3 +- .../tests/openapi/InfoBuilderTest.kt | 3 +- .../tests/openapi/OpenApiBuilderTest.kt | 18 ++-- .../tests/openapi/OperationBuilderTest.kt | 14 +-- .../tests/openapi/PathsBuilderTest.kt | 14 +-- .../openapi/SecuritySchemesBuilderTest.kt | 9 +- .../tests/openapi/ServersBuilderTest.kt | 3 +- .../tests/openapi/TagsBuilderTest.kt | 3 +- .../tests/schema/SchemaContextTest.kt | 5 +- 69 files changed, 832 insertions(+), 325 deletions(-) create mode 100644 src/_todo create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthKeyLocation.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthScheme.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthType.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/data/BaseCustomSchema.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ContactData.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/data/DataUtils.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/data/EncodingData.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ExternalDocsData.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/data/InfoData.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/data/LicenseData.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenIdOAuthFlowData.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenIdOAuthFlowsData.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PathFilter.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PluginConfigData.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SecuritySchemeData.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ServerData.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SpecAssigned.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUIData.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUiSort.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUiSyntaxHighlight.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TagData.kt create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TagGenerator.kt rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{ => dsl}/SwaggerUIPluginConfig.kt (58%) diff --git a/src/_todo b/src/_todo new file mode 100644 index 00000000..c23b7d2f --- /dev/null +++ b/src/_todo @@ -0,0 +1,5 @@ +TODO: +- test +- separate plugin-configs for different specs (another feature/branch?) +- manually serve swagger-files (another feature/branch?) + - add documented-route-selector to "internal" swagger-routes + hidden-flag ? diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt index 00299805..4d371b7b 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt @@ -1,6 +1,8 @@ package io.github.smiley4.ktorswaggerui import com.fasterxml.jackson.databind.ObjectMapper +import io.github.smiley4.ktorswaggerui.data.PluginConfigData +import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.routing.ForwardRouteController import io.github.smiley4.ktorswaggerui.routing.SwaggerController import io.github.smiley4.ktorswaggerui.spec.example.ExampleContext @@ -55,6 +57,9 @@ internal const val SWAGGER_UI_WEBJARS_VERSION = "4.15.0" private val logger = KotlinLogging.logger {} val SwaggerUI = createApplicationPlugin(name = "SwaggerUI", createConfiguration = ::SwaggerUIPluginConfig) { + + val config = pluginConfig.build(PluginConfigData.DEFAULT) + on(MonitoringEvent(ApplicationStarted)) { application -> val apiSpecsJson = mutableMapOf() @@ -63,8 +68,8 @@ val SwaggerUI = createApplicationPlugin(name = "SwaggerUI", createConfiguration if (application.pluginOrNull(Webjars) == null) { application.install(Webjars) } - val routes = routes(application, pluginConfig) - apiSpecsJson.putAll(buildOpenApiSpecs(pluginConfig, routes)) + val routes = routes(application, config) + apiSpecsJson.putAll(buildOpenApiSpecs(config, routes)) } catch (e: Exception) { logger.error("Error during application startup in swagger-ui-plugin", e) } @@ -72,35 +77,35 @@ val SwaggerUI = createApplicationPlugin(name = "SwaggerUI", createConfiguration apiSpecsJson.forEach { (name, json) -> SwaggerController( applicationConfig!!, - pluginConfig.getSwaggerUI(), + config, SWAGGER_UI_WEBJARS_VERSION, if (apiSpecsJson.size > 1) name else null, json ).setup(application) } - if(apiSpecsJson.size == 1 && pluginConfig.getSwaggerUI().forwardRoot) { - ForwardRouteController(applicationConfig!!, pluginConfig.getSwaggerUI()).setup(application) + if (apiSpecsJson.size == 1 && config.swaggerUI.forwardRoot) { + ForwardRouteController(applicationConfig!!, config).setup(application) } } } -private fun buildOpenApiSpecs(pluginConfig: SwaggerUIPluginConfig, routes: List): Map { +private fun buildOpenApiSpecs(config: PluginConfigData, routes: List): Map { val routesBySpec = buildMap> { routes.forEach { route -> - val specName = route.documentation.specId ?: pluginConfig.specAssigner(route.path, route.documentation.tags) + val specName = route.documentation.specId ?: config.specAssigner(route.path, route.documentation.tags) computeIfAbsent(specName) { mutableListOf() }.add(route) } } return buildMap { routesBySpec.forEach { (specName, routes) -> - this[specName] = buildOpenApiSpec(pluginConfig, routes) + this[specName] = buildOpenApiSpec(config, routes) } } } -private fun buildOpenApiSpec(pluginConfig: SwaggerUIPluginConfig, routes: List): String { +private fun buildOpenApiSpec(pluginConfig: PluginConfigData, routes: List): String { return try { val schemaContext = schemaContext(pluginConfig, routes) val exampleContext = exampleContext(pluginConfig, routes) @@ -112,33 +117,33 @@ private fun buildOpenApiSpec(pluginConfig: SwaggerUIPluginConfig, routes: List { +private fun routes(application: Application, config: PluginConfigData): List { return RouteCollector(RouteDocumentationMerger()) - .collectRoutes({ application.plugin(Routing) }, pluginConfig) + .collectRoutes({ application.plugin(Routing) }, config) .toList() } -private fun schemaContext(pluginConfig: SwaggerUIPluginConfig, routes: List): SchemaContext { +private fun schemaContext(config: PluginConfigData, routes: List): SchemaContext { return SchemaContextBuilder( - config = pluginConfig, + config = config, schemaBuilder = SchemaBuilder( - definitionsField = pluginConfig.encodingConfig.schemaDefinitionsField, - schemaEncoder = pluginConfig.encodingConfig.getSchemaEncoder(), + definitionsField = config.encoding.schemaDefsField, + schemaEncoder = config.encoding.schemaEncoder, ObjectMapper(), TypeOverwrites.get() ), ).build(routes.toList()) } -private fun exampleContext(pluginConfig: SwaggerUIPluginConfig, routes: List): ExampleContext { +private fun exampleContext(config: PluginConfigData, routes: List): ExampleContext { return ExampleContextBuilder( exampleBuilder = ExampleBuilder( - config = pluginConfig + config = config ) ).build(routes.toList()) } -private fun builder(config: SwaggerUIPluginConfig, schemaContext: SchemaContext, exampleContext: ExampleContext): OpenApiBuilder { +private fun builder(config: PluginConfigData, schemaContext: SchemaContext, exampleContext: ExampleContext): OpenApiBuilder { return OpenApiBuilder( config = config, schemaContext = schemaContext, diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthKeyLocation.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthKeyLocation.kt new file mode 100644 index 00000000..61c5887b --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthKeyLocation.kt @@ -0,0 +1,9 @@ +package io.github.smiley4.ktorswaggerui.data + +import io.swagger.v3.oas.models.security.SecurityScheme + +enum class AuthKeyLocation(val swaggerType: SecurityScheme.In) { + QUERY(SecurityScheme.In.QUERY), + HEADER(SecurityScheme.In.HEADER), + COOKIE(SecurityScheme.In.COOKIE) +} \ No newline at end of file diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthScheme.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthScheme.kt new file mode 100644 index 00000000..67b2fad7 --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthScheme.kt @@ -0,0 +1,14 @@ +package io.github.smiley4.ktorswaggerui.data + +// https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml +enum class AuthScheme(val swaggerType: String) { + BASIC("Basic"), + BEARER("Bearer"), + DIGEST("Digest"), + HOBA("HOBA"), + MUTUAL("Mutual"), + OAUTH("OAuth"), + SCRAM_SHA_1("SCRAM-SHA-1"), + SCRAM_SHA_256("SCRAM-SHA-256"), + VAPID("vapid") +} \ No newline at end of file diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthType.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthType.kt new file mode 100644 index 00000000..897ae471 --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthType.kt @@ -0,0 +1,11 @@ +package io.github.smiley4.ktorswaggerui.data + +import io.swagger.v3.oas.models.security.SecurityScheme + +enum class AuthType(val swaggerType: SecurityScheme.Type) { + API_KEY(SecurityScheme.Type.APIKEY), + HTTP(SecurityScheme.Type.HTTP), + OAUTH2(SecurityScheme.Type.OAUTH2), + OPENID_CONNECT(SecurityScheme.Type.OPENIDCONNECT), + MUTUAL_TLS(SecurityScheme.Type.MUTUALTLS) +} diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/BaseCustomSchema.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/BaseCustomSchema.kt new file mode 100644 index 00000000..0668d5f8 --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/BaseCustomSchema.kt @@ -0,0 +1,11 @@ +package io.github.smiley4.ktorswaggerui.data + +import io.swagger.v3.oas.models.media.Schema + +sealed class BaseCustomSchema + +class CustomJsonSchema(val provider: () -> String) : BaseCustomSchema() + +class CustomOpenApiSchema(val provider: () -> Schema) : BaseCustomSchema() + +class RemoteSchema(val url: String) : BaseCustomSchema() \ No newline at end of file diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ContactData.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ContactData.kt new file mode 100644 index 00000000..4a5f6968 --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ContactData.kt @@ -0,0 +1,15 @@ +package io.github.smiley4.ktorswaggerui.data + +data class ContactData( + val name: String?, + val url: String?, + val email: String? +) { + companion object { + val DEFAULT = ContactData( + name = null, + url = null, + email = null + ) + } +} diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/DataUtils.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/DataUtils.kt new file mode 100644 index 00000000..fd0154d1 --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/DataUtils.kt @@ -0,0 +1,11 @@ +package io.github.smiley4.ktorswaggerui.data + +object DataUtils { + + fun mergeBoolean(base: Boolean, value: Boolean) = if (value) true else base + + fun mergeDefault(base: T, value: T, default: T) = if (value != default) value else base + + fun merge(base: T?, value: T?) = value ?: base + +} \ No newline at end of file diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/EncodingData.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/EncodingData.kt new file mode 100644 index 00000000..ed259b78 --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/EncodingData.kt @@ -0,0 +1,99 @@ +package io.github.smiley4.ktorswaggerui.data + +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.github.victools.jsonschema.generator.Option +import com.github.victools.jsonschema.generator.OptionPreset +import com.github.victools.jsonschema.generator.SchemaGenerator +import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder +import com.github.victools.jsonschema.generator.SchemaVersion +import com.github.victools.jsonschema.module.jackson.JacksonModule +import com.github.victools.jsonschema.module.swagger2.Swagger2Module +import io.github.smiley4.ktorswaggerui.dsl.ExampleEncoder +import io.github.smiley4.ktorswaggerui.dsl.SchemaEncoder +import io.github.smiley4.ktorswaggerui.dsl.SchemaType +import io.github.smiley4.ktorswaggerui.spec.schema.SchemaTypeAttributeOverride +import kotlin.reflect.jvm.javaType + +data class EncodingData( + val exampleEncoder: ExampleEncoder, + val schemaEncoder: SchemaEncoder, + val schemaDefsField: String +) { + + companion object { + val DEFAULT = EncodingData( + exampleEncoder = defaultExampleEncoder(), + schemaEncoder = defaultSchemaEncoder(), + schemaDefsField = "\$defs" + ) + + + /** + * The default jackson object mapper used for encoding examples to json. + */ + var DEFAULT_EXAMPLE_OBJECT_MAPPER = jacksonObjectMapper() + + + /** + * The default [SchemaGenerator] used to encode types to json-schema. + * See https://victools.github.io/jsonschema-generator/#generator-options for more information. + */ + var DEFAULT_SCHEMA_GENERATOR = SchemaGenerator(schemaGeneratorConfigBuilder().build()) + + + /** + * The default [ExampleEncoder] + */ + fun defaultExampleEncoder(): ExampleEncoder { + return { _, value -> encodeExample(value) } + } + + + /** + * encode the given value to a json string + */ + fun encodeExample(value: Any?): String { + return if (value is String) { + value + } else { + DEFAULT_EXAMPLE_OBJECT_MAPPER.writeValueAsString(value) + } + } + + + /** + * The default [SchemaEncoder] + */ + fun defaultSchemaEncoder(): SchemaEncoder { + return { type -> encodeSchema(type) } + } + + + /** + * encode the given type to a json-schema + */ + fun encodeSchema(type: SchemaType): String { + return DEFAULT_SCHEMA_GENERATOR.generateSchema(type.javaType).toPrettyString() + } + + + /** + * The default [SchemaGeneratorConfigBuilder] + */ + fun schemaGeneratorConfigBuilder(): SchemaGeneratorConfigBuilder = + SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_2019_09, OptionPreset.PLAIN_JSON) + .with(JacksonModule()) + .with(Swagger2Module()) + .with(Option.EXTRA_OPEN_API_FORMAT_VALUES) + .with(Option.ALLOF_CLEANUP_AT_THE_END) + .with(Option.MAP_VALUES_AS_ADDITIONAL_PROPERTIES) + .with(Option.DEFINITIONS_FOR_ALL_OBJECTS) + .without(Option.INLINE_ALL_SCHEMAS) + .also { + it.forTypesInGeneral() + .withTypeAttributeOverride(SchemaTypeAttributeOverride()) + } + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ExternalDocsData.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ExternalDocsData.kt new file mode 100644 index 00000000..83777895 --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ExternalDocsData.kt @@ -0,0 +1,15 @@ +package io.github.smiley4.ktorswaggerui.data + +data class ExternalDocsData( + val url: String, + val description: String?, +) { + + companion object { + val DEFAULT = ExternalDocsData( + url = "/", + description = null + ) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/InfoData.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/InfoData.kt new file mode 100644 index 00000000..eb63cec7 --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/InfoData.kt @@ -0,0 +1,24 @@ +package io.github.smiley4.ktorswaggerui.data + +data class InfoData( + val title: String, + val version: String?, + val description: String?, + val termsOfService: String?, + val contact: ContactData?, + val license: LicenseData? +) { + companion object { + val DEFAULT = InfoData( + title = "API", + version = null, + description = null, + termsOfService = null, + contact = null, + license = null + ) + } +} + + + diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/LicenseData.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/LicenseData.kt new file mode 100644 index 00000000..7a33da0b --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/LicenseData.kt @@ -0,0 +1,13 @@ +package io.github.smiley4.ktorswaggerui.data + +data class LicenseData( + val name: String?, + val url: String?, +) { + companion object { + val DEFAULT = LicenseData( + name = null, + url = null, + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenIdOAuthFlowData.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenIdOAuthFlowData.kt new file mode 100644 index 00000000..3072dec0 --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenIdOAuthFlowData.kt @@ -0,0 +1,19 @@ +package io.github.smiley4.ktorswaggerui.data + +data class OpenIdOAuthFlowData( + val authorizationUrl: String? = null, + val tokenUrl: String? = null, + val refreshUrl: String? = null, + val scopes: Map? = null, +) { + + companion object { + val DEFAULT = OpenIdOAuthFlowData( + authorizationUrl = null, + tokenUrl = null, + refreshUrl = null, + scopes = null, + ) + } + +} diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenIdOAuthFlowsData.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenIdOAuthFlowsData.kt new file mode 100644 index 00000000..9fdf9b90 --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenIdOAuthFlowsData.kt @@ -0,0 +1,19 @@ +package io.github.smiley4.ktorswaggerui.data + +data class OpenIdOAuthFlowsData( + val implicit: OpenIdOAuthFlowData?, + val password: OpenIdOAuthFlowData?, + val clientCredentials: OpenIdOAuthFlowData?, + val authorizationCode: OpenIdOAuthFlowData?, +) { + + companion object { + val DEFAULT = OpenIdOAuthFlowsData( + implicit = null, + password = null, + clientCredentials = null, + authorizationCode = null, + ) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PathFilter.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PathFilter.kt new file mode 100644 index 00000000..230a0b6b --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PathFilter.kt @@ -0,0 +1,6 @@ +package io.github.smiley4.ktorswaggerui.data + +import io.ktor.http.HttpMethod + + +typealias PathFilter = (method: HttpMethod, url: List) -> Boolean \ No newline at end of file diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PluginConfigData.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PluginConfigData.kt new file mode 100644 index 00000000..66172afb --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PluginConfigData.kt @@ -0,0 +1,45 @@ +package io.github.smiley4.ktorswaggerui.data + +import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.dsl.OpenApiResponse +import kotlin.reflect.KClass + +data class PluginConfigData( + val defaultUnauthorizedResponse: OpenApiResponse?, + val defaultSecuritySchemeNames: Set, + val tagGenerator: TagGenerator, + val specAssigner: SpecAssigner, + val pathFilter: PathFilter, + val ignoredRouteSelectors: Set>, + val swaggerUI: SwaggerUIData, + val info: InfoData, + val servers: List, + val externalDocs: ExternalDocsData, + val securitySchemes: List, + val tags: List, + val customSchemas: Map, + val includeAllCustomSchemas: Boolean, + val encoding: EncodingData +) { + + companion object { + val DEFAULT = PluginConfigData( + defaultUnauthorizedResponse = null, + defaultSecuritySchemeNames = emptySet(), + tagGenerator = { emptyList() }, + specAssigner = { _, _ -> SwaggerUIPluginConfig.DEFAULT_SPEC_ID }, + pathFilter = { _, _ -> true }, + ignoredRouteSelectors = emptySet(), + swaggerUI = SwaggerUIData.DEFAULT, + info = InfoData.DEFAULT, + servers = emptyList(), + externalDocs = ExternalDocsData.DEFAULT, + securitySchemes = emptyList(), + tags = emptyList(), + customSchemas = emptyMap(), + includeAllCustomSchemas = false, + encoding = EncodingData.DEFAULT + ) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SecuritySchemeData.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SecuritySchemeData.kt new file mode 100644 index 00000000..1e46bcaf --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SecuritySchemeData.kt @@ -0,0 +1,27 @@ +package io.github.smiley4.ktorswaggerui.data + +data class SecuritySchemeData( + val name: String, + val type: AuthType?, + val location: AuthKeyLocation?, + val scheme: AuthScheme?, + val bearerFormat: String?, + val flows: OpenIdOAuthFlowsData?, + val openIdConnectUrl: String?, + val description: String? +) { + + companion object { + val DEFAULT = SecuritySchemeData( + name = "", + type = null, + location = null, + scheme = null, + bearerFormat = null, + flows = null, + openIdConnectUrl = null, + description = null, + ) + } + +} diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ServerData.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ServerData.kt new file mode 100644 index 00000000..fe243c42 --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ServerData.kt @@ -0,0 +1,15 @@ +package io.github.smiley4.ktorswaggerui.data + +data class ServerData( + val url: String, + val description: String?, +) { + + companion object { + val DEFAULT = ServerData( + url = "/", + description = null + ) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SpecAssigned.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SpecAssigned.kt new file mode 100644 index 00000000..28a5e840 --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SpecAssigned.kt @@ -0,0 +1,7 @@ +package io.github.smiley4.ktorswaggerui.data + +/** + * url - the parts of the route-url split at all `/`. + * tags - the tags assigned to the route + */ +typealias SpecAssigner = (url: String, tags: List) -> String diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUIData.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUIData.kt new file mode 100644 index 00000000..16db8274 --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUIData.kt @@ -0,0 +1,29 @@ +package io.github.smiley4.ktorswaggerui.data + +data class SwaggerUIData( + val forwardRoot: Boolean, + val swaggerUrl: String, + val rootHostPath: String, + val authentication: String?, + val validatorUrl: String?, + val displayOperationId: Boolean, + val showTagFilterInput: Boolean, + val sort: SwaggerUiSort, + val syntaxHighlight: SwaggerUiSyntaxHighlight +) { + + companion object { + val DEFAULT = SwaggerUIData( + forwardRoot = false, + swaggerUrl = "swagger-ui", + rootHostPath = "", + authentication = null, + validatorUrl = null, + displayOperationId = false, + showTagFilterInput = false, + sort = SwaggerUiSort.NONE, + syntaxHighlight = SwaggerUiSyntaxHighlight.AGATE + ) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUiSort.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUiSort.kt new file mode 100644 index 00000000..84a40b13 --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUiSort.kt @@ -0,0 +1,20 @@ +package io.github.smiley4.ktorswaggerui.data + +enum class SwaggerUiSort(val value: String) { + /** + * The order returned by the server unchanged + */ + NONE("undefined"), + + + /** + * sort by paths alphanumerically + */ + ALPHANUMERICALLY("alpha"), + + + /** + * sort by HTTP method + */ + HTTP_METHOD("method") +} diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUiSyntaxHighlight.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUiSyntaxHighlight.kt new file mode 100644 index 00000000..749adf29 --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUiSyntaxHighlight.kt @@ -0,0 +1,10 @@ +package io.github.smiley4.ktorswaggerui.data + +enum class SwaggerUiSyntaxHighlight(val value: String) { + AGATE("agate"), + ARTA("arta"), + MONOKAI("monokai"), + NORD("nord"), + OBSIDIAN("obsidian"), + TOMORROW_NIGHT("tomorrow-night") +} diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TagData.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TagData.kt new file mode 100644 index 00000000..773e77b0 --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TagData.kt @@ -0,0 +1,18 @@ +package io.github.smiley4.ktorswaggerui.data + +data class TagData( + val name: String, + val description: String?, + val externalDocDescription: String?, + val externalDocUrl: String? +) { + + companion object { + val DEFAULT = TagData( + name = "", + description = null, + externalDocDescription = null, + externalDocUrl = null + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TagGenerator.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TagGenerator.kt new file mode 100644 index 00000000..6ba542bb --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TagGenerator.kt @@ -0,0 +1,7 @@ +package io.github.smiley4.ktorswaggerui.data + +/** + * url - the parts of the route-url split at all `/`. + * return a collection of tags. "Null"-entries will be ignored. + */ +typealias TagGenerator = (url: List) -> Collection diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/CustomSchemas.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/CustomSchemas.kt index 6aa07e64..11f5e8fe 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/CustomSchemas.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/CustomSchemas.kt @@ -1,5 +1,9 @@ package io.github.smiley4.ktorswaggerui.dsl +import io.github.smiley4.ktorswaggerui.data.BaseCustomSchema +import io.github.smiley4.ktorswaggerui.data.CustomJsonSchema +import io.github.smiley4.ktorswaggerui.data.CustomOpenApiSchema +import io.github.smiley4.ktorswaggerui.data.RemoteSchema import io.swagger.v3.oas.models.media.Schema @OpenApiDslMarker @@ -43,10 +47,3 @@ class CustomSchemas { } -sealed class BaseCustomSchema - -class CustomJsonSchema(val provider: () -> String) : BaseCustomSchema() - -class CustomOpenApiSchema(val provider: () -> Schema) : BaseCustomSchema() - -class RemoteSchema(val url: String) : BaseCustomSchema() diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/EncodingConfig.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/EncodingConfig.kt index 9acf46b6..008e4474 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/EncodingConfig.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/EncodingConfig.kt @@ -1,15 +1,7 @@ package io.github.smiley4.ktorswaggerui.dsl -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.github.victools.jsonschema.generator.Option -import com.github.victools.jsonschema.generator.OptionPreset -import com.github.victools.jsonschema.generator.SchemaGenerator -import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder -import com.github.victools.jsonschema.generator.SchemaVersion -import com.github.victools.jsonschema.module.jackson.JacksonModule -import com.github.victools.jsonschema.module.swagger2.Swagger2Module -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaTypeAttributeOverride -import kotlin.reflect.jvm.javaType +import io.github.smiley4.ktorswaggerui.data.DataUtils.mergeDefault +import io.github.smiley4.ktorswaggerui.data.EncodingData typealias ExampleEncoder = (type: SchemaType?, example: Any) -> String? @@ -29,7 +21,7 @@ class EncodingConfig { exampleEncoder = encoder } - private var exampleEncoder: ExampleEncoder = defaultExampleEncoder() + private var exampleEncoder: ExampleEncoder = EncodingData.DEFAULT.exampleEncoder fun getExampleEncoder() = exampleEncoder @@ -42,7 +34,7 @@ class EncodingConfig { schemaEncoder = encoder } - private var schemaEncoder: SchemaEncoder = defaultSchemaEncoder() + private var schemaEncoder: SchemaEncoder = EncodingData.DEFAULT.schemaEncoder fun getSchemaEncoder() = schemaEncoder @@ -50,77 +42,13 @@ class EncodingConfig { /** * the name of the field (if it exists) in the json-schema containing schema-definitions. */ - var schemaDefinitionsField = "\$defs" - - companion object { - - /** - * The default jackson object mapper used for encoding examples to json. - */ - var DEFAULT_EXAMPLE_OBJECT_MAPPER = jacksonObjectMapper() - - - /** - * The default [SchemaGenerator] used to encode types to json-schema. - * See https://victools.github.io/jsonschema-generator/#generator-options for more information. - */ - var DEFAULT_SCHEMA_GENERATOR = SchemaGenerator(schemaGeneratorConfigBuilder().build()) - - - /** - * The default [ExampleEncoder] - */ - fun defaultExampleEncoder(): ExampleEncoder { - return { _, value -> encodeExample(value) } - } - - - /** - * encode the given value to a json string - */ - fun encodeExample(value: Any?): String { - return if (value is String) { - value - } else { - DEFAULT_EXAMPLE_OBJECT_MAPPER.writeValueAsString(value) - } - } - - - /** - * The default [SchemaEncoder] - */ - fun defaultSchemaEncoder(): SchemaEncoder { - return { type -> encodeSchema(type) } - } - - - /** - * encode the given type to a json-schema - */ - fun encodeSchema(type: SchemaType): String { - return DEFAULT_SCHEMA_GENERATOR.generateSchema(type.javaType).toPrettyString() - } - - - /** - * The default [SchemaGeneratorConfigBuilder] - */ - fun schemaGeneratorConfigBuilder(): SchemaGeneratorConfigBuilder = - SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_2019_09, OptionPreset.PLAIN_JSON) - .with(JacksonModule()) - .with(Swagger2Module()) - .with(Option.EXTRA_OPEN_API_FORMAT_VALUES) - .with(Option.ALLOF_CLEANUP_AT_THE_END) - .with(Option.MAP_VALUES_AS_ADDITIONAL_PROPERTIES) - .with(Option.DEFINITIONS_FOR_ALL_OBJECTS) - .without(Option.INLINE_ALL_SCHEMAS) - .also { - it.forTypesInGeneral() - .withTypeAttributeOverride(SchemaTypeAttributeOverride()) - } + var schemaDefinitionsField = EncodingData.DEFAULT.schemaDefsField - } + fun build(base: EncodingData) = EncodingData( + exampleEncoder = mergeDefault(base.exampleEncoder, exampleEncoder, EncodingData.DEFAULT.exampleEncoder), + schemaEncoder = mergeDefault(base.schemaEncoder, schemaEncoder, EncodingData.DEFAULT.schemaEncoder), + schemaDefsField = mergeDefault(base.schemaDefsField, schemaDefinitionsField, EncodingData.DEFAULT.schemaDefsField), + ) } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiContact.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiContact.kt index 06403d20..7ab26b64 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiContact.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiContact.kt @@ -1,5 +1,8 @@ package io.github.smiley4.ktorswaggerui.dsl +import io.github.smiley4.ktorswaggerui.data.ContactData +import io.github.smiley4.ktorswaggerui.data.DataUtils.merge + /** * Contact information for the exposed API. */ @@ -9,18 +12,25 @@ class OpenApiContact { /** * The identifying name of the contact person/organization. */ - var name: String? = null + var name: String? = ContactData.DEFAULT.name /** * The URL pointing to the contact information. MUST be in the format of a URL. */ - var url: String? = null + var url: String? = ContactData.DEFAULT.url /** * The email address of the contact person/organization. MUST be in the format of an email address. */ - var email: String? = null + var email: String? = ContactData.DEFAULT.email + + + fun build(base: ContactData) = ContactData( + name = merge(base.name, name), + url = merge(base.url, url), + email = merge(base.email, email) + ) } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiExternalDocs.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiExternalDocs.kt index c1ce30dd..7fd85bce 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiExternalDocs.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiExternalDocs.kt @@ -1,5 +1,8 @@ package io.github.smiley4.ktorswaggerui.dsl +import io.github.smiley4.ktorswaggerui.data.DataUtils +import io.github.smiley4.ktorswaggerui.data.ExternalDocsData + /** * An object representing external documentation. */ @@ -10,8 +13,15 @@ class OpenApiExternalDocs { */ var description: String? = null + /** * A URL to the external documentation */ var url: String = "/" + + fun build(base: ExternalDocsData) = ExternalDocsData( + url = DataUtils.mergeDefault(base.url, url, ExternalDocsData.DEFAULT.url), + description = DataUtils.merge(base.description, description) + ) + } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiInfo.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiInfo.kt index 9671e43f..8a9f716e 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiInfo.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiInfo.kt @@ -1,5 +1,11 @@ package io.github.smiley4.ktorswaggerui.dsl +import io.github.smiley4.ktorswaggerui.data.ContactData +import io.github.smiley4.ktorswaggerui.data.DataUtils.merge +import io.github.smiley4.ktorswaggerui.data.DataUtils.mergeDefault +import io.github.smiley4.ktorswaggerui.data.InfoData +import io.github.smiley4.ktorswaggerui.data.LicenseData + @OpenApiDslMarker class OpenApiInfo { @@ -26,9 +32,9 @@ class OpenApiInfo { */ var termsOfService: String? = null - private var contact: OpenApiContact? = null + /** * The contact information for the exposed API. */ @@ -41,6 +47,7 @@ class OpenApiInfo { private var license: OpenApiLicense? = null + /** * The license information for the exposed API. */ @@ -50,4 +57,15 @@ class OpenApiInfo { fun getLicense() = license + fun build(base: InfoData): InfoData { + return InfoData( + title = mergeDefault(base.title, this.title, InfoData.DEFAULT.title), + version = merge(base.version, this.version), + description = merge(base.description, this.description), + termsOfService = merge(base.termsOfService, this.termsOfService), + contact = contact?.build(base.contact ?: ContactData.DEFAULT) ?: base.contact, + license = license?.build(base.license ?: LicenseData.DEFAULT) ?: base.license + ) + } + } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiLicense.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiLicense.kt index b15cace3..5faf8d9f 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiLicense.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiLicense.kt @@ -1,5 +1,8 @@ package io.github.smiley4.ktorswaggerui.dsl +import io.github.smiley4.ktorswaggerui.data.DataUtils +import io.github.smiley4.ktorswaggerui.data.LicenseData + /** * License information for the exposed API. */ @@ -9,12 +12,17 @@ class OpenApiLicense { /** * The license name used for the API */ - var name: String = "?" + var name: String? = LicenseData.DEFAULT.name /** * A URL to the license used for the API. MUST be in the format of a URL. */ - var url: String? = null + var url: String? = LicenseData.DEFAULT.url + + fun build(base: LicenseData) = LicenseData( + name = DataUtils.merge(base.name, name), + url = DataUtils.merge(base.url, url), + ) } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiSecurityScheme.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiSecurityScheme.kt index 9754230c..3a15ccc5 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiSecurityScheme.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiSecurityScheme.kt @@ -1,35 +1,11 @@ package io.github.smiley4.ktorswaggerui.dsl -import io.swagger.v3.oas.models.security.SecurityScheme - - -enum class AuthType(val swaggerType: SecurityScheme.Type) { - API_KEY(SecurityScheme.Type.APIKEY), - HTTP(SecurityScheme.Type.HTTP), - OAUTH2(SecurityScheme.Type.OAUTH2), - OPENID_CONNECT(SecurityScheme.Type.OPENIDCONNECT), - MUTUAL_TLS(SecurityScheme.Type.MUTUALTLS) -} - -enum class AuthKeyLocation(val swaggerType: SecurityScheme.In) { - QUERY(SecurityScheme.In.QUERY), - HEADER(SecurityScheme.In.HEADER), - COOKIE(SecurityScheme.In.COOKIE) -} - - -// https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml -enum class AuthScheme(val swaggerType: String) { - BASIC("Basic"), - BEARER("Bearer"), - DIGEST("Digest"), - HOBA("HOBA"), - MUTUAL("Mutual"), - OAUTH("OAuth"), - SCRAM_SHA_1("SCRAM-SHA-1"), - SCRAM_SHA_256("SCRAM-SHA-256"), - VAPID("vapid") -} +import io.github.smiley4.ktorswaggerui.data.AuthKeyLocation +import io.github.smiley4.ktorswaggerui.data.AuthScheme +import io.github.smiley4.ktorswaggerui.data.AuthType +import io.github.smiley4.ktorswaggerui.data.DataUtils.merge +import io.github.smiley4.ktorswaggerui.data.OpenIdOAuthFlowsData +import io.github.smiley4.ktorswaggerui.data.SecuritySchemeData /** @@ -98,4 +74,15 @@ class OpenApiSecurityScheme( */ var description: String? = null + + fun build(base: SecuritySchemeData) = SecuritySchemeData( + name = name, + type = merge(base.type, type), + location = merge(base.location, location), + scheme = merge(base.scheme, scheme), + bearerFormat = merge(base.bearerFormat, bearerFormat), + flows = flows?.build(base.flows ?: OpenIdOAuthFlowsData.DEFAULT) ?: base.flows, + openIdConnectUrl = merge(base.openIdConnectUrl, openIdConnectUrl), + description = merge(base.description, description), + ) } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiServer.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiServer.kt index c8914097..74367b63 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiServer.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiServer.kt @@ -1,5 +1,9 @@ package io.github.smiley4.ktorswaggerui.dsl +import io.github.smiley4.ktorswaggerui.data.DataUtils.merge +import io.github.smiley4.ktorswaggerui.data.DataUtils.mergeDefault +import io.github.smiley4.ktorswaggerui.data.ServerData + /** * An object representing a Server. */ @@ -10,12 +14,17 @@ class OpenApiServer { * A URL to the target host. This URL supports Server Variables and MAY be relative, to indicate that the host location is relative to * the location where the OpenAPI document is being served */ - var url: String = "/" + var url: String = ServerData.DEFAULT.url /** * An optional string describing the host designated by the URL */ - var description: String? = null + var description: String? = ServerData.DEFAULT.description + + fun build(base: ServerData) = ServerData( + url = mergeDefault(base.url, url, ServerData.DEFAULT.url), + description = merge(base.description, description) + ) } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiTag.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiTag.kt index 0fcf5f6b..2d81c93b 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiTag.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiTag.kt @@ -1,5 +1,8 @@ package io.github.smiley4.ktorswaggerui.dsl +import io.github.smiley4.ktorswaggerui.data.DataUtils.merge +import io.github.smiley4.ktorswaggerui.data.TagData + /** * Adds metadata to a single tag. */ @@ -28,4 +31,12 @@ class OpenApiTag( */ var externalDocUrl: String? = null + + fun build(base: TagData) = TagData( + name = name, + description = merge(base.description, description), + externalDocDescription = merge(base.externalDocDescription, externalDocDescription), + externalDocUrl = merge(base.externalDocUrl, externalDocUrl) + ) + } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenIdOAuthFlow.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenIdOAuthFlow.kt index 2e31e198..55ececa5 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenIdOAuthFlow.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenIdOAuthFlow.kt @@ -1,4 +1,8 @@ package io.github.smiley4.ktorswaggerui.dsl + +import io.github.smiley4.ktorswaggerui.data.DataUtils.merge +import io.github.smiley4.ktorswaggerui.data.OpenIdOAuthFlowData + /** * Configuration details for a supported OAuth Flow */ @@ -28,4 +32,12 @@ class OpenIdOAuthFlow { */ var scopes: Map? = null + + fun build(base: OpenIdOAuthFlowData) = OpenIdOAuthFlowData( + authorizationUrl = merge(base.authorizationUrl, authorizationUrl), + tokenUrl = merge(base.tokenUrl, tokenUrl), + refreshUrl = merge(base.refreshUrl, refreshUrl), + scopes = merge(base.scopes, scopes), + ) + } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenIdOAuthFlows.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenIdOAuthFlows.kt index 123a4417..336b7f17 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenIdOAuthFlows.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenIdOAuthFlows.kt @@ -1,5 +1,8 @@ package io.github.smiley4.ktorswaggerui.dsl +import io.github.smiley4.ktorswaggerui.data.OpenIdOAuthFlowData +import io.github.smiley4.ktorswaggerui.data.OpenIdOAuthFlowsData + /** * An object containing configuration information for the oauth flow types supported */ @@ -61,4 +64,11 @@ class OpenIdOAuthFlows { fun getAuthorizationCode() = authorizationCode + fun build(base: OpenIdOAuthFlowsData) = OpenIdOAuthFlowsData( + implicit = implicit?.build(base.implicit ?: OpenIdOAuthFlowData.DEFAULT) ?: base.implicit, + password = password?.build(base.password ?: OpenIdOAuthFlowData.DEFAULT) ?: base.password, + clientCredentials = clientCredentials?.build(base.clientCredentials ?: OpenIdOAuthFlowData.DEFAULT) ?: base.clientCredentials, + authorizationCode = authorizationCode?.build(base.authorizationCode ?: OpenIdOAuthFlowData.DEFAULT) ?: base.authorizationCode, + ) + } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/SwaggerUIDsl.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/SwaggerUIDsl.kt index 16544351..0144409b 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/SwaggerUIDsl.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/SwaggerUIDsl.kt @@ -1,27 +1,37 @@ package io.github.smiley4.ktorswaggerui.dsl +import io.github.smiley4.ktorswaggerui.data.DataUtils.merge +import io.github.smiley4.ktorswaggerui.data.DataUtils.mergeBoolean +import io.github.smiley4.ktorswaggerui.data.DataUtils.mergeDefault +import io.github.smiley4.ktorswaggerui.data.SwaggerUIData + + @OpenApiDslMarker class SwaggerUIDsl { /** * Whether to forward the root-url to the swagger-url */ - var forwardRoot: Boolean = false + var forwardRoot: Boolean = SwaggerUIData.DEFAULT.forwardRoot + /** * the url to the swagger-ui */ - var swaggerUrl: String = "swagger-ui" + var swaggerUrl: String = SwaggerUIData.DEFAULT.swaggerUrl + /** * the path under which the KTOR app gets deployed. can be useful if reverse proxy is in use. */ - var rootHostPath: String = "" + var rootHostPath: String = SwaggerUIData.DEFAULT.rootHostPath + /** * The name of the authentication to use for the swagger routes. Null to not protect the swagger-ui. */ - var authentication: String? = null + var authentication: String? = SwaggerUIData.DEFAULT.authentication + /** * Swagger UI can attempt to validate specs against swagger.io's online validator. @@ -30,7 +40,7 @@ class SwaggerUIDsl { * Validation is disabled when the url of the api-spec-file contains localhost. * (see https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/configuration.md#network) */ - private var validatorUrl: String? = null + private var validatorUrl: String? = SwaggerUIData.DEFAULT.validatorUrl fun disableSpecValidator() { validatorUrl = null @@ -46,50 +56,44 @@ class SwaggerUIDsl { fun getSpecValidatorUrl() = validatorUrl + /** * Whether to show the operation-id of endpoints in the list */ - var displayOperationId = false + var displayOperationId = SwaggerUIData.DEFAULT.displayOperationId + /** * Whether the top bar will show an edit box that you can use to filter the tagged operations. */ - var showTagFilterInput = false + var showTagFilterInput = SwaggerUIData.DEFAULT.showTagFilterInput - /** - * Apply a sort to the operation list of each API - */ - var sort = SwaggerUiSort.NONE /** - * Syntax coloring theme to use + * Apply a sort to the operation list of each API */ - var syntaxHighlight = SwaggerUiSyntaxHighlight.AGATE - -} + var sort = SwaggerUIData.DEFAULT.sort -enum class SwaggerUiSort(val value: String) { - /** - * The order returned by the server unchanged - */ - NONE("undefined"), /** - * sort by paths alphanumerically + * Syntax coloring theme to use */ - ALPHANUMERICALLY("alpha"), + var syntaxHighlight = SwaggerUIData.DEFAULT.syntaxHighlight + + + internal fun build(base: SwaggerUIData): SwaggerUIData { + return SwaggerUIData( + forwardRoot = mergeBoolean(base.forwardRoot, this.forwardRoot), + swaggerUrl = mergeDefault(base.swaggerUrl, this.swaggerUrl, SwaggerUIData.DEFAULT.swaggerUrl), + rootHostPath = mergeDefault(base.rootHostPath, this.rootHostPath, SwaggerUIData.DEFAULT.rootHostPath), + authentication = merge(base.authentication, this.authentication), + validatorUrl = merge(base.validatorUrl, this.validatorUrl), + displayOperationId = mergeBoolean(base.displayOperationId, this.displayOperationId), + showTagFilterInput = mergeBoolean(base.showTagFilterInput, this.showTagFilterInput), + sort = mergeDefault(base.sort, this.sort, SwaggerUIData.DEFAULT.sort), + syntaxHighlight = mergeDefault(base.syntaxHighlight, this.syntaxHighlight, SwaggerUIData.DEFAULT.syntaxHighlight) + ) + } - /** - * sort by HTTP method - */ - HTTP_METHOD("method") } -enum class SwaggerUiSyntaxHighlight(val value: String) { - AGATE("agate"), - ARTA("arta"), - MONOKAI("monokai"), - NORD("nord"), - OBSIDIAN("obsidian"), - TOMORROW_NIGHT("tomorrow-night") -} diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIPluginConfig.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/SwaggerUIPluginConfig.kt similarity index 58% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIPluginConfig.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/SwaggerUIPluginConfig.kt index 8940e3f3..8c4c8ec6 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIPluginConfig.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/SwaggerUIPluginConfig.kt @@ -1,16 +1,15 @@ -package io.github.smiley4.ktorswaggerui - -import io.github.smiley4.ktorswaggerui.dsl.CustomSchemas -import io.github.smiley4.ktorswaggerui.dsl.EncodingConfig -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker -import io.github.smiley4.ktorswaggerui.dsl.OpenApiExternalDocs -import io.github.smiley4.ktorswaggerui.dsl.OpenApiInfo -import io.github.smiley4.ktorswaggerui.dsl.OpenApiResponse -import io.github.smiley4.ktorswaggerui.dsl.OpenApiSecurityScheme -import io.github.smiley4.ktorswaggerui.dsl.OpenApiServer -import io.github.smiley4.ktorswaggerui.dsl.OpenApiTag -import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIDsl -import io.ktor.http.HttpMethod +package io.github.smiley4.ktorswaggerui.dsl + +import io.github.smiley4.ktorswaggerui.data.DataUtils.merge +import io.github.smiley4.ktorswaggerui.data.DataUtils.mergeBoolean +import io.github.smiley4.ktorswaggerui.data.ExternalDocsData +import io.github.smiley4.ktorswaggerui.data.PathFilter +import io.github.smiley4.ktorswaggerui.data.PluginConfigData +import io.github.smiley4.ktorswaggerui.data.SecuritySchemeData +import io.github.smiley4.ktorswaggerui.data.ServerData +import io.github.smiley4.ktorswaggerui.data.SpecAssigner +import io.github.smiley4.ktorswaggerui.data.TagData +import io.github.smiley4.ktorswaggerui.data.TagGenerator import io.ktor.http.HttpStatusCode import io.ktor.server.routing.RouteSelector import kotlin.reflect.KClass @@ -25,6 +24,7 @@ class SwaggerUIPluginConfig { const val DEFAULT_SPEC_ID = "api" } + /** * Default response to automatically add to each protected route for the "Unauthorized"-Response-Code. * Generated response can be overwritten with custom response. @@ -33,7 +33,7 @@ class SwaggerUIPluginConfig { defaultUnauthorizedResponse = OpenApiResponse(HttpStatusCode.Unauthorized.value.toString()).apply(block) } - private var defaultUnauthorizedResponse: OpenApiResponse? = null + private var defaultUnauthorizedResponse: OpenApiResponse? = PluginConfigData.DEFAULT.defaultUnauthorizedResponse fun getDefaultUnauthorizedResponse() = defaultUnauthorizedResponse @@ -47,7 +47,7 @@ class SwaggerUIPluginConfig { /** * The names of the security schemes available for use for the protected paths */ - var defaultSecuritySchemeNames: Collection? = null + var defaultSecuritySchemeNames: Collection? = PluginConfigData.DEFAULT.defaultSecuritySchemeNames /** @@ -58,7 +58,7 @@ class SwaggerUIPluginConfig { tagGenerator = generator } - private var tagGenerator: TagGenerator = { emptyList() } + private var tagGenerator: TagGenerator? = PluginConfigData.DEFAULT.tagGenerator fun getTagGenerator() = tagGenerator @@ -66,14 +66,14 @@ class SwaggerUIPluginConfig { /** * Assigns routes without an [io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute.specId] to a specified openapi-spec. */ - var specAssigner: (url: String, tags: List) -> String = { _, _ -> DEFAULT_SPEC_ID } + var specAssigner: SpecAssigner? = PluginConfigData.DEFAULT.specAssigner /** * Filter to apply to all routes. Return 'false' for routes to not include them in the OpenApi-Spec and Swagger-UI. * The url of the paths are already split at '/'. */ - var pathFilter: ((method: HttpMethod, url: List) -> Boolean)? = null + var pathFilter: PathFilter? = PluginConfigData.DEFAULT.pathFilter /** @@ -173,12 +173,46 @@ class SwaggerUIPluginConfig { /** * List of all [RouteSelector] types in that should be ignored in the resulting url of any route. */ - var ignoredRouteSelectors: List> = listOf() + var ignoredRouteSelectors: Set> = PluginConfigData.DEFAULT.ignoredRouteSelectors + + + internal fun build(base: PluginConfigData): PluginConfigData { + return PluginConfigData( + defaultUnauthorizedResponse = merge(base.defaultUnauthorizedResponse, defaultUnauthorizedResponse), + defaultSecuritySchemeNames = buildSet { + addAll(base.defaultSecuritySchemeNames) + defaultSecuritySchemeNames?.also { addAll(it) } + defaultSecuritySchemeName?.also { add(it) } + }, + tagGenerator = merge(base.tagGenerator, tagGenerator) ?: PluginConfigData.DEFAULT.tagGenerator, + specAssigner = merge(base.specAssigner, specAssigner) ?: PluginConfigData.DEFAULT.specAssigner, + pathFilter = merge(base.pathFilter, pathFilter) ?: PluginConfigData.DEFAULT.pathFilter, + ignoredRouteSelectors = buildSet { + addAll(base.ignoredRouteSelectors) + addAll(ignoredRouteSelectors) + }, + swaggerUI = swaggerUI.build(base.swaggerUI), + info = info.build(base.info), + servers = buildList { + addAll(base.servers) + addAll(servers.map { it.build(ServerData.DEFAULT) }) + }, + externalDocs = externalDocs.build(base.externalDocs), + securitySchemes = buildList { + addAll(base.securitySchemes) + addAll(securitySchemes.map { it.build(SecuritySchemeData.DEFAULT) }) + }, + tags = buildList { + addAll(base.tags) + addAll(tags.map { it.build(TagData.DEFAULT) }) + }, + customSchemas = buildMap { + putAll(base.customSchemas) + putAll(customSchemas.getSchemas()) + }, + includeAllCustomSchemas = mergeBoolean(base.includeAllCustomSchemas, customSchemas.includeAll), + encoding = encodingConfig.build(base.encoding) + ) + } } - -/** - * url - the parts of the route-url split at all `/`. - * return a collection of tags. "Null"-entries will be ignored. - */ -typealias TagGenerator = (url: List) -> Collection diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/ForwardRouteController.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/ForwardRouteController.kt index 33f2947c..bcaa16b0 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/ForwardRouteController.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/ForwardRouteController.kt @@ -1,6 +1,6 @@ package io.github.smiley4.ktorswaggerui.routing -import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIDsl +import io.github.smiley4.ktorswaggerui.data.PluginConfigData import io.ktor.server.application.Application import io.ktor.server.application.call import io.ktor.server.config.ApplicationConfig @@ -10,7 +10,7 @@ import io.ktor.server.routing.routing class ForwardRouteController( private val appConfig: ApplicationConfig, - private val swaggerUiConfig: SwaggerUIDsl, + private val swaggerUiConfig: PluginConfigData, ) { fun setup(app: Application) { @@ -24,8 +24,8 @@ class ForwardRouteController( private fun getRootUrl(): String { return "/" + listOf( ControllerUtils.getRootPath(appConfig), - swaggerUiConfig.rootHostPath, - swaggerUiConfig.swaggerUrl, + swaggerUiConfig.swaggerUI.rootHostPath, + swaggerUiConfig.swaggerUI.swaggerUrl, ) .filter { it.isNotBlank() } .map { ControllerUtils.dropSlashes(it) } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/SwaggerController.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/SwaggerController.kt index 6d09f193..9e991f9e 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/SwaggerController.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/SwaggerController.kt @@ -1,7 +1,7 @@ package io.github.smiley4.ktorswaggerui.routing -import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIDsl -import io.github.smiley4.ktorswaggerui.dsl.SwaggerUiSort +import io.github.smiley4.ktorswaggerui.data.PluginConfigData +import io.github.smiley4.ktorswaggerui.data.SwaggerUiSort import io.ktor.http.ContentType import io.ktor.http.HttpStatusCode import io.ktor.server.application.Application @@ -20,7 +20,7 @@ import io.ktor.server.routing.routing class SwaggerController( private val appConfig: ApplicationConfig, - private val swaggerUiConfig: SwaggerUIDsl, + private val pluginConfig: PluginConfigData, private val swaggerWebjarVersion: String, private val specName: String?, private val jsonSpec: String, @@ -32,10 +32,10 @@ class SwaggerController( fun setup(app: Application) { app.routing { - if (swaggerUiConfig.authentication == null) { + if (pluginConfig.swaggerUI.authentication == null) { setup() } else { - authenticate(swaggerUiConfig.authentication) { + authenticate(pluginConfig.swaggerUI.authentication) { setup() } } @@ -60,8 +60,9 @@ class SwaggerController( } private suspend fun serveSwaggerInitializer(call: ApplicationCall) { + val swaggerUiConfig = pluginConfig.swaggerUI // see https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/configuration.md for reference - val propValidatorUrl = swaggerUiConfig.getSpecValidatorUrl()?.let { "validatorUrl: \"$it\"" } ?: "validatorUrl: false" + val propValidatorUrl = swaggerUiConfig.validatorUrl?.let { "validatorUrl: \"$it\"" } ?: "validatorUrl: false" val propDisplayOperationId = "displayOperationId: ${swaggerUiConfig.displayOperationId}" val propFilter = "filter: ${swaggerUiConfig.showTagFilterInput}" val propSort = "operationsSorter: " + @@ -112,8 +113,8 @@ class SwaggerController( private fun getSubUrl(): String { return "/" + listOf( - swaggerUiConfig.rootHostPath, - swaggerUiConfig.swaggerUrl, + pluginConfig.swaggerUI.rootHostPath, + pluginConfig.swaggerUI.swaggerUrl, specName ) .filter { !it.isNullOrBlank() } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ComponentsBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ComponentsBuilder.kt index 3847063a..3c2636a8 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ComponentsBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ComponentsBuilder.kt @@ -1,12 +1,12 @@ package io.github.smiley4.ktorswaggerui.spec.openapi -import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.data.PluginConfigData import io.swagger.v3.oas.models.Components import io.swagger.v3.oas.models.examples.Example import io.swagger.v3.oas.models.media.Schema class ComponentsBuilder( - private val config: SwaggerUIPluginConfig, + private val config: PluginConfigData, private val securitySchemesBuilder: SecuritySchemesBuilder ) { @@ -14,8 +14,8 @@ class ComponentsBuilder( return Components().also { it.schemas = schemas it.examples = examples - if (config.getSecuritySchemes().isNotEmpty()) { - it.securitySchemes = securitySchemesBuilder.build(config.getSecuritySchemes()) + if (config.securitySchemes.isNotEmpty()) { + it.securitySchemes = securitySchemesBuilder.build(config.securitySchemes) } } } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ContactBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ContactBuilder.kt index 2d88df06..9c069e65 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ContactBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ContactBuilder.kt @@ -1,11 +1,11 @@ package io.github.smiley4.ktorswaggerui.spec.openapi -import io.github.smiley4.ktorswaggerui.dsl.OpenApiContact +import io.github.smiley4.ktorswaggerui.data.ContactData import io.swagger.v3.oas.models.info.Contact class ContactBuilder { - fun build(contact: OpenApiContact): Contact = + fun build(contact: ContactData): Contact = Contact().also { it.name = contact.name it.email = contact.email diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ExampleBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ExampleBuilder.kt index 864678c6..a4cbbb3a 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ExampleBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ExampleBuilder.kt @@ -1,12 +1,12 @@ package io.github.smiley4.ktorswaggerui.spec.openapi -import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.data.PluginConfigData import io.github.smiley4.ktorswaggerui.dsl.OpenApiExample import io.github.smiley4.ktorswaggerui.dsl.SchemaType import io.swagger.v3.oas.models.examples.Example class ExampleBuilder( - private val config: SwaggerUIPluginConfig + private val config: PluginConfigData ) { fun build(type: SchemaType?, example: OpenApiExample): Example = @@ -17,7 +17,7 @@ class ExampleBuilder( } fun buildExampleValue(type: SchemaType?, value: Any): String { - return config.encodingConfig.getExampleEncoder()(type, value) ?: value.toString() + return config.encoding.exampleEncoder(type, value) ?: value.toString() } } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ExternalDocumentationBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ExternalDocumentationBuilder.kt index 7c463e4f..73410149 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ExternalDocumentationBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ExternalDocumentationBuilder.kt @@ -1,11 +1,11 @@ package io.github.smiley4.ktorswaggerui.spec.openapi -import io.github.smiley4.ktorswaggerui.dsl.OpenApiExternalDocs +import io.github.smiley4.ktorswaggerui.data.ExternalDocsData import io.swagger.v3.oas.models.ExternalDocumentation class ExternalDocumentationBuilder { - fun build(externalDocs: OpenApiExternalDocs): ExternalDocumentation = + fun build(externalDocs: ExternalDocsData): ExternalDocumentation = ExternalDocumentation().also { it.url = externalDocs.url it.description = externalDocs.description diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/InfoBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/InfoBuilder.kt index 3c2b7a9f..4c319f90 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/InfoBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/InfoBuilder.kt @@ -1,6 +1,6 @@ package io.github.smiley4.ktorswaggerui.spec.openapi -import io.github.smiley4.ktorswaggerui.dsl.OpenApiInfo +import io.github.smiley4.ktorswaggerui.data.InfoData import io.swagger.v3.oas.models.info.Info class InfoBuilder( @@ -8,16 +8,16 @@ class InfoBuilder( private val licenseBuilder: LicenseBuilder ) { - fun build(info: OpenApiInfo): Info = + fun build(info: InfoData): Info = Info().also { it.title = info.title it.version = info.version it.description = info.description it.termsOfService = info.termsOfService - info.getContact()?.also { contact -> + info.contact?.also { contact -> it.contact = contactBuilder.build(contact) } - info.getLicense()?.also { license -> + info.license?.also { license -> it.license = licenseBuilder.build(license) } } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/LicenseBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/LicenseBuilder.kt index 968ea2fb..dcaf0098 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/LicenseBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/LicenseBuilder.kt @@ -1,11 +1,11 @@ package io.github.smiley4.ktorswaggerui.spec.openapi -import io.github.smiley4.ktorswaggerui.dsl.OpenApiLicense +import io.github.smiley4.ktorswaggerui.data.LicenseData import io.swagger.v3.oas.models.info.License class LicenseBuilder { - fun build(license: OpenApiLicense): License = + fun build(license: LicenseData): License = License().also { it.name = license.name it.url = license.url diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OAuthFlowsBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OAuthFlowsBuilder.kt index 1fc2ddfe..4aeb6546 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OAuthFlowsBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OAuthFlowsBuilder.kt @@ -1,23 +1,23 @@ package io.github.smiley4.ktorswaggerui.spec.openapi -import io.github.smiley4.ktorswaggerui.dsl.OpenIdOAuthFlow -import io.github.smiley4.ktorswaggerui.dsl.OpenIdOAuthFlows +import io.github.smiley4.ktorswaggerui.data.OpenIdOAuthFlowData +import io.github.smiley4.ktorswaggerui.data.OpenIdOAuthFlowsData import io.swagger.v3.oas.models.security.OAuthFlow import io.swagger.v3.oas.models.security.OAuthFlows import io.swagger.v3.oas.models.security.Scopes class OAuthFlowsBuilder { - fun build(flows: OpenIdOAuthFlows): OAuthFlows { + fun build(flows: OpenIdOAuthFlowsData): OAuthFlows { return OAuthFlows().apply { - implicit = flows.getImplicit()?.let { build(it) } - password = flows.getPassword()?.let { build(it) } - clientCredentials = flows.getClientCredentials()?.let { build(it) } - authorizationCode = flows.getAuthorizationCode()?.let { build(it) } + implicit = flows.implicit?.let { build(it) } + password = flows.password?.let { build(it) } + clientCredentials = flows.clientCredentials?.let { build(it) } + authorizationCode = flows.authorizationCode?.let { build(it) } } } - private fun build(flow: OpenIdOAuthFlow): OAuthFlow { + private fun build(flow: OpenIdOAuthFlowData): OAuthFlow { return OAuthFlow().apply { authorizationUrl = flow.authorizationUrl tokenUrl = flow.tokenUrl @@ -26,7 +26,7 @@ class OAuthFlowsBuilder { } } - private fun buildScopes(scopes: Map): Scopes { + private fun buildScopes(scopes: Map): Scopes { return Scopes().apply { scopes.forEach { (k, v) -> addString(k, v) } } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OpenApiBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OpenApiBuilder.kt index 0f1c6946..7e2104c7 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OpenApiBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OpenApiBuilder.kt @@ -1,13 +1,13 @@ package io.github.smiley4.ktorswaggerui.spec.openapi -import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.data.PluginConfigData import io.github.smiley4.ktorswaggerui.spec.example.ExampleContext import io.github.smiley4.ktorswaggerui.spec.route.RouteMeta import io.github.smiley4.ktorswaggerui.spec.schema.SchemaContext import io.swagger.v3.oas.models.OpenAPI class OpenApiBuilder( - private val config: SwaggerUIPluginConfig, + private val config: PluginConfigData, private val schemaContext: SchemaContext, private val exampleContext: ExampleContext, private val infoBuilder: InfoBuilder, @@ -20,10 +20,10 @@ class OpenApiBuilder( fun build(routes: Collection): OpenAPI { return OpenAPI().also { - it.info = infoBuilder.build(config.getInfo()) - it.externalDocs = externalDocumentationBuilder.build(config.getExternalDocs()) - it.servers = config.getServers().map { server -> serverBuilder.build(server) } - it.tags = config.getTags().map { tag -> tagBuilder.build(tag) } + it.info = infoBuilder.build(config.info) + it.externalDocs = externalDocumentationBuilder.build(config.externalDocs) + it.servers = config.servers.map { server -> serverBuilder.build(server) } + it.tags = config.tags.map { tag -> tagBuilder.build(tag) } it.paths = pathsBuilder.build(routes) it.components = componentsBuilder.build(schemaContext.getComponentsSection(), exampleContext.getComponentsSection()) } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OperationTagsBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OperationTagsBuilder.kt index 30fa43e6..d71f9567 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OperationTagsBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OperationTagsBuilder.kt @@ -1,10 +1,10 @@ package io.github.smiley4.ktorswaggerui.spec.openapi -import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.data.PluginConfigData import io.github.smiley4.ktorswaggerui.spec.route.RouteMeta class OperationTagsBuilder( - private val config: SwaggerUIPluginConfig + private val config: PluginConfigData ) { fun build(route: RouteMeta): List { @@ -16,6 +16,6 @@ class OperationTagsBuilder( private fun getRouteTags(route: RouteMeta) = route.documentation.tags - private fun getGeneratedTags(route: RouteMeta) = config.getTagGenerator()(route.path.split("/").filter { it.isNotEmpty() }) + private fun getGeneratedTags(route: RouteMeta) = config.tagGenerator(route.path.split("/").filter { it.isNotEmpty() }) } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ResponsesBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ResponsesBuilder.kt index 8a95d268..4a5d9a89 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ResponsesBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ResponsesBuilder.kt @@ -1,13 +1,13 @@ package io.github.smiley4.ktorswaggerui.spec.openapi -import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.data.PluginConfigData import io.github.smiley4.ktorswaggerui.dsl.OpenApiResponses import io.ktor.http.HttpStatusCode import io.swagger.v3.oas.models.responses.ApiResponses class ResponsesBuilder( private val responseBuilder: ResponseBuilder, - private val config: SwaggerUIPluginConfig + private val config: PluginConfigData ) { fun build(responses: OpenApiResponses, isProtected: Boolean): ApiResponses = @@ -16,7 +16,7 @@ class ResponsesBuilder( .map { response -> responseBuilder.build(response) } .forEach { (name, response) -> it.addApiResponse(name, response) } if (shouldAddUnauthorized(responses, isProtected)) { - config.getDefaultUnauthorizedResponse() + config.defaultUnauthorizedResponse ?.let { response -> responseBuilder.build(response) } ?.also { (name, response) -> it.addApiResponse(name, response) } } @@ -24,7 +24,7 @@ class ResponsesBuilder( private fun shouldAddUnauthorized(responses: OpenApiResponses, isProtected: Boolean): Boolean { val unauthorizedCode = HttpStatusCode.Unauthorized.value.toString(); - return config.getDefaultUnauthorizedResponse() != null + return config.defaultUnauthorizedResponse != null && isProtected && responses.getResponses().count { it.statusCode == unauthorizedCode } == 0 } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/SecurityRequirementsBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/SecurityRequirementsBuilder.kt index 473bdef7..dfc2bdc6 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/SecurityRequirementsBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/SecurityRequirementsBuilder.kt @@ -1,11 +1,11 @@ package io.github.smiley4.ktorswaggerui.spec.openapi -import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.data.PluginConfigData import io.github.smiley4.ktorswaggerui.spec.route.RouteMeta import io.swagger.v3.oas.models.security.SecurityRequirement class SecurityRequirementsBuilder( - private val config: SwaggerUIPluginConfig + private val config: PluginConfigData ) { fun build(route: RouteMeta): List { @@ -14,8 +14,7 @@ class SecurityRequirementsBuilder( route.documentation.securitySchemeNames?.also { schemes.addAll(it) } } if (securitySchemes.isEmpty()) { - config.defaultSecuritySchemeName?.also { securitySchemes.add(it) } - config.defaultSecuritySchemeNames?.also { securitySchemes.addAll(it) } + config.defaultSecuritySchemeNames.also { securitySchemes.addAll(it) } } return securitySchemes.map { SecurityRequirement().apply { diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/SecuritySchemesBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/SecuritySchemesBuilder.kt index 017720bf..58b96cfe 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/SecuritySchemesBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/SecuritySchemesBuilder.kt @@ -1,5 +1,6 @@ package io.github.smiley4.ktorswaggerui.spec.openapi +import io.github.smiley4.ktorswaggerui.data.SecuritySchemeData import io.github.smiley4.ktorswaggerui.dsl.OpenApiSecurityScheme import io.swagger.v3.oas.models.security.SecurityScheme @@ -7,7 +8,7 @@ class SecuritySchemesBuilder( private val oAuthFlowsBuilder: OAuthFlowsBuilder ) { - fun build(securitySchemes: List): Map { + fun build(securitySchemes: List): Map { return mutableMapOf().apply { securitySchemes.forEach { put(it.name, build(it)) @@ -15,7 +16,7 @@ class SecuritySchemesBuilder( } } - private fun build(securityScheme: OpenApiSecurityScheme): SecurityScheme { + private fun build(securityScheme: SecuritySchemeData): SecurityScheme { return SecurityScheme().apply { description = securityScheme.description name = securityScheme.name @@ -23,7 +24,7 @@ class SecuritySchemesBuilder( `in` = securityScheme.location?.swaggerType scheme = securityScheme.scheme?.swaggerType bearerFormat = securityScheme.bearerFormat - flows = securityScheme.getFlows()?.let { f -> oAuthFlowsBuilder.build(f) } + flows = securityScheme.flows?.let { f -> oAuthFlowsBuilder.build(f) } openIdConnectUrl = securityScheme.openIdConnectUrl } } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ServerBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ServerBuilder.kt index 84196d42..d2b7d5b0 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ServerBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ServerBuilder.kt @@ -1,11 +1,11 @@ package io.github.smiley4.ktorswaggerui.spec.openapi -import io.github.smiley4.ktorswaggerui.dsl.OpenApiServer +import io.github.smiley4.ktorswaggerui.data.ServerData import io.swagger.v3.oas.models.servers.Server class ServerBuilder { - fun build(server: OpenApiServer): Server = + fun build(server: ServerData): Server = Server().also { it.url = server.url it.description = server.description diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/TagBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/TagBuilder.kt index 60284d77..ae1e6db5 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/TagBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/TagBuilder.kt @@ -1,18 +1,18 @@ package io.github.smiley4.ktorswaggerui.spec.openapi -import io.github.smiley4.ktorswaggerui.dsl.OpenApiTag +import io.github.smiley4.ktorswaggerui.data.TagData import io.swagger.v3.oas.models.tags.Tag class TagBuilder( private val tagExternalDocumentationBuilder: TagExternalDocumentationBuilder ) { - fun build(tag: OpenApiTag): Tag = + fun build(tag: TagData): Tag = Tag().also { it.name = tag.name it.description = tag.description if(tag.externalDocUrl != null && tag.externalDocDescription != null) { - it.externalDocs = tagExternalDocumentationBuilder.build(tag.externalDocUrl!!, tag.externalDocDescription!!) + it.externalDocs = tagExternalDocumentationBuilder.build(tag.externalDocUrl, tag.externalDocDescription) } } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteCollector.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteCollector.kt index 607e2ea3..ae5e37cf 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteCollector.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteCollector.kt @@ -1,6 +1,7 @@ package io.github.smiley4.ktorswaggerui.spec.route -import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.data.PluginConfigData +import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.dsl.DocumentedRouteSelector import io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute import io.ktor.http.HttpMethod @@ -19,7 +20,7 @@ class RouteCollector( /** * Collect all routes from the given application */ - fun collectRoutes(routeProvider: () -> Route, config: SwaggerUIPluginConfig): Sequence { + fun collectRoutes(routeProvider: () -> Route, config: PluginConfigData): Sequence { return allRoutes(routeProvider()) .asSequence() .map { route -> @@ -32,11 +33,7 @@ class RouteCollector( ) } .filter { !it.documentation.hidden } - .filter { path -> - config.pathFilter - ?.let { it(path.method, path.path.split("/").filter { it.isNotEmpty() }) } - ?: true - } + .filter { path -> config.pathFilter(path.method, path.path.split("/").filter { it.isNotEmpty() }) } } private fun getDocumentation(route: Route, base: OpenApiRoute): OpenApiRoute { @@ -56,7 +53,7 @@ class RouteCollector( return (route.selector as HttpMethodRouteSelector).method } - private fun getPath(route: Route, config: SwaggerUIPluginConfig): String { + private fun getPath(route: Route, config: PluginConfigData): String { val selector = route.selector return if (isIgnoredSelector(selector, config)) { route.parent?.let { getPath(it, config) } ?: "" @@ -72,7 +69,7 @@ class RouteCollector( } } - private fun isIgnoredSelector(selector: RouteSelector, config: SwaggerUIPluginConfig): Boolean { + private fun isIgnoredSelector(selector: RouteSelector, config: PluginConfigData): Boolean { return when (selector) { is TrailingSlashRouteSelector -> false is RootRouteSelector -> false diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteDocumentationMerger.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteDocumentationMerger.kt index 5e6439cb..b07b9df8 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteDocumentationMerger.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteDocumentationMerger.kt @@ -4,6 +4,9 @@ import io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute class RouteDocumentationMerger { + /** + * Merges "a" with "b" and returns the result as a new [OpenApiRoute]. "a" has priority over "b". + */ fun merge(a: OpenApiRoute, b: OpenApiRoute): OpenApiRoute { return OpenApiRoute().apply { specId = a.specId ?: b.specId diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/schema/SchemaContextBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/schema/SchemaContextBuilder.kt index 923526a9..82bd33b5 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/schema/SchemaContextBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/schema/SchemaContextBuilder.kt @@ -1,24 +1,24 @@ package io.github.smiley4.ktorswaggerui.spec.schema -import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig -import io.github.smiley4.ktorswaggerui.dsl.BaseCustomSchema +import io.github.smiley4.ktorswaggerui.data.BaseCustomSchema +import io.github.smiley4.ktorswaggerui.data.CustomJsonSchema +import io.github.smiley4.ktorswaggerui.data.CustomOpenApiSchema +import io.github.smiley4.ktorswaggerui.data.PluginConfigData +import io.github.smiley4.ktorswaggerui.data.RemoteSchema import io.github.smiley4.ktorswaggerui.dsl.CustomArraySchemaRef -import io.github.smiley4.ktorswaggerui.dsl.CustomJsonSchema -import io.github.smiley4.ktorswaggerui.dsl.CustomOpenApiSchema import io.github.smiley4.ktorswaggerui.dsl.CustomSchemaRef import io.github.smiley4.ktorswaggerui.dsl.OpenApiBaseBody import io.github.smiley4.ktorswaggerui.dsl.OpenApiMultipartBody import io.github.smiley4.ktorswaggerui.dsl.OpenApiRequestParameter import io.github.smiley4.ktorswaggerui.dsl.OpenApiResponse import io.github.smiley4.ktorswaggerui.dsl.OpenApiSimpleBody -import io.github.smiley4.ktorswaggerui.dsl.RemoteSchema import io.github.smiley4.ktorswaggerui.dsl.SchemaType import io.github.smiley4.ktorswaggerui.dsl.obj import io.github.smiley4.ktorswaggerui.spec.route.RouteMeta import io.swagger.v3.oas.models.media.Schema class SchemaContextBuilder( - private val config: SwaggerUIPluginConfig, + private val config: PluginConfigData, private val schemaBuilder: SchemaBuilder ) { @@ -26,8 +26,8 @@ class SchemaContextBuilder( return SchemaContext() .also { ctx -> routes.forEach { handle(ctx, it) } } .also { ctx -> - if (config.getCustomSchemas().includeAll) { - config.getCustomSchemas().getSchemas().forEach { (id, schema) -> + if (config.includeAllCustomSchemas) { + config.customSchemas.forEach { (id, schema) -> ctx.addSchema(obj(id), createSchema(schema, false)) } } @@ -92,7 +92,7 @@ class SchemaContextBuilder( private fun createSchema(customSchemaRef: CustomSchemaRef): SchemaDefinitions { - val customSchema = config.getCustomSchemas().getSchema(customSchemaRef.schemaId) + val customSchema = config.customSchemas[customSchemaRef.schemaId] return if (customSchema == null) { SchemaDefinitions( root = Schema(), diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/AuthExample.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/AuthExample.kt index 09943daa..79410f64 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/AuthExample.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/AuthExample.kt @@ -1,8 +1,8 @@ package io.github.smiley4.ktorswaggerui.examples import io.github.smiley4.ktorswaggerui.SwaggerUI -import io.github.smiley4.ktorswaggerui.dsl.AuthScheme -import io.github.smiley4.ktorswaggerui.dsl.AuthType +import io.github.smiley4.ktorswaggerui.data.AuthScheme +import io.github.smiley4.ktorswaggerui.data.AuthType import io.github.smiley4.ktorswaggerui.dsl.get import io.ktor.http.HttpStatusCode import io.ktor.server.application.Application diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/CompletePluginConfigExample.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/CompletePluginConfigExample.kt index 539b90d6..70f21ba3 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/CompletePluginConfigExample.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/CompletePluginConfigExample.kt @@ -3,11 +3,12 @@ package io.github.smiley4.ktorswaggerui.examples import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.github.victools.jsonschema.generator.SchemaGenerator import io.github.smiley4.ktorswaggerui.SwaggerUI -import io.github.smiley4.ktorswaggerui.dsl.AuthScheme -import io.github.smiley4.ktorswaggerui.dsl.AuthType +import io.github.smiley4.ktorswaggerui.data.AuthScheme +import io.github.smiley4.ktorswaggerui.data.AuthType +import io.github.smiley4.ktorswaggerui.data.EncodingData +import io.github.smiley4.ktorswaggerui.data.SwaggerUiSort +import io.github.smiley4.ktorswaggerui.data.SwaggerUiSyntaxHighlight import io.github.smiley4.ktorswaggerui.dsl.EncodingConfig -import io.github.smiley4.ktorswaggerui.dsl.SwaggerUiSort -import io.github.smiley4.ktorswaggerui.dsl.SwaggerUiSyntaxHighlight import io.ktor.server.application.Application import io.ktor.server.application.install import io.ktor.server.engine.embeddedServer @@ -98,7 +99,7 @@ private fun Application.myModule() { } encoding { schemaEncoder { type -> - SchemaGenerator(EncodingConfig.schemaGeneratorConfigBuilder().build()) + SchemaGenerator(EncodingData.schemaGeneratorConfigBuilder().build()) .generateSchema(type.javaType) .toPrettyString() } diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/ApplicationTests.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/ApplicationTests.kt index 1b6479ca..d5891a2a 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/ApplicationTests.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/ApplicationTests.kt @@ -1,7 +1,7 @@ package io.github.smiley4.ktorswaggerui.tests import io.github.smiley4.ktorswaggerui.SwaggerUI -import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.dsl.get import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/example/ExampleTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/example/ExampleTest.kt index bcb23358..ec3ba814 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/example/ExampleTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/example/ExampleTest.kt @@ -1,6 +1,7 @@ package io.github.smiley4.ktorswaggerui.tests.example -import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.data.PluginConfigData +import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.dsl.OpenApiRequestParameter import io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute import io.github.smiley4.ktorswaggerui.dsl.OpenApiSimpleBody @@ -174,7 +175,7 @@ class ExampleTest : StringSpec({ ): ExampleContext { return ExampleContextBuilder( exampleBuilder = ExampleBuilder( - config = pluginConfig + config = pluginConfig.build(PluginConfigData.DEFAULT) ) ).build(routes.toList()) } diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/ExternalDocsBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/ExternalDocsBuilderTest.kt index 22138528..b65eb24f 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/ExternalDocsBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/ExternalDocsBuilderTest.kt @@ -1,5 +1,6 @@ package io.github.smiley4.ktorswaggerui.tests.openapi +import io.github.smiley4.ktorswaggerui.data.ExternalDocsData import io.github.smiley4.ktorswaggerui.dsl.OpenApiExternalDocs import io.github.smiley4.ktorswaggerui.spec.openapi.ExternalDocumentationBuilder import io.kotest.core.spec.style.StringSpec @@ -30,7 +31,7 @@ class ExternalDocsBuilderTest : StringSpec({ companion object { private fun buildExternalDocsObject(builder: OpenApiExternalDocs.() -> Unit): ExternalDocumentation { - return ExternalDocumentationBuilder().build(OpenApiExternalDocs().apply(builder)) + return ExternalDocumentationBuilder().build(OpenApiExternalDocs().apply(builder).build(ExternalDocsData.DEFAULT)) } } diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/InfoBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/InfoBuilderTest.kt index 96a6ccd3..31475c73 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/InfoBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/InfoBuilderTest.kt @@ -1,5 +1,6 @@ package io.github.smiley4.ktorswaggerui.tests.openapi +import io.github.smiley4.ktorswaggerui.data.InfoData import io.github.smiley4.ktorswaggerui.dsl.OpenApiInfo import io.github.smiley4.ktorswaggerui.spec.openapi.ContactBuilder import io.github.smiley4.ktorswaggerui.spec.openapi.InfoBuilder @@ -72,7 +73,7 @@ class InfoBuilderTest : StringSpec({ return InfoBuilder( contactBuilder = ContactBuilder(), licenseBuilder = LicenseBuilder() - ).build(OpenApiInfo().apply(builder)) + ).build(OpenApiInfo().apply(builder).build(InfoData.DEFAULT)) } } diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OpenApiBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OpenApiBuilderTest.kt index b63df8ed..dbf5706d 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OpenApiBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OpenApiBuilderTest.kt @@ -1,7 +1,8 @@ package io.github.smiley4.ktorswaggerui.tests.openapi import com.fasterxml.jackson.databind.ObjectMapper -import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.data.PluginConfigData +import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.spec.example.ExampleContext import io.github.smiley4.ktorswaggerui.spec.example.ExampleContextBuilder import io.github.smiley4.ktorswaggerui.spec.openapi.* @@ -81,7 +82,7 @@ class OpenApiBuilderTest : StringSpec({ private fun schemaContext(routes: List, pluginConfig: SwaggerUIPluginConfig): SchemaContext { return SchemaContextBuilder( - config = pluginConfig, + config = pluginConfig.build(PluginConfigData.DEFAULT), schemaBuilder = SchemaBuilder( definitionsField = pluginConfig.encodingConfig.schemaDefinitionsField, schemaEncoder = pluginConfig.encodingConfig.getSchemaEncoder(), @@ -94,7 +95,7 @@ class OpenApiBuilderTest : StringSpec({ private fun exampleContext(routes: List, pluginConfig: SwaggerUIPluginConfig): ExampleContext { return ExampleContextBuilder( exampleBuilder = ExampleBuilder( - config = pluginConfig + config = pluginConfig.build(PluginConfigData.DEFAULT) ) ).build(routes.toList()) } @@ -102,8 +103,9 @@ class OpenApiBuilderTest : StringSpec({ private fun buildOpenApiObject(routes: List, pluginConfig: SwaggerUIPluginConfig = defaultPluginConfig): OpenAPI { val schemaContext = schemaContext(routes, pluginConfig) val exampleContext = exampleContext(routes, pluginConfig) + val pluginConfigData = pluginConfig.build(PluginConfigData.DEFAULT) return OpenApiBuilder( - config = pluginConfig, + config = pluginConfigData, schemaContext = schemaContext, exampleContext = exampleContext, infoBuilder = InfoBuilder( @@ -118,7 +120,7 @@ class OpenApiBuilderTest : StringSpec({ pathsBuilder = PathsBuilder( pathBuilder = PathBuilder( operationBuilder = OperationBuilder( - operationTagsBuilder = OperationTagsBuilder(pluginConfig), + operationTagsBuilder = OperationTagsBuilder(pluginConfigData), parameterBuilder = ParameterBuilder( schemaContext = schemaContext, exampleContext = exampleContext @@ -139,14 +141,14 @@ class OpenApiBuilderTest : StringSpec({ headerBuilder = HeaderBuilder(schemaContext) ) ), - config = pluginConfig + config = pluginConfigData ), - securityRequirementsBuilder = SecurityRequirementsBuilder(pluginConfig), + securityRequirementsBuilder = SecurityRequirementsBuilder(pluginConfigData), ) ) ), componentsBuilder = ComponentsBuilder( - config = pluginConfig, + config = pluginConfigData, securitySchemesBuilder = SecuritySchemesBuilder( oAuthFlowsBuilder = OAuthFlowsBuilder() ) diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OperationBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OperationBuilderTest.kt index d230cabc..6572d248 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OperationBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OperationBuilderTest.kt @@ -1,7 +1,8 @@ package io.github.smiley4.ktorswaggerui.tests.openapi import com.fasterxml.jackson.databind.ObjectMapper -import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.data.PluginConfigData +import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute import io.github.smiley4.ktorswaggerui.dsl.obj import io.github.smiley4.ktorswaggerui.spec.example.ExampleContext @@ -934,7 +935,7 @@ class OperationBuilderTest : StringSpec({ pluginConfig: SwaggerUIPluginConfig = defaultPluginConfig ): SchemaContext { return SchemaContextBuilder( - config = pluginConfig, + config = pluginConfig.build(PluginConfigData.DEFAULT), schemaBuilder = SchemaBuilder( definitionsField = pluginConfig.encodingConfig.schemaDefinitionsField, schemaEncoder = pluginConfig.encodingConfig.getSchemaEncoder(), @@ -950,7 +951,7 @@ class OperationBuilderTest : StringSpec({ ): ExampleContext { return ExampleContextBuilder( exampleBuilder = ExampleBuilder( - config = pluginConfig + config = pluginConfig.build(PluginConfigData.DEFAULT) ) ).build(routes.toList()) } @@ -962,8 +963,9 @@ class OperationBuilderTest : StringSpec({ exampleContext: ExampleContext, pluginConfig: SwaggerUIPluginConfig = defaultPluginConfig ): Operation { + val pluginConfigData = pluginConfig.build(PluginConfigData.DEFAULT) return OperationBuilder( - operationTagsBuilder = OperationTagsBuilder(pluginConfig), + operationTagsBuilder = OperationTagsBuilder(pluginConfigData), parameterBuilder = ParameterBuilder( schemaContext = schemaContext, exampleContext = exampleContext @@ -984,9 +986,9 @@ class OperationBuilderTest : StringSpec({ headerBuilder = HeaderBuilder(schemaContext) ) ), - config = pluginConfig + config = pluginConfigData ), - securityRequirementsBuilder = SecurityRequirementsBuilder(pluginConfig), + securityRequirementsBuilder = SecurityRequirementsBuilder(pluginConfigData), ).build(route) } diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/PathsBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/PathsBuilderTest.kt index ac6efdb0..1908879b 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/PathsBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/PathsBuilderTest.kt @@ -1,7 +1,8 @@ package io.github.smiley4.ktorswaggerui.tests.openapi import com.fasterxml.jackson.databind.ObjectMapper -import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.data.PluginConfigData +import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute import io.github.smiley4.ktorswaggerui.spec.example.ExampleContext import io.github.smiley4.ktorswaggerui.spec.example.ExampleContextBuilder @@ -92,7 +93,7 @@ class PathsBuilderTest : StringSpec({ private fun schemaContext(routes: List, pluginConfig: SwaggerUIPluginConfig = defaultPluginConfig): SchemaContext { return SchemaContextBuilder( - config = pluginConfig, + config = pluginConfig.build(PluginConfigData.DEFAULT), schemaBuilder = SchemaBuilder( definitionsField = pluginConfig.encodingConfig.schemaDefinitionsField, schemaEncoder = pluginConfig.encodingConfig.getSchemaEncoder(), @@ -105,7 +106,7 @@ class PathsBuilderTest : StringSpec({ private fun exampleContext(routes: List, pluginConfig: SwaggerUIPluginConfig = defaultPluginConfig): ExampleContext { return ExampleContextBuilder( exampleBuilder = ExampleBuilder( - config = pluginConfig + config = pluginConfig.build(PluginConfigData.DEFAULT) ) ).build(routes.toList()) } @@ -116,10 +117,11 @@ class PathsBuilderTest : StringSpec({ exampleContext: ExampleContext, pluginConfig: SwaggerUIPluginConfig = defaultPluginConfig ): Paths { + val pluginConfigData = pluginConfig.build(PluginConfigData.DEFAULT) return PathsBuilder( pathBuilder = PathBuilder( operationBuilder = OperationBuilder( - operationTagsBuilder = OperationTagsBuilder(pluginConfig), + operationTagsBuilder = OperationTagsBuilder(pluginConfigData), parameterBuilder = ParameterBuilder( schemaContext = schemaContext, exampleContext = exampleContext @@ -140,9 +142,9 @@ class PathsBuilderTest : StringSpec({ headerBuilder = HeaderBuilder(schemaContext) ) ), - config = pluginConfig + config = pluginConfigData ), - securityRequirementsBuilder = SecurityRequirementsBuilder(pluginConfig), + securityRequirementsBuilder = SecurityRequirementsBuilder(pluginConfigData), ) ) ).build(routes) diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/SecuritySchemesBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/SecuritySchemesBuilderTest.kt index 7e8f8135..453b0234 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/SecuritySchemesBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/SecuritySchemesBuilderTest.kt @@ -1,8 +1,9 @@ package io.github.smiley4.ktorswaggerui.tests.openapi -import io.github.smiley4.ktorswaggerui.dsl.AuthKeyLocation -import io.github.smiley4.ktorswaggerui.dsl.AuthScheme -import io.github.smiley4.ktorswaggerui.dsl.AuthType +import io.github.smiley4.ktorswaggerui.data.AuthKeyLocation +import io.github.smiley4.ktorswaggerui.data.AuthScheme +import io.github.smiley4.ktorswaggerui.data.AuthType +import io.github.smiley4.ktorswaggerui.data.SecuritySchemeData import io.github.smiley4.ktorswaggerui.dsl.OpenApiSecurityScheme import io.github.smiley4.ktorswaggerui.spec.openapi.OAuthFlowsBuilder import io.github.smiley4.ktorswaggerui.spec.openapi.SecuritySchemesBuilder @@ -191,7 +192,7 @@ class SecuritySchemesBuilderTest : StringSpec({ private fun buildSecuritySchemeObjects(builders: Map Unit>): Map { return SecuritySchemesBuilder( oAuthFlowsBuilder = OAuthFlowsBuilder() - ).build(builders.map { (name, entry) -> OpenApiSecurityScheme(name).apply(entry) }) + ).build(builders.map { (name, entry) -> OpenApiSecurityScheme(name).apply(entry).build(SecuritySchemeData.DEFAULT) }) } } diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/ServersBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/ServersBuilderTest.kt index 0c8c4487..78baf154 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/ServersBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/ServersBuilderTest.kt @@ -1,5 +1,6 @@ package io.github.smiley4.ktorswaggerui.tests.openapi +import io.github.smiley4.ktorswaggerui.data.ServerData import io.github.smiley4.ktorswaggerui.dsl.OpenApiServer import io.github.smiley4.ktorswaggerui.spec.openapi.ServerBuilder import io.kotest.core.spec.style.StringSpec @@ -35,7 +36,7 @@ class ServersBuilderTest : StringSpec({ companion object { private fun buildServerObject(builder: OpenApiServer.() -> Unit): Server { - return ServerBuilder().build(OpenApiServer().apply(builder)) + return ServerBuilder().build(OpenApiServer().apply(builder).build(ServerData.DEFAULT)) } } diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/TagsBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/TagsBuilderTest.kt index bc2f3356..5e52076f 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/TagsBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/TagsBuilderTest.kt @@ -1,5 +1,6 @@ package io.github.smiley4.ktorswaggerui.tests.openapi +import io.github.smiley4.ktorswaggerui.data.TagData import io.github.smiley4.ktorswaggerui.dsl.OpenApiTag import io.github.smiley4.ktorswaggerui.spec.openapi.TagBuilder import io.github.smiley4.ktorswaggerui.spec.openapi.TagExternalDocumentationBuilder @@ -46,7 +47,7 @@ class TagsBuilderTest : StringSpec({ private fun buildTagObject(name: String, builder: OpenApiTag.() -> Unit): Tag { return TagBuilder( tagExternalDocumentationBuilder = TagExternalDocumentationBuilder() - ).build(OpenApiTag(name).apply(builder)) + ).build(OpenApiTag(name).apply(builder).build(TagData.DEFAULT)) } } diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaContextTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaContextTest.kt index c2012226..0851cda4 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaContextTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaContextTest.kt @@ -6,7 +6,8 @@ import com.github.victools.jsonschema.generator.OptionPreset import com.github.victools.jsonschema.generator.SchemaGenerator import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder import com.github.victools.jsonschema.generator.SchemaVersion -import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.data.PluginConfigData +import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute import io.github.smiley4.ktorswaggerui.dsl.array import io.github.smiley4.ktorswaggerui.dsl.asSchemaType @@ -505,7 +506,7 @@ class SchemaContextTest : StringSpec({ pluginConfig: SwaggerUIPluginConfig = defaultPluginConfig ): SchemaContext { return SchemaContextBuilder( - config = pluginConfig, + config = pluginConfig.build(PluginConfigData.DEFAULT), schemaBuilder = SchemaBuilder( definitionsField = pluginConfig.encodingConfig.schemaDefinitionsField, schemaEncoder = pluginConfig.encodingConfig.getSchemaEncoder(), From 1a11f19437c7db7a658444fbd5a35b6623f3f105 Mon Sep 17 00:00:00 2001 From: Lukas Ruegner Date: Fri, 20 Oct 2023 14:51:57 +0200 Subject: [PATCH 07/12] rename directory spec to builder --- .../smiley4/ktorswaggerui/SwaggerPlugin.kt | 64 +++++++++---------- .../example/ExampleContext.kt | 2 +- .../example/ExampleContextBuilder.kt | 6 +- .../openapi/ComponentsBuilder.kt | 2 +- .../openapi/ContactBuilder.kt | 2 +- .../openapi/ContentBuilder.kt | 6 +- .../openapi/ExampleBuilder.kt | 2 +- .../openapi/ExternalDocumentationBuilder.kt | 2 +- .../openapi/HeaderBuilder.kt | 4 +- .../{spec => builder}/openapi/InfoBuilder.kt | 2 +- .../openapi/LicenseBuilder.kt | 2 +- .../openapi/OAuthFlowsBuilder.kt | 2 +- .../openapi/OpenApiBuilder.kt | 8 +-- .../openapi/OperationBuilder.kt | 4 +- .../openapi/OperationTagsBuilder.kt | 4 +- .../openapi/ParameterBuilder.kt | 6 +- .../{spec => builder}/openapi/PathBuilder.kt | 4 +- .../{spec => builder}/openapi/PathsBuilder.kt | 4 +- .../openapi/RequestBodyBuilder.kt | 2 +- .../openapi/ResponseBuilder.kt | 2 +- .../openapi/ResponsesBuilder.kt | 2 +- .../openapi/SecurityRequirementsBuilder.kt | 4 +- .../openapi/SecuritySchemesBuilder.kt | 3 +- .../openapi/ServerBuilder.kt | 2 +- .../{spec => builder}/openapi/TagBuilder.kt | 2 +- .../TagExternalDocumentationBuilder.kt | 2 +- .../{spec => builder}/route/RouteCollector.kt | 3 +- .../route/RouteDocumentationMerger.kt | 2 +- .../{spec => builder}/route/RouteMeta.kt | 2 +- .../{spec => builder}/schema/SchemaBuilder.kt | 2 +- .../{spec => builder}/schema/SchemaContext.kt | 2 +- .../schema/SchemaContextBuilder.kt | 4 +- .../schema/SchemaTypeAttributeOverride.kt | 2 +- .../schema/TypeOverwrites.kt | 2 +- .../ktorswaggerui/data/EncodingData.kt | 2 +- .../tests/example/ExampleTest.kt | 8 +-- .../tests/openapi/ExternalDocsBuilderTest.kt | 2 +- .../tests/openapi/InfoBuilderTest.kt | 6 +- .../tests/openapi/OpenApiBuilderTest.kt | 16 ++--- .../tests/openapi/OperationBuilderTest.kt | 34 +++++----- .../tests/openapi/PathsBuilderTest.kt | 38 +++++------ .../openapi/SecuritySchemesBuilderTest.kt | 4 +- .../tests/openapi/ServersBuilderTest.kt | 2 +- .../tests/openapi/TagsBuilderTest.kt | 4 +- .../route/RouteDocumentationMergerTest.kt | 2 +- .../tests/schema/SchemaBuilderTest.kt | 8 +-- .../tests/schema/SchemaContextTest.kt | 10 +-- 47 files changed, 149 insertions(+), 151 deletions(-) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/example/ExampleContext.kt (94%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/example/ExampleContextBuilder.kt (91%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/openapi/ComponentsBuilder.kt (92%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/openapi/ContactBuilder.kt (84%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/openapi/ContentBuilder.kt (96%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/openapi/ExampleBuilder.kt (92%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/openapi/ExternalDocumentationBuilder.kt (86%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/openapi/HeaderBuilder.kt (79%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/openapi/InfoBuilder.kt (92%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/openapi/LicenseBuilder.kt (83%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/openapi/OAuthFlowsBuilder.kt (95%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/openapi/OpenApiBuilder.kt (81%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/openapi/OperationBuilder.kt (92%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/openapi/OperationTagsBuilder.kt (82%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/openapi/ParameterBuilder.kt (84%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/openapi/PathBuilder.kt (86%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/openapi/PathsBuilder.kt (91%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/openapi/RequestBodyBuilder.kt (88%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/openapi/ResponseBuilder.kt (91%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/openapi/ResponsesBuilder.kt (95%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/openapi/SecurityRequirementsBuilder.kt (86%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/openapi/SecuritySchemesBuilder.kt (89%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/openapi/ServerBuilder.kt (83%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/openapi/TagBuilder.kt (90%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/openapi/TagExternalDocumentationBuilder.kt (83%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/route/RouteCollector.kt (97%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/route/RouteDocumentationMerger.kt (96%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/route/RouteMeta.kt (83%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/schema/SchemaBuilder.kt (98%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/schema/SchemaContext.kt (99%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/schema/SchemaContextBuilder.kt (97%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/schema/SchemaTypeAttributeOverride.kt (95%) rename src/main/kotlin/io/github/smiley4/ktorswaggerui/{spec => builder}/schema/TypeOverwrites.kt (88%) diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt index 4d371b7b..927bd089 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt @@ -5,38 +5,38 @@ import io.github.smiley4.ktorswaggerui.data.PluginConfigData import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.routing.ForwardRouteController import io.github.smiley4.ktorswaggerui.routing.SwaggerController -import io.github.smiley4.ktorswaggerui.spec.example.ExampleContext -import io.github.smiley4.ktorswaggerui.spec.example.ExampleContextBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.ComponentsBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.ContactBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.ContentBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.ExampleBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.ExternalDocumentationBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.HeaderBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.InfoBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.LicenseBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.OAuthFlowsBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.OpenApiBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.OperationBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.OperationTagsBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.ParameterBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.PathBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.PathsBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.RequestBodyBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.ResponseBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.ResponsesBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.SecurityRequirementsBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.SecuritySchemesBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.ServerBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.TagBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.TagExternalDocumentationBuilder -import io.github.smiley4.ktorswaggerui.spec.route.RouteCollector -import io.github.smiley4.ktorswaggerui.spec.route.RouteDocumentationMerger -import io.github.smiley4.ktorswaggerui.spec.route.RouteMeta -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaBuilder -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaContext -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaContextBuilder -import io.github.smiley4.ktorswaggerui.spec.schema.TypeOverwrites +import io.github.smiley4.ktorswaggerui.builder.example.ExampleContext +import io.github.smiley4.ktorswaggerui.builder.example.ExampleContextBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.ComponentsBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.ContactBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.ContentBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.ExampleBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.ExternalDocumentationBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.HeaderBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.InfoBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.LicenseBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.OAuthFlowsBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.OpenApiBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.OperationBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.OperationTagsBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.ParameterBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.PathBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.PathsBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.RequestBodyBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.ResponseBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.ResponsesBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.SecurityRequirementsBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.SecuritySchemesBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.ServerBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.TagBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.TagExternalDocumentationBuilder +import io.github.smiley4.ktorswaggerui.builder.route.RouteCollector +import io.github.smiley4.ktorswaggerui.builder.route.RouteDocumentationMerger +import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaBuilder +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContext +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContextBuilder +import io.github.smiley4.ktorswaggerui.builder.schema.TypeOverwrites import io.ktor.server.application.Application import io.ktor.server.application.ApplicationStarted import io.ktor.server.application.createApplicationPlugin diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/example/ExampleContext.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/example/ExampleContext.kt similarity index 94% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/example/ExampleContext.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/example/ExampleContext.kt index 05dd8d5f..8c031c32 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/example/ExampleContext.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/example/ExampleContext.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.spec.example +package io.github.smiley4.ktorswaggerui.builder.example import io.github.smiley4.ktorswaggerui.dsl.OpenApiRequestParameter import io.github.smiley4.ktorswaggerui.dsl.OpenApiSimpleBody diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/example/ExampleContextBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/example/ExampleContextBuilder.kt similarity index 91% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/example/ExampleContextBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/example/ExampleContextBuilder.kt index 426ca98d..c56012e0 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/example/ExampleContextBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/example/ExampleContextBuilder.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.spec.example +package io.github.smiley4.ktorswaggerui.builder.example import io.github.smiley4.ktorswaggerui.dsl.OpenApiBaseBody import io.github.smiley4.ktorswaggerui.dsl.OpenApiExample @@ -7,8 +7,8 @@ import io.github.smiley4.ktorswaggerui.dsl.OpenApiResponse import io.github.smiley4.ktorswaggerui.dsl.OpenApiSimpleBody import io.github.smiley4.ktorswaggerui.dsl.SchemaType import io.github.smiley4.ktorswaggerui.dsl.getSchemaType -import io.github.smiley4.ktorswaggerui.spec.openapi.ExampleBuilder -import io.github.smiley4.ktorswaggerui.spec.route.RouteMeta +import io.github.smiley4.ktorswaggerui.builder.openapi.ExampleBuilder +import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta import io.swagger.v3.oas.models.examples.Example class ExampleContextBuilder( diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ComponentsBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ComponentsBuilder.kt similarity index 92% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ComponentsBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ComponentsBuilder.kt index 3c2636a8..7d09097b 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ComponentsBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ComponentsBuilder.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.spec.openapi +package io.github.smiley4.ktorswaggerui.builder.openapi import io.github.smiley4.ktorswaggerui.data.PluginConfigData import io.swagger.v3.oas.models.Components diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ContactBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ContactBuilder.kt similarity index 84% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ContactBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ContactBuilder.kt index 9c069e65..5fc0e483 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ContactBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ContactBuilder.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.spec.openapi +package io.github.smiley4.ktorswaggerui.builder.openapi import io.github.smiley4.ktorswaggerui.data.ContactData import io.swagger.v3.oas.models.info.Contact diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ContentBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ContentBuilder.kt similarity index 96% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ContentBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ContentBuilder.kt index 34896aa1..eef6a721 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ContentBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ContentBuilder.kt @@ -1,11 +1,11 @@ -package io.github.smiley4.ktorswaggerui.spec.openapi +package io.github.smiley4.ktorswaggerui.builder.openapi import io.github.smiley4.ktorswaggerui.dsl.OpenApiBaseBody import io.github.smiley4.ktorswaggerui.dsl.OpenApiMultipartBody import io.github.smiley4.ktorswaggerui.dsl.OpenApiSimpleBody import io.github.smiley4.ktorswaggerui.dsl.OpenApiMultipartPart -import io.github.smiley4.ktorswaggerui.spec.example.ExampleContext -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaContext +import io.github.smiley4.ktorswaggerui.builder.example.ExampleContext +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContext import io.ktor.http.ContentType import io.swagger.v3.oas.models.media.Content import io.swagger.v3.oas.models.media.Encoding diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ExampleBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ExampleBuilder.kt similarity index 92% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ExampleBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ExampleBuilder.kt index a4cbbb3a..2b452fa1 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ExampleBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ExampleBuilder.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.spec.openapi +package io.github.smiley4.ktorswaggerui.builder.openapi import io.github.smiley4.ktorswaggerui.data.PluginConfigData import io.github.smiley4.ktorswaggerui.dsl.OpenApiExample diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ExternalDocumentationBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ExternalDocumentationBuilder.kt similarity index 86% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ExternalDocumentationBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ExternalDocumentationBuilder.kt index 73410149..49f4fd2e 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ExternalDocumentationBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ExternalDocumentationBuilder.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.spec.openapi +package io.github.smiley4.ktorswaggerui.builder.openapi import io.github.smiley4.ktorswaggerui.data.ExternalDocsData import io.swagger.v3.oas.models.ExternalDocumentation diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/HeaderBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/HeaderBuilder.kt similarity index 79% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/HeaderBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/HeaderBuilder.kt index b9f2e11c..4c0fd1a7 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/HeaderBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/HeaderBuilder.kt @@ -1,7 +1,7 @@ -package io.github.smiley4.ktorswaggerui.spec.openapi +package io.github.smiley4.ktorswaggerui.builder.openapi import io.github.smiley4.ktorswaggerui.dsl.OpenApiHeader -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaContext +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContext import io.swagger.v3.oas.models.headers.Header class HeaderBuilder( diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/InfoBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/InfoBuilder.kt similarity index 92% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/InfoBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/InfoBuilder.kt index 4c319f90..bfc03dc2 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/InfoBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/InfoBuilder.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.spec.openapi +package io.github.smiley4.ktorswaggerui.builder.openapi import io.github.smiley4.ktorswaggerui.data.InfoData import io.swagger.v3.oas.models.info.Info diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/LicenseBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/LicenseBuilder.kt similarity index 83% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/LicenseBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/LicenseBuilder.kt index dcaf0098..30f727c0 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/LicenseBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/LicenseBuilder.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.spec.openapi +package io.github.smiley4.ktorswaggerui.builder.openapi import io.github.smiley4.ktorswaggerui.data.LicenseData import io.swagger.v3.oas.models.info.License diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OAuthFlowsBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OAuthFlowsBuilder.kt similarity index 95% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OAuthFlowsBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OAuthFlowsBuilder.kt index 4aeb6546..c8f8c965 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OAuthFlowsBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OAuthFlowsBuilder.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.spec.openapi +package io.github.smiley4.ktorswaggerui.builder.openapi import io.github.smiley4.ktorswaggerui.data.OpenIdOAuthFlowData import io.github.smiley4.ktorswaggerui.data.OpenIdOAuthFlowsData diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OpenApiBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OpenApiBuilder.kt similarity index 81% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OpenApiBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OpenApiBuilder.kt index 7e2104c7..f3adac85 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OpenApiBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OpenApiBuilder.kt @@ -1,9 +1,9 @@ -package io.github.smiley4.ktorswaggerui.spec.openapi +package io.github.smiley4.ktorswaggerui.builder.openapi import io.github.smiley4.ktorswaggerui.data.PluginConfigData -import io.github.smiley4.ktorswaggerui.spec.example.ExampleContext -import io.github.smiley4.ktorswaggerui.spec.route.RouteMeta -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaContext +import io.github.smiley4.ktorswaggerui.builder.example.ExampleContext +import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContext import io.swagger.v3.oas.models.OpenAPI class OpenApiBuilder( diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OperationBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OperationBuilder.kt similarity index 92% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OperationBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OperationBuilder.kt index 0b2df038..3e48ce88 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OperationBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OperationBuilder.kt @@ -1,6 +1,6 @@ -package io.github.smiley4.ktorswaggerui.spec.openapi +package io.github.smiley4.ktorswaggerui.builder.openapi -import io.github.smiley4.ktorswaggerui.spec.route.RouteMeta +import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta import io.swagger.v3.oas.models.Operation class OperationBuilder( diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OperationTagsBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OperationTagsBuilder.kt similarity index 82% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OperationTagsBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OperationTagsBuilder.kt index d71f9567..962ae049 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/OperationTagsBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OperationTagsBuilder.kt @@ -1,7 +1,7 @@ -package io.github.smiley4.ktorswaggerui.spec.openapi +package io.github.smiley4.ktorswaggerui.builder.openapi import io.github.smiley4.ktorswaggerui.data.PluginConfigData -import io.github.smiley4.ktorswaggerui.spec.route.RouteMeta +import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta class OperationTagsBuilder( private val config: PluginConfigData diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ParameterBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ParameterBuilder.kt similarity index 84% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ParameterBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ParameterBuilder.kt index 8b7a811b..bbf76039 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ParameterBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ParameterBuilder.kt @@ -1,8 +1,8 @@ -package io.github.smiley4.ktorswaggerui.spec.openapi +package io.github.smiley4.ktorswaggerui.builder.openapi import io.github.smiley4.ktorswaggerui.dsl.OpenApiRequestParameter -import io.github.smiley4.ktorswaggerui.spec.example.ExampleContext -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaContext +import io.github.smiley4.ktorswaggerui.builder.example.ExampleContext +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContext import io.swagger.v3.oas.models.parameters.Parameter class ParameterBuilder( diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/PathBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/PathBuilder.kt similarity index 86% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/PathBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/PathBuilder.kt index a77b865f..a6c5c93e 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/PathBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/PathBuilder.kt @@ -1,6 +1,6 @@ -package io.github.smiley4.ktorswaggerui.spec.openapi +package io.github.smiley4.ktorswaggerui.builder.openapi -import io.github.smiley4.ktorswaggerui.spec.route.RouteMeta +import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta import io.ktor.http.HttpMethod import io.swagger.v3.oas.models.PathItem diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/PathsBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/PathsBuilder.kt similarity index 91% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/PathsBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/PathsBuilder.kt index 615d45d3..46a3c6eb 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/PathsBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/PathsBuilder.kt @@ -1,6 +1,6 @@ -package io.github.smiley4.ktorswaggerui.spec.openapi +package io.github.smiley4.ktorswaggerui.builder.openapi -import io.github.smiley4.ktorswaggerui.spec.route.RouteMeta +import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta import io.swagger.v3.oas.models.PathItem import io.swagger.v3.oas.models.Paths diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/RequestBodyBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/RequestBodyBuilder.kt similarity index 88% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/RequestBodyBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/RequestBodyBuilder.kt index 189fada8..f5a6383b 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/RequestBodyBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/RequestBodyBuilder.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.spec.openapi +package io.github.smiley4.ktorswaggerui.builder.openapi import io.github.smiley4.ktorswaggerui.dsl.OpenApiBaseBody import io.swagger.v3.oas.models.parameters.RequestBody diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ResponseBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ResponseBuilder.kt similarity index 91% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ResponseBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ResponseBuilder.kt index 66cf191d..7e59eec0 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ResponseBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ResponseBuilder.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.spec.openapi +package io.github.smiley4.ktorswaggerui.builder.openapi import io.github.smiley4.ktorswaggerui.dsl.OpenApiResponse import io.swagger.v3.oas.models.responses.ApiResponse diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ResponsesBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ResponsesBuilder.kt similarity index 95% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ResponsesBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ResponsesBuilder.kt index 4a5d9a89..bd148dd7 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ResponsesBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ResponsesBuilder.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.spec.openapi +package io.github.smiley4.ktorswaggerui.builder.openapi import io.github.smiley4.ktorswaggerui.data.PluginConfigData import io.github.smiley4.ktorswaggerui.dsl.OpenApiResponses diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/SecurityRequirementsBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/SecurityRequirementsBuilder.kt similarity index 86% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/SecurityRequirementsBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/SecurityRequirementsBuilder.kt index dfc2bdc6..3b1df677 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/SecurityRequirementsBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/SecurityRequirementsBuilder.kt @@ -1,7 +1,7 @@ -package io.github.smiley4.ktorswaggerui.spec.openapi +package io.github.smiley4.ktorswaggerui.builder.openapi import io.github.smiley4.ktorswaggerui.data.PluginConfigData -import io.github.smiley4.ktorswaggerui.spec.route.RouteMeta +import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta import io.swagger.v3.oas.models.security.SecurityRequirement class SecurityRequirementsBuilder( diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/SecuritySchemesBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/SecuritySchemesBuilder.kt similarity index 89% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/SecuritySchemesBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/SecuritySchemesBuilder.kt index 58b96cfe..9216f0fe 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/SecuritySchemesBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/SecuritySchemesBuilder.kt @@ -1,7 +1,6 @@ -package io.github.smiley4.ktorswaggerui.spec.openapi +package io.github.smiley4.ktorswaggerui.builder.openapi import io.github.smiley4.ktorswaggerui.data.SecuritySchemeData -import io.github.smiley4.ktorswaggerui.dsl.OpenApiSecurityScheme import io.swagger.v3.oas.models.security.SecurityScheme class SecuritySchemesBuilder( diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ServerBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ServerBuilder.kt similarity index 83% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ServerBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ServerBuilder.kt index d2b7d5b0..5878483f 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/ServerBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ServerBuilder.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.spec.openapi +package io.github.smiley4.ktorswaggerui.builder.openapi import io.github.smiley4.ktorswaggerui.data.ServerData import io.swagger.v3.oas.models.servers.Server diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/TagBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/TagBuilder.kt similarity index 90% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/TagBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/TagBuilder.kt index ae1e6db5..d550e6a3 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/TagBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/TagBuilder.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.spec.openapi +package io.github.smiley4.ktorswaggerui.builder.openapi import io.github.smiley4.ktorswaggerui.data.TagData import io.swagger.v3.oas.models.tags.Tag diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/TagExternalDocumentationBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/TagExternalDocumentationBuilder.kt similarity index 83% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/TagExternalDocumentationBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/TagExternalDocumentationBuilder.kt index 805cd313..4e53c08b 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/openapi/TagExternalDocumentationBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/TagExternalDocumentationBuilder.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.spec.openapi +package io.github.smiley4.ktorswaggerui.builder.openapi import io.swagger.v3.oas.models.ExternalDocumentation diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteCollector.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/route/RouteCollector.kt similarity index 97% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteCollector.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/route/RouteCollector.kt index ae5e37cf..040af3cc 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteCollector.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/route/RouteCollector.kt @@ -1,7 +1,6 @@ -package io.github.smiley4.ktorswaggerui.spec.route +package io.github.smiley4.ktorswaggerui.builder.route import io.github.smiley4.ktorswaggerui.data.PluginConfigData -import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.dsl.DocumentedRouteSelector import io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute import io.ktor.http.HttpMethod diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteDocumentationMerger.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/route/RouteDocumentationMerger.kt similarity index 96% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteDocumentationMerger.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/route/RouteDocumentationMerger.kt index b07b9df8..f0509b9b 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteDocumentationMerger.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/route/RouteDocumentationMerger.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.spec.route +package io.github.smiley4.ktorswaggerui.builder.route import io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteMeta.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/route/RouteMeta.kt similarity index 83% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteMeta.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/route/RouteMeta.kt index e7975984..21452920 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/route/RouteMeta.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/route/RouteMeta.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.spec.route +package io.github.smiley4.ktorswaggerui.builder.route import io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute import io.ktor.http.HttpMethod diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/schema/SchemaBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaBuilder.kt similarity index 98% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/schema/SchemaBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaBuilder.kt index e7f50baa..5d85e608 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/schema/SchemaBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaBuilder.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.spec.schema +package io.github.smiley4.ktorswaggerui.builder.schema import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.ObjectMapper diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/schema/SchemaContext.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaContext.kt similarity index 99% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/schema/SchemaContext.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaContext.kt index 4fb81fdf..1bb6a767 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/schema/SchemaContext.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaContext.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.spec.schema +package io.github.smiley4.ktorswaggerui.builder.schema import io.github.smiley4.ktorswaggerui.dsl.CustomSchemaRef import io.github.smiley4.ktorswaggerui.dsl.SchemaType diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/schema/SchemaContextBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaContextBuilder.kt similarity index 97% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/schema/SchemaContextBuilder.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaContextBuilder.kt index 82bd33b5..2db25d42 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/schema/SchemaContextBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaContextBuilder.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.spec.schema +package io.github.smiley4.ktorswaggerui.builder.schema import io.github.smiley4.ktorswaggerui.data.BaseCustomSchema import io.github.smiley4.ktorswaggerui.data.CustomJsonSchema @@ -14,7 +14,7 @@ import io.github.smiley4.ktorswaggerui.dsl.OpenApiResponse import io.github.smiley4.ktorswaggerui.dsl.OpenApiSimpleBody import io.github.smiley4.ktorswaggerui.dsl.SchemaType import io.github.smiley4.ktorswaggerui.dsl.obj -import io.github.smiley4.ktorswaggerui.spec.route.RouteMeta +import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta import io.swagger.v3.oas.models.media.Schema class SchemaContextBuilder( diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/schema/SchemaTypeAttributeOverride.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaTypeAttributeOverride.kt similarity index 95% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/schema/SchemaTypeAttributeOverride.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaTypeAttributeOverride.kt index 0fc80fca..f160b369 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/schema/SchemaTypeAttributeOverride.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaTypeAttributeOverride.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.spec.schema +package io.github.smiley4.ktorswaggerui.builder.schema import com.fasterxml.jackson.databind.node.ObjectNode import com.github.victools.jsonschema.generator.FieldScope diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/schema/TypeOverwrites.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/TypeOverwrites.kt similarity index 88% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/schema/TypeOverwrites.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/TypeOverwrites.kt index 12bbae0f..c74646f3 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/spec/schema/TypeOverwrites.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/TypeOverwrites.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.spec.schema +package io.github.smiley4.ktorswaggerui.builder.schema import io.github.smiley4.ktorswaggerui.dsl.getSchemaType import java.io.File diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/EncodingData.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/EncodingData.kt index ed259b78..293442b8 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/EncodingData.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/EncodingData.kt @@ -11,7 +11,7 @@ import com.github.victools.jsonschema.module.swagger2.Swagger2Module import io.github.smiley4.ktorswaggerui.dsl.ExampleEncoder import io.github.smiley4.ktorswaggerui.dsl.SchemaEncoder import io.github.smiley4.ktorswaggerui.dsl.SchemaType -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaTypeAttributeOverride +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaTypeAttributeOverride import kotlin.reflect.jvm.javaType data class EncodingData( diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/example/ExampleTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/example/ExampleTest.kt index ec3ba814..6ed4ba7e 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/example/ExampleTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/example/ExampleTest.kt @@ -5,10 +5,10 @@ import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.dsl.OpenApiRequestParameter import io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute import io.github.smiley4.ktorswaggerui.dsl.OpenApiSimpleBody -import io.github.smiley4.ktorswaggerui.spec.example.ExampleContext -import io.github.smiley4.ktorswaggerui.spec.example.ExampleContextBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.ExampleBuilder -import io.github.smiley4.ktorswaggerui.spec.route.RouteMeta +import io.github.smiley4.ktorswaggerui.builder.example.ExampleContext +import io.github.smiley4.ktorswaggerui.builder.example.ExampleContextBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.ExampleBuilder +import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/ExternalDocsBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/ExternalDocsBuilderTest.kt index b65eb24f..d49ab313 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/ExternalDocsBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/ExternalDocsBuilderTest.kt @@ -2,7 +2,7 @@ package io.github.smiley4.ktorswaggerui.tests.openapi import io.github.smiley4.ktorswaggerui.data.ExternalDocsData import io.github.smiley4.ktorswaggerui.dsl.OpenApiExternalDocs -import io.github.smiley4.ktorswaggerui.spec.openapi.ExternalDocumentationBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.ExternalDocumentationBuilder import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import io.swagger.v3.oas.models.ExternalDocumentation diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/InfoBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/InfoBuilderTest.kt index 31475c73..51475ef2 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/InfoBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/InfoBuilderTest.kt @@ -2,9 +2,9 @@ package io.github.smiley4.ktorswaggerui.tests.openapi import io.github.smiley4.ktorswaggerui.data.InfoData import io.github.smiley4.ktorswaggerui.dsl.OpenApiInfo -import io.github.smiley4.ktorswaggerui.spec.openapi.ContactBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.InfoBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.LicenseBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.ContactBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.InfoBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.LicenseBuilder import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OpenApiBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OpenApiBuilderTest.kt index dbf5706d..6a0db031 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OpenApiBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OpenApiBuilderTest.kt @@ -3,14 +3,14 @@ package io.github.smiley4.ktorswaggerui.tests.openapi import com.fasterxml.jackson.databind.ObjectMapper import io.github.smiley4.ktorswaggerui.data.PluginConfigData import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIPluginConfig -import io.github.smiley4.ktorswaggerui.spec.example.ExampleContext -import io.github.smiley4.ktorswaggerui.spec.example.ExampleContextBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.* -import io.github.smiley4.ktorswaggerui.spec.route.RouteMeta -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaBuilder -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaContext -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaContextBuilder -import io.github.smiley4.ktorswaggerui.spec.schema.TypeOverwrites +import io.github.smiley4.ktorswaggerui.builder.example.ExampleContext +import io.github.smiley4.ktorswaggerui.builder.example.ExampleContextBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.* +import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaBuilder +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContext +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContextBuilder +import io.github.smiley4.ktorswaggerui.builder.schema.TypeOverwrites import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder import io.kotest.matchers.collections.shouldHaveSize diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OperationBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OperationBuilderTest.kt index 6572d248..05f221a3 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OperationBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OperationBuilderTest.kt @@ -5,23 +5,23 @@ import io.github.smiley4.ktorswaggerui.data.PluginConfigData import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute import io.github.smiley4.ktorswaggerui.dsl.obj -import io.github.smiley4.ktorswaggerui.spec.example.ExampleContext -import io.github.smiley4.ktorswaggerui.spec.example.ExampleContextBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.ContentBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.ExampleBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.HeaderBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.OperationBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.OperationTagsBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.ParameterBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.RequestBodyBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.ResponseBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.ResponsesBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.SecurityRequirementsBuilder -import io.github.smiley4.ktorswaggerui.spec.route.RouteMeta -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaBuilder -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaContext -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaContextBuilder -import io.github.smiley4.ktorswaggerui.spec.schema.TypeOverwrites +import io.github.smiley4.ktorswaggerui.builder.example.ExampleContext +import io.github.smiley4.ktorswaggerui.builder.example.ExampleContextBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.ContentBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.ExampleBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.HeaderBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.OperationBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.OperationTagsBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.ParameterBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.RequestBodyBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.ResponseBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.ResponsesBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.SecurityRequirementsBuilder +import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaBuilder +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContext +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContextBuilder +import io.github.smiley4.ktorswaggerui.builder.schema.TypeOverwrites import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/PathsBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/PathsBuilderTest.kt index 1908879b..41e02245 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/PathsBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/PathsBuilderTest.kt @@ -4,25 +4,25 @@ import com.fasterxml.jackson.databind.ObjectMapper import io.github.smiley4.ktorswaggerui.data.PluginConfigData import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute -import io.github.smiley4.ktorswaggerui.spec.example.ExampleContext -import io.github.smiley4.ktorswaggerui.spec.example.ExampleContextBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.ContentBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.ExampleBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.HeaderBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.OperationBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.OperationTagsBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.ParameterBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.PathBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.PathsBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.RequestBodyBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.ResponseBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.ResponsesBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.SecurityRequirementsBuilder -import io.github.smiley4.ktorswaggerui.spec.route.RouteMeta -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaBuilder -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaContext -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaContextBuilder -import io.github.smiley4.ktorswaggerui.spec.schema.TypeOverwrites +import io.github.smiley4.ktorswaggerui.builder.example.ExampleContext +import io.github.smiley4.ktorswaggerui.builder.example.ExampleContextBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.ContentBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.ExampleBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.HeaderBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.OperationBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.OperationTagsBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.ParameterBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.PathBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.PathsBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.RequestBodyBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.ResponseBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.ResponsesBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.SecurityRequirementsBuilder +import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaBuilder +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContext +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContextBuilder +import io.github.smiley4.ktorswaggerui.builder.schema.TypeOverwrites import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder import io.kotest.matchers.maps.shouldHaveSize diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/SecuritySchemesBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/SecuritySchemesBuilderTest.kt index 453b0234..96de733c 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/SecuritySchemesBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/SecuritySchemesBuilderTest.kt @@ -5,8 +5,8 @@ import io.github.smiley4.ktorswaggerui.data.AuthScheme import io.github.smiley4.ktorswaggerui.data.AuthType import io.github.smiley4.ktorswaggerui.data.SecuritySchemeData import io.github.smiley4.ktorswaggerui.dsl.OpenApiSecurityScheme -import io.github.smiley4.ktorswaggerui.spec.openapi.OAuthFlowsBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.SecuritySchemesBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.OAuthFlowsBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.SecuritySchemesBuilder import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder import io.kotest.matchers.maps.shouldBeEmpty diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/ServersBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/ServersBuilderTest.kt index 78baf154..44192f6f 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/ServersBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/ServersBuilderTest.kt @@ -2,7 +2,7 @@ package io.github.smiley4.ktorswaggerui.tests.openapi import io.github.smiley4.ktorswaggerui.data.ServerData import io.github.smiley4.ktorswaggerui.dsl.OpenApiServer -import io.github.smiley4.ktorswaggerui.spec.openapi.ServerBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.ServerBuilder import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import io.swagger.v3.oas.models.servers.Server diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/TagsBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/TagsBuilderTest.kt index 5e52076f..54123f80 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/TagsBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/TagsBuilderTest.kt @@ -2,8 +2,8 @@ package io.github.smiley4.ktorswaggerui.tests.openapi import io.github.smiley4.ktorswaggerui.data.TagData import io.github.smiley4.ktorswaggerui.dsl.OpenApiTag -import io.github.smiley4.ktorswaggerui.spec.openapi.TagBuilder -import io.github.smiley4.ktorswaggerui.spec.openapi.TagExternalDocumentationBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.TagBuilder +import io.github.smiley4.ktorswaggerui.builder.openapi.TagExternalDocumentationBuilder import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/route/RouteDocumentationMergerTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/route/RouteDocumentationMergerTest.kt index 6799788a..d4c81249 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/route/RouteDocumentationMergerTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/route/RouteDocumentationMergerTest.kt @@ -1,7 +1,7 @@ package io.github.smiley4.ktorswaggerui.tests.route import io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute -import io.github.smiley4.ktorswaggerui.spec.route.RouteDocumentationMerger +import io.github.smiley4.ktorswaggerui.builder.route.RouteDocumentationMerger import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaBuilderTest.kt index 29047be5..44ef34d6 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaBuilderTest.kt @@ -12,10 +12,10 @@ import com.github.victools.jsonschema.module.swagger2.Swagger2Module import io.github.smiley4.ktorswaggerui.dsl.Example import io.github.smiley4.ktorswaggerui.dsl.SchemaEncoder import io.github.smiley4.ktorswaggerui.dsl.getSchemaType -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaBuilder -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaDefinitions -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaTypeAttributeOverride -import io.github.smiley4.ktorswaggerui.spec.schema.TypeOverwrites +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaBuilder +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaDefinitions +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaTypeAttributeOverride +import io.github.smiley4.ktorswaggerui.builder.schema.TypeOverwrites import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldContainExactly import io.kotest.matchers.maps.shouldHaveSize diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaContextTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaContextTest.kt index 0851cda4..35a2d82a 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaContextTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaContextTest.kt @@ -13,11 +13,11 @@ import io.github.smiley4.ktorswaggerui.dsl.array import io.github.smiley4.ktorswaggerui.dsl.asSchemaType import io.github.smiley4.ktorswaggerui.dsl.getSchemaType import io.github.smiley4.ktorswaggerui.dsl.obj -import io.github.smiley4.ktorswaggerui.spec.route.RouteMeta -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaBuilder -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaContext -import io.github.smiley4.ktorswaggerui.spec.schema.SchemaContextBuilder -import io.github.smiley4.ktorswaggerui.spec.schema.TypeOverwrites +import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaBuilder +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContext +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContextBuilder +import io.github.smiley4.ktorswaggerui.builder.schema.TypeOverwrites import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder From 19c37ce3f544124bfe410e1b7185d4b5b1e22d57 Mon Sep 17 00:00:00 2001 From: Lukas Ruegner Date: Fri, 20 Oct 2023 15:07:20 +0200 Subject: [PATCH 08/12] use different config for different specs --- src/_todo | 5 -- .../smiley4/ktorswaggerui/SwaggerPlugin.kt | 22 ++++---- .../ktorswaggerui/data/AuthKeyLocation.kt | 2 +- .../smiley4/ktorswaggerui/data/AuthScheme.kt | 2 +- .../ktorswaggerui/data/BaseCustomSchema.kt | 2 +- .../smiley4/ktorswaggerui/data/DataUtils.kt | 2 +- .../ktorswaggerui/data/EncodingData.kt | 2 +- .../ktorswaggerui/data/ExternalDocsData.kt | 2 +- .../smiley4/ktorswaggerui/data/InfoData.kt | 3 -- .../smiley4/ktorswaggerui/data/LicenseData.kt | 2 +- .../data/OpenIdOAuthFlowsData.kt | 2 +- .../smiley4/ktorswaggerui/data/PathFilter.kt | 2 +- .../ktorswaggerui/data/PluginConfigData.kt | 12 +++-- .../smiley4/ktorswaggerui/data/ServerData.kt | 2 +- .../ktorswaggerui/data/SwaggerUIData.kt | 2 +- .../smiley4/ktorswaggerui/data/TagData.kt | 2 +- ...erUIPluginConfig.kt => PluginConfigDsl.kt} | 19 +++++-- .../examples/MultipleSpecsExample.kt | 50 +++++++++++++++++-- .../ktorswaggerui/tests/ApplicationTests.kt | 40 +++++++++++++-- .../tests/example/ExampleTest.kt | 6 +-- .../tests/openapi/OpenApiBuilderTest.kt | 14 +++--- .../tests/openapi/OperationBuilderTest.kt | 20 ++++---- .../tests/openapi/PathsBuilderTest.kt | 10 ++-- .../tests/schema/SchemaContextTest.kt | 16 +++--- 24 files changed, 163 insertions(+), 78 deletions(-) delete mode 100644 src/_todo rename src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/{SwaggerUIPluginConfig.kt => PluginConfigDsl.kt} (93%) diff --git a/src/_todo b/src/_todo deleted file mode 100644 index c23b7d2f..00000000 --- a/src/_todo +++ /dev/null @@ -1,5 +0,0 @@ -TODO: -- test -- separate plugin-configs for different specs (another feature/branch?) -- manually serve swagger-files (another feature/branch?) - - add documented-route-selector to "internal" swagger-routes + hidden-flag ? diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt index 927bd089..372f874c 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt @@ -2,7 +2,7 @@ package io.github.smiley4.ktorswaggerui import com.fasterxml.jackson.databind.ObjectMapper import io.github.smiley4.ktorswaggerui.data.PluginConfigData -import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.dsl.PluginConfigDsl import io.github.smiley4.ktorswaggerui.routing.ForwardRouteController import io.github.smiley4.ktorswaggerui.routing.SwaggerController import io.github.smiley4.ktorswaggerui.builder.example.ExampleContext @@ -56,30 +56,31 @@ internal const val SWAGGER_UI_WEBJARS_VERSION = "4.15.0" private val logger = KotlinLogging.logger {} -val SwaggerUI = createApplicationPlugin(name = "SwaggerUI", createConfiguration = ::SwaggerUIPluginConfig) { +val SwaggerUI = createApplicationPlugin(name = "SwaggerUI", createConfiguration = ::PluginConfigDsl) { val config = pluginConfig.build(PluginConfigData.DEFAULT) on(MonitoringEvent(ApplicationStarted)) { application -> - val apiSpecsJson = mutableMapOf() + if (application.pluginOrNull(Webjars) == null) { + application.install(Webjars) + } + val apiSpecsJson = mutableMapOf() try { - if (application.pluginOrNull(Webjars) == null) { - application.install(Webjars) - } val routes = routes(application, config) apiSpecsJson.putAll(buildOpenApiSpecs(config, routes)) } catch (e: Exception) { logger.error("Error during application startup in swagger-ui-plugin", e) } - apiSpecsJson.forEach { (name, json) -> + apiSpecsJson.forEach { (specId, json) -> + val specConfig = config.specConfigs[specId] ?: config SwaggerController( applicationConfig!!, - config, + specConfig, SWAGGER_UI_WEBJARS_VERSION, - if (apiSpecsJson.size > 1) name else null, + if (apiSpecsJson.size > 1) specId else null, json ).setup(application) } @@ -100,7 +101,8 @@ private fun buildOpenApiSpecs(config: PluginConfigData, routes: List) } return buildMap { routesBySpec.forEach { (specName, routes) -> - this[specName] = buildOpenApiSpec(config, routes) + val specConfig = config.specConfigs[specName] ?: config + this[specName] = buildOpenApiSpec(specConfig, routes) } } } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthKeyLocation.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthKeyLocation.kt index 61c5887b..d94ab76a 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthKeyLocation.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthKeyLocation.kt @@ -6,4 +6,4 @@ enum class AuthKeyLocation(val swaggerType: SecurityScheme.In) { QUERY(SecurityScheme.In.QUERY), HEADER(SecurityScheme.In.HEADER), COOKIE(SecurityScheme.In.COOKIE) -} \ No newline at end of file +} diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthScheme.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthScheme.kt index 67b2fad7..e90593d8 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthScheme.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthScheme.kt @@ -11,4 +11,4 @@ enum class AuthScheme(val swaggerType: String) { SCRAM_SHA_1("SCRAM-SHA-1"), SCRAM_SHA_256("SCRAM-SHA-256"), VAPID("vapid") -} \ No newline at end of file +} diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/BaseCustomSchema.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/BaseCustomSchema.kt index 0668d5f8..5be9f7fa 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/BaseCustomSchema.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/BaseCustomSchema.kt @@ -8,4 +8,4 @@ class CustomJsonSchema(val provider: () -> String) : BaseCustomSchema() class CustomOpenApiSchema(val provider: () -> Schema) : BaseCustomSchema() -class RemoteSchema(val url: String) : BaseCustomSchema() \ No newline at end of file +class RemoteSchema(val url: String) : BaseCustomSchema() diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/DataUtils.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/DataUtils.kt index fd0154d1..0a59d037 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/DataUtils.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/DataUtils.kt @@ -8,4 +8,4 @@ object DataUtils { fun merge(base: T?, value: T?) = value ?: base -} \ No newline at end of file +} diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/EncodingData.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/EncodingData.kt index 293442b8..a87c9245 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/EncodingData.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/EncodingData.kt @@ -96,4 +96,4 @@ data class EncodingData( } -} \ No newline at end of file +} diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ExternalDocsData.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ExternalDocsData.kt index 83777895..8ef5db07 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ExternalDocsData.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ExternalDocsData.kt @@ -12,4 +12,4 @@ data class ExternalDocsData( ) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/InfoData.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/InfoData.kt index eb63cec7..fe8e1642 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/InfoData.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/InfoData.kt @@ -19,6 +19,3 @@ data class InfoData( ) } } - - - diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/LicenseData.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/LicenseData.kt index 7a33da0b..154be47b 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/LicenseData.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/LicenseData.kt @@ -10,4 +10,4 @@ data class LicenseData( url = null, ) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenIdOAuthFlowsData.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenIdOAuthFlowsData.kt index 9fdf9b90..44acedf5 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenIdOAuthFlowsData.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenIdOAuthFlowsData.kt @@ -16,4 +16,4 @@ data class OpenIdOAuthFlowsData( ) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PathFilter.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PathFilter.kt index 230a0b6b..73a75534 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PathFilter.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PathFilter.kt @@ -3,4 +3,4 @@ package io.github.smiley4.ktorswaggerui.data import io.ktor.http.HttpMethod -typealias PathFilter = (method: HttpMethod, url: List) -> Boolean \ No newline at end of file +typealias PathFilter = (method: HttpMethod, url: List) -> Boolean diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PluginConfigData.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PluginConfigData.kt index 66172afb..0e5a605f 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PluginConfigData.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PluginConfigData.kt @@ -1,6 +1,6 @@ package io.github.smiley4.ktorswaggerui.data -import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.dsl.PluginConfigDsl import io.github.smiley4.ktorswaggerui.dsl.OpenApiResponse import kotlin.reflect.KClass @@ -19,7 +19,8 @@ data class PluginConfigData( val tags: List, val customSchemas: Map, val includeAllCustomSchemas: Boolean, - val encoding: EncodingData + val encoding: EncodingData, + val specConfigs: MutableMap ) { companion object { @@ -27,7 +28,7 @@ data class PluginConfigData( defaultUnauthorizedResponse = null, defaultSecuritySchemeNames = emptySet(), tagGenerator = { emptyList() }, - specAssigner = { _, _ -> SwaggerUIPluginConfig.DEFAULT_SPEC_ID }, + specAssigner = { _, _ -> PluginConfigDsl.DEFAULT_SPEC_ID }, pathFilter = { _, _ -> true }, ignoredRouteSelectors = emptySet(), swaggerUI = SwaggerUIData.DEFAULT, @@ -38,8 +39,9 @@ data class PluginConfigData( tags = emptyList(), customSchemas = emptyMap(), includeAllCustomSchemas = false, - encoding = EncodingData.DEFAULT + encoding = EncodingData.DEFAULT, + specConfigs = mutableMapOf() ) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ServerData.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ServerData.kt index fe243c42..a98ca613 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ServerData.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ServerData.kt @@ -12,4 +12,4 @@ data class ServerData( ) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUIData.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUIData.kt index 16db8274..2b7014cf 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUIData.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUIData.kt @@ -26,4 +26,4 @@ data class SwaggerUIData( ) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TagData.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TagData.kt index 773e77b0..29b30b6a 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TagData.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TagData.kt @@ -15,4 +15,4 @@ data class TagData( externalDocUrl = null ) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/SwaggerUIPluginConfig.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/PluginConfigDsl.kt similarity index 93% rename from src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/SwaggerUIPluginConfig.kt rename to src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/PluginConfigDsl.kt index 8c4c8ec6..449fa3ee 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/SwaggerUIPluginConfig.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/PluginConfigDsl.kt @@ -2,7 +2,6 @@ package io.github.smiley4.ktorswaggerui.dsl import io.github.smiley4.ktorswaggerui.data.DataUtils.merge import io.github.smiley4.ktorswaggerui.data.DataUtils.mergeBoolean -import io.github.smiley4.ktorswaggerui.data.ExternalDocsData import io.github.smiley4.ktorswaggerui.data.PathFilter import io.github.smiley4.ktorswaggerui.data.PluginConfigData import io.github.smiley4.ktorswaggerui.data.SecuritySchemeData @@ -18,13 +17,20 @@ import kotlin.reflect.KClass * Main-Configuration of the "SwaggerUI"-Plugin */ @OpenApiDslMarker -class SwaggerUIPluginConfig { +class PluginConfigDsl { companion object { const val DEFAULT_SPEC_ID = "api" } + private val specConfigs = mutableMapOf() + + fun spec(specId: String, block: PluginConfigDsl.() -> Unit) { + specConfigs[specId] = PluginConfigDsl().apply(block) + } + + /** * Default response to automatically add to each protected route for the "Unauthorized"-Response-Code. * Generated response can be overwritten with custom response. @@ -211,8 +217,13 @@ class SwaggerUIPluginConfig { putAll(customSchemas.getSchemas()) }, includeAllCustomSchemas = mergeBoolean(base.includeAllCustomSchemas, customSchemas.includeAll), - encoding = encodingConfig.build(base.encoding) - ) + encoding = encodingConfig.build(base.encoding), + specConfigs = mutableMapOf() + ).also { + specConfigs.forEach { (specId, config) -> + it.specConfigs[specId] = config.build(it) + } + } } } diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/MultipleSpecsExample.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/MultipleSpecsExample.kt index a489ffdb..9b712d48 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/MultipleSpecsExample.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/MultipleSpecsExample.kt @@ -6,6 +6,9 @@ import io.github.smiley4.ktorswaggerui.dsl.route import io.ktor.server.application.Application import io.ktor.server.application.call import io.ktor.server.application.install +import io.ktor.server.auth.Authentication +import io.ktor.server.auth.UserIdPrincipal +import io.ktor.server.auth.basic import io.ktor.server.engine.embeddedServer import io.ktor.server.netty.Netty import io.ktor.server.response.respondText @@ -24,12 +27,51 @@ fun main() { } private fun Application.myModule() { + + install(Authentication) { + basic("auth-swagger") { + realm = "Access to the Swagger UI" + validate { credentials -> + if (credentials.name == "user" && credentials.password == "pass") { + UserIdPrincipal(credentials.name) + } else { + null + } + } + } + } + install(SwaggerUI) { - specAssigner = { _, _ -> "v2" } + // general configuration + info { + title = "Example API" + } + specAssigner = { _, _ -> "v2" } // assign all unassigned routes to spec "v2" (here e.g. '/hi') + + // configuration specific for spec "v1" + spec("v1") { + info { + version = "1.0" + } + } + + // configuration specific for spec "v2" + spec("v2") { + info { + version = "2.0" + } + swagger { + authentication = "auth-swagger" + } + } } + + routing { + + // version 1.0 routes route("v1", { - specId = "v1" + specId = "v1" // assign all sub-routes to spec "v1" }) { get("hello", { description = "Simple version 1 'Hello World'-Route" @@ -38,8 +80,9 @@ private fun Application.myModule() { } } + // version 2.0 routes route("v2", { - specId = "v2" + specId = "v2" // assign all sub-routes to spec "v2" }) { get("hello", { description = "Simple version 2 'Hello World'-Route" @@ -48,6 +91,7 @@ private fun Application.myModule() { } } + // other routes get("hi", { description = "Alternative version of 'Hello World'-Route" }) { diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/ApplicationTests.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/ApplicationTests.kt index d5891a2a..e9c7fe79 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/ApplicationTests.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/ApplicationTests.kt @@ -1,7 +1,7 @@ package io.github.smiley4.ktorswaggerui.tests import io.github.smiley4.ktorswaggerui.SwaggerUI -import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.dsl.PluginConfigDsl import io.github.smiley4.ktorswaggerui.dsl.get import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain @@ -131,6 +131,7 @@ class ApplicationTests { } } + @Test fun forwardRootWithCustomSwaggerUrl() = swaggerUITestApplication({ swagger { @@ -305,7 +306,7 @@ class ApplicationTests { @Test fun multipleSwaggerUI() = swaggerUITestApplication({ - specAssigner = {_, tags -> tags.firstOrNull() ?: "other"} + specAssigner = { _, tags -> tags.firstOrNull() ?: "other" } }) { get("hello").also { it.status shouldBe HttpStatusCode.OK @@ -357,11 +358,44 @@ class ApplicationTests { } + @Test + fun multipleSwaggerUIWithDifferentAuthConfig() = swaggerUITestApplication({ + specAssigner = { _, tags -> tags.firstOrNull() ?: "other" } + spec("hello") { + swagger { + authentication = null + } + } + spec("world") { + swagger { + authentication = "my-auth" + } + } + }) { + get("/swagger-ui/hello/index.html").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Text.Html + it.body.shouldNotBeEmpty() + } + get("/swagger-ui/hello/hello.json").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Application.Json.withCharset(Charsets.UTF_8) + it.body.shouldNotBeEmpty() + } + get("/swagger-ui/world/index.html").also { + it.status shouldBe HttpStatusCode.Unauthorized + } + get("/swagger-ui/world/world.json").also { + it.status shouldBe HttpStatusCode.Unauthorized + } + } + + private fun swaggerUITestApplication(block: suspend ApplicationTestBuilder.() -> Unit) { swaggerUITestApplication({}, block) } - private fun swaggerUITestApplication(pluginConfig: SwaggerUIPluginConfig.() -> Unit, block: suspend ApplicationTestBuilder.() -> Unit) { + private fun swaggerUITestApplication(pluginConfig: PluginConfigDsl.() -> Unit, block: suspend ApplicationTestBuilder.() -> Unit) { testApplication { application { install(Authentication) { diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/example/ExampleTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/example/ExampleTest.kt index 6ed4ba7e..7dae2e56 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/example/ExampleTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/example/ExampleTest.kt @@ -1,7 +1,7 @@ package io.github.smiley4.ktorswaggerui.tests.example import io.github.smiley4.ktorswaggerui.data.PluginConfigData -import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.dsl.PluginConfigDsl import io.github.smiley4.ktorswaggerui.dsl.OpenApiRequestParameter import io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute import io.github.smiley4.ktorswaggerui.dsl.OpenApiSimpleBody @@ -167,11 +167,11 @@ class ExampleTest : StringSpec({ } - private val defaultPluginConfig = SwaggerUIPluginConfig() + private val defaultPluginConfig = PluginConfigDsl() private fun exampleContext( routes: List, - pluginConfig: SwaggerUIPluginConfig = defaultPluginConfig + pluginConfig: PluginConfigDsl = defaultPluginConfig ): ExampleContext { return ExampleContextBuilder( exampleBuilder = ExampleBuilder( diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OpenApiBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OpenApiBuilderTest.kt index 6a0db031..41db1df6 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OpenApiBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OpenApiBuilderTest.kt @@ -2,7 +2,7 @@ package io.github.smiley4.ktorswaggerui.tests.openapi import com.fasterxml.jackson.databind.ObjectMapper import io.github.smiley4.ktorswaggerui.data.PluginConfigData -import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.dsl.PluginConfigDsl import io.github.smiley4.ktorswaggerui.builder.example.ExampleContext import io.github.smiley4.ktorswaggerui.builder.example.ExampleContextBuilder import io.github.smiley4.ktorswaggerui.builder.openapi.* @@ -37,7 +37,7 @@ class OpenApiBuilderTest : StringSpec({ } "multiple servers" { - val config = SwaggerUIPluginConfig().also { + val config = PluginConfigDsl().also { it.server { url = "http://localhost:8080" description = "Development Server" @@ -57,7 +57,7 @@ class OpenApiBuilderTest : StringSpec({ } "multiple tags" { - val config = SwaggerUIPluginConfig().also { + val config = PluginConfigDsl().also { it.tag("tag-1") { description = "first test tag" } @@ -78,9 +78,9 @@ class OpenApiBuilderTest : StringSpec({ companion object { - private val defaultPluginConfig = SwaggerUIPluginConfig() + private val defaultPluginConfig = PluginConfigDsl() - private fun schemaContext(routes: List, pluginConfig: SwaggerUIPluginConfig): SchemaContext { + private fun schemaContext(routes: List, pluginConfig: PluginConfigDsl): SchemaContext { return SchemaContextBuilder( config = pluginConfig.build(PluginConfigData.DEFAULT), schemaBuilder = SchemaBuilder( @@ -92,7 +92,7 @@ class OpenApiBuilderTest : StringSpec({ ).build(routes) } - private fun exampleContext(routes: List, pluginConfig: SwaggerUIPluginConfig): ExampleContext { + private fun exampleContext(routes: List, pluginConfig: PluginConfigDsl): ExampleContext { return ExampleContextBuilder( exampleBuilder = ExampleBuilder( config = pluginConfig.build(PluginConfigData.DEFAULT) @@ -100,7 +100,7 @@ class OpenApiBuilderTest : StringSpec({ ).build(routes.toList()) } - private fun buildOpenApiObject(routes: List, pluginConfig: SwaggerUIPluginConfig = defaultPluginConfig): OpenAPI { + private fun buildOpenApiObject(routes: List, pluginConfig: PluginConfigDsl = defaultPluginConfig): OpenAPI { val schemaContext = schemaContext(routes, pluginConfig) val exampleContext = exampleContext(routes, pluginConfig) val pluginConfigData = pluginConfig.build(PluginConfigData.DEFAULT) diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OperationBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OperationBuilderTest.kt index 05f221a3..0a030fab 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OperationBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OperationBuilderTest.kt @@ -2,7 +2,7 @@ package io.github.smiley4.ktorswaggerui.tests.openapi import com.fasterxml.jackson.databind.ObjectMapper import io.github.smiley4.ktorswaggerui.data.PluginConfigData -import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.dsl.PluginConfigDsl import io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute import io.github.smiley4.ktorswaggerui.dsl.obj import io.github.smiley4.ktorswaggerui.builder.example.ExampleContext @@ -96,7 +96,7 @@ class OperationBuilderTest : StringSpec({ } "operation with auto-generated tags" { - val config = SwaggerUIPluginConfig().also { + val config = PluginConfigDsl().also { it.generateTags { url -> listOf(url.firstOrNull()) } } val routeA = RouteMeta( @@ -683,7 +683,7 @@ class OperationBuilderTest : StringSpec({ } "automatic unauthorized response for protected route" { - val config = SwaggerUIPluginConfig().also { + val config = PluginConfigDsl().also { it.defaultUnauthorizedResponse { description = "Default unauthorized Response" } @@ -717,7 +717,7 @@ class OperationBuilderTest : StringSpec({ } "automatic unauthorized response for unprotected route" { - val config = SwaggerUIPluginConfig().also { + val config = PluginConfigDsl().also { it.defaultUnauthorizedResponse { description = "Default unauthorized Response" } @@ -804,7 +804,7 @@ class OperationBuilderTest : StringSpec({ } "custom body schema" { - val config = SwaggerUIPluginConfig().also { + val config = PluginConfigDsl().also { it.customSchemas { openApi("myCustomSchema") { Schema().also { schema -> @@ -861,7 +861,7 @@ class OperationBuilderTest : StringSpec({ } "custom multipart-body schema" { - val config = SwaggerUIPluginConfig().also { + val config = PluginConfigDsl().also { it.customSchemas { openApi("myCustomSchema") { Schema().also { schema -> @@ -928,11 +928,11 @@ class OperationBuilderTest : StringSpec({ val number: Int ) - private val defaultPluginConfig = SwaggerUIPluginConfig() + private val defaultPluginConfig = PluginConfigDsl() private fun schemaContext( routes: List, - pluginConfig: SwaggerUIPluginConfig = defaultPluginConfig + pluginConfig: PluginConfigDsl = defaultPluginConfig ): SchemaContext { return SchemaContextBuilder( config = pluginConfig.build(PluginConfigData.DEFAULT), @@ -947,7 +947,7 @@ class OperationBuilderTest : StringSpec({ private fun exampleContext( routes: List, - pluginConfig: SwaggerUIPluginConfig = defaultPluginConfig + pluginConfig: PluginConfigDsl = defaultPluginConfig ): ExampleContext { return ExampleContextBuilder( exampleBuilder = ExampleBuilder( @@ -961,7 +961,7 @@ class OperationBuilderTest : StringSpec({ route: RouteMeta, schemaContext: SchemaContext, exampleContext: ExampleContext, - pluginConfig: SwaggerUIPluginConfig = defaultPluginConfig + pluginConfig: PluginConfigDsl = defaultPluginConfig ): Operation { val pluginConfigData = pluginConfig.build(PluginConfigData.DEFAULT) return OperationBuilder( diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/PathsBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/PathsBuilderTest.kt index 41e02245..16fcba24 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/PathsBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/PathsBuilderTest.kt @@ -2,7 +2,7 @@ package io.github.smiley4.ktorswaggerui.tests.openapi import com.fasterxml.jackson.databind.ObjectMapper import io.github.smiley4.ktorswaggerui.data.PluginConfigData -import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.dsl.PluginConfigDsl import io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute import io.github.smiley4.ktorswaggerui.builder.example.ExampleContext import io.github.smiley4.ktorswaggerui.builder.example.ExampleContextBuilder @@ -89,9 +89,9 @@ class PathsBuilderTest : StringSpec({ protected = false ) - private val defaultPluginConfig = SwaggerUIPluginConfig() + private val defaultPluginConfig = PluginConfigDsl() - private fun schemaContext(routes: List, pluginConfig: SwaggerUIPluginConfig = defaultPluginConfig): SchemaContext { + private fun schemaContext(routes: List, pluginConfig: PluginConfigDsl = defaultPluginConfig): SchemaContext { return SchemaContextBuilder( config = pluginConfig.build(PluginConfigData.DEFAULT), schemaBuilder = SchemaBuilder( @@ -103,7 +103,7 @@ class PathsBuilderTest : StringSpec({ ).build(routes) } - private fun exampleContext(routes: List, pluginConfig: SwaggerUIPluginConfig = defaultPluginConfig): ExampleContext { + private fun exampleContext(routes: List, pluginConfig: PluginConfigDsl = defaultPluginConfig): ExampleContext { return ExampleContextBuilder( exampleBuilder = ExampleBuilder( config = pluginConfig.build(PluginConfigData.DEFAULT) @@ -115,7 +115,7 @@ class PathsBuilderTest : StringSpec({ routes: Collection, schemaContext: SchemaContext, exampleContext: ExampleContext, - pluginConfig: SwaggerUIPluginConfig = defaultPluginConfig + pluginConfig: PluginConfigDsl = defaultPluginConfig ): Paths { val pluginConfigData = pluginConfig.build(PluginConfigData.DEFAULT) return PathsBuilder( diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaContextTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaContextTest.kt index 35a2d82a..e24b6c7b 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaContextTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaContextTest.kt @@ -7,7 +7,7 @@ import com.github.victools.jsonschema.generator.SchemaGenerator import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder import com.github.victools.jsonschema.generator.SchemaVersion import io.github.smiley4.ktorswaggerui.data.PluginConfigData -import io.github.smiley4.ktorswaggerui.dsl.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.dsl.PluginConfigDsl import io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute import io.github.smiley4.ktorswaggerui.dsl.array import io.github.smiley4.ktorswaggerui.dsl.asSchemaType @@ -290,7 +290,7 @@ class SchemaContextTest : StringSpec({ } "custom schema object" { - val config = SwaggerUIPluginConfig().also { + val config = PluginConfigDsl().also { it.customSchemas { openApi("myCustomSchema") { Schema().also { schema -> @@ -328,7 +328,7 @@ class SchemaContextTest : StringSpec({ } "custom schema array" { - val config = SwaggerUIPluginConfig().also { + val config = PluginConfigDsl().also { it.customSchemas { openApi("myCustomSchema") { Schema().also { schema -> @@ -379,7 +379,7 @@ class SchemaContextTest : StringSpec({ .with(Option.ALLOF_CLEANUP_AT_THE_END) .build() ) - val config = SwaggerUIPluginConfig().also { + val config = PluginConfigDsl().also { it.encoding { schemaEncoder { type -> generator.generateSchema(type.javaType).toString() @@ -415,7 +415,7 @@ class SchemaContextTest : StringSpec({ } "don't include unused custom schema" { - val config = SwaggerUIPluginConfig().also { + val config = PluginConfigDsl().also { it.customSchemas { includeAll = false openApi("myCustomSchema") { @@ -438,7 +438,7 @@ class SchemaContextTest : StringSpec({ } "include unused custom schema" { - val config = SwaggerUIPluginConfig().also { + val config = PluginConfigDsl().also { it.customSchemas { includeAll = true openApi("myCustomSchema") { @@ -499,11 +499,11 @@ class SchemaContextTest : StringSpec({ inline fun getType() = getSchemaType() - private val defaultPluginConfig = SwaggerUIPluginConfig() + private val defaultPluginConfig = PluginConfigDsl() private fun schemaContext( routes: Collection, - pluginConfig: SwaggerUIPluginConfig = defaultPluginConfig + pluginConfig: PluginConfigDsl = defaultPluginConfig ): SchemaContext { return SchemaContextBuilder( config = pluginConfig.build(PluginConfigData.DEFAULT), From c62bfd51c2ea70ab0d89295aa58796c022abbaa1 Mon Sep 17 00:00:00 2001 From: Lukas Ruegner Date: Fri, 20 Oct 2023 18:16:03 +0200 Subject: [PATCH 09/12] changes --- .../builder/example/ExampleContextBuilder.kt | 19 +++++- .../builder/openapi/ContentBuilder.kt | 65 +++++++++++-------- .../builder/schema/SchemaContext.kt | 14 ++-- .../builder/schema/SchemaContextBuilder.kt | 62 +++++++++--------- .../ktorswaggerui/dsl/BodyTypeDescriptor.kt | 49 ++++++++++++++ .../ktorswaggerui/dsl/CustomSchemaRef.kt | 17 ++--- .../ktorswaggerui/dsl/OpenApiMultipartBody.kt | 19 +++--- .../ktorswaggerui/dsl/OpenApiMultipartPart.kt | 8 +-- .../ktorswaggerui/dsl/OpenApiRequest.kt | 36 +++++----- .../ktorswaggerui/dsl/OpenApiResponse.kt | 25 +++---- .../ktorswaggerui/dsl/OpenApiSimpleBody.kt | 8 +-- ...chemaExample.kt => CustomSchemaExample.kt} | 46 +++++++++++-- .../tests/openapi/OperationBuilderTest.kt | 7 +- .../tests/schema/SchemaContextTest.kt | 25 +++---- 14 files changed, 240 insertions(+), 160 deletions(-) create mode 100644 src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/BodyTypeDescriptor.kt rename src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/{CustomJsonSchemaExample.kt => CustomSchemaExample.kt} (73%) diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/example/ExampleContextBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/example/ExampleContextBuilder.kt index c56012e0..608eef52 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/example/ExampleContextBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/example/ExampleContextBuilder.kt @@ -9,6 +9,12 @@ import io.github.smiley4.ktorswaggerui.dsl.SchemaType import io.github.smiley4.ktorswaggerui.dsl.getSchemaType import io.github.smiley4.ktorswaggerui.builder.openapi.ExampleBuilder import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta +import io.github.smiley4.ktorswaggerui.dsl.BodyTypeDescriptor +import io.github.smiley4.ktorswaggerui.dsl.CollectionBodyTypeDescriptor +import io.github.smiley4.ktorswaggerui.dsl.CustomRefBodyTypeDescriptor +import io.github.smiley4.ktorswaggerui.dsl.EmptyBodyTypeDescriptor +import io.github.smiley4.ktorswaggerui.dsl.OneOfBodyTypeDescriptor +import io.github.smiley4.ktorswaggerui.dsl.SchemaBodyTypeDescriptor import io.swagger.v3.oas.models.examples.Example class ExampleContextBuilder( @@ -42,7 +48,18 @@ class ExampleContextBuilder( private fun handle(ctx: ExampleContext, body: OpenApiSimpleBody) { body.getExamples().forEach { (name, value) -> - ctx.addExample(body, name, createExample(body.type ?: getSchemaType(), value)) + val bodyType = getRelevantSchemaType(body.type, getSchemaType()) + ctx.addExample(body, name, createExample(bodyType, value)) + } + } + + private fun getRelevantSchemaType(typeDescriptor: BodyTypeDescriptor, fallback: SchemaType): SchemaType { + return when(typeDescriptor) { + is EmptyBodyTypeDescriptor -> fallback + is SchemaBodyTypeDescriptor -> typeDescriptor.schemaType + is CollectionBodyTypeDescriptor -> getRelevantSchemaType(typeDescriptor.schemaType, fallback) + is OneOfBodyTypeDescriptor -> fallback + is CustomRefBodyTypeDescriptor -> fallback } } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ContentBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ContentBuilder.kt index eef6a721..ee2bd7d5 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ContentBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ContentBuilder.kt @@ -1,29 +1,23 @@ package io.github.smiley4.ktorswaggerui.builder.openapi +import io.github.smiley4.ktorswaggerui.builder.example.ExampleContext +import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContext +import io.github.smiley4.ktorswaggerui.dsl.BodyTypeDescriptor +import io.github.smiley4.ktorswaggerui.dsl.CollectionBodyTypeDescriptor +import io.github.smiley4.ktorswaggerui.dsl.CustomRefBodyTypeDescriptor +import io.github.smiley4.ktorswaggerui.dsl.EmptyBodyTypeDescriptor +import io.github.smiley4.ktorswaggerui.dsl.OneOfBodyTypeDescriptor import io.github.smiley4.ktorswaggerui.dsl.OpenApiBaseBody import io.github.smiley4.ktorswaggerui.dsl.OpenApiMultipartBody -import io.github.smiley4.ktorswaggerui.dsl.OpenApiSimpleBody import io.github.smiley4.ktorswaggerui.dsl.OpenApiMultipartPart -import io.github.smiley4.ktorswaggerui.builder.example.ExampleContext -import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContext +import io.github.smiley4.ktorswaggerui.dsl.OpenApiSimpleBody +import io.github.smiley4.ktorswaggerui.dsl.SchemaBodyTypeDescriptor import io.ktor.http.ContentType import io.swagger.v3.oas.models.media.Content import io.swagger.v3.oas.models.media.Encoding import io.swagger.v3.oas.models.media.MediaType import io.swagger.v3.oas.models.media.Schema -import kotlin.collections.Map -import kotlin.collections.MutableMap -import kotlin.collections.associateWith -import kotlin.collections.filter -import kotlin.collections.flatMap -import kotlin.collections.forEach -import kotlin.collections.ifEmpty -import kotlin.collections.isNotEmpty -import kotlin.collections.joinToString -import kotlin.collections.mapValues -import kotlin.collections.mutableMapOf import kotlin.collections.set -import kotlin.collections.setOf class ContentBuilder( private val schemaContext: SchemaContext, @@ -106,22 +100,37 @@ class ContentBuilder( } private fun getSchema(body: OpenApiSimpleBody): Schema<*>? { - return if (body.customSchema != null) { - schemaContext.getSchema(body.customSchema!!) - } else if (body.type != null) { - schemaContext.getSchema(body.type) - } else { - null - } + return getSchema(body.type) } private fun getSchema(part: OpenApiMultipartPart): Schema<*>? { - return if (part.customSchema != null) { - schemaContext.getSchema(part.customSchema!!) - } else if (part.type != null) { - schemaContext.getSchema(part.type) - } else { - null + return getSchema(part.type) + } + + private fun getSchema(typeDescriptor: BodyTypeDescriptor): Schema<*>? { + return when (typeDescriptor) { + is EmptyBodyTypeDescriptor -> { + null + } + is SchemaBodyTypeDescriptor -> { + schemaContext.getSchema(typeDescriptor.schemaType) + } + is OneOfBodyTypeDescriptor -> { + Schema().also { schema -> + typeDescriptor.elements.forEach { + schema.addOneOfItem(getSchema(it)) + } + } + } + is CollectionBodyTypeDescriptor -> { + Schema().also { schema -> + schema.type = "array" + schema.items = getSchema(typeDescriptor.schemaType) + } + } + is CustomRefBodyTypeDescriptor -> { + schemaContext.getSchema(typeDescriptor.customSchemaId) + } } } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaContext.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaContext.kt index 1bb6a767..ab0f9a56 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaContext.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaContext.kt @@ -1,12 +1,10 @@ package io.github.smiley4.ktorswaggerui.builder.schema -import io.github.smiley4.ktorswaggerui.dsl.CustomSchemaRef import io.github.smiley4.ktorswaggerui.dsl.SchemaType import io.github.smiley4.ktorswaggerui.dsl.getSchemaType import io.github.smiley4.ktorswaggerui.dsl.getSimpleArrayElementTypeName import io.github.smiley4.ktorswaggerui.dsl.getSimpleTypeName import io.swagger.v3.oas.models.media.Schema -import java.lang.IllegalArgumentException import kotlin.collections.set @@ -52,22 +50,24 @@ class SchemaContext { } - fun addSchema(ref: CustomSchemaRef, schema: SchemaDefinitions) { - schemasCustom[ref.schemaId] = schema + fun addSchema(customSchemaId: String, schema: SchemaDefinitions) { + schemasCustom[customSchemaId] = schema } fun getComponentsSection(): Map> = componentsSection - fun getSchema(type: SchemaType) = getSchemaOrNull(type) ?: throw NoSuchElementException ("No schema for type '$type'!") + fun getSchema(type: SchemaType) = getSchemaOrNull(type) + ?: throw NoSuchElementException("No schema for type '$type'!") fun getSchemaOrNull(type: SchemaType) = inlineSchemas[type] - fun getSchema(ref: CustomSchemaRef) = getSchemaOrNull(ref) ?: throw NoSuchElementException("No schema for ref '$ref'!") + fun getSchema(customSchemaId: String) = getSchemaOrNull(customSchemaId) + ?: throw NoSuchElementException("No schema for ref '$customSchemaId'!") - fun getSchemaOrNull(ref: CustomSchemaRef) = inlineSchemasCustom[ref.schemaId] + fun getSchemaOrNull(customSchemaId: String) = inlineSchemasCustom[customSchemaId] fun finalize() { diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaContextBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaContextBuilder.kt index 2db25d42..3dbea962 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaContextBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaContextBuilder.kt @@ -1,20 +1,23 @@ package io.github.smiley4.ktorswaggerui.builder.schema +import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta import io.github.smiley4.ktorswaggerui.data.BaseCustomSchema import io.github.smiley4.ktorswaggerui.data.CustomJsonSchema import io.github.smiley4.ktorswaggerui.data.CustomOpenApiSchema import io.github.smiley4.ktorswaggerui.data.PluginConfigData import io.github.smiley4.ktorswaggerui.data.RemoteSchema -import io.github.smiley4.ktorswaggerui.dsl.CustomArraySchemaRef -import io.github.smiley4.ktorswaggerui.dsl.CustomSchemaRef +import io.github.smiley4.ktorswaggerui.dsl.BodyTypeDescriptor +import io.github.smiley4.ktorswaggerui.dsl.CollectionBodyTypeDescriptor +import io.github.smiley4.ktorswaggerui.dsl.CustomRefBodyTypeDescriptor +import io.github.smiley4.ktorswaggerui.dsl.EmptyBodyTypeDescriptor +import io.github.smiley4.ktorswaggerui.dsl.OneOfBodyTypeDescriptor import io.github.smiley4.ktorswaggerui.dsl.OpenApiBaseBody import io.github.smiley4.ktorswaggerui.dsl.OpenApiMultipartBody import io.github.smiley4.ktorswaggerui.dsl.OpenApiRequestParameter import io.github.smiley4.ktorswaggerui.dsl.OpenApiResponse import io.github.smiley4.ktorswaggerui.dsl.OpenApiSimpleBody +import io.github.smiley4.ktorswaggerui.dsl.SchemaBodyTypeDescriptor import io.github.smiley4.ktorswaggerui.dsl.SchemaType -import io.github.smiley4.ktorswaggerui.dsl.obj -import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta import io.swagger.v3.oas.models.media.Schema class SchemaContextBuilder( @@ -28,7 +31,7 @@ class SchemaContextBuilder( .also { ctx -> if (config.includeAllCustomSchemas) { config.customSchemas.forEach { (id, schema) -> - ctx.addSchema(obj(id), createSchema(schema, false)) + ctx.addSchema(id, createSchema(schema)) } } } @@ -62,20 +65,29 @@ class SchemaContextBuilder( private fun handle(ctx: SchemaContext, body: OpenApiSimpleBody) { - if (body.customSchema != null) { - body.customSchema?.also { ctx.addSchema(it, createSchema(it)) } - } else { - body.type?.also { ctx.addSchema(it, createSchema(it)) } - } + addSchemas(ctx, body.type) } - private fun handle(ctx: SchemaContext, body: OpenApiMultipartBody) { body.getParts().forEach { part -> - if (part.customSchema != null) { - part.customSchema?.also { ctx.addSchema(it, createSchema(it)) } - } else { - part.type?.also { ctx.addSchema(it, createSchema(it)) } + part.type.also { addSchemas(ctx, part.type) } + } + } + + private fun addSchemas(ctx: SchemaContext, typeDescriptor: BodyTypeDescriptor) { + when (typeDescriptor) { + is EmptyBodyTypeDescriptor -> Unit + is SchemaBodyTypeDescriptor -> { + ctx.addSchema(typeDescriptor.schemaType, createSchema(typeDescriptor.schemaType)) + } + is CollectionBodyTypeDescriptor -> { + addSchemas(ctx, typeDescriptor.schemaType) + } + is OneOfBodyTypeDescriptor -> { + typeDescriptor.elements.forEach { addSchemas(ctx, it) } + } + is CustomRefBodyTypeDescriptor -> { + ctx.addSchema(typeDescriptor.customSchemaId, createSchema(typeDescriptor.customSchemaId)) } } } @@ -91,20 +103,20 @@ class SchemaContextBuilder( } - private fun createSchema(customSchemaRef: CustomSchemaRef): SchemaDefinitions { - val customSchema = config.customSchemas[customSchemaRef.schemaId] + private fun createSchema(customSchemaId: String): SchemaDefinitions { + val customSchema = config.customSchemas[customSchemaId] return if (customSchema == null) { SchemaDefinitions( root = Schema(), definitions = emptyMap() ) } else { - createSchema(customSchema, customSchemaRef is CustomArraySchemaRef) + createSchema(customSchema) } } - private fun createSchema(customSchema: BaseCustomSchema, isArray: Boolean): SchemaDefinitions { + private fun createSchema(customSchema: BaseCustomSchema): SchemaDefinitions { return when (customSchema) { is CustomJsonSchema -> { schemaBuilder.create(customSchema.provider()) @@ -125,18 +137,6 @@ class SchemaContextBuilder( definitions = emptyMap() ) } - }.let { schemaDefinitions -> - if (isArray) { - SchemaDefinitions( - root = Schema().apply { - type = "array" - items = schemaDefinitions.root - }, - definitions = schemaDefinitions.definitions - ) - } else { - schemaDefinitions - } } } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/BodyTypeDescriptor.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/BodyTypeDescriptor.kt new file mode 100644 index 00000000..fe5654aa --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/BodyTypeDescriptor.kt @@ -0,0 +1,49 @@ +package io.github.smiley4.ktorswaggerui.dsl + +import kotlin.reflect.KClass + +sealed interface BodyTypeDescriptor { + + companion object { + + fun typeOf(type: KClass<*>?) = type?.let { SchemaBodyTypeDescriptor(it.asSchemaType()) } ?: EmptyBodyTypeDescriptor() + + fun typeOf(type: SchemaType?) = type?.let { SchemaBodyTypeDescriptor(it) } ?: EmptyBodyTypeDescriptor() + + fun oneOf(vararg type: KClass<*>) = OneOfBodyTypeDescriptor(type.toList().map { typeOf(it.asSchemaType()) }) + + @JvmName("oneOfClass") + fun oneOf(types: Collection>) = OneOfBodyTypeDescriptor(types.map { typeOf(it.asSchemaType()) }) + + fun oneOf(vararg type: SchemaType) = OneOfBodyTypeDescriptor(type.map { typeOf(it) }) + + @JvmName("oneOfType") + fun oneOf(types: Collection) = OneOfBodyTypeDescriptor(types.map { typeOf(it) }) + + fun oneOf(vararg type: BodyTypeDescriptor) = OneOfBodyTypeDescriptor(type.toList()) + + @JvmName("oneOfDescriptor") + fun oneOf(types: Collection) = OneOfBodyTypeDescriptor(types.toList()) + + fun multipleOf(type: KClass<*>) = CollectionBodyTypeDescriptor(typeOf(type.asSchemaType())) + + fun multipleOf(type: SchemaType) = CollectionBodyTypeDescriptor(typeOf(type)) + + fun multipleOf(type: BodyTypeDescriptor) = CollectionBodyTypeDescriptor(type) + + fun custom(customSchemaId: String) = CustomRefBodyTypeDescriptor(customSchemaId) + + fun empty() = EmptyBodyTypeDescriptor() + } + +} + +class EmptyBodyTypeDescriptor : BodyTypeDescriptor + +class SchemaBodyTypeDescriptor(val schemaType: SchemaType) : BodyTypeDescriptor + +class OneOfBodyTypeDescriptor(val elements: List) : BodyTypeDescriptor + +class CollectionBodyTypeDescriptor(val schemaType: BodyTypeDescriptor) : BodyTypeDescriptor + +class CustomRefBodyTypeDescriptor(val customSchemaId: String) : BodyTypeDescriptor diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/CustomSchemaRef.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/CustomSchemaRef.kt index ef0c7fd2..417bef81 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/CustomSchemaRef.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/CustomSchemaRef.kt @@ -1,13 +1,14 @@ package io.github.smiley4.ktorswaggerui.dsl -sealed class CustomSchemaRef( - val schemaId: String +@Deprecated( + "Use BodyTypeDescriptor instead", + ReplaceWith("BodyTypeDescriptor.custom(schemaId)") ) +fun obj(schemaId: String) = BodyTypeDescriptor.custom(schemaId) -class CustomObjectSchemaRef(schemaId: String) : CustomSchemaRef(schemaId) -class CustomArraySchemaRef(schemaId: String) : CustomSchemaRef(schemaId) - -fun obj(schemaId: String) = CustomObjectSchemaRef(schemaId) - -fun array(schemaId: String) = CustomArraySchemaRef(schemaId) +@Deprecated( + "Use BodyTypeDescriptor instead", + ReplaceWith("BodyTypeDescriptor.multipleOf(BodyTypeDescriptor.custom(schemaId))") +) +fun array(schemaId: String) = BodyTypeDescriptor.multipleOf(BodyTypeDescriptor.custom(schemaId)) diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiMultipartBody.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiMultipartBody.kt index 67f2568b..fd5a61d2 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiMultipartBody.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiMultipartBody.kt @@ -18,7 +18,7 @@ class OpenApiMultipartBody : OpenApiBaseBody() { /** * One part of a multipart-body */ - fun part(name: String, type: SchemaType, block: OpenApiMultipartPart.() -> Unit) { + fun part(name: String, type: BodyTypeDescriptor, block: OpenApiMultipartPart.() -> Unit) { parts.add(OpenApiMultipartPart(name, type).apply(block)) } @@ -26,41 +26,38 @@ class OpenApiMultipartBody : OpenApiBaseBody() { /** * One part of a multipart-body */ - fun part(name: String, type: KClass<*>) = part(name, type.asSchemaType()) {} + fun part(name: String, type: BodyTypeDescriptor) = part(name, type) {} /** * One part of a multipart-body */ - inline fun part(name: String) = part(name, getSchemaType()) {} + fun part(name: String, type: SchemaType, block: OpenApiMultipartPart.() -> Unit) = part(name, BodyTypeDescriptor.typeOf(type), block) /** * One part of a multipart-body */ - inline fun part(name: String, noinline block: OpenApiMultipartPart.() -> Unit) = part(name, getSchemaType(), block) + fun part(name: String, type: KClass<*>) = part(name, type.asSchemaType()) {} /** * One part of a multipart-body */ - fun part(name: String, customSchema: CustomSchemaRef, block: OpenApiMultipartPart.() -> Unit) { - parts.add(OpenApiMultipartPart(name, null).apply(block).apply { - this.customSchema = customSchema - }) - } + inline fun part(name: String) = part(name, getSchemaType()) {} /** * One part of a multipart-body */ - fun part(name: String, customSchema: CustomSchemaRef) = part(name, customSchema) {} + inline fun part(name: String, noinline block: OpenApiMultipartPart.() -> Unit) = part(name, getSchemaType(), block) /** * One part of a multipart-body */ - fun part(name: String, customSchemaId: String, block: OpenApiMultipartPart.() -> Unit) = part(name, obj(customSchemaId), block) + fun part(name: String, customSchemaId: String, block: OpenApiMultipartPart.() -> Unit) = + part(name, BodyTypeDescriptor.custom(customSchemaId), block) /** diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiMultipartPart.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiMultipartPart.kt index f06e8088..60ae1f7d 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiMultipartPart.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiMultipartPart.kt @@ -14,15 +14,9 @@ class OpenApiMultipartPart( */ val name: String, - val type: SchemaType? + val type: BodyTypeDescriptor ) { - /** - * reference to a custom schema (alternative to 'type') - */ - var customSchema: CustomSchemaRef? = null - - /** * Set a specific content type for this part */ diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiRequest.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiRequest.kt index 0ef7ad49..0f851e7b 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiRequest.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiRequest.kt @@ -110,23 +110,21 @@ class OpenApiRequest { /** - * The request body applicable for this operation + * The body returned with this request */ - fun body(type: SchemaType?, block: OpenApiSimpleBody.() -> Unit) { - body = OpenApiSimpleBody(type).apply(block) + fun body(typeDescriptor: BodyTypeDescriptor, block: OpenApiSimpleBody.() -> Unit) { + body = OpenApiSimpleBody(typeDescriptor).apply(block) } /** - * The request body applicable for this operation + * The body returned with this request */ - fun body(type: KClass<*>, block: OpenApiSimpleBody.() -> Unit) = body(type.asSchemaType(), block) - + fun body(typeDescriptor: BodyTypeDescriptor) = body(typeDescriptor) {} /** * The request body applicable for this operation */ - @JvmName("bodyGenericType") - inline fun body(noinline block: OpenApiSimpleBody.() -> Unit) = body(getSchemaType(), block) + fun body(type: SchemaType?, block: OpenApiSimpleBody.() -> Unit) = body(BodyTypeDescriptor.typeOf(type), block) /** @@ -138,41 +136,38 @@ class OpenApiRequest { /** * The request body applicable for this operation */ - inline fun body() = body(getSchemaType()) {} + fun body(type: KClass<*>, block: OpenApiSimpleBody.() -> Unit) = body(type.asSchemaType(), block) /** * The request body applicable for this operation */ - fun body(block: OpenApiSimpleBody.() -> Unit) = body(null, block) + @JvmName("bodyGenericType") + inline fun body(noinline block: OpenApiSimpleBody.() -> Unit) = body(getSchemaType(), block) /** - * The body returned with this request + * The request body applicable for this operation */ - fun body(customSchema: CustomSchemaRef, block: OpenApiSimpleBody.() -> Unit) { - body = OpenApiSimpleBody(null).apply(block).apply { - this.customSchema = customSchema - } - } + inline fun body() = body(getSchemaType()) {} /** - * The body returned with this request + * The request body applicable for this operation */ - fun body(customSchema: CustomSchemaRef) = body(customSchema) {} + fun body(block: OpenApiSimpleBody.() -> Unit) = body(BodyTypeDescriptor.empty(), block) /** * The body returned with this request */ - fun body(customSchemaId: String, block: OpenApiSimpleBody.() -> Unit) = body(obj(customSchemaId), block) + fun body(customSchemaId: String) = body(customSchemaId) {} /** * The body returned with this request */ - fun body(customSchemaId: String) = body(customSchemaId) {} + fun body(customSchemaId: String, block: OpenApiSimpleBody.() -> Unit) = body(BodyTypeDescriptor.custom(customSchemaId), block) /** @@ -190,5 +185,4 @@ class OpenApiRequest { this.body = body } - } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiResponse.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiResponse.kt index 77b244a4..4d9c4b92 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiResponse.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiResponse.kt @@ -61,7 +61,7 @@ class OpenApiResponse(val statusCode: String) { /** * The body returned with this response */ - fun body(type: SchemaType?, block: OpenApiSimpleBody.() -> Unit) { + fun body(type: BodyTypeDescriptor, block: OpenApiSimpleBody.() -> Unit) { body = OpenApiSimpleBody(type).apply(block) } @@ -69,54 +69,49 @@ class OpenApiResponse(val statusCode: String) { /** * The body returned with this response */ - fun body(type: KClass<*>, block: OpenApiSimpleBody.() -> Unit) = body(type.asSchemaType(), block) + fun body(type: BodyTypeDescriptor) = body(type) {} /** * The body returned with this response */ - @JvmName("bodyGenericType") - inline fun body(noinline block: OpenApiSimpleBody.() -> Unit) = body(getSchemaType(), block) + fun body(type: SchemaType?, block: OpenApiSimpleBody.() -> Unit) = body(BodyTypeDescriptor.typeOf(type), block) /** * The body returned with this response */ - fun body(type: KClass<*>) = body(type) {} + fun body(type: KClass<*>, block: OpenApiSimpleBody.() -> Unit) = body(type.asSchemaType(), block) /** * The body returned with this response */ - inline fun body() = body(getSchemaType()) {} + @JvmName("bodyGenericType") + inline fun body(noinline block: OpenApiSimpleBody.() -> Unit) = body(getSchemaType(), block) /** * The body returned with this response */ - fun body(block: OpenApiSimpleBody.() -> Unit) = body(null, block) + fun body(type: KClass<*>) = body(type) {} /** * The body returned with this response */ - fun body(customSchema: CustomSchemaRef, block: OpenApiSimpleBody.() -> Unit) { - body = OpenApiSimpleBody(null).apply(block).apply { - this.customSchema = customSchema - } - } + inline fun body() = body(getSchemaType()) {} /** * The body returned with this response */ - fun body(customSchema: CustomSchemaRef) = body(customSchema) {} - + fun body(block: OpenApiSimpleBody.() -> Unit) = body(null, block) /** * The body returned with this response */ - fun body(customSchemaId: String, block: OpenApiSimpleBody.() -> Unit) = body(obj(customSchemaId), block) + fun body(customSchemaId: String, block: OpenApiSimpleBody.() -> Unit) = body(BodyTypeDescriptor.custom(customSchemaId), block) /** diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiSimpleBody.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiSimpleBody.kt index 4ea77e51..cd2de62a 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiSimpleBody.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiSimpleBody.kt @@ -9,15 +9,9 @@ class OpenApiSimpleBody( /** * The type defining the schema used for the body. */ - val type: SchemaType?, + val type: BodyTypeDescriptor, ) : OpenApiBaseBody() { - /** - * reference to a custom schema (alternative to 'type') - */ - var customSchema: CustomSchemaRef? = null - - /** * Examples for this body */ diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/CustomJsonSchemaExample.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/CustomSchemaExample.kt similarity index 73% rename from src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/CustomJsonSchemaExample.kt rename to src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/CustomSchemaExample.kt index 23b3ffdc..2730a284 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/CustomJsonSchemaExample.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/CustomSchemaExample.kt @@ -1,9 +1,11 @@ package io.github.smiley4.ktorswaggerui.examples import io.github.smiley4.ktorswaggerui.SwaggerUI -import io.github.smiley4.ktorswaggerui.dsl.array +import io.github.smiley4.ktorswaggerui.dsl.BodyTypeDescriptor.Companion.custom +import io.github.smiley4.ktorswaggerui.dsl.BodyTypeDescriptor.Companion.multipleOf +import io.github.smiley4.ktorswaggerui.dsl.BodyTypeDescriptor.Companion.oneOf +import io.github.smiley4.ktorswaggerui.dsl.BodyTypeDescriptor.Companion.typeOf import io.github.smiley4.ktorswaggerui.dsl.get -import io.github.smiley4.ktorswaggerui.dsl.obj import io.ktor.http.HttpStatusCode import io.ktor.server.application.Application import io.ktor.server.application.call @@ -14,6 +16,7 @@ import io.ktor.server.request.receive import io.ktor.server.response.respond import io.ktor.server.response.respondText import io.ktor.server.routing.routing +import java.awt.SystemColor.text /** * An example for defining custom json-schemas @@ -34,6 +37,20 @@ private fun Application.myModule() { val someNumber: Long ) + data class Rectangle( + val width: Int, + val height: Int + ) + + data class Circle( + val radius: Int + ) + + data class Point( + val x: Int, + val y: Int + ) + install(SwaggerUI) { // don't show the test-routes providing json-schemas pathFilter = { _, url -> url.firstOrNull() != "schema" } @@ -64,12 +81,12 @@ private fun Application.myModule() { get("something", { request { // body referencing the custom schema with id 'myRequestData' - body(obj("myRequestData")) + body("myRequestData") } response { HttpStatusCode.OK to { // body referencing the custom schema with id 'myResponseData' - body(obj("myResponseData")) + body("myResponseData") } } }) { @@ -80,12 +97,12 @@ private fun Application.myModule() { get("something/many", { request { // body referencing the custom schema with id 'myRequestData' - body(array("myRequestData")) + body(multipleOf(custom("myRequestData"))) } response { HttpStatusCode.OK to { // body referencing the custom schema with id 'myResponseData' - body(array("myResponseData")) + body(multipleOf(custom("myResponseData"))) } } }) { @@ -93,6 +110,23 @@ private fun Application.myModule() { call.respond(HttpStatusCode.OK, MyResponseData(text, 42)) } + get("oneof/shapes", { + request { + // body allowing a mixed list of rectangles, circles and points + body( + multipleOf( + oneOf( + typeOf(Rectangle::class), + typeOf(Circle::class), + typeOf(Point::class), + ) + ) + ) + } + }) { + call.respond(HttpStatusCode.OK, Unit) + } + // (external) endpoint providing a json-schema get("schema/myResponseData") { call.respondText( diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OperationBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OperationBuilderTest.kt index 0a030fab..219a1175 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OperationBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OperationBuilderTest.kt @@ -4,7 +4,6 @@ import com.fasterxml.jackson.databind.ObjectMapper import io.github.smiley4.ktorswaggerui.data.PluginConfigData import io.github.smiley4.ktorswaggerui.dsl.PluginConfigDsl import io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute -import io.github.smiley4.ktorswaggerui.dsl.obj import io.github.smiley4.ktorswaggerui.builder.example.ExampleContext import io.github.smiley4.ktorswaggerui.builder.example.ExampleContextBuilder import io.github.smiley4.ktorswaggerui.builder.openapi.ContentBuilder @@ -22,6 +21,8 @@ import io.github.smiley4.ktorswaggerui.builder.schema.SchemaBuilder import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContext import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContextBuilder import io.github.smiley4.ktorswaggerui.builder.schema.TypeOverwrites +import io.github.smiley4.ktorswaggerui.dsl.BodyTypeDescriptor +import io.github.smiley4.ktorswaggerui.dsl.BodyTypeDescriptor.Companion.custom import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder @@ -823,7 +824,7 @@ class OperationBuilderTest : StringSpec({ method = HttpMethod.Get, documentation = OpenApiRoute().also { route -> route.request { - body(obj("myCustomSchema")) + body(custom("myCustomSchema")) } }, protected = false @@ -882,7 +883,7 @@ class OperationBuilderTest : StringSpec({ route.request { multipartBody { mediaType(ContentType.MultiPart.FormData) - part("customData", obj("myCustomSchema")) + part("customData", custom("myCustomSchema")) } } }, diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaContextTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaContextTest.kt index e24b6c7b..d9d0c0e2 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaContextTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaContextTest.kt @@ -9,15 +9,15 @@ import com.github.victools.jsonschema.generator.SchemaVersion import io.github.smiley4.ktorswaggerui.data.PluginConfigData import io.github.smiley4.ktorswaggerui.dsl.PluginConfigDsl import io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute -import io.github.smiley4.ktorswaggerui.dsl.array import io.github.smiley4.ktorswaggerui.dsl.asSchemaType import io.github.smiley4.ktorswaggerui.dsl.getSchemaType -import io.github.smiley4.ktorswaggerui.dsl.obj import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta import io.github.smiley4.ktorswaggerui.builder.schema.SchemaBuilder import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContext import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContextBuilder import io.github.smiley4.ktorswaggerui.builder.schema.TypeOverwrites +import io.github.smiley4.ktorswaggerui.dsl.BodyTypeDescriptor.Companion.custom +import io.github.smiley4.ktorswaggerui.dsl.BodyTypeDescriptor.Companion.multipleOf import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder @@ -307,12 +307,12 @@ class SchemaContextTest : StringSpec({ val routes = listOf( route { request { - body(obj("myCustomSchema")) + body(custom("myCustomSchema")) } } ) val schemaContext = schemaContext(routes, config) - schemaContext.getSchema(obj("myCustomSchema")).also { schema -> + schemaContext.getSchema("myCustomSchema").also { schema -> schema.type shouldBe null schema.`$ref` shouldBe "#/components/schemas/myCustomSchema" } @@ -345,19 +345,14 @@ class SchemaContextTest : StringSpec({ val routes = listOf( route { request { - body(array("myCustomSchema")) + body(multipleOf(custom("myCustomSchema"))) } } ) val schemaContext = schemaContext(routes, config) - schemaContext.getSchema(array("myCustomSchema")).also { schema -> - schema.type shouldBe "array" - schema.`$ref` shouldBe null - schema.items - .also { it shouldNotBe null } - ?.also { items -> - items.`$ref` shouldBe "#/components/schemas/myCustomSchema" - } + schemaContext.getSchema("myCustomSchema").also { schema -> + schema.type shouldBe null + schema.`$ref` shouldBe "#/components/schemas/myCustomSchema" } schemaContext.getComponentsSection().also { components -> components.keys shouldContainExactlyInAnyOrder listOf( @@ -431,7 +426,7 @@ class SchemaContextTest : StringSpec({ } } val schemaContext = schemaContext(emptyList(), config) - schemaContext.getSchemaOrNull(obj("myCustomSchema")) shouldBe null + schemaContext.getSchemaOrNull("myCustomSchema") shouldBe null schemaContext.getComponentsSection().also { components -> components.keys shouldHaveSize 0 } @@ -454,7 +449,7 @@ class SchemaContextTest : StringSpec({ } } val schemaContext = schemaContext(emptyList(), config) - schemaContext.getSchema(obj("myCustomSchema")).also { schema -> + schemaContext.getSchema("myCustomSchema").also { schema -> schema.type shouldBe null schema.`$ref` shouldBe "#/components/schemas/myCustomSchema" } From 4e1929c554a4368f0d7d6cd56396c7e26d731d4f Mon Sep 17 00:00:00 2001 From: Lukas Ruegner Date: Fri, 20 Oct 2023 18:20:47 +0200 Subject: [PATCH 10/12] cleanup dsl --- .../ktorswaggerui/dsl/EncodingConfig.kt | 4 ---- .../smiley4/ktorswaggerui/dsl/OpenApiInfo.kt | 4 ---- .../ktorswaggerui/dsl/OpenApiSecurityScheme.kt | 3 --- .../ktorswaggerui/dsl/OpenIdOAuthFlows.kt | 11 ----------- .../ktorswaggerui/dsl/PluginConfigDsl.kt | 18 ------------------ .../tests/openapi/OpenApiBuilderTest.kt | 7 ++++--- 6 files changed, 4 insertions(+), 43 deletions(-) diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/EncodingConfig.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/EncodingConfig.kt index 008e4474..1714a227 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/EncodingConfig.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/EncodingConfig.kt @@ -23,8 +23,6 @@ class EncodingConfig { private var exampleEncoder: ExampleEncoder = EncodingData.DEFAULT.exampleEncoder - fun getExampleEncoder() = exampleEncoder - /** * Encode the given type into a valid json-schema. @@ -36,8 +34,6 @@ class EncodingConfig { private var schemaEncoder: SchemaEncoder = EncodingData.DEFAULT.schemaEncoder - fun getSchemaEncoder() = schemaEncoder - /** * the name of the field (if it exists) in the json-schema containing schema-definitions. diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiInfo.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiInfo.kt index 8a9f716e..98efa891 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiInfo.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiInfo.kt @@ -42,8 +42,6 @@ class OpenApiInfo { contact = OpenApiContact().apply(block) } - fun getContact() = contact - private var license: OpenApiLicense? = null @@ -55,8 +53,6 @@ class OpenApiInfo { license = OpenApiLicense().apply(block) } - fun getLicense() = license - fun build(base: InfoData): InfoData { return InfoData( title = mergeDefault(base.title, this.title, InfoData.DEFAULT.title), diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiSecurityScheme.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiSecurityScheme.kt index 3a15ccc5..4a6e265d 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiSecurityScheme.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiSecurityScheme.kt @@ -59,9 +59,6 @@ class OpenApiSecurityScheme( } - fun getFlows() = flows - - /** * OpenId Connect URL to discover OAuth2 configuration values. * Required for type [AuthType.OPENID_CONNECT] diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenIdOAuthFlows.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenIdOAuthFlows.kt index 336b7f17..b13bb47b 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenIdOAuthFlows.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenIdOAuthFlows.kt @@ -20,9 +20,6 @@ class OpenIdOAuthFlows { } - fun getImplicit() = implicit - - private var password: OpenIdOAuthFlow? = null @@ -34,9 +31,6 @@ class OpenIdOAuthFlows { } - fun getPassword() = password - - private var clientCredentials: OpenIdOAuthFlow? = null @@ -48,9 +42,6 @@ class OpenIdOAuthFlows { } - fun getClientCredentials() = clientCredentials - - private var authorizationCode: OpenIdOAuthFlow? = null @@ -62,8 +53,6 @@ class OpenIdOAuthFlows { } - fun getAuthorizationCode() = authorizationCode - fun build(base: OpenIdOAuthFlowsData) = OpenIdOAuthFlowsData( implicit = implicit?.build(base.implicit ?: OpenIdOAuthFlowData.DEFAULT) ?: base.implicit, password = password?.build(base.password ?: OpenIdOAuthFlowData.DEFAULT) ?: base.password, diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/PluginConfigDsl.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/PluginConfigDsl.kt index 449fa3ee..b89ef3e4 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/PluginConfigDsl.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/PluginConfigDsl.kt @@ -41,8 +41,6 @@ class PluginConfigDsl { private var defaultUnauthorizedResponse: OpenApiResponse? = PluginConfigData.DEFAULT.defaultUnauthorizedResponse - fun getDefaultUnauthorizedResponse() = defaultUnauthorizedResponse - /** * The name of the security scheme to use for the protected paths @@ -66,8 +64,6 @@ class PluginConfigDsl { private var tagGenerator: TagGenerator? = PluginConfigData.DEFAULT.tagGenerator - fun getTagGenerator() = tagGenerator - /** * Assigns routes without an [io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute.specId] to a specified openapi-spec. @@ -91,8 +87,6 @@ class PluginConfigDsl { private var swaggerUI = SwaggerUIDsl() - fun getSwaggerUI() = swaggerUI - /** * OpenAPI info configuration - provides metadata about the API @@ -103,8 +97,6 @@ class PluginConfigDsl { private var info = OpenApiInfo() - fun getInfo() = info - /** * OpenAPI server configuration - an array of servers, which provide connectivity information to a target server @@ -115,8 +107,6 @@ class PluginConfigDsl { private val servers = mutableListOf() - fun getServers(): List = servers - /** * OpenAPI external docs configuration - link and description of an external documentation @@ -127,8 +117,6 @@ class PluginConfigDsl { private var externalDocs = OpenApiExternalDocs() - fun getExternalDocs() = externalDocs - /** * Defines security schemes that can be used by operations @@ -139,8 +127,6 @@ class PluginConfigDsl { private val securitySchemes = mutableListOf() - fun getSecuritySchemes(): List = securitySchemes - /** * Tags used by the specification with additional metadata. Not all tags that are used must be declared @@ -151,8 +137,6 @@ class PluginConfigDsl { private val tags = mutableListOf() - fun getTags(): List = tags - /** * Custom schemas to reference via [io.github.smiley4.ktorswaggerui.dsl.CustomSchemaRef] @@ -163,8 +147,6 @@ class PluginConfigDsl { private var customSchemas = CustomSchemas() - fun getCustomSchemas() = customSchemas - /** * customize the behaviour of different encoders (examples, schemas, ...) diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OpenApiBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OpenApiBuilderTest.kt index 41db1df6..f682ce89 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OpenApiBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OpenApiBuilderTest.kt @@ -81,11 +81,12 @@ class OpenApiBuilderTest : StringSpec({ private val defaultPluginConfig = PluginConfigDsl() private fun schemaContext(routes: List, pluginConfig: PluginConfigDsl): SchemaContext { + val pluginConfigData = pluginConfig.build(PluginConfigData.DEFAULT) return SchemaContextBuilder( - config = pluginConfig.build(PluginConfigData.DEFAULT), + config =pluginConfigData, schemaBuilder = SchemaBuilder( - definitionsField = pluginConfig.encodingConfig.schemaDefinitionsField, - schemaEncoder = pluginConfig.encodingConfig.getSchemaEncoder(), + definitionsField = pluginConfigData.encoding.schemaDefsField, + schemaEncoder = pluginConfigData.encoding.schemaEncoder, ObjectMapper(), TypeOverwrites.get() ) From 3fcde4a0cb86653a93e281dfd2518e95c0230cda Mon Sep 17 00:00:00 2001 From: Lukas Ruegner Date: Fri, 20 Oct 2023 18:24:41 +0200 Subject: [PATCH 11/12] fix --- .../ktorswaggerui/tests/openapi/OperationBuilderTest.kt | 7 ++++--- .../ktorswaggerui/tests/openapi/PathsBuilderTest.kt | 7 ++++--- .../ktorswaggerui/tests/schema/SchemaContextTest.kt | 7 ++++--- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OperationBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OperationBuilderTest.kt index 219a1175..39184ed1 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OperationBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/OperationBuilderTest.kt @@ -935,11 +935,12 @@ class OperationBuilderTest : StringSpec({ routes: List, pluginConfig: PluginConfigDsl = defaultPluginConfig ): SchemaContext { + val pluginConfigData = pluginConfig.build(PluginConfigData.DEFAULT) return SchemaContextBuilder( - config = pluginConfig.build(PluginConfigData.DEFAULT), + config = pluginConfigData, schemaBuilder = SchemaBuilder( - definitionsField = pluginConfig.encodingConfig.schemaDefinitionsField, - schemaEncoder = pluginConfig.encodingConfig.getSchemaEncoder(), + definitionsField = pluginConfigData.encoding.schemaDefsField, + schemaEncoder = pluginConfigData.encoding.schemaEncoder, ObjectMapper(), TypeOverwrites.get() ) diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/PathsBuilderTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/PathsBuilderTest.kt index 16fcba24..29f69a44 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/PathsBuilderTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/openapi/PathsBuilderTest.kt @@ -92,11 +92,12 @@ class PathsBuilderTest : StringSpec({ private val defaultPluginConfig = PluginConfigDsl() private fun schemaContext(routes: List, pluginConfig: PluginConfigDsl = defaultPluginConfig): SchemaContext { + val pluginConfigData = pluginConfig.build(PluginConfigData.DEFAULT) return SchemaContextBuilder( - config = pluginConfig.build(PluginConfigData.DEFAULT), + config = pluginConfigData, schemaBuilder = SchemaBuilder( - definitionsField = pluginConfig.encodingConfig.schemaDefinitionsField, - schemaEncoder = pluginConfig.encodingConfig.getSchemaEncoder(), + definitionsField = pluginConfigData.encoding.schemaDefsField, + schemaEncoder = pluginConfigData.encoding.schemaEncoder, ObjectMapper(), TypeOverwrites.get() ) diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaContextTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaContextTest.kt index d9d0c0e2..f4040d46 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaContextTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/schema/SchemaContextTest.kt @@ -500,11 +500,12 @@ class SchemaContextTest : StringSpec({ routes: Collection, pluginConfig: PluginConfigDsl = defaultPluginConfig ): SchemaContext { + val pluginConfigData = pluginConfig.build(PluginConfigData.DEFAULT) return SchemaContextBuilder( - config = pluginConfig.build(PluginConfigData.DEFAULT), + config = pluginConfigData, schemaBuilder = SchemaBuilder( - definitionsField = pluginConfig.encodingConfig.schemaDefinitionsField, - schemaEncoder = pluginConfig.encodingConfig.getSchemaEncoder(), + definitionsField = pluginConfigData.encoding.schemaDefsField, + schemaEncoder = pluginConfigData.encoding.schemaEncoder, ObjectMapper(), TypeOverwrites.get() ) From 92e8b8f6c69ec4b0bce0780cb11953499c55b92f Mon Sep 17 00:00:00 2001 From: Lukas Ruegner Date: Fri, 20 Oct 2023 18:36:36 +0200 Subject: [PATCH 12/12] minor changes --- .../ktorswaggerui/dsl/BodyTypeDescriptor.kt | 81 +++++++++++++++++++ .../examples/CustomSchemaExample.kt | 1 - 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/BodyTypeDescriptor.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/BodyTypeDescriptor.kt index fe5654aa..72d74f88 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/BodyTypeDescriptor.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/BodyTypeDescriptor.kt @@ -2,48 +2,129 @@ package io.github.smiley4.ktorswaggerui.dsl import kotlin.reflect.KClass +/** + * Describes the type/schema of a request or response body. + * [BodyTypeDescriptor]s can be nested to build more specific bodies from simple types + */ sealed interface BodyTypeDescriptor { companion object { + /** + * A [BodyTypeDescriptor] of the specific given type (or empty if type is null). + */ fun typeOf(type: KClass<*>?) = type?.let { SchemaBodyTypeDescriptor(it.asSchemaType()) } ?: EmptyBodyTypeDescriptor() + + /** + * A [BodyTypeDescriptor] of the specific given type (or empty if type is null). + */ fun typeOf(type: SchemaType?) = type?.let { SchemaBodyTypeDescriptor(it) } ?: EmptyBodyTypeDescriptor() + + /** + * A [BodyTypeDescriptor] of the specific given generic type. + */ + inline fun typeOf() = SchemaBodyTypeDescriptor(getSchemaType()) + + + /** + * Type can be any one of the given types. + */ fun oneOf(vararg type: KClass<*>) = OneOfBodyTypeDescriptor(type.toList().map { typeOf(it.asSchemaType()) }) + + /** + * Type can be any one of the given types. + */ @JvmName("oneOfClass") fun oneOf(types: Collection>) = OneOfBodyTypeDescriptor(types.map { typeOf(it.asSchemaType()) }) + + /** + * Type can be any one of the given types. + */ fun oneOf(vararg type: SchemaType) = OneOfBodyTypeDescriptor(type.map { typeOf(it) }) + + /** + * Type can be any one of the given types. + */ @JvmName("oneOfType") fun oneOf(types: Collection) = OneOfBodyTypeDescriptor(types.map { typeOf(it) }) + + /** + * Type can be any one of the given types. + */ fun oneOf(vararg type: BodyTypeDescriptor) = OneOfBodyTypeDescriptor(type.toList()) + + /** + * Type can be any one of the given types. + */ @JvmName("oneOfDescriptor") fun oneOf(types: Collection) = OneOfBodyTypeDescriptor(types.toList()) + + /** + * Type is an array of the specific given type. + */ fun multipleOf(type: KClass<*>) = CollectionBodyTypeDescriptor(typeOf(type.asSchemaType())) + + /** + * Type is an array of the specific given type. + */ fun multipleOf(type: SchemaType) = CollectionBodyTypeDescriptor(typeOf(type)) + + /** + * Type is an array of the given type. + */ fun multipleOf(type: BodyTypeDescriptor) = CollectionBodyTypeDescriptor(type) + + /** + * A [BodyTypeDescriptor] of the specific given custom schema. + */ fun custom(customSchemaId: String) = CustomRefBodyTypeDescriptor(customSchemaId) + + /** + * An empty type. + */ fun empty() = EmptyBodyTypeDescriptor() } } + +/** + * Describes an empty type + */ class EmptyBodyTypeDescriptor : BodyTypeDescriptor + +/** + * Describes a specific type/schema + */ class SchemaBodyTypeDescriptor(val schemaType: SchemaType) : BodyTypeDescriptor + +/** + * Describes any one of the given types + */ class OneOfBodyTypeDescriptor(val elements: List) : BodyTypeDescriptor + +/** + * Describes an array of the given type + */ class CollectionBodyTypeDescriptor(val schemaType: BodyTypeDescriptor) : BodyTypeDescriptor + +/** + * Describes the custom schema/type with the given id + */ class CustomRefBodyTypeDescriptor(val customSchemaId: String) : BodyTypeDescriptor diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/CustomSchemaExample.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/CustomSchemaExample.kt index 2730a284..73b71f7e 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/CustomSchemaExample.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/CustomSchemaExample.kt @@ -16,7 +16,6 @@ import io.ktor.server.request.receive import io.ktor.server.response.respond import io.ktor.server.response.respondText import io.ktor.server.routing.routing -import java.awt.SystemColor.text /** * An example for defining custom json-schemas