diff --git a/.gitignore b/.gitignore index ce181e5a..254e8bb4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea .gradle +.kotlin build \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 76611365..3d7d0861 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,6 +4,7 @@ plugins { id("org.owasp.dependencycheck") version "8.2.1" apply false id("io.gitlab.arturbosch.detekt") version "1.23.0" apply false id("com.vanniktech.maven.publish") version "0.28.0" apply false + id("com.github.ben-manes.versions") version "0.51.0" apply false } repositories { diff --git a/gradle.properties b/gradle.properties index 62dfb8cd..bfaede2e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,12 +2,9 @@ kotlin.code.style=official # project id projectGroupId=io.github.smiley4 -projectArtifactIdBase=ktor-swagger-ui -projectVersion=4.2.0 +projectVersion=5.0.0-beta.1 -# publishing information -projectNameBase=Ktor Swagger UI -projectDescriptionBase=Ktor plugin to document routes and provide a Swagger-UI +# common publishing information projectScmUrl=https://github.com/SMILEY4/ktor-swagger-ui projectScmConnection=scm:git:git://github.com/SMILEY4/ktor-swagger-ui.git projectLicenseName=The Apache License, Version 2.0 diff --git a/ktor-openapi/build.gradle.kts b/ktor-openapi/build.gradle.kts new file mode 100644 index 00000000..72297dea --- /dev/null +++ b/ktor-openapi/build.gradle.kts @@ -0,0 +1,124 @@ +import com.vanniktech.maven.publish.JavadocJar +import com.vanniktech.maven.publish.KotlinJvm +import com.vanniktech.maven.publish.SonatypeHost +import io.gitlab.arturbosch.detekt.Detekt + +val projectGroupId: String by project +val projectVersion: String by project +group = projectGroupId +version = projectVersion + +plugins { + kotlin("jvm") + id("org.owasp.dependencycheck") + id("com.github.ben-manes.versions") + id("io.gitlab.arturbosch.detekt") + id("com.vanniktech.maven.publish") + id("org.jetbrains.dokka") +} + +repositories { + mavenCentral() +} + +dependencies { + + val versionKtor: String by project + implementation("io.ktor:ktor-server-core-jvm:$versionKtor") + implementation("io.ktor:ktor-server-auth:$versionKtor") + implementation("io.ktor:ktor-server-resources:$versionKtor") + testImplementation("io.ktor:ktor-server-netty-jvm:$versionKtor") + testImplementation("io.ktor:ktor-server-content-negotiation:$versionKtor") + testImplementation("io.ktor:ktor-serialization-jackson:$versionKtor") + testImplementation("io.ktor:ktor-server-auth:$versionKtor") + testImplementation("io.ktor:ktor-server-call-logging:$versionKtor") + testImplementation("io.ktor:ktor-server-test-host:$versionKtor") + + val versionSwaggerParser: String by project + implementation("io.swagger.parser.v3:swagger-parser:$versionSwaggerParser") + + val versionJackson: String by project + implementation("com.fasterxml.jackson.module:jackson-module-kotlin:$versionJackson") + + val versionSchemaKenerator: String by project + implementation("io.github.smiley4:schema-kenerator-core:$versionSchemaKenerator") + implementation("io.github.smiley4:schema-kenerator-reflection:$versionSchemaKenerator") + implementation("io.github.smiley4:schema-kenerator-swagger:$versionSchemaKenerator") + + val versionKotlinLogging: String by project + implementation("io.github.oshai:kotlin-logging-jvm:$versionKotlinLogging") + + val versionKotest: String by project + testImplementation("io.kotest:kotest-runner-junit5:$versionKotest") + testImplementation("io.kotest:kotest-assertions-core:$versionKotest") + + val versionKotlinTest: String by project + testImplementation("org.jetbrains.kotlin:kotlin-test:$versionKotlinTest") + + val versionMockk: String by project + testImplementation("io.mockk:mockk:$versionMockk") + +} + +kotlin { + jvmToolchain(11) +} + +tasks.withType().configureEach { + useJUnitPlatform() +} + +detekt { + ignoreFailures = false + buildUponDefaultConfig = true + allRules = false + config.setFrom("$projectDir/../detekt/detekt.yml") +} +tasks.withType().configureEach { + reports { + html.required.set(true) + md.required.set(true) + xml.required.set(false) + txt.required.set(false) + sarif.required.set(false) + } +} + +mavenPublishing { + val projectGroupId: String by project + val projectVersion: String by project + val projectScmUrl: String by project + val projectScmConnection: String by project + val projectLicenseName: String by project + val projectLicenseUrl: String by project + val projectDeveloperName: String by project + val projectDeveloperUrl: String by project + + configure(KotlinJvm(JavadocJar.Dokka("dokkaHtml"), true)) + publishToMavenCentral(SonatypeHost.S01) + signAllPublications() + coordinates(projectGroupId, "ktor-openapi", projectVersion) + pom { + name.set("Ktor OpenApi") + description.set("Ktor plugin to automatically generate and provide OpenApi") + url.set(projectScmUrl) + licenses { + license { + name.set(projectLicenseName) + url.set(projectLicenseUrl) + distribution.set(projectLicenseUrl) + } + } + scm { + url.set(projectScmUrl) + connection.set(projectScmConnection) + } + developers { + developer { + id.set(projectDeveloperName) + name.set(projectDeveloperName) + url.set(projectDeveloperUrl) + } + } + } +} \ No newline at end of file diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routing/DocumentedRouteSelector.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/DocumentedRouteSelector.kt similarity index 63% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routing/DocumentedRouteSelector.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/DocumentedRouteSelector.kt index 26b3594a..d26cd5b2 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routing/DocumentedRouteSelector.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/DocumentedRouteSelector.kt @@ -1,10 +1,13 @@ -package io.github.smiley4.ktorswaggerui.dsl.routing +package io.github.smiley4.ktoropenapi -import io.github.smiley4.ktorswaggerui.dsl.routes.OpenApiRoute +import io.github.smiley4.ktoropenapi.config.RouteConfig +import io.github.smiley4.ktoropenapi.resources.handle import io.ktor.http.HttpMethod +import io.ktor.server.resources.resource import io.ktor.server.routing.Route import io.ktor.server.routing.RouteSelector import io.ktor.server.routing.RouteSelectorEvaluation +import io.ktor.server.routing.RoutingContext import io.ktor.server.routing.RoutingResolveContext import io.ktor.server.routing.delete import io.ktor.server.routing.get @@ -17,7 +20,7 @@ import io.ktor.server.routing.put import io.ktor.server.routing.route import io.ktor.utils.io.KtorDsl -class DocumentedRouteSelector(val documentation: OpenApiRoute) : RouteSelector() { +class DocumentedRouteSelector(val documentation: RouteConfig) : RouteSelector() { companion object { private var includeDocumentedRouteInRouteToString = false @@ -33,10 +36,10 @@ class DocumentedRouteSelector(val documentation: OpenApiRoute) : RouteSelector() @KtorDsl fun Route.documentation( - documentation: OpenApiRoute.() -> Unit = { }, + documentation: RouteConfig.() -> Unit = { }, build: Route.() -> Unit ): Route { - val documentedRoute = createChild(DocumentedRouteSelector(OpenApiRoute().apply(documentation))) + val documentedRoute = createChild(DocumentedRouteSelector(RouteConfig().apply(documentation))) documentedRoute.build() return documentedRoute } @@ -47,7 +50,7 @@ fun Route.documentation( @KtorDsl fun Route.route( - builder: OpenApiRoute.() -> Unit = { }, + builder: RouteConfig.() -> Unit = { }, build: Route.() -> Unit ): Route { return documentation(builder) { route("", build) } @@ -56,7 +59,7 @@ fun Route.route( @KtorDsl fun Route.route( method: HttpMethod, - builder: OpenApiRoute.() -> Unit = { }, + builder: RouteConfig.() -> Unit = { }, build: Route.() -> Unit ): Route { return documentation(builder) { route("", method, build) } @@ -65,7 +68,7 @@ fun Route.route( @KtorDsl fun Route.route( path: String, - builder: OpenApiRoute.() -> Unit = { }, + builder: RouteConfig.() -> Unit = { }, build: Route.() -> Unit ): Route { return documentation(builder) { route(path, build) } @@ -75,7 +78,7 @@ fun Route.route( fun Route.route( path: String, method: HttpMethod, - builder: OpenApiRoute.() -> Unit = { }, + builder: RouteConfig.() -> Unit = { }, build: Route.() -> Unit ): Route { return documentation(builder) { route(path, method, build) } @@ -84,7 +87,7 @@ fun Route.route( @KtorDsl fun Route.method( method: HttpMethod, - builder: OpenApiRoute.() -> Unit = { }, + builder: RouteConfig.() -> Unit = { }, body: Route.() -> Unit ): Route { return documentation(builder) { method(method, body) } @@ -97,16 +100,16 @@ fun Route.method( @KtorDsl fun Route.get( path: String, - builder: OpenApiRoute.() -> Unit = { }, - body: suspend io.ktor.server.routing.RoutingContext.() -> Unit + builder: RouteConfig.() -> Unit = { }, + body: suspend RoutingContext.() -> Unit ): Route { return documentation(builder) { get(path, body) } } @KtorDsl fun Route.get( - builder: OpenApiRoute.() -> Unit = { }, - body: suspend io.ktor.server.routing.RoutingContext.() -> Unit + builder: RouteConfig.() -> Unit = { }, + body: suspend RoutingContext.() -> Unit ): Route { return documentation(builder) { get(body) } } @@ -119,8 +122,8 @@ fun Route.get( @KtorDsl fun Route.post( path: String, - builder: OpenApiRoute.() -> Unit = { }, - body: suspend io.ktor.server.routing.RoutingContext.() -> Unit + builder: RouteConfig.() -> Unit = { }, + body: suspend RoutingContext.() -> Unit ): Route { return documentation(builder) { post(path, body) } } @@ -128,8 +131,8 @@ fun Route.post( @KtorDsl @JvmName("postTyped") inline fun Route.post( - noinline builder: OpenApiRoute.() -> Unit = { }, - crossinline body: suspend io.ktor.server.routing.RoutingContext.(R) -> Unit + noinline builder: RouteConfig.() -> Unit = { }, + crossinline body: suspend RoutingContext.(R) -> Unit ): Route { return documentation(builder) { post(body) } } @@ -138,16 +141,16 @@ inline fun Route.post( @JvmName("postTypedPath") inline fun Route.post( path: String, - noinline builder: OpenApiRoute.() -> Unit = { }, - crossinline body: suspend io.ktor.server.routing.RoutingContext.(R) -> Unit + noinline builder: RouteConfig.() -> Unit = { }, + crossinline body: suspend RoutingContext.(R) -> Unit ): Route { return documentation(builder) { post(path, body) } } @KtorDsl fun Route.post( - builder: OpenApiRoute.() -> Unit = { }, - body: suspend io.ktor.server.routing.RoutingContext.() -> Unit + builder: RouteConfig.() -> Unit = { }, + body: suspend RoutingContext.() -> Unit ): Route { return documentation(builder) { post(body) } } @@ -160,16 +163,16 @@ fun Route.post( @KtorDsl fun Route.put( path: String, - builder: OpenApiRoute.() -> Unit = { }, - body: suspend io.ktor.server.routing.RoutingContext.() -> Unit + builder: RouteConfig.() -> Unit = { }, + body: suspend RoutingContext.() -> Unit ): Route { return documentation(builder) { put(path, body) } } @KtorDsl fun Route.put( - builder: OpenApiRoute.() -> Unit = { }, - body: suspend io.ktor.server.routing.RoutingContext.() -> Unit + builder: RouteConfig.() -> Unit = { }, + body: suspend RoutingContext.() -> Unit ): Route { return documentation(builder) { put(body) } } @@ -177,8 +180,8 @@ fun Route.put( @KtorDsl @JvmName("putTyped") inline fun Route.put( - noinline builder: OpenApiRoute.() -> Unit = { }, - crossinline body: suspend io.ktor.server.routing.RoutingContext.(R) -> Unit + noinline builder: RouteConfig.() -> Unit = { }, + crossinline body: suspend RoutingContext.(R) -> Unit ): Route { return documentation(builder) { put(body) } } @@ -187,8 +190,8 @@ inline fun Route.put( @JvmName("putTypedPath") inline fun Route.put( path: String, - noinline builder: OpenApiRoute.() -> Unit = { }, - crossinline body: suspend io.ktor.server.routing.RoutingContext.(R) -> Unit + noinline builder: RouteConfig.() -> Unit = { }, + crossinline body: suspend RoutingContext.(R) -> Unit ): Route { return documentation(builder) { put(path, body) } } @@ -201,16 +204,16 @@ inline fun Route.put( @KtorDsl fun Route.delete( path: String, - builder: OpenApiRoute.() -> Unit = { }, - body: suspend io.ktor.server.routing.RoutingContext.() -> Unit + builder: RouteConfig.() -> Unit = { }, + body: suspend RoutingContext.() -> Unit ): Route { return documentation(builder) { delete(path, body) } } @KtorDsl fun Route.delete( - builder: OpenApiRoute.() -> Unit = { }, - body: suspend io.ktor.server.routing.RoutingContext.() -> Unit + builder: RouteConfig.() -> Unit = { }, + body: suspend RoutingContext.() -> Unit ): Route { return documentation(builder) { delete(body) } } @@ -223,24 +226,24 @@ fun Route.delete( @KtorDsl fun Route.patch( path: String, - builder: OpenApiRoute.() -> Unit = { }, - body: suspend io.ktor.server.routing.RoutingContext.() -> Unit + builder: RouteConfig.() -> Unit = { }, + body: suspend RoutingContext.() -> Unit ): Route { return documentation(builder) { patch(path, body) } } @KtorDsl fun Route.patch( - builder: OpenApiRoute.() -> Unit = { }, - body: suspend io.ktor.server.routing.RoutingContext.() -> Unit + builder: RouteConfig.() -> Unit = { }, + body: suspend RoutingContext.() -> Unit ): Route { return documentation(builder) { patch(body) } } @JvmName("patchTyped") inline fun Route.patch( - noinline builder: OpenApiRoute.() -> Unit = { }, - crossinline body: suspend io.ktor.server.routing.RoutingContext.(R) -> Unit + noinline builder: RouteConfig.() -> Unit = { }, + crossinline body: suspend RoutingContext.(R) -> Unit ): Route { return documentation(builder) { patch(body) } @@ -249,8 +252,8 @@ inline fun Route.patch( @JvmName("patchTypedPath") inline fun Route.patch( path: String, - noinline builder: OpenApiRoute.() -> Unit = { }, - crossinline body: suspend io.ktor.server.routing.RoutingContext.(R) -> Unit + noinline builder: RouteConfig.() -> Unit = { }, + crossinline body: suspend RoutingContext.(R) -> Unit ): Route { return documentation(builder) { patch(path, body) } } @@ -263,16 +266,16 @@ inline fun Route.patch( @KtorDsl fun Route.options( path: String, - builder: OpenApiRoute.() -> Unit = { }, - body: suspend io.ktor.server.routing.RoutingContext.() -> Unit + builder: RouteConfig.() -> Unit = { }, + body: suspend RoutingContext.() -> Unit ): Route { return documentation(builder) { options(path, body) } } @KtorDsl fun Route.options( - builder: OpenApiRoute.() -> Unit = { }, - body: suspend io.ktor.server.routing.RoutingContext.() -> Unit + builder: RouteConfig.() -> Unit = { }, + body: suspend RoutingContext.() -> Unit ): Route { return documentation(builder) { options(body) } } @@ -285,16 +288,16 @@ fun Route.options( @KtorDsl fun Route.head( path: String, - builder: OpenApiRoute.() -> Unit = { }, - body: suspend io.ktor.server.routing.RoutingContext.() -> Unit + builder: RouteConfig.() -> Unit = { }, + body: suspend RoutingContext.() -> Unit ): Route { return documentation(builder) { head(path, body) } } @KtorDsl fun Route.head( - builder: OpenApiRoute.() -> Unit = { }, - body: suspend io.ktor.server.routing.RoutingContext.() -> Unit + builder: RouteConfig.() -> Unit = { }, + body: suspend RoutingContext.() -> Unit ): Route { return documentation(builder) { head(body) } } diff --git a/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/OpenApiPlugin.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/OpenApiPlugin.kt new file mode 100644 index 00000000..3f6cc7e5 --- /dev/null +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/OpenApiPlugin.kt @@ -0,0 +1,77 @@ +package io.github.smiley4.ktoropenapi + +import io.github.oshai.kotlinlogging.KotlinLogging +import io.github.smiley4.ktoropenapi.builder.OpenApiSpecBuilder +import io.github.smiley4.ktoropenapi.builder.route.RouteCollector +import io.github.smiley4.ktoropenapi.data.OpenApiPluginData +import io.github.smiley4.ktoropenapi.config.OutputFormat +import io.github.smiley4.ktoropenapi.config.OpenApiPluginConfig +import io.ktor.http.ContentType +import io.ktor.http.HttpStatusCode +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.plugin +import io.ktor.server.response.respondText +import io.ktor.server.routing.Route +import io.ktor.server.routing.RoutingRoot +import io.ktor.server.routing.get + +private val logger = KotlinLogging.logger {} + +val OpenApi = createApplicationPlugin(name = "OpenApi", createConfiguration = ::OpenApiPluginConfig) { + OpenApiPlugin.config = pluginConfig.build( + OpenApiPluginData.DEFAULT, + application.environment.config.propertyOrNull("ktor.deployment.rootPath")?.getString() + ) + on(MonitoringEvent(ApplicationStarted)) { application -> + try { + OpenApiPlugin.generateOpenApiSpecs(application) + } catch (e: Exception) { + logger.error(e) { "Error during application startup in openapi-plugin" } + } + } +} + + +object OpenApiPlugin { + + internal var config = OpenApiPluginData.DEFAULT + + private val openApiSpecs = mutableMapOf>() + + + /** + * Generates new openapi + */ + fun generateOpenApiSpecs(application: Application) { + val routes = RouteCollector().collect({ application.plugin(RoutingRoot) }, config) + val specs = OpenApiSpecBuilder().build(config, routes) + openApiSpecs.clear() + openApiSpecs.putAll(specs) + } + + fun getOpenApiSpec(name: String): String = openApiSpecs[name]?.first + ?: throw IllegalArgumentException("No OpenAPI documentation exists with name '$name'") + + fun getOpenApiSpecFormat(name: String): OutputFormat = openApiSpecs[name]?.second + ?: throw IllegalArgumentException("No OpenAPI documentation exists with name '$name'") + +} + + +/** + * Registers the route for serving an openapi-spec. When multiple specs are configured, the name of the one to serve has to be provided. + */ +fun Route.openApi(specName: String = OpenApiPluginConfig.DEFAULT_SPEC_ID) { + route({ hidden = true }) { + get { + val contentType = when (OpenApiPlugin.getOpenApiSpecFormat(specName)) { + OutputFormat.JSON -> ContentType.Application.Json + OutputFormat.YAML -> ContentType.Text.Plain + } + call.respondText(contentType, HttpStatusCode.OK) { OpenApiPlugin.getOpenApiSpec(specName) } + } + } +} diff --git a/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/OpenApiSpecBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/OpenApiSpecBuilder.kt new file mode 100644 index 00000000..82a26faa --- /dev/null +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/OpenApiSpecBuilder.kt @@ -0,0 +1,145 @@ +package io.github.smiley4.ktoropenapi.builder + +import io.github.oshai.kotlinlogging.KotlinLogging +import io.github.smiley4.ktoropenapi.builder.example.ExampleContext +import io.github.smiley4.ktoropenapi.builder.example.ExampleContextImpl +import io.github.smiley4.ktoropenapi.builder.openapi.ComponentsBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ContactBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ContentBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ExternalDocumentationBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.HeaderBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.InfoBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.LicenseBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.OAuthFlowsBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.OpenApiBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.OperationBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.OperationTagsBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ParameterBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.PathBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.PathsBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.RequestBodyBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ResponseBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ResponsesBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.SecurityRequirementsBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.SecuritySchemesBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ServerBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.TagBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.TagExternalDocumentationBuilder +import io.github.smiley4.ktoropenapi.builder.route.RouteMeta +import io.github.smiley4.ktoropenapi.builder.schema.SchemaContext +import io.github.smiley4.ktoropenapi.builder.schema.SchemaContextImpl +import io.github.smiley4.ktoropenapi.config.OutputFormat +import io.github.smiley4.ktoropenapi.data.OpenApiPluginData +import io.swagger.v3.core.util.Json31 +import io.swagger.v3.core.util.Yaml31 + +/** + * Builds the final openapi-specs. + */ +internal class OpenApiSpecBuilder { + + private val logger = KotlinLogging.logger {} + + + /** + * Create the openapi-spec in json and yaml for the with the given routes and configuration + * @return a map of "specName" to pairs ("api-spec", "json"|"yaml") + */ + fun build(config: OpenApiPluginData, routes: List): Map> { + val routesBySpec = buildMap> { + routes.forEach { route -> + val specName = + route.documentation.specName ?: config.specAssigner(route.path, route.documentation.tags.toList()) + computeIfAbsent(specName) { mutableListOf() }.add(route) + } + } + return buildMap { + routesBySpec.forEach { (specName, routes) -> + val specConfig = config.specConfigs[specName] ?: config + this[specName] = buildOpenApiSpec(specName, specConfig, routes) + } + } + } + + private fun buildOpenApiSpec(specName: String, pluginConfig: OpenApiPluginData, routes: List): Pair { + return try { + val schemaContext = SchemaContextImpl(pluginConfig.schemaConfig).also { + it.addGlobal(pluginConfig.schemaConfig) + it.add(routes) + } + val exampleContext = ExampleContextImpl(pluginConfig.exampleConfig.exampleEncoder).also { + it.addShared(pluginConfig.exampleConfig) + it.add(routes) + } + val openApi = builder(pluginConfig, schemaContext, exampleContext).build(routes) + pluginConfig.postBuild?.let { it(openApi, specName) } + when (pluginConfig.outputFormat) { + OutputFormat.JSON -> Json31.pretty(openApi) to pluginConfig.outputFormat + OutputFormat.YAML -> Yaml31.pretty(openApi) to pluginConfig.outputFormat + } + } catch (e: Exception) { + logger.error(e) { "Error during openapi-spec generation" } + return pluginConfig.outputFormat.empty to pluginConfig.outputFormat + } + } + + private fun builder( + config: OpenApiPluginData, + schemaContext: SchemaContext, + exampleContext: ExampleContext, + ): OpenApiBuilder { + return OpenApiBuilder( + config = config, + schemaContext = schemaContext, + exampleContext = exampleContext, + infoBuilder = InfoBuilder( + contactBuilder = ContactBuilder(), + licenseBuilder = LicenseBuilder() + ), + externalDocumentationBuilder = ExternalDocumentationBuilder(), + serverBuilder = ServerBuilder(), + tagBuilder = TagBuilder( + tagExternalDocumentationBuilder = TagExternalDocumentationBuilder() + ), + pathsBuilder = PathsBuilder( + config = config, + pathBuilder = PathBuilder( + operationBuilder = OperationBuilder( + operationTagsBuilder = OperationTagsBuilder(config), + parameterBuilder = ParameterBuilder( + schemaContext = schemaContext, + exampleContext = exampleContext, + ), + requestBodyBuilder = RequestBodyBuilder( + contentBuilder = ContentBuilder( + schemaContext = schemaContext, + exampleContext = exampleContext, + headerBuilder = HeaderBuilder(schemaContext) + ) + ), + responsesBuilder = ResponsesBuilder( + responseBuilder = ResponseBuilder( + headerBuilder = HeaderBuilder(schemaContext), + contentBuilder = ContentBuilder( + schemaContext = schemaContext, + exampleContext = exampleContext, + headerBuilder = HeaderBuilder(schemaContext) + ) + ), + config = config + ), + securityRequirementsBuilder = SecurityRequirementsBuilder(config), + externalDocumentationBuilder = ExternalDocumentationBuilder(), + serverBuilder = ServerBuilder() + ) + ) + ), + componentsBuilder = ComponentsBuilder( + config = config, + securitySchemesBuilder = SecuritySchemesBuilder( + oAuthFlowsBuilder = OAuthFlowsBuilder() + ) + ) + ) + } +} diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/example/ExampleContext.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/example/ExampleContext.kt similarity index 72% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/example/ExampleContext.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/example/ExampleContext.kt index 216b98cf..a9125b5a 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/example/ExampleContext.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/example/ExampleContext.kt @@ -1,12 +1,12 @@ -package io.github.smiley4.ktorswaggerui.builder.example +package io.github.smiley4.ktoropenapi.builder.example -import io.github.smiley4.ktorswaggerui.data.ExampleDescriptor +import io.github.smiley4.ktoropenapi.config.ExampleDescriptor import io.swagger.v3.oas.models.examples.Example /** * Provides examples for an openapi-spec */ -interface ExampleContext { +internal interface ExampleContext { /** * Get an [Example] (or a ref to an example) by its [ExampleDescriptor]. diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/example/ExampleContextImpl.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/example/ExampleContextImpl.kt similarity index 82% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/example/ExampleContextImpl.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/example/ExampleContextImpl.kt index a4a764c6..c18ed643 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/example/ExampleContextImpl.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/example/ExampleContextImpl.kt @@ -1,13 +1,19 @@ -package io.github.smiley4.ktorswaggerui.builder.example - -import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta -import io.github.smiley4.ktorswaggerui.data.* +package io.github.smiley4.ktoropenapi.builder.example + +import io.github.smiley4.ktoropenapi.builder.route.RouteMeta +import io.github.smiley4.ktoropenapi.config.ExampleDescriptor +import io.github.smiley4.ktoropenapi.config.ExampleEncoder +import io.github.smiley4.ktoropenapi.config.RefExampleDescriptor +import io.github.smiley4.ktoropenapi.config.SwaggerExampleDescriptor +import io.github.smiley4.ktoropenapi.config.TypeDescriptor +import io.github.smiley4.ktoropenapi.config.ValueExampleDescriptor +import io.github.smiley4.ktoropenapi.data.* import io.swagger.v3.oas.models.examples.Example /** * Implementation of an [ExampleContext]. */ -class ExampleContextImpl(private val encoder: ExampleEncoder?) : ExampleContext { +internal class ExampleContextImpl(private val encoder: ExampleEncoder?) : ExampleContext { private val rootExamples = mutableMapOf() private val componentExamples = mutableMapOf() @@ -54,14 +60,14 @@ class ExampleContextImpl(private val encoder: ExampleEncoder?) : ExampleContext parameter.example?.also { add(it to parameter.type) } } request.body?.also { body -> - if (body is OpenApiSimpleBodyData) { + if (body is SimpleBodyData) { addAll(body.examples.map { it to body.type }) } } } route.documentation.responses.forEach { response -> response.body?.also { body -> - if (body is OpenApiSimpleBodyData) { + if (body is SimpleBodyData) { addAll(body.examples.map { it to body.type }) } } diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ComponentsBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ComponentsBuilder.kt similarity index 81% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ComponentsBuilder.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ComponentsBuilder.kt index c39dc3bd..aedde910 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ComponentsBuilder.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ComponentsBuilder.kt @@ -1,6 +1,6 @@ -package io.github.smiley4.ktorswaggerui.builder.openapi +package io.github.smiley4.ktoropenapi.builder.openapi -import io.github.smiley4.ktorswaggerui.data.PluginConfigData +import io.github.smiley4.ktoropenapi.data.OpenApiPluginData import io.swagger.v3.oas.models.Components import io.swagger.v3.oas.models.examples.Example import io.swagger.v3.oas.models.media.Schema @@ -9,8 +9,8 @@ import io.swagger.v3.oas.models.media.Schema * Builds the openapi [Components]-object containing shared reusable schemas and examples. * See [OpenAPI Specification - Components Object](https://swagger.io/specification/#components-object). */ -class ComponentsBuilder( - private val config: PluginConfigData, +internal class ComponentsBuilder( + private val config: OpenApiPluginData, private val securitySchemesBuilder: SecuritySchemesBuilder ) { diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ContactBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ContactBuilder.kt similarity index 75% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ContactBuilder.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ContactBuilder.kt index b09e227b..efe54bf7 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ContactBuilder.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ContactBuilder.kt @@ -1,13 +1,13 @@ -package io.github.smiley4.ktorswaggerui.builder.openapi +package io.github.smiley4.ktoropenapi.builder.openapi -import io.github.smiley4.ktorswaggerui.data.ContactData +import io.github.smiley4.ktoropenapi.data.ContactData import io.swagger.v3.oas.models.info.Contact /** * Builds the openapi [Contact]-object. Holds Contact information for the exposed API. * See [OpenAPI Specification - Contact Object](https://swagger.io/specification/#contact-object). */ -class ContactBuilder { +internal class ContactBuilder { fun build(contact: ContactData): Contact = Contact().also { diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ContentBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ContentBuilder.kt similarity index 73% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ContentBuilder.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ContentBuilder.kt index fc2e213e..07face68 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ContentBuilder.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ContentBuilder.kt @@ -1,10 +1,10 @@ -package io.github.smiley4.ktorswaggerui.builder.openapi +package io.github.smiley4.ktoropenapi.builder.openapi -import io.github.smiley4.ktorswaggerui.builder.example.ExampleContext -import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContext -import io.github.smiley4.ktorswaggerui.data.OpenApiBaseBodyData -import io.github.smiley4.ktorswaggerui.data.OpenApiMultipartBodyData -import io.github.smiley4.ktorswaggerui.data.OpenApiSimpleBodyData +import io.github.smiley4.ktoropenapi.builder.example.ExampleContext +import io.github.smiley4.ktoropenapi.builder.schema.SchemaContext +import io.github.smiley4.ktoropenapi.data.BaseBodyData +import io.github.smiley4.ktoropenapi.data.MultipartBodyData +import io.github.smiley4.ktoropenapi.data.SimpleBodyData import io.ktor.http.* import io.swagger.v3.oas.models.media.Content import io.swagger.v3.oas.models.media.Encoding @@ -17,26 +17,26 @@ import kotlin.collections.set * See [OpenAPI Specification - Request Body Object](https://swagger.io/specification/#request-body-object) * and [OpenAPI Specification - Response Object](https://swagger.io/specification/#response-object). */ -class ContentBuilder( +internal class ContentBuilder( private val schemaContext: SchemaContext, private val exampleContext: ExampleContext, private val headerBuilder: HeaderBuilder ) { - fun build(body: OpenApiBaseBodyData): Content = + fun build(body: BaseBodyData): Content = when (body) { - is OpenApiSimpleBodyData -> buildSimpleBody(body) - is OpenApiMultipartBodyData -> buildMultipartBody(body) + is SimpleBodyData -> buildSimpleBody(body) + is MultipartBodyData -> buildMultipartBody(body) } - private fun buildSimpleBody(body: OpenApiSimpleBodyData): Content = + private fun buildSimpleBody(body: SimpleBodyData): Content = Content().also { content -> buildSimpleMediaTypes(body, schemaContext.getSchema(body.type)).forEach { (contentType, mediaType) -> content.addMediaType(contentType.toString(), mediaType) } } - private fun buildMultipartBody(body: OpenApiMultipartBodyData): Content { + private fun buildMultipartBody(body: MultipartBodyData): Content { return Content().also { content -> buildMultipartMediaTypes(body).forEach { (contentType, mediaType) -> content.addMediaType(contentType.toString(), mediaType) @@ -44,12 +44,12 @@ class ContentBuilder( } } - private fun buildSimpleMediaTypes(body: OpenApiSimpleBodyData, schema: Schema<*>?): Map { + private fun buildSimpleMediaTypes(body: SimpleBodyData, schema: Schema<*>?): Map { val mediaTypes = body.mediaTypes.ifEmpty { schema?.let { setOf(chooseMediaType(schema)) } ?: setOf() } return mediaTypes.associateWith { buildSimpleMediaType(schema, body) } } - private fun buildSimpleMediaType(schema: Schema<*>?, body: OpenApiSimpleBodyData): MediaType { + private fun buildSimpleMediaType(schema: Schema<*>?, body: SimpleBodyData): MediaType { return MediaType().also { it.schema = schema body.examples.forEach { descriptor -> @@ -58,12 +58,12 @@ class ContentBuilder( } } - private fun buildMultipartMediaTypes(body: OpenApiMultipartBodyData): Map { + private fun buildMultipartMediaTypes(body: MultipartBodyData): Map { val mediaTypes = body.mediaTypes.ifEmpty { setOf(ContentType.MultiPart.FormData) } return mediaTypes.associateWith { buildMultipartMediaType(body) } } - private fun buildMultipartMediaType(body: OpenApiMultipartBodyData): MediaType { + private fun buildMultipartMediaType(body: MultipartBodyData): MediaType { return MediaType().also { mediaType -> mediaType.schema = Schema().also { schema -> schema.type = "object" @@ -77,7 +77,7 @@ class ContentBuilder( } } - private fun buildMultipartEncoding(body: OpenApiMultipartBodyData): MutableMap? { + private fun buildMultipartEncoding(body: MultipartBodyData): MutableMap? { return if (body.parts.flatMap { it.mediaTypes }.isEmpty()) { null } else { diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ExternalDocumentationBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ExternalDocumentationBuilder.kt similarity index 76% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ExternalDocumentationBuilder.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ExternalDocumentationBuilder.kt index 437dd499..28311873 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ExternalDocumentationBuilder.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ExternalDocumentationBuilder.kt @@ -1,13 +1,13 @@ -package io.github.smiley4.ktorswaggerui.builder.openapi +package io.github.smiley4.ktoropenapi.builder.openapi -import io.github.smiley4.ktorswaggerui.data.ExternalDocsData +import io.github.smiley4.ktoropenapi.data.ExternalDocsData import io.swagger.v3.oas.models.ExternalDocumentation /** * Build the openapi [ExternalDocumentation]-object. Allows referencing an external resource for extended documentation. * See [OpenAPI Specification - External Documentation Object](https://swagger.io/specification/#external-documentation-object). */ -class ExternalDocumentationBuilder { +internal class ExternalDocumentationBuilder { fun build(externalDocs: ExternalDocsData): ExternalDocumentation = ExternalDocumentation().also { diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/HeaderBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/HeaderBuilder.kt similarity index 64% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/HeaderBuilder.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/HeaderBuilder.kt index 0ecece33..1f509b4d 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/HeaderBuilder.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/HeaderBuilder.kt @@ -1,19 +1,18 @@ -package io.github.smiley4.ktorswaggerui.builder.openapi +package io.github.smiley4.ktoropenapi.builder.openapi -import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContext -import io.github.smiley4.ktorswaggerui.data.OpenApiHeaderData -import io.swagger.v3.oas.models.ExternalDocumentation +import io.github.smiley4.ktoropenapi.builder.schema.SchemaContext +import io.github.smiley4.ktoropenapi.data.HeaderData import io.swagger.v3.oas.models.headers.Header /** * Build the openapi [Header]-object. * See [OpenAPI Specification - Header Object](https://swagger.io/specification/#header-object). */ -class HeaderBuilder( +internal class HeaderBuilder( private val schemaContext: SchemaContext ) { - fun build(header: OpenApiHeaderData): Header = + fun build(header: HeaderData): Header = Header().also { it.description = header.description it.required = header.required diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/InfoBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/InfoBuilder.kt similarity index 86% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/InfoBuilder.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/InfoBuilder.kt index e620be47..7b7f3a43 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/InfoBuilder.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/InfoBuilder.kt @@ -1,13 +1,13 @@ -package io.github.smiley4.ktorswaggerui.builder.openapi +package io.github.smiley4.ktoropenapi.builder.openapi -import io.github.smiley4.ktorswaggerui.data.InfoData +import io.github.smiley4.ktoropenapi.data.InfoData import io.swagger.v3.oas.models.info.Info /** * Build the openapi [Info]-object. Holds metadata about the API. * See [OpenAPI Specification - Info Object](https://swagger.io/specification/#info-object). */ -class InfoBuilder( +internal class InfoBuilder( private val contactBuilder: ContactBuilder, private val licenseBuilder: LicenseBuilder ) { diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/LicenseBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/LicenseBuilder.kt similarity index 75% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/LicenseBuilder.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/LicenseBuilder.kt index a6d5e1f4..04e21e8d 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/LicenseBuilder.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/LicenseBuilder.kt @@ -1,13 +1,13 @@ -package io.github.smiley4.ktorswaggerui.builder.openapi +package io.github.smiley4.ktoropenapi.builder.openapi -import io.github.smiley4.ktorswaggerui.data.LicenseData +import io.github.smiley4.ktoropenapi.data.LicenseData import io.swagger.v3.oas.models.info.License /** * Build the openapi [License]-object. Holds license information for the exposed API. * See [OpenAPI Specification - License Object](https://swagger.io/specification/#license-object). */ -class LicenseBuilder { +internal class LicenseBuilder { fun build(license: LicenseData): License = License().also { diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OAuthFlowsBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/OAuthFlowsBuilder.kt similarity index 85% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OAuthFlowsBuilder.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/OAuthFlowsBuilder.kt index 44692017..97da428c 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OAuthFlowsBuilder.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/OAuthFlowsBuilder.kt @@ -1,7 +1,7 @@ -package io.github.smiley4.ktorswaggerui.builder.openapi +package io.github.smiley4.ktoropenapi.builder.openapi -import io.github.smiley4.ktorswaggerui.data.OpenIdOAuthFlowData -import io.github.smiley4.ktorswaggerui.data.OpenIdOAuthFlowsData +import io.github.smiley4.ktoropenapi.data.OpenIdOAuthFlowData +import io.github.smiley4.ktoropenapi.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 @@ -10,7 +10,7 @@ import io.swagger.v3.oas.models.security.Scopes * Build the openapi [OAuthFlows]-object. Holds configuration of the supported OAuth Flows. * See [OpenAPI Specification - OAuth Flows Object](https://swagger.io/specification/#oauth-flows-object). */ -class OAuthFlowsBuilder { +internal class OAuthFlowsBuilder { fun build(flows: OpenIdOAuthFlowsData): OAuthFlows { return OAuthFlows().apply { diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OpenApiBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/OpenApiBuilder.kt similarity index 77% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OpenApiBuilder.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/OpenApiBuilder.kt index 359217a5..22cbd779 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OpenApiBuilder.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/OpenApiBuilder.kt @@ -1,9 +1,9 @@ -package io.github.smiley4.ktorswaggerui.builder.openapi +package io.github.smiley4.ktoropenapi.builder.openapi -import io.github.smiley4.ktorswaggerui.builder.example.ExampleContext -import io.github.smiley4.ktorswaggerui.data.PluginConfigData -import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta -import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContext +import io.github.smiley4.ktoropenapi.builder.example.ExampleContext +import io.github.smiley4.ktoropenapi.data.OpenApiPluginData +import io.github.smiley4.ktoropenapi.builder.route.RouteMeta +import io.github.smiley4.ktoropenapi.builder.schema.SchemaContext import io.swagger.v3.oas.models.OpenAPI import io.swagger.v3.oas.models.SpecVersion @@ -11,8 +11,8 @@ import io.swagger.v3.oas.models.SpecVersion * Build the openapi [OpenAPI]-object. Is the root of the openapi document. * See [OpenAPI Specification - OpenAPI Object](https://swagger.io/specification/#openapi-object). */ -class OpenApiBuilder( - private val config: PluginConfigData, +internal class OpenApiBuilder( + private val config: OpenApiPluginData, private val schemaContext: SchemaContext, private val exampleContext: ExampleContext, private val infoBuilder: InfoBuilder, diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OperationBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/OperationBuilder.kt similarity index 93% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OperationBuilder.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/OperationBuilder.kt index d793cb79..717259b6 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OperationBuilder.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/OperationBuilder.kt @@ -1,13 +1,13 @@ -package io.github.smiley4.ktorswaggerui.builder.openapi +package io.github.smiley4.ktoropenapi.builder.openapi -import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta +import io.github.smiley4.ktoropenapi.builder.route.RouteMeta import io.swagger.v3.oas.models.Operation /** * Build the openapi [Operation]-object. Holds information describing a single API operation on a path. * See [OpenAPI Specification - Operation Object](https://swagger.io/specification/#operation-object). */ -class OperationBuilder( +internal class OperationBuilder( private val operationTagsBuilder: OperationTagsBuilder, private val parameterBuilder: ParameterBuilder, private val requestBodyBuilder: RequestBodyBuilder, diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OperationTagsBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/OperationTagsBuilder.kt similarity index 66% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OperationTagsBuilder.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/OperationTagsBuilder.kt index 3e8f7bfc..f4d238c1 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/OperationTagsBuilder.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/OperationTagsBuilder.kt @@ -1,13 +1,13 @@ -package io.github.smiley4.ktorswaggerui.builder.openapi +package io.github.smiley4.ktoropenapi.builder.openapi -import io.github.smiley4.ktorswaggerui.data.PluginConfigData -import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta +import io.github.smiley4.ktoropenapi.data.OpenApiPluginData +import io.github.smiley4.ktoropenapi.builder.route.RouteMeta /** * Builds the list of tags for a single route. */ -class OperationTagsBuilder( - private val config: PluginConfigData +internal class OperationTagsBuilder( + private val config: OpenApiPluginData ) { fun build(route: RouteMeta): List { diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ParameterBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ParameterBuilder.kt similarity index 74% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ParameterBuilder.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ParameterBuilder.kt index 62c1adf1..7281d14f 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ParameterBuilder.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ParameterBuilder.kt @@ -1,21 +1,21 @@ -package io.github.smiley4.ktorswaggerui.builder.openapi +package io.github.smiley4.ktoropenapi.builder.openapi -import io.github.smiley4.ktorswaggerui.builder.example.ExampleContext -import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContext -import io.github.smiley4.ktorswaggerui.data.OpenApiRequestParameterData -import io.github.smiley4.ktorswaggerui.data.ParameterLocation +import io.github.smiley4.ktoropenapi.builder.example.ExampleContext +import io.github.smiley4.ktoropenapi.builder.schema.SchemaContext +import io.github.smiley4.ktoropenapi.data.RequestParameterData +import io.github.smiley4.ktoropenapi.config.ParameterLocation import io.swagger.v3.oas.models.parameters.Parameter /** * Build the openapi [Parameter]-object. Holds information describing a single operation (query, path or header) parameter. * See [OpenAPI Specification - Parameter Object](https://swagger.io/specification/#parameter-object). */ -class ParameterBuilder( +internal class ParameterBuilder( private val schemaContext: SchemaContext, private val exampleContext: ExampleContext ) { - fun build(parameter: OpenApiRequestParameterData): Parameter = + fun build(parameter: RequestParameterData): Parameter = Parameter().also { it.`in` = when (parameter.location) { ParameterLocation.QUERY -> "query" diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/PathBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/PathBuilder.kt similarity index 86% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/PathBuilder.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/PathBuilder.kt index a0328e50..3d8ff38b 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/PathBuilder.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/PathBuilder.kt @@ -1,6 +1,6 @@ -package io.github.smiley4.ktorswaggerui.builder.openapi +package io.github.smiley4.ktoropenapi.builder.openapi -import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta +import io.github.smiley4.ktoropenapi.builder.route.RouteMeta import io.ktor.http.HttpMethod import io.swagger.v3.oas.models.PathItem @@ -8,7 +8,7 @@ import io.swagger.v3.oas.models.PathItem * Build the openapi [PathItem]-object. Holds information describing the operations available on a single path. * See [OpenAPI Specification - Path Item Object](https://swagger.io/specification/#path-item-object). */ -class PathBuilder( +internal class PathBuilder( private val operationBuilder: OperationBuilder ) { diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/PathsBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/PathsBuilder.kt similarity index 65% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/PathsBuilder.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/PathsBuilder.kt index f34ad9fc..407dbd0f 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/PathsBuilder.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/PathsBuilder.kt @@ -1,6 +1,7 @@ -package io.github.smiley4.ktorswaggerui.builder.openapi +package io.github.smiley4.ktoropenapi.builder.openapi -import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta +import io.github.smiley4.ktoropenapi.builder.route.RouteMeta +import io.github.smiley4.ktoropenapi.data.OpenApiPluginData import io.swagger.v3.oas.models.PathItem import io.swagger.v3.oas.models.Paths @@ -8,24 +9,30 @@ import io.swagger.v3.oas.models.Paths * Build the openapi [Paths]-object. Holds the relative paths to the individual endpoints and their operations. * See [OpenAPI Specification - Paths Object](https://swagger.io/specification/#paths-object). */ -class PathsBuilder( +internal class PathsBuilder( + private val config: OpenApiPluginData, private val pathBuilder: PathBuilder ) { fun build(routes: Collection): Paths = Paths().also { routes.forEach { route -> - val existingPath = it[route.path] + val url = createUrl(route) + val existingPath = it[url] if (existingPath != null) { addToExistingPath(existingPath, route) } else { - addAsNewPath(it, route) + addAsNewPath(url, it, route) } } } - private fun addAsNewPath(paths: Paths, route: RouteMeta) { - paths.addPathItem(route.path, pathBuilder.build(route)) + private fun createUrl(route: RouteMeta): String { + return "${config.rootPath ?: ""}${route.path}" + } + + private fun addAsNewPath(url: String, paths: Paths, route: RouteMeta) { + paths.addPathItem(url, pathBuilder.build(route)) } private fun addToExistingPath(existing: PathItem, route: RouteMeta) { diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/RequestBodyBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/RequestBodyBuilder.kt similarity index 71% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/RequestBodyBuilder.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/RequestBodyBuilder.kt index 9e5c82fa..b3d33462 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/RequestBodyBuilder.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/RequestBodyBuilder.kt @@ -1,17 +1,17 @@ -package io.github.smiley4.ktorswaggerui.builder.openapi +package io.github.smiley4.ktoropenapi.builder.openapi -import io.github.smiley4.ktorswaggerui.data.OpenApiBaseBodyData +import io.github.smiley4.ktoropenapi.data.BaseBodyData import io.swagger.v3.oas.models.parameters.RequestBody /** * Build the openapi [RequestBody]-object. Holds information describing a single request body. * See [OpenAPI Specification - Request Body Object](https://swagger.io/specification/#request-body-object). */ -class RequestBodyBuilder( +internal class RequestBodyBuilder( private val contentBuilder: ContentBuilder ) { - fun build(body: OpenApiBaseBodyData): RequestBody = + fun build(body: BaseBodyData): RequestBody = RequestBody().also { it.description = body.description it.required = body.required diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ResponseBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ResponseBuilder.kt similarity index 77% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ResponseBuilder.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ResponseBuilder.kt index 27512d4e..a379e70c 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ResponseBuilder.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ResponseBuilder.kt @@ -1,18 +1,18 @@ -package io.github.smiley4.ktorswaggerui.builder.openapi +package io.github.smiley4.ktoropenapi.builder.openapi -import io.github.smiley4.ktorswaggerui.data.OpenApiResponseData +import io.github.smiley4.ktoropenapi.data.ResponseData import io.swagger.v3.oas.models.responses.ApiResponse /** * Build the openapi [ApiResponse]-objects by status-code. Holds information describing status-codes and responses from an API Operation. * See [OpenAPI Specification - Response Object](https://swagger.io/specification/#response-object). */ -class ResponseBuilder( +internal class ResponseBuilder( private val headerBuilder: HeaderBuilder, private val contentBuilder: ContentBuilder ) { - fun build(response: OpenApiResponseData): Pair = + fun build(response: ResponseData): Pair = response.statusCode to ApiResponse().also { it.description = response.description it.headers = response.headers.mapValues { header -> headerBuilder.build(header.value) } diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ResponsesBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ResponsesBuilder.kt similarity index 71% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ResponsesBuilder.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ResponsesBuilder.kt index 957daccf..785262e0 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ResponsesBuilder.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ResponsesBuilder.kt @@ -1,7 +1,7 @@ -package io.github.smiley4.ktorswaggerui.builder.openapi +package io.github.smiley4.ktoropenapi.builder.openapi -import io.github.smiley4.ktorswaggerui.data.OpenApiResponseData -import io.github.smiley4.ktorswaggerui.data.PluginConfigData +import io.github.smiley4.ktoropenapi.data.ResponseData +import io.github.smiley4.ktoropenapi.data.OpenApiPluginData import io.ktor.http.HttpStatusCode import io.swagger.v3.oas.models.responses.ApiResponses @@ -9,12 +9,12 @@ import io.swagger.v3.oas.models.responses.ApiResponses * Build the openapi [ApiResponses]-object. A container for the expected responses of an operation. * See [OpenAPI Specification - Responses Object](https://swagger.io/specification/#responses-object). */ -class ResponsesBuilder( +internal class ResponsesBuilder( private val responseBuilder: ResponseBuilder, - private val config: PluginConfigData + private val config: OpenApiPluginData ) { - fun build(responses: List, isProtected: Boolean): ApiResponses = + fun build(responses: List, isProtected: Boolean): ApiResponses = ApiResponses().also { responses .map { response -> responseBuilder.build(response) } @@ -26,8 +26,8 @@ class ResponsesBuilder( } } - private fun shouldAddUnauthorized(responses: List, isProtected: Boolean): Boolean { - val unauthorizedCode = HttpStatusCode.Unauthorized.value.toString(); + private fun shouldAddUnauthorized(responses: List, isProtected: Boolean): Boolean { + val unauthorizedCode = HttpStatusCode.Unauthorized.value.toString() return config.securityConfig.defaultUnauthorizedResponse != null && isProtected && responses.count { it.statusCode == unauthorizedCode } == 0 diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/SecurityRequirementsBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/SecurityRequirementsBuilder.kt similarity index 72% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/SecurityRequirementsBuilder.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/SecurityRequirementsBuilder.kt index 52d3ec27..12cf2bff 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/SecurityRequirementsBuilder.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/SecurityRequirementsBuilder.kt @@ -1,15 +1,15 @@ -package io.github.smiley4.ktorswaggerui.builder.openapi +package io.github.smiley4.ktoropenapi.builder.openapi -import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta -import io.github.smiley4.ktorswaggerui.data.PluginConfigData +import io.github.smiley4.ktoropenapi.builder.route.RouteMeta +import io.github.smiley4.ktoropenapi.data.OpenApiPluginData import io.swagger.v3.oas.models.security.SecurityRequirement /** * Build the openapi [SecurityRequirement]-objects. * See [OpenAPI Specification - Security Requirement Object](https://swagger.io/specification/#security-requirement-object). */ -class SecurityRequirementsBuilder( - private val config: PluginConfigData +internal class SecurityRequirementsBuilder( + private val config: OpenApiPluginData ) { fun build(route: RouteMeta): List { diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/SecuritySchemesBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/SecuritySchemesBuilder.kt similarity index 86% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/SecuritySchemesBuilder.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/SecuritySchemesBuilder.kt index 1a354e85..56354c99 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/SecuritySchemesBuilder.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/SecuritySchemesBuilder.kt @@ -1,14 +1,14 @@ -package io.github.smiley4.ktorswaggerui.builder.openapi +package io.github.smiley4.ktoropenapi.builder.openapi -import io.github.smiley4.ktorswaggerui.data.AuthType -import io.github.smiley4.ktorswaggerui.data.SecuritySchemeData +import io.github.smiley4.ktoropenapi.config.AuthType +import io.github.smiley4.ktoropenapi.data.SecuritySchemeData import io.swagger.v3.oas.models.security.SecurityScheme /** * Build the openapi [SecurityScheme]-objects with their names. Holds information defining security schemes that can be used by operations. * See [OpenAPI Specification - Security Scheme Object](https://swagger.io/specification/#security-scheme-object). */ -class SecuritySchemesBuilder( +internal class SecuritySchemesBuilder( private val oAuthFlowsBuilder: OAuthFlowsBuilder ) { diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ServerBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ServerBuilder.kt similarity index 88% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ServerBuilder.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ServerBuilder.kt index 09d41fe0..f897957f 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/ServerBuilder.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ServerBuilder.kt @@ -1,6 +1,6 @@ -package io.github.smiley4.ktorswaggerui.builder.openapi +package io.github.smiley4.ktoropenapi.builder.openapi -import io.github.smiley4.ktorswaggerui.data.ServerData +import io.github.smiley4.ktoropenapi.data.ServerData import io.swagger.v3.oas.models.servers.Server import io.swagger.v3.oas.models.servers.ServerVariable import io.swagger.v3.oas.models.servers.ServerVariables @@ -9,7 +9,7 @@ import io.swagger.v3.oas.models.servers.ServerVariables * Build the openapi [Server]-object. Holds information representing a Server. * See [OpenAPI Specification - Server Object](https://swagger.io/specification/#server-object). */ -class ServerBuilder { +internal class ServerBuilder { fun build(server: ServerData): Server = Server().also { diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/TagBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/TagBuilder.kt similarity index 83% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/TagBuilder.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/TagBuilder.kt index c1d9ed48..69571cb1 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/TagBuilder.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/TagBuilder.kt @@ -1,13 +1,13 @@ -package io.github.smiley4.ktorswaggerui.builder.openapi +package io.github.smiley4.ktoropenapi.builder.openapi -import io.github.smiley4.ktorswaggerui.data.TagData +import io.github.smiley4.ktoropenapi.data.TagData import io.swagger.v3.oas.models.tags.Tag /** * Build the openapi [Tag]-object. Holds metadata of a single tag. * See [OpenAPI Specification - Tag Object](https://swagger.io/specification/#tag-object). */ -class TagBuilder( +internal class TagBuilder( private val tagExternalDocumentationBuilder: TagExternalDocumentationBuilder ) { diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/TagExternalDocumentationBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/TagExternalDocumentationBuilder.kt similarity index 81% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/TagExternalDocumentationBuilder.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/TagExternalDocumentationBuilder.kt index a3883490..5ca1291d 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/openapi/TagExternalDocumentationBuilder.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/TagExternalDocumentationBuilder.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.builder.openapi +package io.github.smiley4.ktoropenapi.builder.openapi import io.swagger.v3.oas.models.ExternalDocumentation @@ -6,7 +6,7 @@ import io.swagger.v3.oas.models.ExternalDocumentation * Build the openapi [ExternalDocumentation]-object for a tag. * See [OpenAPI Specification - External Documentation Object](https://swagger.io/specification/#external-documentation-object). */ -class TagExternalDocumentationBuilder { +internal class TagExternalDocumentationBuilder { fun build(url: String, description: String): ExternalDocumentation = ExternalDocumentation().also { diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/route/RouteCollector.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/route/RouteCollector.kt similarity index 81% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/route/RouteCollector.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/route/RouteCollector.kt index ee11cfa0..bb708888 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/route/RouteCollector.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/route/RouteCollector.kt @@ -1,8 +1,8 @@ -package io.github.smiley4.ktorswaggerui.builder.route +package io.github.smiley4.ktoropenapi.builder.route -import io.github.smiley4.ktorswaggerui.data.PluginConfigData -import io.github.smiley4.ktorswaggerui.dsl.routing.DocumentedRouteSelector -import io.github.smiley4.ktorswaggerui.dsl.routes.OpenApiRoute +import io.github.smiley4.ktoropenapi.data.OpenApiPluginData +import io.github.smiley4.ktoropenapi.config.RouteConfig +import io.github.smiley4.ktoropenapi.DocumentedRouteSelector import io.ktor.http.HttpMethod import io.ktor.server.auth.AuthenticationRouteSelector import io.ktor.server.routing.ConstantParameterRouteSelector @@ -16,20 +16,25 @@ import io.ktor.server.routing.TrailingSlashRouteSelector import kotlin.reflect.full.isSubclassOf /** - * Collect all routes from the given application + * Collect all routes of the given application. */ -class RouteCollector( - private val routeDocumentationMerger: RouteDocumentationMerger -) { +internal class RouteCollector { + + private val routeDocumentationMerger = RouteDocumentationMerger() + + private val hiddenRouteMarkers = setOf( + "io.github.smiley4.ktorswaggerui.SwaggerUIRouteSelector" + ) + /** * Collect all routes from the given application */ - fun collectRoutes(routeProvider: () -> RoutingNode, config: PluginConfigData): Sequence { + fun collect(routeProvider: () -> RoutingNode, config: OpenApiPluginData): List { return allRoutes(routeProvider()) .asSequence() .map { route -> - val documentation = getDocumentation(route, OpenApiRoute()) + val documentation = getDocumentation(route, RouteConfig()) RouteMeta( method = getMethod(route), path = getPath(route, config), @@ -38,11 +43,13 @@ class RouteCollector( ) } .filter { !it.documentation.hidden } + .filter { path -> hiddenRouteMarkers.none { path.path.contains(it) } } .filter { path -> config.pathFilter(path.method, path.path.split("/").filter { it.isNotEmpty() }) } + .toList() } - private fun getDocumentation(route: RoutingNode, base: OpenApiRoute): OpenApiRoute { + private fun getDocumentation(route: RoutingNode, base: RouteConfig): RouteConfig { var documentation = base if (route.selector is DocumentedRouteSelector) { documentation = routeDocumentationMerger.merge(documentation, (route.selector as DocumentedRouteSelector).documentation) @@ -61,7 +68,7 @@ class RouteCollector( @Suppress("CyclomaticComplexMethod") - private fun getPath(route: RoutingNode, config: PluginConfigData): String { + private fun getPath(route: RoutingNode, config: OpenApiPluginData): String { val selector = route.selector return if (isIgnoredSelector(selector, config)) { route.parent?.let { getPath(it, config) } ?: "" @@ -81,7 +88,7 @@ class RouteCollector( } - private fun isIgnoredSelector(selector: RouteSelector, config: PluginConfigData): Boolean { + private fun isIgnoredSelector(selector: RouteSelector, config: OpenApiPluginData): Boolean { return when (selector) { is TrailingSlashRouteSelector -> false is RootRouteSelector -> false diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/route/RouteDocumentationMerger.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/route/RouteDocumentationMerger.kt similarity index 75% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/route/RouteDocumentationMerger.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/route/RouteDocumentationMerger.kt index 59818584..33d73585 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/route/RouteDocumentationMerger.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/route/RouteDocumentationMerger.kt @@ -1,15 +1,15 @@ -package io.github.smiley4.ktorswaggerui.builder.route +package io.github.smiley4.ktoropenapi.builder.route -import io.github.smiley4.ktorswaggerui.dsl.routes.OpenApiRoute +import io.github.smiley4.ktoropenapi.config.RouteConfig -class RouteDocumentationMerger { +internal class RouteDocumentationMerger { /** - * Merges "a" with "b" and returns the result as a new [OpenApiRoute]. "a" has priority over "b". + * Merges "a" with "b" and returns the result as a new [RouteConfig]. "a" has priority over "b". */ - fun merge(a: OpenApiRoute, b: OpenApiRoute): OpenApiRoute { - return OpenApiRoute().apply { - specId = a.specId ?: b.specId + fun merge(a: RouteConfig, b: RouteConfig): RouteConfig { + return RouteConfig().apply { + specName = a.specName ?: b.specName tags = mutableListOf().also { it.addAll(a.tags) it.addAll(b.tags) diff --git a/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/route/RouteMeta.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/route/RouteMeta.kt new file mode 100644 index 00000000..3bdc7190 --- /dev/null +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/route/RouteMeta.kt @@ -0,0 +1,14 @@ +package io.github.smiley4.ktoropenapi.builder.route + +import io.github.smiley4.ktoropenapi.data.RouteData +import io.ktor.http.HttpMethod + +/** + * Information about a route + */ +internal data class RouteMeta( + val path: String, + val method: HttpMethod, + val documentation: RouteData, + val protected: Boolean +) diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaContext.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/schema/SchemaContext.kt similarity index 72% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaContext.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/schema/SchemaContext.kt index 3af0259a..82588ff0 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaContext.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/schema/SchemaContext.kt @@ -1,12 +1,12 @@ -package io.github.smiley4.ktorswaggerui.builder.schema +package io.github.smiley4.ktoropenapi.builder.schema -import io.github.smiley4.ktorswaggerui.data.TypeDescriptor +import io.github.smiley4.ktoropenapi.config.TypeDescriptor import io.swagger.v3.oas.models.media.Schema /** * Provides schemas for an openapi-spec */ -interface SchemaContext { +internal interface SchemaContext { /** * Get a [Schema] (or a ref to a schema) by its [TypeDescriptor] diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaContextImpl.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/schema/SchemaContextImpl.kt similarity index 86% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaContextImpl.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/schema/SchemaContextImpl.kt index e1cfac5c..7e53cf67 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/schema/SchemaContextImpl.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/schema/SchemaContextImpl.kt @@ -1,14 +1,21 @@ -package io.github.smiley4.ktorswaggerui.builder.schema +package io.github.smiley4.ktoropenapi.builder.schema -import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta -import io.github.smiley4.ktorswaggerui.data.* +import io.github.smiley4.ktoropenapi.builder.route.RouteMeta +import io.github.smiley4.ktoropenapi.config.AnyOfTypeDescriptor +import io.github.smiley4.ktoropenapi.config.ArrayTypeDescriptor +import io.github.smiley4.ktoropenapi.config.EmptyTypeDescriptor +import io.github.smiley4.ktoropenapi.config.KTypeDescriptor +import io.github.smiley4.ktoropenapi.config.RefTypeDescriptor +import io.github.smiley4.ktoropenapi.config.SwaggerTypeDescriptor +import io.github.smiley4.ktoropenapi.config.TypeDescriptor +import io.github.smiley4.ktoropenapi.data.* import io.github.smiley4.schemakenerator.core.data.WildcardTypeData import io.github.smiley4.schemakenerator.swagger.data.CompiledSwaggerSchema import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaUtils import io.swagger.v3.oas.models.media.Schema import kotlin.reflect.KType -class SchemaContextImpl(private val schemaConfig: SchemaConfigData) : SchemaContext { +internal class SchemaContextImpl(private val schemaConfig: SchemaConfigData) : SchemaContext { private val rootSchemas = mutableMapOf>() private val componentSchemas = mutableMapOf>() @@ -114,10 +121,10 @@ class SchemaContextImpl(private val schemaConfig: SchemaConfigData) : SchemaCont } request.body?.also { body -> when (body) { - is OpenApiSimpleBodyData -> { + is SimpleBodyData -> { descriptors.add(body.type) } - is OpenApiMultipartBodyData -> { + is MultipartBodyData -> { body.parts.forEach { part -> descriptors.add(part.type) } @@ -131,10 +138,10 @@ class SchemaContextImpl(private val schemaConfig: SchemaConfigData) : SchemaCont } response.body?.also { body -> when (body) { - is OpenApiSimpleBodyData -> { + is SimpleBodyData -> { descriptors.add(body.type) } - is OpenApiMultipartBodyData -> { + is MultipartBodyData -> { body.parts.forEach { part -> descriptors.add(part.type) } diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthKeyLocation.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/AuthKeyLocation.kt similarity index 86% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthKeyLocation.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/AuthKeyLocation.kt index 5865f601..c924e126 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthKeyLocation.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/AuthKeyLocation.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.config import io.swagger.v3.oas.models.security.SecurityScheme diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthScheme.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/AuthScheme.kt similarity index 62% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthScheme.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/AuthScheme.kt index 70d09eb0..682c67d1 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthScheme.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/AuthScheme.kt @@ -1,6 +1,9 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.config -// https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml +/** + * The authentication scheme. + * https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml + */ enum class AuthScheme(val swaggerType: String) { BASIC("Basic"), BEARER("bearer"), diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthType.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/AuthType.kt similarity index 89% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthType.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/AuthType.kt index e2f053e3..2e17de01 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/AuthType.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/AuthType.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.config import io.swagger.v3.oas.models.security.SecurityScheme diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiBaseBody.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/BaseBodyConfig.kt similarity index 81% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiBaseBody.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/BaseBodyConfig.kt index 05573cd8..7d32d262 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiBaseBody.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/BaseBodyConfig.kt @@ -1,14 +1,13 @@ -package io.github.smiley4.ktorswaggerui.dsl.routes +package io.github.smiley4.ktoropenapi.config -import io.github.smiley4.ktorswaggerui.data.OpenApiBaseBodyData -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker +import io.github.smiley4.ktoropenapi.data.BaseBodyData import io.ktor.http.ContentType /** * Describes a single request/response body with a single schema. */ @OpenApiDslMarker -sealed class OpenApiBaseBody { +sealed class BaseBodyConfig { /** * A brief description of the request body @@ -42,5 +41,5 @@ sealed class OpenApiBaseBody { /** * Build the data object for this config. */ - abstract fun build(): OpenApiBaseBodyData + internal abstract fun build(): BaseBodyData } diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiContact.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ContactConfig.kt similarity index 73% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiContact.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ContactConfig.kt index b27b454c..8195f761 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiContact.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ContactConfig.kt @@ -1,14 +1,13 @@ -package io.github.smiley4.ktorswaggerui.dsl.config +package io.github.smiley4.ktoropenapi.config -import io.github.smiley4.ktorswaggerui.data.ContactData -import io.github.smiley4.ktorswaggerui.data.DataUtils.merge -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker +import io.github.smiley4.ktoropenapi.data.ContactData +import io.github.smiley4.ktoropenapi.data.DataUtils.merge /** * Contact information for the exposed API. */ @OpenApiDslMarker -class OpenApiContact { +class ContactConfig { /** * The identifying name of the contact person/organization. @@ -32,7 +31,7 @@ class OpenApiContact { * Build the data object for this config. * @param base the base config to "inherit" from. Values from the base should be copied, replaced or merged together. */ - fun build(base: ContactData) = ContactData( + internal fun build(base: ContactData) = ContactData( name = merge(base.name, name), url = merge(base.url, url), email = merge(base.email, email) diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/ExampleConfig.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ExampleConfig.kt similarity index 61% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/ExampleConfig.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ExampleConfig.kt index 59ddd3c6..717a9813 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/ExampleConfig.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ExampleConfig.kt @@ -1,9 +1,30 @@ -package io.github.smiley4.ktorswaggerui.dsl.config +package io.github.smiley4.ktoropenapi.config -import io.github.smiley4.ktorswaggerui.data.* -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker -import io.github.smiley4.ktorswaggerui.dsl.routes.ValueExampleDescriptorDsl +import com.fasterxml.jackson.core.type.TypeReference +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import io.github.smiley4.ktoropenapi.data.* import io.swagger.v3.oas.models.examples.Example +import kotlinx.serialization.json.Json +import kotlinx.serialization.serializer + +/** + * Encoder to produce the final example value. + * Return the unmodified example to fall back to the default encoder. + */ +typealias ExampleEncoder = (type: TypeDescriptor?, example: Any?) -> Any? + +/** + * [ExampleEncoder] using kotlinx-serialization to encode example objects. + */ +val kotlinxExampleEncoder: ExampleEncoder = { type, example -> + if (type is KTypeDescriptor) { + val jsonString = Json.encodeToString(serializer(type.type), example) + val jsonObj = jacksonObjectMapper().readValue(jsonString, object : TypeReference() {}) + jsonObj + } else { + example + } +} /** * Configuration for examples @@ -34,8 +55,8 @@ class ExampleConfig { * Add a shared example that can be referenced by all routes by the given name. * The provided name has to be unique among all shared examples and acts as its id. */ - fun example(name: String, example: ValueExampleDescriptorDsl.() -> Unit) = example( - ValueExampleDescriptorDsl() + fun example(name: String, example: ValueExampleDescriptorConfig.() -> Unit) = example( + ValueExampleDescriptorConfig() .apply(example) .let { result -> ValueExampleDescriptor( @@ -59,12 +80,12 @@ class ExampleConfig { * Build the data object for this config. * @param securityConfig the data for security config that might contain additional examples */ - fun build(securityConfig: SecurityData) = ExampleConfigData( + internal fun build(securityConfig: SecurityData) = ExampleConfigData( sharedExamples = sharedExamples, securityExamples = securityConfig.defaultUnauthorizedResponse?.body?.let { when (it) { - is OpenApiSimpleBodyData -> it - is OpenApiMultipartBodyData -> null + is SimpleBodyData -> it + is MultipartBodyData -> null } }, exampleEncoder = exampleEncoder diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ExampleDescriptor.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ExampleDescriptor.kt similarity index 94% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ExampleDescriptor.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ExampleDescriptor.kt index dfbe272f..6436f4c1 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ExampleDescriptor.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ExampleDescriptor.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.config import io.swagger.v3.oas.models.examples.Example diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiExternalDocs.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ExternalDocsConfig.kt similarity index 66% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiExternalDocs.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ExternalDocsConfig.kt index c3c81b61..18d7b278 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiExternalDocs.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ExternalDocsConfig.kt @@ -1,14 +1,13 @@ -package io.github.smiley4.ktorswaggerui.dsl.config +package io.github.smiley4.ktoropenapi.config -import io.github.smiley4.ktorswaggerui.data.DataUtils -import io.github.smiley4.ktorswaggerui.data.ExternalDocsData -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker +import io.github.smiley4.ktoropenapi.data.DataUtils +import io.github.smiley4.ktoropenapi.data.ExternalDocsData /** * An object representing external documentation. */ @OpenApiDslMarker -class OpenApiExternalDocs { +class ExternalDocsConfig { /** * A short description of the external documentation @@ -25,7 +24,7 @@ class OpenApiExternalDocs { * Build the data object for this config. * @param base the base config to "inherit" from. Values from the base should be copied, replaced or merged together. */ - fun build(base: ExternalDocsData) = ExternalDocsData( + internal fun build(base: ExternalDocsData) = ExternalDocsData( url = DataUtils.mergeDefault(base.url, url, ExternalDocsData.DEFAULT.url), description = DataUtils.merge(base.description, description) ) diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiHeader.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/HeaderConfig.kt similarity index 77% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiHeader.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/HeaderConfig.kt index acb61706..0780370a 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiHeader.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/HeaderConfig.kt @@ -1,10 +1,6 @@ -package io.github.smiley4.ktorswaggerui.dsl.routes +package io.github.smiley4.ktoropenapi.config -import io.github.smiley4.ktorswaggerui.data.KTypeDescriptor -import io.github.smiley4.ktorswaggerui.data.OpenApiHeaderData -import io.github.smiley4.ktorswaggerui.data.SwaggerTypeDescriptor -import io.github.smiley4.ktorswaggerui.data.TypeDescriptor -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker +import io.github.smiley4.ktoropenapi.data.HeaderData import io.swagger.v3.oas.models.media.Schema import kotlin.reflect.KType import kotlin.reflect.typeOf @@ -13,7 +9,7 @@ import kotlin.reflect.typeOf * Describes a single header. */ @OpenApiDslMarker -class OpenApiHeader { +class HeaderConfig { /** * A description of the header @@ -73,7 +69,7 @@ class OpenApiHeader { /** * Build the data object for this config. */ - fun build() = OpenApiHeaderData( + internal fun build() = HeaderData( description = description, type = type, required = required ?: false, diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiInfo.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/InfoConfig.kt similarity index 65% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiInfo.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/InfoConfig.kt index 9fe4c8dd..2f0affc4 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiInfo.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/InfoConfig.kt @@ -1,17 +1,16 @@ -package io.github.smiley4.ktorswaggerui.dsl.config +package io.github.smiley4.ktoropenapi.config -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 -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker +import io.github.smiley4.ktoropenapi.data.ContactData +import io.github.smiley4.ktoropenapi.data.DataUtils.merge +import io.github.smiley4.ktoropenapi.data.DataUtils.mergeDefault +import io.github.smiley4.ktoropenapi.data.InfoData +import io.github.smiley4.ktoropenapi.data.LicenseData /** * Basic information for the exposed API. */ @OpenApiDslMarker -class OpenApiInfo { +class InfoConfig { /** * The title of the api @@ -41,32 +40,32 @@ class OpenApiInfo { */ var termsOfService: String? = null - private var contact: OpenApiContact? = null + private var contact: ContactConfig? = null /** * The contact information for the exposed API. */ - fun contact(block: OpenApiContact.() -> Unit) { - contact = OpenApiContact().apply(block) + fun contact(block: ContactConfig.() -> Unit) { + contact = ContactConfig().apply(block) } - private var license: OpenApiLicense? = null + private var license: LicenseConfig? = null /** * The license information for the exposed API. */ - fun license(block: OpenApiLicense.() -> Unit) { - license = OpenApiLicense().apply(block) + fun license(block: LicenseConfig.() -> Unit) { + license = LicenseConfig().apply(block) } /** * Build the data object for this config. * @param base the base config to "inherit" from. Values from the base should be copied, replaced or merged together. */ - fun build(base: InfoData): InfoData { + internal fun build(base: InfoData): InfoData { return InfoData( title = mergeDefault(base.title, this.title, InfoData.DEFAULT.title), version = merge(base.version, this.version), diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiLicense.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/LicenseConfig.kt similarity index 75% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiLicense.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/LicenseConfig.kt index 10b4040a..fb3bbf69 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiLicense.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/LicenseConfig.kt @@ -1,14 +1,13 @@ -package io.github.smiley4.ktorswaggerui.dsl.config +package io.github.smiley4.ktoropenapi.config -import io.github.smiley4.ktorswaggerui.data.DataUtils -import io.github.smiley4.ktorswaggerui.data.LicenseData -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker +import io.github.smiley4.ktoropenapi.data.DataUtils +import io.github.smiley4.ktoropenapi.data.LicenseData /** * License information for the exposed API. */ @OpenApiDslMarker -class OpenApiLicense { +class LicenseConfig { /** * The license name used for the API @@ -31,7 +30,7 @@ class OpenApiLicense { * Build the data object for this config. * @param base the base config to "inherit" from. Values from the base should be copied, replaced or merged together. */ - fun build(base: LicenseData) = LicenseData( + internal fun build(base: LicenseData) = LicenseData( name = DataUtils.merge(base.name, name), url = DataUtils.merge(base.url, url), identifier = DataUtils.merge(base.identifier, identifier) diff --git a/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/MultipartBodyConfig.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/MultipartBodyConfig.kt new file mode 100644 index 00000000..b768be4b --- /dev/null +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/MultipartBodyConfig.kt @@ -0,0 +1,52 @@ +package io.github.smiley4.ktoropenapi.config + +import io.github.smiley4.ktoropenapi.data.MultipartBodyData +import io.swagger.v3.oas.models.media.Schema +import kotlin.reflect.KType +import kotlin.reflect.typeOf + + +/** + * Describes a single request/response body with multipart content. + * See https://swagger.io/docs/specification/describing-request-body/multipart-requests/ for more info + */ +@OpenApiDslMarker +class MultipartBodyConfig : BaseBodyConfig() { + + private val parts = mutableListOf() + + + /** + * One part of a multipart-body + */ + fun part(name: String, type: TypeDescriptor, block: MultipartPartConfig.() -> Unit = {}) { + parts.add(MultipartPartConfig(name, type).apply(block)) + } + + + /** + * One part of a multipart-body + */ + fun part(name: String, type: Schema<*>, block: MultipartPartConfig.() -> Unit = {}) = part(name, SwaggerTypeDescriptor(type), block) + + + /** + * One part of a multipart-body + */ + fun part(name: String, type: KType, block: MultipartPartConfig.() -> Unit = {}) = part(name, KTypeDescriptor(type), block) + + + /** + * One part of a multipart-body + */ + inline fun part(name: String, noinline block: MultipartPartConfig.() -> Unit = {}) = + part(name, KTypeDescriptor(typeOf()), block) + + + override fun build() = MultipartBodyData( + description = description, + required = required ?: false, + mediaTypes = mediaTypes.toSet(), + parts = parts.map { it.build() } + ) +} diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiMultipartPart.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/MultipartPartConfig.kt similarity index 61% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiMultipartPart.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/MultipartPartConfig.kt index dabb8499..75a61133 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiMultipartPart.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/MultipartPartConfig.kt @@ -1,10 +1,6 @@ -package io.github.smiley4.ktorswaggerui.dsl.routes +package io.github.smiley4.ktoropenapi.config -import io.github.smiley4.ktorswaggerui.data.KTypeDescriptor -import io.github.smiley4.ktorswaggerui.data.OpenApiMultipartPartData -import io.github.smiley4.ktorswaggerui.data.SwaggerTypeDescriptor -import io.github.smiley4.ktorswaggerui.data.TypeDescriptor -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker +import io.github.smiley4.ktoropenapi.data.MultipartPartData import io.ktor.http.ContentType import io.swagger.v3.oas.models.media.Schema import kotlin.reflect.KType @@ -15,7 +11,7 @@ import kotlin.reflect.typeOf * See https://swagger.io/docs/specification/describing-request-body/multipart-requests/ for more info */ @OpenApiDslMarker -class OpenApiMultipartPart( +class MultipartPartConfig( /** * The name of this part */ @@ -46,14 +42,14 @@ class OpenApiMultipartPart( /** * List of headers of this part */ - val headers = mutableMapOf() + val headers = mutableMapOf() /** * Possible headers for this part */ - fun header(name: String, type: TypeDescriptor, block: OpenApiHeader.() -> Unit = {}) { - headers[name] = OpenApiHeader().apply(block).apply { + fun header(name: String, type: TypeDescriptor, block: HeaderConfig.() -> Unit = {}) { + headers[name] = HeaderConfig().apply(block).apply { this.type = type } } @@ -62,25 +58,25 @@ class OpenApiMultipartPart( /** * Possible headers for this part */ - fun header(name: String, type: Schema<*>, block: OpenApiHeader.() -> Unit = {}) = header(name, SwaggerTypeDescriptor(type), block) + fun header(name: String, type: Schema<*>, block: HeaderConfig.() -> Unit = {}) = header(name, SwaggerTypeDescriptor(type), block) /** * Possible headers for this part */ - fun header(name: String, type: KType, block: OpenApiHeader.() -> Unit = {}) = header(name, KTypeDescriptor(type), block) + fun header(name: String, type: KType, block: HeaderConfig.() -> Unit = {}) = header(name, KTypeDescriptor(type), block) /** * Possible headers for this part */ - inline fun header(name: String, noinline block: OpenApiHeader.() -> Unit = {}) = + inline fun header(name: String, noinline block: HeaderConfig.() -> Unit = {}) = header(name, KTypeDescriptor(typeOf()), block) /** * Build the data object for this config. */ - fun build() = OpenApiMultipartPartData( + internal fun build() = MultipartPartData( name = name, type = type, mediaTypes = mediaTypes.toSet(), diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiDslMarker.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/OpenApiDslMarker.kt similarity index 50% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiDslMarker.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/OpenApiDslMarker.kt index 50fa6c52..539ea3cb 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiDslMarker.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/OpenApiDslMarker.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.dsl +package io.github.smiley4.ktoropenapi.config @DslMarker annotation class OpenApiDslMarker diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/PluginConfigDsl.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/OpenApiPluginConfig.kt similarity index 57% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/PluginConfigDsl.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/OpenApiPluginConfig.kt index 81a9bd64..09c38ee4 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/PluginConfigDsl.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/OpenApiPluginConfig.kt @@ -1,13 +1,8 @@ -package io.github.smiley4.ktorswaggerui.dsl.config - -import io.github.smiley4.ktorswaggerui.data.DataUtils.merge -import io.github.smiley4.ktorswaggerui.data.OutputFormat -import io.github.smiley4.ktorswaggerui.data.PathFilter -import io.github.smiley4.ktorswaggerui.data.PluginConfigData -import io.github.smiley4.ktorswaggerui.data.PostBuild -import io.github.smiley4.ktorswaggerui.data.ServerData -import io.github.smiley4.ktorswaggerui.data.SpecAssigner -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker +package io.github.smiley4.ktoropenapi.config + +import io.github.smiley4.ktoropenapi.data.DataUtils.merge +import io.github.smiley4.ktoropenapi.data.OpenApiPluginData +import io.github.smiley4.ktoropenapi.data.ServerData import io.ktor.server.routing.RouteSelector import kotlin.collections.component1 import kotlin.collections.component2 @@ -15,10 +10,10 @@ import kotlin.collections.set import kotlin.reflect.KClass /** - * Main-Configuration of the "SwaggerUI"-Plugin + * Main-Configuration of the "OpenApi"-Plugin */ @OpenApiDslMarker -class PluginConfigDsl { +class OpenApiPluginConfig { companion object { const val DEFAULT_SPEC_ID = "api" @@ -28,61 +23,51 @@ class PluginConfigDsl { /** * OpenAPI info configuration - provides metadata about the API */ - fun info(block: OpenApiInfo.() -> Unit) { - info = OpenApiInfo().apply(block) + fun info(block: InfoConfig.() -> Unit) { + info = InfoConfig().apply(block) } - private var info = OpenApiInfo() + private var info = InfoConfig() /** * OpenAPI external docs configuration - link and description of an external documentation */ - fun externalDocs(block: OpenApiExternalDocs.() -> Unit) { - externalDocs = OpenApiExternalDocs().apply(block) + fun externalDocs(block: ExternalDocsConfig.() -> Unit) { + externalDocs = ExternalDocsConfig().apply(block) } - private var externalDocs = OpenApiExternalDocs() + private var externalDocs = ExternalDocsConfig() /** * OpenAPI server configuration - an array of servers, which provide connectivity information to a target server */ - fun server(block: OpenApiServer.() -> Unit) { - servers.add(OpenApiServer().apply(block)) - } - - private val servers = mutableListOf() - - - /** - * Swagger-UI configuration - */ - fun swagger(block: SwaggerUIDsl.() -> Unit) { - swaggerUI = SwaggerUIDsl().apply(block) + fun server(block: ServerConfig.() -> Unit) { + servers.add(ServerConfig().apply(block)) } - private var swaggerUI = SwaggerUIDsl() + private val servers = mutableListOf() /** * Configuration for security and authentication. */ - fun security(block: OpenApiSecurity.() -> Unit) { + fun security(block: SecurityConfig.() -> Unit) { security.apply(block) } - private val security = OpenApiSecurity() + private val security = SecurityConfig() /** * Configuration for openapi-tags */ - fun tags(block: OpenApiTags.() -> Unit) { + fun tags(block: TagsConfig.() -> Unit) { tags.also(block) } - private val tags = OpenApiTags() + private val tags = TagsConfig() /** @@ -108,40 +93,42 @@ class PluginConfigDsl { /** * Configure specific separate specs */ - fun spec(specId: String, block: PluginConfigDsl.() -> Unit) { - specConfigs[specId] = PluginConfigDsl().apply(block) + fun spec(specName: String, block: OpenApiPluginConfig.() -> Unit) { + specConfigs[specName] = OpenApiPluginConfig().apply(block) } - private val specConfigs = mutableMapOf() + private val specConfigs = mutableMapOf() /** - * Assigns routes without an [io.github.smiley4.ktorswaggerui.dsl.routes.OpenApiRoute.specId]] to a specified openapi-spec. + * Assigns routes without an [io.github.smiley4.ktoropenapi.dsl.routes.RouteConfig.specName]] to a specified openapi-spec. */ - var specAssigner: SpecAssigner? = PluginConfigData.DEFAULT.specAssigner + var specAssigner: SpecAssigner? = OpenApiPluginData.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: PathFilter? = PluginConfigData.DEFAULT.pathFilter + var pathFilter: PathFilter? = OpenApiPluginData.DEFAULT.pathFilter /** * List of all [RouteSelector] types in that should be ignored in the resulting url of any route. */ - var ignoredRouteSelectors: Set> = PluginConfigData.DEFAULT.ignoredRouteSelectors + var ignoredRouteSelectors: Set> = OpenApiPluginData.DEFAULT.ignoredRouteSelectors + /** * List of all [RouteSelector] class names that should be ignored in the resulting url of any route. */ var ignoredRouteSelectorClassNames: Set = emptySet() + /** * The format of the generated api-spec */ - var outputFormat: OutputFormat = PluginConfigData.DEFAULT.outputFormat + var outputFormat: OutputFormat = OpenApiPluginData.DEFAULT.outputFormat /** @@ -150,26 +137,31 @@ class PluginConfigDsl { var postBuild: PostBuild? = null + /** + * Root path of the ktor-application to prepend to the paths. "null" to automatically fetch from ktor configuration. + */ + var rootPath: String? = OpenApiPluginData.DEFAULT.rootPath + + /** * Build the data object for this config. * @param base the base config to "inherit" from. Values from the base should be copied, replaced or merged together. */ - internal fun build(base: PluginConfigData): PluginConfigData { + internal fun build(base: OpenApiPluginData, ktorRootPath: String?): OpenApiPluginData { val securityConfig = security.build(base.securityConfig) - return PluginConfigData( + return OpenApiPluginData( info = info.build(base.info), externalDocs = externalDocs.build(base.externalDocs), servers = buildList { addAll(base.servers) addAll(servers.map { it.build(ServerData.DEFAULT) }) }, - swagger = swaggerUI.build(base.swagger), securityConfig = securityConfig, tagsConfig = tags.build(base.tagsConfig), schemaConfig = schemaConfig.build(securityConfig), exampleConfig = exampleConfig.build(securityConfig), - specAssigner = merge(base.specAssigner, specAssigner) ?: PluginConfigData.DEFAULT.specAssigner, - pathFilter = merge(base.pathFilter, pathFilter) ?: PluginConfigData.DEFAULT.pathFilter, + specAssigner = merge(base.specAssigner, specAssigner) ?: OpenApiPluginData.DEFAULT.specAssigner, + pathFilter = merge(base.pathFilter, pathFilter) ?: OpenApiPluginData.DEFAULT.pathFilter, ignoredRouteSelectors = buildSet { addAll(base.ignoredRouteSelectors) addAll(ignoredRouteSelectors) @@ -180,11 +172,13 @@ class PluginConfigDsl { }, specConfigs = mutableMapOf(), postBuild = merge(base.postBuild, postBuild), - outputFormat = outputFormat + outputFormat = outputFormat, + rootPath = merge(rootPath ?: ktorRootPath, base.rootPath) ).also { - specConfigs.forEach { (specId, config) -> - it.specConfigs[specId] = config.build(it) + specConfigs.forEach { (specName, config) -> + it.specConfigs[specName] = config.build(it, ktorRootPath) } } } + } diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenIdOAuthFlow.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/OpenIdOAuthFlowConfig.kt similarity index 75% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenIdOAuthFlow.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/OpenIdOAuthFlowConfig.kt index 7beaeb8b..a5259ec5 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenIdOAuthFlow.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/OpenIdOAuthFlowConfig.kt @@ -1,14 +1,13 @@ -package io.github.smiley4.ktorswaggerui.dsl.config +package io.github.smiley4.ktoropenapi.config -import io.github.smiley4.ktorswaggerui.data.DataUtils.merge -import io.github.smiley4.ktorswaggerui.data.OpenIdOAuthFlowData -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker +import io.github.smiley4.ktoropenapi.data.DataUtils.merge +import io.github.smiley4.ktoropenapi.data.OpenIdOAuthFlowData /** * Configuration details for a supported OAuth Flow */ @OpenApiDslMarker -class OpenIdOAuthFlow { +class OpenIdOAuthFlowConfig { /** * The authorization URL to be used for this flow @@ -34,7 +33,7 @@ class OpenIdOAuthFlow { * Build the data object for this config. * @param base the base config to "inherit" from. Values from the base should be copied, replaced or merged together. */ - fun build(base: OpenIdOAuthFlowData) = OpenIdOAuthFlowData( + internal fun build(base: OpenIdOAuthFlowData) = OpenIdOAuthFlowData( authorizationUrl = merge(base.authorizationUrl, authorizationUrl), tokenUrl = merge(base.tokenUrl, tokenUrl), refreshUrl = merge(base.refreshUrl, refreshUrl), diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenIdOAuthFlows.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/OpenIdOAuthFlowsConfig.kt similarity index 52% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenIdOAuthFlows.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/OpenIdOAuthFlowsConfig.kt index 122b64dc..fb8d3fa7 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenIdOAuthFlows.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/OpenIdOAuthFlowsConfig.kt @@ -1,63 +1,62 @@ -package io.github.smiley4.ktorswaggerui.dsl.config +package io.github.smiley4.ktoropenapi.config -import io.github.smiley4.ktorswaggerui.data.OpenIdOAuthFlowData -import io.github.smiley4.ktorswaggerui.data.OpenIdOAuthFlowsData -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker +import io.github.smiley4.ktoropenapi.data.OpenIdOAuthFlowData +import io.github.smiley4.ktoropenapi.data.OpenIdOAuthFlowsData /** * An object containing configuration information for the oauth flow types supported */ @OpenApiDslMarker -class OpenIdOAuthFlows { +class OpenIdOAuthFlowsConfig { - private var implicit: OpenIdOAuthFlow? = null + private var implicit: OpenIdOAuthFlowConfig? = null /** * Configuration for the OAuth Implicit flow */ - fun implicit(block: OpenIdOAuthFlow.() -> Unit) { - implicit = OpenIdOAuthFlow().apply(block) + fun implicit(block: OpenIdOAuthFlowConfig.() -> Unit) { + implicit = OpenIdOAuthFlowConfig().apply(block) } - private var password: OpenIdOAuthFlow? = null + private var password: OpenIdOAuthFlowConfig? = null /** * Configuration for the OAuth Resource Owner Password flow */ - fun password(block: OpenIdOAuthFlow.() -> Unit) { - password = OpenIdOAuthFlow().apply(block) + fun password(block: OpenIdOAuthFlowConfig.() -> Unit) { + password = OpenIdOAuthFlowConfig().apply(block) } - private var clientCredentials: OpenIdOAuthFlow? = null + private var clientCredentials: OpenIdOAuthFlowConfig? = null /** * Configuration for the OAuth Client Credentials flow. */ - fun clientCredentials(block: OpenIdOAuthFlow.() -> Unit) { - clientCredentials = OpenIdOAuthFlow().apply(block) + fun clientCredentials(block: OpenIdOAuthFlowConfig.() -> Unit) { + clientCredentials = OpenIdOAuthFlowConfig().apply(block) } - private var authorizationCode: OpenIdOAuthFlow? = null + private var authorizationCode: OpenIdOAuthFlowConfig? = null /** * Configuration for the OAuth Authorization Code flow. */ - fun authorizationCode(block: OpenIdOAuthFlow.() -> Unit) { - authorizationCode = OpenIdOAuthFlow().apply(block) + fun authorizationCode(block: OpenIdOAuthFlowConfig.() -> Unit) { + authorizationCode = OpenIdOAuthFlowConfig().apply(block) } /** * Build the data object for this config. * @param base the base config to "inherit" from. Values from the base should be copied, replaced or merged together. */ - fun build(base: OpenIdOAuthFlowsData) = OpenIdOAuthFlowsData( + internal 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, diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OutputFormat.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/OutputFormat.kt similarity index 63% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OutputFormat.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/OutputFormat.kt index 3d6b276c..bc17e08b 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OutputFormat.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/OutputFormat.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.config enum class OutputFormat(val empty: String) { JSON("{}"), diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ParameterLocation.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ParameterLocation.kt similarity index 71% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ParameterLocation.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ParameterLocation.kt index 58b23130..5a592b87 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ParameterLocation.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ParameterLocation.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.config /** * Locations for request parameters. diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PathFilter.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/PathFilter.kt similarity index 83% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PathFilter.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/PathFilter.kt index 6eaa3267..b34a56ce 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PathFilter.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/PathFilter.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.config import io.ktor.http.HttpMethod diff --git a/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/PostBuild.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/PostBuild.kt new file mode 100644 index 00000000..77a929d9 --- /dev/null +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/PostBuild.kt @@ -0,0 +1,8 @@ +package io.github.smiley4.ktoropenapi.config + +import io.swagger.v3.oas.models.OpenAPI + +/** + * Function executed after building the openapi-spec. + */ +typealias PostBuild = (openApi: OpenAPI, specName: String) -> Unit diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiRequest.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/RequestConfig.kt similarity index 61% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiRequest.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/RequestConfig.kt index 1caaf944..d9cb29c6 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiRequest.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/RequestConfig.kt @@ -1,11 +1,6 @@ -package io.github.smiley4.ktorswaggerui.dsl.routes - -import io.github.smiley4.ktorswaggerui.data.KTypeDescriptor -import io.github.smiley4.ktorswaggerui.data.OpenApiRequestData -import io.github.smiley4.ktorswaggerui.data.ParameterLocation -import io.github.smiley4.ktorswaggerui.data.SwaggerTypeDescriptor -import io.github.smiley4.ktorswaggerui.data.TypeDescriptor -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker +package io.github.smiley4.ktoropenapi.config + +import io.github.smiley4.ktoropenapi.data.RequestData import io.swagger.v3.oas.models.media.Schema import kotlin.reflect.KType import kotlin.reflect.typeOf @@ -14,135 +9,135 @@ import kotlin.reflect.typeOf * Describes a single request. */ @OpenApiDslMarker -class OpenApiRequest { +class RequestConfig { /** * A list of parameters that are applicable for this operation */ - val parameters = mutableListOf() + val parameters = mutableListOf() /** * A path parameters that is applicable for this operation */ - fun parameter(location: ParameterLocation, name: String, type: TypeDescriptor, block: OpenApiRequestParameter.() -> Unit = {}) { - parameters.add(OpenApiRequestParameter(name, type, location).apply(block)) + fun parameter(location: ParameterLocation, name: String, type: TypeDescriptor, block: RequestParameterConfig.() -> Unit = {}) { + parameters.add(RequestParameterConfig(name, type, location).apply(block)) } /** * A path parameters that is applicable for this operation */ - fun pathParameter(name: String, type: TypeDescriptor, block: OpenApiRequestParameter.() -> Unit = {}) = + fun pathParameter(name: String, type: TypeDescriptor, block: RequestParameterConfig.() -> Unit = {}) = parameter(ParameterLocation.PATH, name, type, block) /** * A path parameters that is applicable for this operation */ - fun pathParameter(name: String, type: Schema<*>, block: OpenApiRequestParameter.() -> Unit = {}) = + fun pathParameter(name: String, type: Schema<*>, block: RequestParameterConfig.() -> Unit = {}) = parameter(ParameterLocation.PATH, name, SwaggerTypeDescriptor(type), block) /** * A path parameters that is applicable for this operation */ - fun pathParameter(name: String, type: KType, block: OpenApiRequestParameter.() -> Unit = {}) = + fun pathParameter(name: String, type: KType, block: RequestParameterConfig.() -> Unit = {}) = parameter(ParameterLocation.PATH, name, KTypeDescriptor(type), block) /** * A path parameters that is applicable for this operation */ - inline fun pathParameter(name: String, noinline block: OpenApiRequestParameter.() -> Unit = {}) = + inline fun pathParameter(name: String, noinline block: RequestParameterConfig.() -> Unit = {}) = parameter(ParameterLocation.PATH, name, KTypeDescriptor(typeOf()), block) /** * A query parameters that is applicable for this operation */ - fun queryParameter(name: String, type: TypeDescriptor, block: OpenApiRequestParameter.() -> Unit = {}) = + fun queryParameter(name: String, type: TypeDescriptor, block: RequestParameterConfig.() -> Unit = {}) = parameter(ParameterLocation.QUERY, name, type, block) /** * A query parameters that is applicable for this operation */ - fun queryParameter(name: String, type: Schema<*>, block: OpenApiRequestParameter.() -> Unit = {}) = + fun queryParameter(name: String, type: Schema<*>, block: RequestParameterConfig.() -> Unit = {}) = parameter(ParameterLocation.QUERY, name, SwaggerTypeDescriptor(type), block) /** * A query parameters that is applicable for this operation */ - fun queryParameter(name: String, type: KType, block: OpenApiRequestParameter.() -> Unit = {}) = + fun queryParameter(name: String, type: KType, block: RequestParameterConfig.() -> Unit = {}) = parameter(ParameterLocation.QUERY, name, KTypeDescriptor(type), block) /** * A query parameters that is applicable for this operation */ - inline fun queryParameter(name: String, noinline block: OpenApiRequestParameter.() -> Unit = {}) = + inline fun queryParameter(name: String, noinline block: RequestParameterConfig.() -> Unit = {}) = parameter(ParameterLocation.QUERY, name, KTypeDescriptor(typeOf()), block) /** * A header parameters that is applicable for this operation */ - fun headerParameter(name: String, type: TypeDescriptor, block: OpenApiRequestParameter.() -> Unit = {}) = + fun headerParameter(name: String, type: TypeDescriptor, block: RequestParameterConfig.() -> Unit = {}) = parameter(ParameterLocation.HEADER, name, type, block) /** * A header parameters that is applicable for this operation */ - fun headerParameter(name: String, type: Schema<*>, block: OpenApiRequestParameter.() -> Unit = {}) = + fun headerParameter(name: String, type: Schema<*>, block: RequestParameterConfig.() -> Unit = {}) = parameter(ParameterLocation.HEADER, name, SwaggerTypeDescriptor(type), block) /** * A header parameters that is applicable for this operation */ - fun headerParameter(name: String, type: KType, block: OpenApiRequestParameter.() -> Unit = {}) = + fun headerParameter(name: String, type: KType, block: RequestParameterConfig.() -> Unit = {}) = parameter(ParameterLocation.HEADER, name, KTypeDescriptor(type), block) /** * A header parameters that is applicable for this operation */ - inline fun headerParameter(name: String, noinline block: OpenApiRequestParameter.() -> Unit = {}) = + inline fun headerParameter(name: String, noinline block: RequestParameterConfig.() -> Unit = {}) = parameter(ParameterLocation.HEADER, name, KTypeDescriptor(typeOf()), block) /** * A cookie parameters that is applicable for this operation */ - fun cookieParameter(name: String, type: TypeDescriptor, block: OpenApiRequestParameter.() -> Unit = {}) = + fun cookieParameter(name: String, type: TypeDescriptor, block: RequestParameterConfig.() -> Unit = {}) = parameter(ParameterLocation.COOKIE, name, type, block) /** * A cookie parameters that is applicable for this operation */ - fun cookieParameter(name: String, type: Schema<*>, block: OpenApiRequestParameter.() -> Unit = {}) = + fun cookieParameter(name: String, type: Schema<*>, block: RequestParameterConfig.() -> Unit = {}) = parameter(ParameterLocation.COOKIE, name, SwaggerTypeDescriptor(type), block) /** * A cookie parameters that is applicable for this operation */ - fun cookieParameter(name: String, type: KType, block: OpenApiRequestParameter.() -> Unit = {}) = + fun cookieParameter(name: String, type: KType, block: RequestParameterConfig.() -> Unit = {}) = parameter(ParameterLocation.COOKIE, name, KTypeDescriptor(type), block) /** * A cookie parameters that is applicable for this operation */ - inline fun cookieParameter(name: String, noinline block: OpenApiRequestParameter.() -> Unit = {}) = + inline fun cookieParameter(name: String, noinline block: RequestParameterConfig.() -> Unit = {}) = parameter(ParameterLocation.COOKIE, name, KTypeDescriptor(typeOf()), block) - private var body: OpenApiBaseBody? = null + private var body: BaseBodyConfig? = null fun getBody() = body @@ -150,8 +145,8 @@ class OpenApiRequest { /** * The body returned with this request */ - fun body(type: TypeDescriptor, block: OpenApiSimpleBody.() -> Unit = {}) { - val result = OpenApiSimpleBody(type).apply(block) + fun body(type: TypeDescriptor, block: SimpleBodyConfig.() -> Unit = {}) { + val result = SimpleBodyConfig(type).apply(block) if (!result.isEmptyBody()) { body = result } @@ -161,33 +156,33 @@ class OpenApiRequest { /** * The body returned with this request */ - fun body(type: Schema<*>, block: OpenApiSimpleBody.() -> Unit = {}) = body(SwaggerTypeDescriptor(type), block) + fun body(type: Schema<*>, block: SimpleBodyConfig.() -> Unit = {}) = body(SwaggerTypeDescriptor(type), block) /** * The body returned with this request */ - fun body(type: KType, block: OpenApiSimpleBody.() -> Unit = {}) = body(KTypeDescriptor(type), block) + fun body(type: KType, block: SimpleBodyConfig.() -> Unit = {}) = body(KTypeDescriptor(type), block) /** * The body returned with this request */ - inline fun body(noinline block: OpenApiSimpleBody.() -> Unit = {}) = body(KTypeDescriptor(typeOf()), block) + inline fun body(noinline block: SimpleBodyConfig.() -> Unit = {}) = body(KTypeDescriptor(typeOf()), block) /** * The multipart-body returned with this request */ - fun multipartBody(block: OpenApiMultipartBody.() -> Unit) { - body = OpenApiMultipartBody().apply(block) + fun multipartBody(block: MultipartBodyConfig.() -> Unit) { + body = MultipartBodyConfig().apply(block) } /** * Set the body of this request. Intended for internal use. */ - fun setBody(body: OpenApiBaseBody?) { + fun setBody(body: BaseBodyConfig?) { this.body = body } @@ -195,18 +190,18 @@ class OpenApiRequest { /** * Build the data object for this config. */ - fun build() = OpenApiRequestData( + internal fun build() = RequestData( parameters = parameters.map { it.build() }, body = body?.build() ) - private fun OpenApiBaseBody.isEmptyBody(): Boolean { + private fun BaseBodyConfig.isEmptyBody(): Boolean { return when (this) { - is OpenApiSimpleBody -> when (type) { + is SimpleBodyConfig -> when (type) { is KTypeDescriptor -> type.type == typeOf() else -> false } - is OpenApiMultipartBody -> false + is MultipartBodyConfig -> false } } diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiRequestParameter.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/RequestParameterConfig.kt similarity index 80% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiRequestParameter.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/RequestParameterConfig.kt index 8b49cdcc..13a09279 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiRequestParameter.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/RequestParameterConfig.kt @@ -1,12 +1,6 @@ -package io.github.smiley4.ktorswaggerui.dsl.routes - -import io.github.smiley4.ktorswaggerui.data.ExampleDescriptor -import io.github.smiley4.ktorswaggerui.data.OpenApiRequestParameterData -import io.github.smiley4.ktorswaggerui.data.ParameterLocation -import io.github.smiley4.ktorswaggerui.data.SwaggerExampleDescriptor -import io.github.smiley4.ktorswaggerui.data.TypeDescriptor -import io.github.smiley4.ktorswaggerui.data.ValueExampleDescriptor -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker +package io.github.smiley4.ktoropenapi.config + +import io.github.smiley4.ktoropenapi.data.RequestParameterData import io.swagger.v3.oas.models.examples.Example import io.swagger.v3.oas.models.parameters.Parameter @@ -14,7 +8,7 @@ import io.swagger.v3.oas.models.parameters.Parameter * Describes a single request parameter. */ @OpenApiDslMarker -class OpenApiRequestParameter( +class RequestParameterConfig( /** * The name (case-sensitive) of the parameter */ @@ -55,8 +49,8 @@ class OpenApiRequestParameter( /** * An example value for this parameter */ - fun example(name: String, example: ValueExampleDescriptorDsl.() -> Unit) = example( - ValueExampleDescriptorDsl() + fun example(name: String, example: ValueExampleDescriptorConfig.() -> Unit) = example( + ValueExampleDescriptorConfig() .apply(example) .let { result -> ValueExampleDescriptor( @@ -110,7 +104,7 @@ class OpenApiRequestParameter( /** * Build the data object for this config. */ - fun build() = OpenApiRequestParameterData( + internal fun build() = RequestParameterData( name = name, type = type, location = location, diff --git a/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ResponseConfig.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ResponseConfig.kt new file mode 100644 index 00000000..24e8fa3a --- /dev/null +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ResponseConfig.kt @@ -0,0 +1,97 @@ +package io.github.smiley4.ktoropenapi.config + +import io.github.smiley4.ktoropenapi.data.ResponseData +import io.swagger.v3.oas.models.media.Schema +import kotlin.reflect.KType +import kotlin.reflect.typeOf + +/** + * A container for the expected responses of an operation. The container maps an HTTP response code to the expected response. + * A response code can only have one response object. + */ +@OpenApiDslMarker +class ResponseConfig(val statusCode: String) { + + /** + * A short description of the response + */ + var description: String? = null + + val headers = mutableMapOf() + + + /** + * Possible headers returned with this response + */ + fun header(name: String, type: TypeDescriptor, block: HeaderConfig.() -> Unit = {}) { + headers[name] = HeaderConfig().apply(block).apply { + this.type = type + } + } + + + /** + * Possible headers returned with this response + */ + fun header(name: String, type: Schema<*>, block: HeaderConfig.() -> Unit = {}) = header(name, SwaggerTypeDescriptor(type), block) + + + /** + * Possible headers returned with this response + */ + fun header(name: String, type: KType, block: HeaderConfig.() -> Unit = {}) = header(name, KTypeDescriptor(type), block) + + + /** + * Possible headers returned with this response + */ + inline fun header(name: String, noinline block: HeaderConfig.() -> Unit = {}) = + header(name, KTypeDescriptor(typeOf()), block) + + + private var body: BaseBodyConfig? = null + + + /** + * The body returned with this response + */ + fun body(type: TypeDescriptor, block: SimpleBodyConfig.() -> Unit = {}) { + body = SimpleBodyConfig(type).apply(block) + } + + /** + * The body returned with this response + */ + fun body(type: Schema<*>, block: SimpleBodyConfig.() -> Unit = {}) = body(SwaggerTypeDescriptor(type), block) + + /** + * The body returned with this response + */ + fun body(type: KType, block: SimpleBodyConfig.() -> Unit = {}) = body(KTypeDescriptor(type), block) + + /** + * The body returned with this response + */ + inline fun body(noinline block: SimpleBodyConfig.() -> Unit = {}) = body(KTypeDescriptor(typeOf()), block) + + + + + /** + * The multipart-body returned with this response + */ + fun multipartBody(block: MultipartBodyConfig.() -> Unit) { + body = MultipartBodyConfig().apply(block) + } + + /** + * Build the data object for this config. + */ + internal fun build() = ResponseData( + statusCode = statusCode, + description = description, + headers = headers.mapValues { it.value.build() }, + body = body?.build() + ) + +} diff --git a/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ResponsesConfig.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ResponsesConfig.kt new file mode 100644 index 00000000..81a0507c --- /dev/null +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ResponsesConfig.kt @@ -0,0 +1,56 @@ +package io.github.smiley4.ktoropenapi.config + +import io.ktor.http.HttpStatusCode + +/** + * All possible responses of an operation + */ +@OpenApiDslMarker +class ResponsesConfig { + + private val responses = mutableMapOf() + + + /** + * Information of response for a given http status code + */ + infix fun String.to(block: ResponseConfig.() -> Unit) { + responses[this] = ResponseConfig(this).apply(block) + } + + + /** + * Information of response for a given http status code + */ + infix fun HttpStatusCode.to(block: ResponseConfig.() -> Unit) = this.value.toString() to block + + /** + * Information of response for a given http status code + */ + fun code(statusCode: String, block: ResponseConfig.() -> Unit) { + responses[statusCode] = ResponseConfig(statusCode).apply(block) + } + + /** + * Information of response for a given http status code + */ + fun code(statusCode: HttpStatusCode, block: ResponseConfig.() -> Unit) = code(statusCode.value.toString(), block) + + + /** + * Information of the default response + */ + fun default(block: ResponseConfig.() -> Unit) = "default" to block + + + /** + * Add the given response. Intended for internal use. + */ + fun addResponse(response: ResponseConfig) { + responses[response.statusCode] = response + } + + + fun getResponses() = responses.values.toList() + +} diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiRoute.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/RouteConfig.kt similarity index 79% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiRoute.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/RouteConfig.kt index eec28d14..1a2dc826 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiRoute.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/RouteConfig.kt @@ -1,22 +1,19 @@ -package io.github.smiley4.ktorswaggerui.dsl.routes +package io.github.smiley4.ktoropenapi.config -import io.github.smiley4.ktorswaggerui.data.ExternalDocsData -import io.github.smiley4.ktorswaggerui.data.OpenApiRouteData -import io.github.smiley4.ktorswaggerui.data.ServerData -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker -import io.github.smiley4.ktorswaggerui.dsl.config.OpenApiExternalDocs -import io.github.smiley4.ktorswaggerui.dsl.config.OpenApiServer +import io.github.smiley4.ktoropenapi.data.ExternalDocsData +import io.github.smiley4.ktoropenapi.data.RouteData +import io.github.smiley4.ktoropenapi.data.ServerData /** * Describes a single route including request and responses. */ @OpenApiDslMarker -class OpenApiRoute { +class RouteConfig { /** * the id of the openapi-spec this route belongs to. 'Null' to use default spec. */ - var specId: String? = null + var specName: String? = null /** @@ -105,25 +102,25 @@ class OpenApiRoute { } - private val request = OpenApiRequest() + private val request = RequestConfig() /** * Information about the request */ - fun request(block: OpenApiRequest.() -> Unit) { + fun request(block: RequestConfig.() -> Unit) { request.apply(block) } fun getRequest() = request - private val responses = OpenApiResponses() + private val responses = ResponsesConfig() /** * Possible responses as they are returned from executing this operation. */ - fun response(block: OpenApiResponses.() -> Unit) { + fun response(block: ResponsesConfig.() -> Unit) { responses.apply(block) } @@ -133,27 +130,27 @@ class OpenApiRoute { /** * OpenAPI external docs configuration - link and description of an external documentation */ - fun externalDocs(block: OpenApiExternalDocs.() -> Unit) { - externalDocs = OpenApiExternalDocs().apply(block) + fun externalDocs(block: ExternalDocsConfig.() -> Unit) { + externalDocs = ExternalDocsConfig().apply(block) } - private var externalDocs: OpenApiExternalDocs? = null + private var externalDocs: ExternalDocsConfig? = null /** * OpenAPI server configuration - an array of servers, which provide connectivity information to a target server */ - fun server(block: OpenApiServer.() -> Unit) { - servers.add(OpenApiServer().apply(block)) + fun server(block: ServerConfig.() -> Unit) { + servers.add(ServerConfig().apply(block)) } - private val servers = mutableListOf() + private val servers = mutableListOf() /** * Build the data object for this config. */ - fun build() = OpenApiRouteData( - specId = specId, + internal fun build() = RouteData( + specName = specName, tags = tags.toSet(), summary = summary, description = description, diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/SchemaConfig.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/SchemaConfig.kt similarity index 91% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/SchemaConfig.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/SchemaConfig.kt index 1c80d9b4..ad9d67ee 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/SchemaConfig.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/SchemaConfig.kt @@ -1,7 +1,6 @@ -package io.github.smiley4.ktorswaggerui.dsl.config +package io.github.smiley4.ktoropenapi.config -import io.github.smiley4.ktorswaggerui.data.* -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker +import io.github.smiley4.ktoropenapi.data.* import io.github.smiley4.schemakenerator.swagger.data.CompiledSwaggerSchema import io.swagger.v3.oas.models.media.Schema import kotlin.reflect.KType @@ -87,14 +86,14 @@ class SchemaConfig { * Build the data object for this config. * @param securityConfig configuration that might contain additional schemas */ - fun build(securityConfig: SecurityData) = SchemaConfigData( + internal fun build(securityConfig: SecurityData) = SchemaConfigData( generator = generator, schemas = schemas, overwrite = overwrite, securitySchemas = securityConfig.defaultUnauthorizedResponse?.body?.let { body -> when (body) { - is OpenApiSimpleBodyData -> listOf(body.type) - is OpenApiMultipartBodyData -> body.parts.map { it.type } + is SimpleBodyData -> listOf(body.type) + is MultipartBodyData -> body.parts.map { it.type } } } ?: emptyList() ) diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiSecurity.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/SecurityConfig.kt similarity index 65% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiSecurity.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/SecurityConfig.kt index 85ca7650..ead497b7 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiSecurity.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/SecurityConfig.kt @@ -1,27 +1,25 @@ -package io.github.smiley4.ktorswaggerui.dsl.config +package io.github.smiley4.ktoropenapi.config -import io.github.smiley4.ktorswaggerui.data.DataUtils.merge -import io.github.smiley4.ktorswaggerui.data.SecurityData -import io.github.smiley4.ktorswaggerui.data.SecuritySchemeData -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker -import io.github.smiley4.ktorswaggerui.dsl.routes.OpenApiResponse +import io.github.smiley4.ktoropenapi.data.DataUtils.merge +import io.github.smiley4.ktoropenapi.data.SecurityData +import io.github.smiley4.ktoropenapi.data.SecuritySchemeData import io.ktor.http.HttpStatusCode /** * Configuration for security and authentication. */ @OpenApiDslMarker -class OpenApiSecurity { +class SecurityConfig { /** * Default response to automatically add to each protected route for the "Unauthorized"-Response-Code. * Generated response can be overwritten with custom response. */ - fun defaultUnauthorizedResponse(block: OpenApiResponse.() -> Unit) { - defaultUnauthorizedResponse = OpenApiResponse(HttpStatusCode.Unauthorized.value.toString()).apply(block) + fun defaultUnauthorizedResponse(block: ResponseConfig.() -> Unit) { + defaultUnauthorizedResponse = ResponseConfig(HttpStatusCode.Unauthorized.value.toString()).apply(block) } - private var defaultUnauthorizedResponse: OpenApiResponse? = null + private var defaultUnauthorizedResponse: ResponseConfig? = null /** @@ -46,17 +44,17 @@ class OpenApiSecurity { /** * Defines security schemes that can be used by operations */ - fun securityScheme(name: String, block: OpenApiSecurityScheme.() -> Unit) { - securitySchemes.add(OpenApiSecurityScheme(name).apply(block)) + fun securityScheme(name: String, block: SecuritySchemeConfig.() -> Unit) { + securitySchemes.add(SecuritySchemeConfig(name).apply(block)) } - private val securitySchemes = mutableListOf() + private val securitySchemes = mutableListOf() /** * Build the data object for this config. * @param base the base config to "inherit" from. Values from the base should be copied, replaced or merged together. */ - fun build(base: SecurityData) = SecurityData( + internal fun build(base: SecurityData) = SecurityData( defaultUnauthorizedResponse = merge(base.defaultUnauthorizedResponse, defaultUnauthorizedResponse?.build()), defaultSecuritySchemeNames = buildSet { addAll(base.defaultSecuritySchemeNames) diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiSecurityScheme.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/SecuritySchemeConfig.kt similarity index 75% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiSecurityScheme.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/SecuritySchemeConfig.kt index d7dfc11e..c953f70e 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiSecurityScheme.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/SecuritySchemeConfig.kt @@ -1,12 +1,8 @@ -package io.github.smiley4.ktorswaggerui.dsl.config +package io.github.smiley4.ktoropenapi.config -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 -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker +import io.github.smiley4.ktoropenapi.data.DataUtils.merge +import io.github.smiley4.ktoropenapi.data.OpenIdOAuthFlowsData +import io.github.smiley4.ktoropenapi.data.SecuritySchemeData /** @@ -14,7 +10,7 @@ import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker * a cookie parameter or as a query parameter), OAuth2's common flows (implicit, password, client credentials and authorization code) */ @OpenApiDslMarker -class OpenApiSecurityScheme( +class SecuritySchemeConfig( /** * The name of the security scheme. */ @@ -51,15 +47,15 @@ class OpenApiSecurityScheme( */ var bearerFormat: String? = null - private var flows: OpenIdOAuthFlows? = null + private var flows: OpenIdOAuthFlowsConfig? = null /** * Information for the oauth flow types supported. * Required for type [AuthType.OAUTH2] */ - fun flows(block: OpenIdOAuthFlows.() -> Unit) { - flows = OpenIdOAuthFlows().apply(block) + fun flows(block: OpenIdOAuthFlowsConfig.() -> Unit) { + flows = OpenIdOAuthFlowsConfig().apply(block) } @@ -79,7 +75,7 @@ class OpenApiSecurityScheme( * Build the data object for this config. * @param base the base config to "inherit" from. Values from the base should be copied, replaced or merged together. */ - fun build(base: SecuritySchemeData) = SecuritySchemeData( + internal fun build(base: SecuritySchemeData) = SecuritySchemeData( schemeName = schemeName, type = merge(base.type, type), name = merge(base.name, name), diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiServer.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ServerConfig.kt similarity index 64% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiServer.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ServerConfig.kt index fb12e4fa..ecb6eda0 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiServer.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ServerConfig.kt @@ -1,15 +1,14 @@ -package io.github.smiley4.ktorswaggerui.dsl.config +package io.github.smiley4.ktoropenapi.config -import io.github.smiley4.ktorswaggerui.data.DataUtils.merge -import io.github.smiley4.ktorswaggerui.data.DataUtils.mergeDefault -import io.github.smiley4.ktorswaggerui.data.ServerData -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker +import io.github.smiley4.ktoropenapi.data.DataUtils.merge +import io.github.smiley4.ktoropenapi.data.DataUtils.mergeDefault +import io.github.smiley4.ktoropenapi.data.ServerData /** * An object representing a Server. */ @OpenApiDslMarker -class OpenApiServer { +class ServerConfig { /** * A URL to the target host. This URL supports Server Variables and MAY be relative, to indicate that the host location is relative to @@ -22,21 +21,21 @@ class OpenApiServer { */ var description: String? = ServerData.DEFAULT.description - private val variables = mutableMapOf() + private val variables = mutableMapOf() /** * Adds a new server variable with the given name */ - fun variable(name: String, block: OpenApiServerVariable.() -> Unit) { - variables[name] = OpenApiServerVariable(name).apply(block) + fun variable(name: String, block: ServerVariableConfig.() -> Unit) { + variables[name] = ServerVariableConfig(name).apply(block) } /** * Build the data object for this config. * @param base the base config to "inherit" from. Values from the base should be copied, replaced or merged together. */ - fun build(base: ServerData) = ServerData( + internal fun build(base: ServerData) = ServerData( url = mergeDefault(base.url, url, ServerData.DEFAULT.url), description = merge(base.description, description), variables = buildMap { diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiServerVariable.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ServerVariableConfig.kt similarity index 77% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiServerVariable.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ServerVariableConfig.kt index bdd64955..997b512a 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiServerVariable.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ServerVariableConfig.kt @@ -1,13 +1,12 @@ -package io.github.smiley4.ktorswaggerui.dsl.config +package io.github.smiley4.ktoropenapi.config -import io.github.smiley4.ktorswaggerui.data.ServerVariableData -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker +import io.github.smiley4.ktoropenapi.data.ServerVariableData /** * An object representing a Server Variable for server URL template substitution. */ @OpenApiDslMarker -class OpenApiServerVariable( +class ServerVariableConfig( /** * The name of this variable */ @@ -34,7 +33,7 @@ class OpenApiServerVariable( /** * Build the data object for this config. */ - fun build() = ServerVariableData( + internal fun build() = ServerVariableData( name = name, enum = enum.toSet(), default = (default ?: enum.firstOrNull()) ?: "", diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiSimpleBody.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/SimpleBodyConfig.kt similarity index 70% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiSimpleBody.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/SimpleBodyConfig.kt index 6cdc357d..c5dbca85 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiSimpleBody.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/SimpleBodyConfig.kt @@ -1,12 +1,6 @@ -package io.github.smiley4.ktorswaggerui.dsl.routes +package io.github.smiley4.ktoropenapi.config -import io.github.smiley4.ktorswaggerui.data.ExampleDescriptor -import io.github.smiley4.ktorswaggerui.data.OpenApiSimpleBodyData -import io.github.smiley4.ktorswaggerui.data.RefExampleDescriptor -import io.github.smiley4.ktorswaggerui.data.SwaggerExampleDescriptor -import io.github.smiley4.ktorswaggerui.data.TypeDescriptor -import io.github.smiley4.ktorswaggerui.data.ValueExampleDescriptor -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker +import io.github.smiley4.ktoropenapi.data.SimpleBodyData import io.swagger.v3.oas.models.examples.Example @@ -14,12 +8,12 @@ import io.swagger.v3.oas.models.examples.Example * Describes the base of a single request/response body. */ @OpenApiDslMarker -class OpenApiSimpleBody( +class SimpleBodyConfig( /** * The type defining the schema used for the body. */ val type: TypeDescriptor, -) : OpenApiBaseBody() { +) : BaseBodyConfig() { /** * Examples for this body @@ -41,8 +35,8 @@ class OpenApiSimpleBody( /** * Add the given example as an example to this body */ - fun example(name: String, example: ValueExampleDescriptorDsl.() -> Unit) = example( - ValueExampleDescriptorDsl() + fun example(name: String, example: ValueExampleDescriptorConfig.() -> Unit) = example( + ValueExampleDescriptorConfig() .apply(example) .let { result -> ValueExampleDescriptor( @@ -68,7 +62,7 @@ class OpenApiSimpleBody( */ fun exampleRef(name: String) = example(RefExampleDescriptor(name, name)) - override fun build() = OpenApiSimpleBodyData( + override fun build() = SimpleBodyData( description = description, required = required ?: false, mediaTypes = mediaTypes.toSet(), diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SpecAssigner.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/SpecAssigner.kt similarity index 82% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SpecAssigner.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/SpecAssigner.kt index f87522d3..41a63c22 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SpecAssigner.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/SpecAssigner.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.config /** * Assigns (unassigned) routes to api-specs. diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiTag.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/TagConfig.kt similarity index 72% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiTag.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/TagConfig.kt index 27b38c69..3baf13e1 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiTag.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/TagConfig.kt @@ -1,14 +1,13 @@ -package io.github.smiley4.ktorswaggerui.dsl.config +package io.github.smiley4.ktoropenapi.config -import io.github.smiley4.ktorswaggerui.data.DataUtils.merge -import io.github.smiley4.ktorswaggerui.data.TagData -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker +import io.github.smiley4.ktoropenapi.data.DataUtils.merge +import io.github.smiley4.ktoropenapi.data.TagData /** * Adds metadata to a single tag. */ @OpenApiDslMarker -class OpenApiTag( +class TagConfig( /** * The name of the tag. */ @@ -31,7 +30,7 @@ class OpenApiTag( var externalDocUrl: String? = null - fun build(base: TagData) = TagData( + internal fun build(base: TagData) = TagData( name = name, description = merge(base.description, description), externalDocDescription = merge(base.externalDocDescription, externalDocDescription), diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TagGenerator.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/TagGenerator.kt similarity index 83% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TagGenerator.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/TagGenerator.kt index 0a6372b3..8e6950cd 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TagGenerator.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/TagGenerator.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.config /** * Generates additional tags for routes. diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiTags.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/TagsConfig.kt similarity index 58% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiTags.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/TagsConfig.kt index 1d93ace6..897b42d8 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/OpenApiTags.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/TagsConfig.kt @@ -1,26 +1,23 @@ -package io.github.smiley4.ktorswaggerui.dsl.config +package io.github.smiley4.ktoropenapi.config -import io.github.smiley4.ktorswaggerui.data.DataUtils.merge -import io.github.smiley4.ktorswaggerui.data.PluginConfigData -import io.github.smiley4.ktorswaggerui.data.TagData -import io.github.smiley4.ktorswaggerui.data.TagGenerator -import io.github.smiley4.ktorswaggerui.data.TagsData -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker +import io.github.smiley4.ktoropenapi.data.DataUtils.merge +import io.github.smiley4.ktoropenapi.data.TagData +import io.github.smiley4.ktoropenapi.data.TagsData /** * Configuration for tags */ @OpenApiDslMarker -class OpenApiTags { +class TagsConfig { - private val tags = mutableListOf() + private val tags = mutableListOf() /** * Tags used by the specification with additional metadata. Not all tags that are used must be declared */ - fun tag(name: String, block: OpenApiTag.() -> Unit) { - tags.add(OpenApiTag(name).apply(block)) + fun tag(name: String, block: TagConfig.() -> Unit) { + tags.add(TagConfig(name).apply(block)) } @@ -34,7 +31,7 @@ class OpenApiTags { * Build the data object for this config. * @param base the base config to "inherit" from. Values from the base should be copied, replaced or merged together. */ - fun build(base: TagsData) = TagsData( + internal fun build(base: TagsData) = TagsData( tags = buildList { addAll(base.tags) addAll(tags.map { it.build(TagData.DEFAULT) }) diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TypeDescriptor.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/TypeDescriptor.kt similarity index 97% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TypeDescriptor.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/TypeDescriptor.kt index 69d4b4cd..cdfaf3f9 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TypeDescriptor.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/TypeDescriptor.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.config import io.swagger.v3.oas.models.media.Schema import kotlin.reflect.KType diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/ValueExampleDescriptorDsl.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ValueExampleDescriptorConfig.kt similarity index 74% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/ValueExampleDescriptorDsl.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ValueExampleDescriptorConfig.kt index a66ac234..1f8a4203 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/ValueExampleDescriptorDsl.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/ValueExampleDescriptorConfig.kt @@ -1,6 +1,6 @@ -package io.github.smiley4.ktorswaggerui.dsl.routes +package io.github.smiley4.ktoropenapi.config -class ValueExampleDescriptorDsl { +class ValueExampleDescriptorConfig { /** * the example value diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenApiBaseBodyData.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/BaseBodyData.kt similarity index 58% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenApiBaseBodyData.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/BaseBodyData.kt index eb91c3de..95b38729 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenApiBaseBodyData.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/BaseBodyData.kt @@ -1,11 +1,13 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.data +import io.github.smiley4.ktoropenapi.config.ExampleDescriptor +import io.github.smiley4.ktoropenapi.config.TypeDescriptor import io.ktor.http.ContentType /** * The common information for request and response bodies. */ -sealed class OpenApiBaseBodyData( +internal sealed class BaseBodyData( val description: String?, val required: Boolean, val mediaTypes: Set, @@ -15,21 +17,21 @@ sealed class OpenApiBaseBodyData( /** * Information for a "simple" request or response body. */ -class OpenApiSimpleBodyData( +internal class SimpleBodyData( description: String?, required: Boolean, mediaTypes: Set, val type: TypeDescriptor, val examples: List -) : OpenApiBaseBodyData(description, required, mediaTypes) +) : BaseBodyData(description, required, mediaTypes) /** * Information for a multipart request or response body. */ -class OpenApiMultipartBodyData( +internal class MultipartBodyData( description: String?, required: Boolean, mediaTypes: Set, - val parts: List -) : OpenApiBaseBodyData(description, required, mediaTypes) + val parts: List +) : BaseBodyData(description, required, mediaTypes) diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ContactData.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/ContactData.kt similarity index 81% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ContactData.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/ContactData.kt index 920ecaff..2bd49e1c 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ContactData.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/ContactData.kt @@ -1,9 +1,9 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.data /** * See [OpenAPI Specification - Contact Object](https://swagger.io/specification/#contact-object). */ -data class ContactData( +internal data class ContactData( val name: String?, val url: String?, val email: String? diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/DataUtils.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/DataUtils.kt similarity index 89% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/DataUtils.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/DataUtils.kt index 398cecae..f40f0ee2 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/DataUtils.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/DataUtils.kt @@ -1,6 +1,6 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.data -object DataUtils { +internal object DataUtils { /** * Merges the two boolean values. @@ -8,7 +8,6 @@ object DataUtils { */ fun mergeBoolean(base: Boolean, value: Boolean) = if (value) true else base - /** * Merges the two values. * @return "value" if "value" is different from the given default value, "base" otherwise diff --git a/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/ExampleConfigData.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/ExampleConfigData.kt new file mode 100644 index 00000000..682ad5fe --- /dev/null +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/ExampleConfigData.kt @@ -0,0 +1,20 @@ +package io.github.smiley4.ktoropenapi.data + +import io.github.smiley4.ktoropenapi.config.ExampleDescriptor +import io.github.smiley4.ktoropenapi.config.ExampleEncoder + +internal class ExampleConfigData( + val sharedExamples: Map, + val securityExamples: SimpleBodyData?, + val exampleEncoder: ExampleEncoder? +) { + + companion object { + val DEFAULT = ExampleConfigData( + sharedExamples = emptyMap(), + securityExamples = null, + exampleEncoder = null + ) + } + +} diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ExternalDocsData.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/ExternalDocsData.kt similarity index 80% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ExternalDocsData.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/ExternalDocsData.kt index ac3d72aa..3e306417 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ExternalDocsData.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/ExternalDocsData.kt @@ -1,9 +1,9 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.data /** * See [OpenAPI Specification - External Documentation Object](https://swagger.io/specification/#external-documentation-object). */ -data class ExternalDocsData( +internal data class ExternalDocsData( val url: String, val description: String?, ) { diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenApiHeaderData.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/HeaderData.kt similarity index 65% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenApiHeaderData.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/HeaderData.kt index aa4c4f90..2fc2cbfe 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenApiHeaderData.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/HeaderData.kt @@ -1,9 +1,11 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.data + +import io.github.smiley4.ktoropenapi.config.TypeDescriptor /** * See [OpenAPI Specification - Header Object](https://swagger.io/specification/#header-object). */ -data class OpenApiHeaderData( +internal data class HeaderData( val description: String?, val type: TypeDescriptor?, val required: Boolean, diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/InfoData.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/InfoData.kt similarity index 88% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/InfoData.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/InfoData.kt index 4de183ab..e0aff603 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/InfoData.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/InfoData.kt @@ -1,9 +1,9 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.data /** * See [OpenAPI Specification - Info Object](https://swagger.io/specification/#info-object). */ -data class InfoData( +internal data class InfoData( val title: String, val version: String?, val description: String?, diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/LicenseData.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/LicenseData.kt similarity index 81% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/LicenseData.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/LicenseData.kt index bea8f45a..74ed953b 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/LicenseData.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/LicenseData.kt @@ -1,9 +1,9 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.data /** * See [OpenAPI Specification - License Object](https://swagger.io/specification/#license-object). */ -data class LicenseData( +internal data class LicenseData( val name: String?, val url: String?, val identifier: String? diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenApiMultipartPartData.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/MultipartPartData.kt similarity index 52% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenApiMultipartPartData.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/MultipartPartData.kt index 4f2da8c8..f73a0b15 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenApiMultipartPartData.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/MultipartPartData.kt @@ -1,13 +1,14 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.data +import io.github.smiley4.ktoropenapi.config.TypeDescriptor import io.ktor.http.ContentType /** * Information about a part of a multipart request or response body. */ -data class OpenApiMultipartPartData( +internal data class MultipartPartData( val name: String, val type: TypeDescriptor, val mediaTypes: Set, - val headers: Map, + val headers: Map, ) diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PluginConfigData.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/OpenApiPluginData.kt similarity index 62% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PluginConfigData.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/OpenApiPluginData.kt index ce1ad466..e1137b6d 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PluginConfigData.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/OpenApiPluginData.kt @@ -1,36 +1,39 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.data -import io.github.smiley4.ktorswaggerui.dsl.config.PluginConfigDsl +import io.github.smiley4.ktoropenapi.config.OpenApiPluginConfig +import io.github.smiley4.ktoropenapi.config.OutputFormat +import io.github.smiley4.ktoropenapi.config.PathFilter +import io.github.smiley4.ktoropenapi.config.PostBuild +import io.github.smiley4.ktoropenapi.config.SpecAssigner import kotlin.reflect.KClass /** * Complete plugin configuration */ -data class PluginConfigData( +internal data class OpenApiPluginData( val specAssigner: SpecAssigner, val pathFilter: PathFilter, val ignoredRouteSelectors: Set>, val ignoredRouteSelectorClassNames: Set, - val swagger: SwaggerUIData, val info: InfoData, val servers: List, val externalDocs: ExternalDocsData, - val specConfigs: MutableMap, + val specConfigs: MutableMap, val postBuild: PostBuild?, val schemaConfig: SchemaConfigData, val exampleConfig: ExampleConfigData, val securityConfig: SecurityData, val tagsConfig: TagsData, - val outputFormat: OutputFormat + val outputFormat: OutputFormat, + val rootPath: String? ) { companion object { - val DEFAULT = PluginConfigData( - specAssigner = { _, _ -> PluginConfigDsl.DEFAULT_SPEC_ID }, + val DEFAULT = OpenApiPluginData( + specAssigner = { _, _ -> OpenApiPluginConfig.DEFAULT_SPEC_ID }, pathFilter = { _, _ -> true }, ignoredRouteSelectors = emptySet(), ignoredRouteSelectorClassNames = emptySet(), - swagger = SwaggerUIData.DEFAULT, info = InfoData.DEFAULT, servers = emptyList(), externalDocs = ExternalDocsData.DEFAULT, @@ -40,7 +43,8 @@ data class PluginConfigData( exampleConfig = ExampleConfigData.DEFAULT, securityConfig = SecurityData.DEFAULT, tagsConfig = TagsData.DEFAULT, - outputFormat = OutputFormat.JSON + outputFormat = OutputFormat.JSON, + rootPath = null ) } diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenIdOAuthFlowData.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/OpenIdOAuthFlowData.kt similarity index 85% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenIdOAuthFlowData.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/OpenIdOAuthFlowData.kt index a9563d25..8255d822 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenIdOAuthFlowData.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/OpenIdOAuthFlowData.kt @@ -1,9 +1,9 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.data /** * See [OpenAPI Specification - OAuth Flow Object](https://swagger.io/specification/#oauth-flow-object). */ -data class OpenIdOAuthFlowData( +internal data class OpenIdOAuthFlowData( val authorizationUrl: String? = null, val tokenUrl: String? = null, val refreshUrl: String? = null, diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenIdOAuthFlowsData.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/OpenIdOAuthFlowsData.kt similarity index 85% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenIdOAuthFlowsData.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/OpenIdOAuthFlowsData.kt index 639820a1..78e911de 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenIdOAuthFlowsData.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/OpenIdOAuthFlowsData.kt @@ -1,9 +1,9 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.data /** * See [OpenAPI Specification - OAuth Flows Object](https://swagger.io/specification/#oauth-flows-object). */ -data class OpenIdOAuthFlowsData( +internal data class OpenIdOAuthFlowsData( val implicit: OpenIdOAuthFlowData?, val password: OpenIdOAuthFlowData?, val clientCredentials: OpenIdOAuthFlowData?, diff --git a/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/RequestData.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/RequestData.kt new file mode 100644 index 00000000..52fbf9e3 --- /dev/null +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/RequestData.kt @@ -0,0 +1,9 @@ +package io.github.smiley4.ktoropenapi.data + +/** + * Information about a request + */ +internal data class RequestData( + val parameters: List, + val body: BaseBodyData?, +) diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenApiRequestParameterData.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/RequestParameterData.kt similarity index 63% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenApiRequestParameterData.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/RequestParameterData.kt index ef2b3910..a3b0e343 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenApiRequestParameterData.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/RequestParameterData.kt @@ -1,11 +1,14 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.data +import io.github.smiley4.ktoropenapi.config.ExampleDescriptor +import io.github.smiley4.ktoropenapi.config.ParameterLocation +import io.github.smiley4.ktoropenapi.config.TypeDescriptor import io.swagger.v3.oas.models.parameters.Parameter /** * Information about a request (query, path or header) parameter. */ -data class OpenApiRequestParameterData( +internal data class RequestParameterData( val name: String, val type: TypeDescriptor, val location: ParameterLocation, diff --git a/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/ResponseData.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/ResponseData.kt new file mode 100644 index 00000000..6dc3b554 --- /dev/null +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/ResponseData.kt @@ -0,0 +1,11 @@ +package io.github.smiley4.ktoropenapi.data + +/** + * Information about a response for a status-code. + */ +internal data class ResponseData( + val statusCode: String, + val description: String?, + val headers: Map, + val body: BaseBodyData?, +) diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenApiRouteData.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/RouteData.kt similarity index 66% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenApiRouteData.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/RouteData.kt index 67364ef3..82cb6c85 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenApiRouteData.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/RouteData.kt @@ -1,10 +1,10 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.data /** * Information about a single route. */ -data class OpenApiRouteData( - val specId: String?, +internal data class RouteData( + val specName: String?, val tags: Set, val summary: String?, val description: String?, @@ -13,8 +13,8 @@ data class OpenApiRouteData( val hidden: Boolean, val securitySchemeNames: List, val protected: Boolean?, - val request: OpenApiRequestData, - val responses: List, + val request: RequestData, + val responses: List, val externalDocs: ExternalDocsData?, val servers: List, ) diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SchemaConfigData.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/SchemaConfigData.kt similarity index 91% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SchemaConfigData.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/SchemaConfigData.kt index 29c254e6..b343608e 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SchemaConfigData.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/SchemaConfigData.kt @@ -1,6 +1,6 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.data -import io.github.smiley4.schemakenerator.core.addDiscriminatorProperty +import io.github.smiley4.ktoropenapi.config.TypeDescriptor import io.github.smiley4.schemakenerator.core.connectSubTypes import io.github.smiley4.schemakenerator.core.handleNameAnnotation import io.github.smiley4.schemakenerator.reflection.collectSubTypes @@ -16,7 +16,7 @@ import kotlin.reflect.KType /** * Common configuration for schemas. */ -data class SchemaConfigData( +internal data class SchemaConfigData( val schemas: Map, val generator: (type: KType) -> CompiledSwaggerSchema, val overwrite: Map, diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SecurityData.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/SecurityData.kt similarity index 74% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SecurityData.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/SecurityData.kt index ee663787..9970e597 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SecurityData.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/SecurityData.kt @@ -1,10 +1,10 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.data /** * Common security configuration information. */ -data class SecurityData( - val defaultUnauthorizedResponse: OpenApiResponseData?, +internal data class SecurityData( + val defaultUnauthorizedResponse: ResponseData?, val defaultSecuritySchemeNames: Set, val securitySchemes: List, ) { diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SecuritySchemeData.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/SecuritySchemeData.kt similarity index 74% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SecuritySchemeData.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/SecuritySchemeData.kt index ddb05057..04fd87d6 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SecuritySchemeData.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/SecuritySchemeData.kt @@ -1,9 +1,13 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.data + +import io.github.smiley4.ktoropenapi.config.AuthKeyLocation +import io.github.smiley4.ktoropenapi.config.AuthScheme +import io.github.smiley4.ktoropenapi.config.AuthType /** * See [OpenAPI Specification - Security Scheme Object](https://swagger.io/specification/#security-scheme-object). */ -data class SecuritySchemeData( +internal data class SecuritySchemeData( val schemeName: String, val type: AuthType?, val name: String?, diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ServerData.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/ServerData.kt similarity index 83% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ServerData.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/ServerData.kt index 7e9e3347..976c4680 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ServerData.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/ServerData.kt @@ -1,9 +1,9 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.data /** * See [OpenAPI Specification - Server Object](https://swagger.io/specification/#server-object). */ -data class ServerData( +internal data class ServerData( val url: String, val description: String?, val variables: List diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ServerVariableData.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/ServerVariableData.kt similarity index 73% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ServerVariableData.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/ServerVariableData.kt index c61ecc2e..f4e3909c 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ServerVariableData.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/ServerVariableData.kt @@ -1,10 +1,10 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.data /** * See [OpenAPI Specification - Server Variable Object](https://swagger.io/specification/#server-variable-object). */ -data class ServerVariableData( +internal data class ServerVariableData( val name: String, val enum: Set, val default: String, diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TagData.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/TagData.kt similarity index 85% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TagData.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/TagData.kt index 60b438f6..7b98104f 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TagData.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/TagData.kt @@ -1,9 +1,9 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.data /** * See [OpenAPI Specification - Tag Object](https://swagger.io/specification/#tag-object). */ -data class TagData( +internal data class TagData( val name: String, val description: String?, val externalDocDescription: String?, diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TagsData.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/TagsData.kt similarity index 66% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TagsData.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/TagsData.kt index d54479e7..137531b3 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/TagsData.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/data/TagsData.kt @@ -1,9 +1,11 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktoropenapi.data + +import io.github.smiley4.ktoropenapi.config.TagGenerator /** * Common configuration for tags. */ -data class TagsData( +internal data class TagsData( val tags: List, val generator: TagGenerator, ) { diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routing/resources/DocumentedResourceRoutes.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/resources/DocumentedResourceRoutes.kt similarity index 82% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routing/resources/DocumentedResourceRoutes.kt rename to ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/resources/DocumentedResourceRoutes.kt index 3bc1e8de..7c68c9bc 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routing/resources/DocumentedResourceRoutes.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/resources/DocumentedResourceRoutes.kt @@ -1,8 +1,8 @@ -package io.github.smiley4.ktorswaggerui.dsl.routing.resources +package io.github.smiley4.ktoropenapi.resources -import io.github.smiley4.ktorswaggerui.dsl.routes.OpenApiRoute -import io.github.smiley4.ktorswaggerui.dsl.routing.documentation -import io.github.smiley4.ktorswaggerui.dsl.routing.method +import io.github.smiley4.ktoropenapi.config.RouteConfig +import io.github.smiley4.ktoropenapi.documentation +import io.github.smiley4.ktoropenapi.method import io.ktor.http.HttpMethod import io.ktor.server.resources.* import io.ktor.server.routing.Route @@ -16,7 +16,7 @@ import kotlinx.serialization.serializer @KtorDsl inline fun Route.get( - noinline builder: OpenApiRoute.() -> Unit = { }, + noinline builder: RouteConfig.() -> Unit = { }, noinline body: suspend RoutingContext.(T) -> Unit ): Route { return documentation(builder) { @@ -34,7 +34,7 @@ inline fun Route.get( @KtorDsl inline fun Route.post( - noinline builder: OpenApiRoute.() -> Unit = { }, + noinline builder: RouteConfig.() -> Unit = { }, noinline body: suspend RoutingContext.(T) -> Unit ): Route { return documentation(builder) { @@ -52,7 +52,7 @@ inline fun Route.post( @KtorDsl inline fun Route.put( - noinline builder: OpenApiRoute.() -> Unit = { }, + noinline builder: RouteConfig.() -> Unit = { }, noinline body: suspend RoutingContext.(T) -> Unit ): Route { return documentation(builder) { @@ -70,7 +70,7 @@ inline fun Route.put( @KtorDsl inline fun Route.delete( - noinline builder: OpenApiRoute.() -> Unit = { }, + noinline builder: RouteConfig.() -> Unit = { }, noinline body: suspend RoutingContext.(T) -> Unit ): Route { return documentation(builder) { @@ -88,7 +88,7 @@ inline fun Route.delete( @KtorDsl inline fun Route.patch( - noinline builder: OpenApiRoute.() -> Unit = { }, + noinline builder: RouteConfig.() -> Unit = { }, noinline body: suspend RoutingContext.(T) -> Unit ): Route { return documentation(builder) { @@ -106,7 +106,7 @@ inline fun Route.patch( @KtorDsl inline fun Route.options( - noinline builder: OpenApiRoute.() -> Unit = { }, + noinline builder: RouteConfig.() -> Unit = { }, noinline body: suspend RoutingContext.(T) -> Unit ): Route { return documentation(builder) { @@ -124,7 +124,7 @@ inline fun Route.options( @KtorDsl inline fun Route.head( - noinline builder: OpenApiRoute.() -> Unit = { }, + noinline builder: RouteConfig.() -> Unit = { }, noinline body: suspend RoutingContext.(T) -> Unit ): Route { return documentation(builder) { diff --git a/ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/ExternalDocsBuilderTest.kt b/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/ExternalDocsBuilderTest.kt similarity index 62% rename from ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/ExternalDocsBuilderTest.kt rename to ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/ExternalDocsBuilderTest.kt index b82b2cce..af84a89c 100644 --- a/ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/ExternalDocsBuilderTest.kt +++ b/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/ExternalDocsBuilderTest.kt @@ -1,8 +1,8 @@ package io.github.smiley4.ktorswaggerui.builder -import io.github.smiley4.ktorswaggerui.data.ExternalDocsData -import io.github.smiley4.ktorswaggerui.builder.openapi.ExternalDocumentationBuilder -import io.github.smiley4.ktorswaggerui.dsl.config.OpenApiExternalDocs +import io.github.smiley4.ktoropenapi.builder.openapi.ExternalDocumentationBuilder +import io.github.smiley4.ktoropenapi.data.ExternalDocsData +import io.github.smiley4.ktoropenapi.config.ExternalDocsConfig import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import io.swagger.v3.oas.models.ExternalDocumentation @@ -30,8 +30,8 @@ class ExternalDocsBuilderTest : StringSpec({ companion object { - private fun buildExternalDocsObject(builder: OpenApiExternalDocs.() -> Unit): ExternalDocumentation { - return ExternalDocumentationBuilder().build(OpenApiExternalDocs().apply(builder).build(ExternalDocsData.DEFAULT)) + private fun buildExternalDocsObject(builder: ExternalDocsConfig.() -> Unit): ExternalDocumentation { + return ExternalDocumentationBuilder().build(ExternalDocsConfig().apply(builder).build(ExternalDocsData.DEFAULT)) } } diff --git a/ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/InfoBuilderTest.kt b/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/InfoBuilderTest.kt similarity index 83% rename from ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/InfoBuilderTest.kt rename to ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/InfoBuilderTest.kt index efa98ce6..a012a1fc 100644 --- a/ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/InfoBuilderTest.kt +++ b/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/InfoBuilderTest.kt @@ -1,9 +1,9 @@ package io.github.smiley4.ktorswaggerui.builder -import io.github.smiley4.ktorswaggerui.data.InfoData -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.github.smiley4.ktorswaggerui.dsl.config.OpenApiInfo +import io.github.smiley4.ktoropenapi.builder.openapi.ContactBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.InfoBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.LicenseBuilder +import io.github.smiley4.ktoropenapi.data.InfoData +import io.github.smiley4.ktoropenapi.config.InfoConfig import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe @@ -71,11 +71,11 @@ class InfoBuilderTest : StringSpec({ companion object { - private fun buildInfoObject(builder: OpenApiInfo.() -> Unit): Info { + private fun buildInfoObject(builder: InfoConfig.() -> Unit): Info { return InfoBuilder( contactBuilder = ContactBuilder(), licenseBuilder = LicenseBuilder() - ).build(OpenApiInfo().apply(builder).build(InfoData.DEFAULT)) + ).build(InfoConfig().apply(builder).build(InfoData.DEFAULT)) } } diff --git a/ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/OpenApiBuilderTest.kt b/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/OpenApiBuilderTest.kt similarity index 70% rename from ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/OpenApiBuilderTest.kt rename to ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/OpenApiBuilderTest.kt index 4b23947a..8bea1c6f 100644 --- a/ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/OpenApiBuilderTest.kt +++ b/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/OpenApiBuilderTest.kt @@ -1,34 +1,34 @@ package io.github.smiley4.ktorswaggerui.builder -import io.github.smiley4.ktorswaggerui.builder.example.ExampleContext -import io.github.smiley4.ktorswaggerui.builder.example.ExampleContextImpl -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.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.RouteMeta -import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContext -import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContextImpl -import io.github.smiley4.ktorswaggerui.data.PluginConfigData -import io.github.smiley4.ktorswaggerui.dsl.config.PluginConfigDsl +import io.github.smiley4.ktoropenapi.builder.example.ExampleContext +import io.github.smiley4.ktoropenapi.builder.example.ExampleContextImpl +import io.github.smiley4.ktoropenapi.builder.openapi.ComponentsBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ContactBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ContentBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ExternalDocumentationBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.HeaderBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.InfoBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.LicenseBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.OAuthFlowsBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.OpenApiBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.OperationBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.OperationTagsBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ParameterBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.PathBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.PathsBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.RequestBodyBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ResponseBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ResponsesBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.SecurityRequirementsBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.SecuritySchemesBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ServerBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.TagBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.TagExternalDocumentationBuilder +import io.github.smiley4.ktoropenapi.builder.route.RouteMeta +import io.github.smiley4.ktoropenapi.builder.schema.SchemaContext +import io.github.smiley4.ktoropenapi.builder.schema.SchemaContextImpl +import io.github.smiley4.ktoropenapi.data.OpenApiPluginData +import io.github.smiley4.ktoropenapi.config.OpenApiPluginConfig import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder import io.kotest.matchers.collections.shouldHaveSize @@ -55,7 +55,7 @@ class OpenApiBuilderTest : StringSpec({ } "multiple servers" { - val config = PluginConfigDsl().also { + val config = OpenApiPluginConfig().also { it.server { url = "http://localhost:8080" description = "Development Server" @@ -81,7 +81,7 @@ class OpenApiBuilderTest : StringSpec({ } "multiple tags" { - val config = PluginConfigDsl().also { + val config = OpenApiPluginConfig().also { it.tags { tag("tag-1") { description = "first test tag" @@ -104,28 +104,28 @@ class OpenApiBuilderTest : StringSpec({ companion object { - private val defaultPluginConfig = PluginConfigDsl() + private val defaultPluginConfig = OpenApiPluginConfig() - private fun schemaContext(routes: List, pluginConfig: PluginConfigDsl): SchemaContext { - val pluginConfigData = pluginConfig.build(PluginConfigData.DEFAULT) + private fun schemaContext(routes: List, pluginConfig: OpenApiPluginConfig): SchemaContext { + val pluginConfigData = pluginConfig.build(OpenApiPluginData.DEFAULT, null) return SchemaContextImpl(pluginConfigData.schemaConfig).also { it.addGlobal(pluginConfigData.schemaConfig) it.add(routes) } } - private fun exampleContext(routes: List, pluginConfig: PluginConfigDsl): ExampleContext { - val pluginConfigData = pluginConfig.build(PluginConfigData.DEFAULT) + private fun exampleContext(routes: List, pluginConfig: OpenApiPluginConfig): ExampleContext { + val pluginConfigData = pluginConfig.build(OpenApiPluginData.DEFAULT, null) return ExampleContextImpl(pluginConfigData.exampleConfig.exampleEncoder).also { it.addShared(pluginConfigData.exampleConfig) it.add(routes) } } - private fun buildOpenApiObject(routes: List, pluginConfig: PluginConfigDsl = defaultPluginConfig): OpenAPI { + private fun buildOpenApiObject(routes: List, pluginConfig: OpenApiPluginConfig = defaultPluginConfig): OpenAPI { val schemaContext = schemaContext(routes, pluginConfig) val exampleContext = exampleContext(routes, pluginConfig) - val pluginConfigData = pluginConfig.build(PluginConfigData.DEFAULT) + val pluginConfigData = pluginConfig.build(OpenApiPluginData.DEFAULT, null) return OpenApiBuilder( config = pluginConfigData, schemaContext = schemaContext, @@ -140,6 +140,7 @@ class OpenApiBuilderTest : StringSpec({ tagExternalDocumentationBuilder = TagExternalDocumentationBuilder() ), pathsBuilder = PathsBuilder( + config = pluginConfigData, pathBuilder = PathBuilder( operationBuilder = OperationBuilder( operationTagsBuilder = OperationTagsBuilder(pluginConfigData), diff --git a/ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/OperationBuilderTest.kt b/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/OperationBuilderTest.kt similarity index 92% rename from ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/OperationBuilderTest.kt rename to ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/OperationBuilderTest.kt index ef51e82a..e1404622 100644 --- a/ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/OperationBuilderTest.kt +++ b/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/OperationBuilderTest.kt @@ -1,14 +1,28 @@ package io.github.smiley4.ktorswaggerui.builder -import io.github.smiley4.ktorswaggerui.builder.example.ExampleContext -import io.github.smiley4.ktorswaggerui.builder.example.ExampleContextImpl -import io.github.smiley4.ktorswaggerui.builder.openapi.* -import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta -import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContext -import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContextImpl -import io.github.smiley4.ktorswaggerui.data.* -import io.github.smiley4.ktorswaggerui.dsl.config.PluginConfigDsl -import io.github.smiley4.ktorswaggerui.dsl.routes.OpenApiRoute +import io.github.smiley4.ktoropenapi.builder.example.ExampleContext +import io.github.smiley4.ktoropenapi.builder.example.ExampleContextImpl +import io.github.smiley4.ktoropenapi.builder.openapi.ContentBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ExternalDocumentationBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.HeaderBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.OperationBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.OperationTagsBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ParameterBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.RequestBodyBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ResponseBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ResponsesBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.SecurityRequirementsBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ServerBuilder +import io.github.smiley4.ktoropenapi.builder.route.RouteMeta +import io.github.smiley4.ktoropenapi.builder.schema.SchemaContext +import io.github.smiley4.ktoropenapi.builder.schema.SchemaContextImpl +import io.github.smiley4.ktoropenapi.config.KTypeDescriptor +import io.github.smiley4.ktoropenapi.data.OpenApiPluginData +import io.github.smiley4.ktoropenapi.config.RefTypeDescriptor +import io.github.smiley4.ktoropenapi.config.SwaggerTypeDescriptor +import io.github.smiley4.ktoropenapi.config.OpenApiPluginConfig +import io.github.smiley4.ktoropenapi.config.RouteConfig +import io.github.smiley4.ktoropenapi.config.ValueExampleDescriptor import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder @@ -30,7 +44,7 @@ class OperationBuilderTest : StringSpec({ val route = RouteMeta( path = "/test", method = HttpMethod.Get, - documentation = OpenApiRoute().build(), + documentation = RouteConfig().build(), protected = false ) val schemaContext = schemaContext(listOf(route)) @@ -55,7 +69,7 @@ class OperationBuilderTest : StringSpec({ val route = RouteMeta( path = "/test", method = HttpMethod.Get, - documentation = OpenApiRoute().also { route -> + documentation = RouteConfig().also { route -> route.tags = listOf("tag1", "tag2") route.description = "route for testing" route.summary = "this is some test route" @@ -83,7 +97,7 @@ class OperationBuilderTest : StringSpec({ } "operation with auto-generated tags" { - val config = PluginConfigDsl().also { + val config = OpenApiPluginConfig().also { it.tags { tagGenerator = { url -> listOf(url.firstOrNull()) } } @@ -91,7 +105,7 @@ class OperationBuilderTest : StringSpec({ val routeA = RouteMeta( path = "a/test", method = HttpMethod.Get, - documentation = OpenApiRoute().also { route -> + documentation = RouteConfig().also { route -> route.tags = listOf("defaultTag") }.build(), protected = false @@ -99,7 +113,7 @@ class OperationBuilderTest : StringSpec({ val routeB = RouteMeta( path = "b/test", method = HttpMethod.Get, - documentation = OpenApiRoute().also { route -> + documentation = RouteConfig().also { route -> route.tags = listOf("defaultTag") }.build(), protected = false @@ -118,7 +132,7 @@ class OperationBuilderTest : StringSpec({ val route = RouteMeta( path = "/test", method = HttpMethod.Get, - documentation = OpenApiRoute().also { route -> + documentation = RouteConfig().also { route -> route.securitySchemeNames = listOf("security1", "security2") }.build(), protected = true @@ -140,7 +154,7 @@ class OperationBuilderTest : StringSpec({ val route = RouteMeta( path = "/test", method = HttpMethod.Get, - documentation = OpenApiRoute().build(), + documentation = RouteConfig().build(), protected = true ) val schemaContext = schemaContext(listOf(route)) @@ -165,7 +179,7 @@ class OperationBuilderTest : StringSpec({ val route = RouteMeta( path = "/test", method = HttpMethod.Get, - documentation = OpenApiRoute().also { route -> + documentation = RouteConfig().also { route -> route.securitySchemeNames = listOf("security1", "security2") }.build(), protected = false @@ -192,7 +206,7 @@ class OperationBuilderTest : StringSpec({ val route = RouteMeta( path = "/test", method = HttpMethod.Get, - documentation = OpenApiRoute().also { route -> + documentation = RouteConfig().also { route -> route.request { queryParameter("queryParam", KTypeDescriptor(typeOf())) {} pathParameter("pathParam", KTypeDescriptor(typeOf())) {} @@ -314,7 +328,7 @@ class OperationBuilderTest : StringSpec({ val route = RouteMeta( path = "/test", method = HttpMethod.Get, - documentation = OpenApiRoute().also { route -> + documentation = RouteConfig().also { route -> route.response { "test" to { description = "Test Response" @@ -383,7 +397,7 @@ class OperationBuilderTest : StringSpec({ val route = RouteMeta( path = "/test", method = HttpMethod.Get, - documentation = OpenApiRoute().also { route -> + documentation = RouteConfig().also { route -> route.request { queryParameter("param", KTypeDescriptor(typeOf())) { description = "test parameter" @@ -429,7 +443,7 @@ class OperationBuilderTest : StringSpec({ val route = RouteMeta( path = "/test", method = HttpMethod.Get, - documentation = OpenApiRoute().also { route -> + documentation = RouteConfig().also { route -> route.request { body(KTypeDescriptor(typeOf())) { description = "the test body" @@ -522,7 +536,7 @@ class OperationBuilderTest : StringSpec({ val route = RouteMeta( path = "/test", method = HttpMethod.Get, - documentation = OpenApiRoute().also { route -> + documentation = RouteConfig().also { route -> route.request { multipartBody { mediaTypes = setOf( @@ -601,7 +615,7 @@ class OperationBuilderTest : StringSpec({ val route = RouteMeta( path = "/test", method = HttpMethod.Get, - documentation = OpenApiRoute().also { route -> + documentation = RouteConfig().also { route -> route.request { multipartBody { mediaTypes = setOf( @@ -641,7 +655,7 @@ class OperationBuilderTest : StringSpec({ val route = RouteMeta( path = "/test", method = HttpMethod.Get, - documentation = OpenApiRoute().also { route -> + documentation = RouteConfig().also { route -> route.response { default { description = "Default Response" @@ -682,7 +696,7 @@ class OperationBuilderTest : StringSpec({ } "automatic unauthorized response for protected route" { - val config = PluginConfigDsl().also { + val config = OpenApiPluginConfig().also { it.security { defaultUnauthorizedResponse { description = "Default unauthorized Response" @@ -692,7 +706,7 @@ class OperationBuilderTest : StringSpec({ val route = RouteMeta( path = "/test", method = HttpMethod.Get, - documentation = OpenApiRoute().also { route -> + documentation = RouteConfig().also { route -> route.response { default { description = "Default Response" @@ -718,7 +732,7 @@ class OperationBuilderTest : StringSpec({ } "automatic unauthorized response with body type and example" { - val config = PluginConfigDsl().also { + val config = OpenApiPluginConfig().also { it.security { defaultUnauthorizedResponse { description = "Default unauthorized Response" @@ -733,7 +747,7 @@ class OperationBuilderTest : StringSpec({ val route = RouteMeta( path = "/test", method = HttpMethod.Get, - documentation = OpenApiRoute().also { route -> + documentation = RouteConfig().also { route -> route.response { default { description = "Default Response" @@ -767,7 +781,7 @@ class OperationBuilderTest : StringSpec({ } "automatic unauthorized response for unprotected route" { - val config = PluginConfigDsl().also { + val config = OpenApiPluginConfig().also { it.security { defaultUnauthorizedResponse { description = "Default unauthorized Response" @@ -777,7 +791,7 @@ class OperationBuilderTest : StringSpec({ val route = RouteMeta( path = "/test", method = HttpMethod.Get, - documentation = OpenApiRoute().also { route -> + documentation = RouteConfig().also { route -> route.response { default { description = "Default Response" @@ -803,7 +817,7 @@ class OperationBuilderTest : StringSpec({ val route = RouteMeta( path = "/test", method = HttpMethod.Get, - documentation = OpenApiRoute().also { route -> + documentation = RouteConfig().also { route -> route.request { body(KTypeDescriptor(typeOf>())) {} } @@ -859,7 +873,7 @@ class OperationBuilderTest : StringSpec({ } "custom body schema" { - val config = PluginConfigDsl().also { + val config = OpenApiPluginConfig().also { it.schemas { schema("myCustomSchema", SwaggerTypeDescriptor( Schema().also { schema -> @@ -876,7 +890,7 @@ class OperationBuilderTest : StringSpec({ val route = RouteMeta( path = "/test", method = HttpMethod.Get, - documentation = OpenApiRoute().also { route -> + documentation = RouteConfig().also { route -> route.request { body(RefTypeDescriptor("myCustomSchema")) } @@ -916,7 +930,7 @@ class OperationBuilderTest : StringSpec({ } "custom multipart-body schema" { - val config = PluginConfigDsl().also { + val config = OpenApiPluginConfig().also { it.schemas { schema("myCustomSchema", SwaggerTypeDescriptor( Schema().also { schema -> @@ -933,7 +947,7 @@ class OperationBuilderTest : StringSpec({ val route = RouteMeta( path = "/test", method = HttpMethod.Get, - documentation = OpenApiRoute().also { route -> + documentation = RouteConfig().also { route -> route.request { multipartBody { mediaTypes = setOf( @@ -985,19 +999,19 @@ class OperationBuilderTest : StringSpec({ val number: Int ) - private val defaultPluginConfig = PluginConfigDsl() + private val defaultPluginConfig = OpenApiPluginConfig() - private fun schemaContext(routes: List, pluginConfig: PluginConfigDsl = defaultPluginConfig): SchemaContext { - val pluginConfigData = pluginConfig.build(PluginConfigData.DEFAULT) + private fun schemaContext(routes: List, pluginConfig: OpenApiPluginConfig = defaultPluginConfig): SchemaContext { + val pluginConfigData = pluginConfig.build(OpenApiPluginData.DEFAULT, null) return SchemaContextImpl(pluginConfigData.schemaConfig).also { it.addGlobal(pluginConfigData.schemaConfig) it.add(routes) } } - private fun exampleContext(routes: List, pluginConfig: PluginConfigDsl = defaultPluginConfig): ExampleContext { - val pluginConfigData = pluginConfig.build(PluginConfigData.DEFAULT) + private fun exampleContext(routes: List, pluginConfig: OpenApiPluginConfig = defaultPluginConfig): ExampleContext { + val pluginConfigData = pluginConfig.build(OpenApiPluginData.DEFAULT, null) return ExampleContextImpl(pluginConfigData.exampleConfig.exampleEncoder).also { it.addShared(pluginConfigData.exampleConfig) it.add(routes) @@ -1008,9 +1022,9 @@ class OperationBuilderTest : StringSpec({ route: RouteMeta, schemaContext: SchemaContext, exampleContext: ExampleContext, - pluginConfig: PluginConfigDsl = defaultPluginConfig + pluginConfig: OpenApiPluginConfig = defaultPluginConfig ): Operation { - val pluginConfigData = pluginConfig.build(PluginConfigData.DEFAULT) + val pluginConfigData = pluginConfig.build(OpenApiPluginData.DEFAULT, null) return OperationBuilder( operationTagsBuilder = OperationTagsBuilder(pluginConfigData), parameterBuilder = ParameterBuilder( diff --git a/ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/PathsBuilderTest.kt b/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/PathsBuilderTest.kt similarity index 61% rename from ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/PathsBuilderTest.kt rename to ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/PathsBuilderTest.kt index d9c9d657..7bf8276d 100644 --- a/ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/PathsBuilderTest.kt +++ b/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/PathsBuilderTest.kt @@ -1,26 +1,26 @@ package io.github.smiley4.ktorswaggerui.builder -import io.github.smiley4.ktorswaggerui.builder.example.ExampleContext -import io.github.smiley4.ktorswaggerui.builder.example.ExampleContextImpl -import io.github.smiley4.ktorswaggerui.builder.openapi.ContentBuilder -import io.github.smiley4.ktorswaggerui.builder.openapi.ExternalDocumentationBuilder -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.openapi.ServerBuilder -import io.github.smiley4.ktorswaggerui.builder.route.RouteMeta -import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContext -import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContextImpl -import io.github.smiley4.ktorswaggerui.data.PluginConfigData -import io.github.smiley4.ktorswaggerui.dsl.config.PluginConfigDsl -import io.github.smiley4.ktorswaggerui.dsl.routes.OpenApiRoute +import io.github.smiley4.ktoropenapi.builder.example.ExampleContext +import io.github.smiley4.ktoropenapi.builder.example.ExampleContextImpl +import io.github.smiley4.ktoropenapi.builder.openapi.ContentBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ExternalDocumentationBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.HeaderBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.OperationBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.OperationTagsBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ParameterBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.PathBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.PathsBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.RequestBodyBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ResponseBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ResponsesBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.SecurityRequirementsBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.ServerBuilder +import io.github.smiley4.ktoropenapi.builder.route.RouteMeta +import io.github.smiley4.ktoropenapi.builder.schema.SchemaContext +import io.github.smiley4.ktoropenapi.builder.schema.SchemaContextImpl +import io.github.smiley4.ktoropenapi.data.OpenApiPluginData +import io.github.smiley4.ktoropenapi.config.OpenApiPluginConfig +import io.github.smiley4.ktoropenapi.config.RouteConfig import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder import io.kotest.matchers.maps.shouldHaveSize @@ -72,6 +72,29 @@ class PathsBuilderTest : StringSpec({ } } + "custom root path" { + val config = defaultPluginConfig.apply { + rootPath = "custom/root/path" + } + val routes = listOf( + route(HttpMethod.Get, "/different/path"), + route(HttpMethod.Get, "/test/path"), + route(HttpMethod.Post, "/test/path"), + ) + val schemaContext = schemaContext(routes, config) + val exampleContext = exampleContext(routes, config) + buildPathsObject(routes, schemaContext, exampleContext, config).also { paths -> + paths shouldHaveSize 2 + paths.keys shouldContainExactlyInAnyOrder listOf( + "custom/root/path/different/path", + "custom/root/path/test/path" + ) + paths["custom/root/path/different/path"]!!.get.shouldNotBeNull() + paths["custom/root/path/test/path"]!!.get.shouldNotBeNull() + paths["custom/root/path/test/path"]!!.post.shouldNotBeNull() + } + } + }) { companion object { @@ -79,22 +102,22 @@ class PathsBuilderTest : StringSpec({ private fun route(method: HttpMethod, url: String) = RouteMeta( path = url, method = method, - documentation = OpenApiRoute().build(), + documentation = RouteConfig().build(), protected = false ) - private val defaultPluginConfig = PluginConfigDsl() + private val defaultPluginConfig = OpenApiPluginConfig() - private fun schemaContext(routes: List, pluginConfig: PluginConfigDsl): SchemaContext { - val pluginConfigData = pluginConfig.build(PluginConfigData.DEFAULT) + private fun schemaContext(routes: List, pluginConfig: OpenApiPluginConfig): SchemaContext { + val pluginConfigData = pluginConfig.build(OpenApiPluginData.DEFAULT, null) return SchemaContextImpl(pluginConfigData.schemaConfig).also { it.addGlobal(pluginConfigData.schemaConfig) it.add(routes) } } - private fun exampleContext(routes: List, pluginConfig: PluginConfigDsl): ExampleContext { - val pluginConfigData = pluginConfig.build(PluginConfigData.DEFAULT) + private fun exampleContext(routes: List, pluginConfig: OpenApiPluginConfig): ExampleContext { + val pluginConfigData = pluginConfig.build(OpenApiPluginData.DEFAULT, null) return ExampleContextImpl(pluginConfigData.exampleConfig.exampleEncoder).also { it.addShared(pluginConfigData.exampleConfig) it.add(routes) @@ -105,10 +128,11 @@ class PathsBuilderTest : StringSpec({ routes: Collection, schemaContext: SchemaContext, exampleContext: ExampleContext, - pluginConfig: PluginConfigDsl = defaultPluginConfig + pluginConfig: OpenApiPluginConfig = defaultPluginConfig ): Paths { - val pluginConfigData = pluginConfig.build(PluginConfigData.DEFAULT) + val pluginConfigData = pluginConfig.build(OpenApiPluginData.DEFAULT, null) return PathsBuilder( + config = pluginConfigData, pathBuilder = PathBuilder( operationBuilder = OperationBuilder( operationTagsBuilder = OperationTagsBuilder(pluginConfigData), diff --git a/ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/SecuritySchemesBuilderTest.kt b/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/SecuritySchemesBuilderTest.kt similarity index 93% rename from ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/SecuritySchemesBuilderTest.kt rename to ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/SecuritySchemesBuilderTest.kt index 9484aa39..3cac1a16 100644 --- a/ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/SecuritySchemesBuilderTest.kt +++ b/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/SecuritySchemesBuilderTest.kt @@ -1,12 +1,12 @@ package io.github.smiley4.ktorswaggerui.builder -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.builder.openapi.OAuthFlowsBuilder -import io.github.smiley4.ktorswaggerui.builder.openapi.SecuritySchemesBuilder -import io.github.smiley4.ktorswaggerui.dsl.config.OpenApiSecurityScheme +import io.github.smiley4.ktoropenapi.builder.openapi.OAuthFlowsBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.SecuritySchemesBuilder +import io.github.smiley4.ktoropenapi.config.AuthKeyLocation +import io.github.smiley4.ktoropenapi.config.AuthScheme +import io.github.smiley4.ktoropenapi.config.AuthType +import io.github.smiley4.ktoropenapi.data.SecuritySchemeData +import io.github.smiley4.ktoropenapi.config.SecuritySchemeConfig import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder import io.kotest.matchers.maps.shouldBeEmpty @@ -199,10 +199,11 @@ class SecuritySchemesBuilderTest : StringSpec({ companion object { - private fun buildSecuritySchemeObjects(builders: Map Unit>): Map { + private fun buildSecuritySchemeObjects(builders: Map Unit>): Map { return SecuritySchemesBuilder( oAuthFlowsBuilder = OAuthFlowsBuilder() - ).build(builders.map { (name, entry) -> OpenApiSecurityScheme(name).apply(entry).build(SecuritySchemeData.DEFAULT) }) + ).build(builders.map { (name, entry) -> SecuritySchemeConfig(name).apply(entry).build( + SecuritySchemeData.DEFAULT) }) } } diff --git a/ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/ServersBuilderTest.kt b/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/ServersBuilderTest.kt similarity index 82% rename from ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/ServersBuilderTest.kt rename to ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/ServersBuilderTest.kt index afa7dec7..da333bc0 100644 --- a/ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/ServersBuilderTest.kt +++ b/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/ServersBuilderTest.kt @@ -1,11 +1,10 @@ package io.github.smiley4.ktorswaggerui.builder -import io.github.smiley4.ktorswaggerui.builder.openapi.ServerBuilder -import io.github.smiley4.ktorswaggerui.data.ServerData -import io.github.smiley4.ktorswaggerui.dsl.config.OpenApiServer +import io.github.smiley4.ktoropenapi.builder.openapi.ServerBuilder +import io.github.smiley4.ktoropenapi.data.ServerData +import io.github.smiley4.ktoropenapi.config.ServerConfig import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder -import io.kotest.matchers.should import io.kotest.matchers.shouldBe import io.swagger.v3.oas.models.servers.Server @@ -56,8 +55,8 @@ class ServersBuilderTest : StringSpec({ companion object { - private fun buildServerObject(builder: OpenApiServer.() -> Unit): Server { - return ServerBuilder().build(OpenApiServer().apply(builder).build(ServerData.DEFAULT)) + private fun buildServerObject(builder: ServerConfig.() -> Unit): Server { + return ServerBuilder().build(ServerConfig().apply(builder).build(ServerData.DEFAULT)) } } diff --git a/ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/TagsBuilderTest.kt b/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/TagsBuilderTest.kt similarity index 76% rename from ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/TagsBuilderTest.kt rename to ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/TagsBuilderTest.kt index 0c1e537b..832f411f 100644 --- a/ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/TagsBuilderTest.kt +++ b/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/TagsBuilderTest.kt @@ -1,9 +1,9 @@ package io.github.smiley4.ktorswaggerui.builder -import io.github.smiley4.ktorswaggerui.builder.openapi.TagBuilder -import io.github.smiley4.ktorswaggerui.builder.openapi.TagExternalDocumentationBuilder -import io.github.smiley4.ktorswaggerui.data.TagData -import io.github.smiley4.ktorswaggerui.dsl.config.OpenApiTag +import io.github.smiley4.ktoropenapi.builder.openapi.TagBuilder +import io.github.smiley4.ktoropenapi.builder.openapi.TagExternalDocumentationBuilder +import io.github.smiley4.ktoropenapi.data.TagData +import io.github.smiley4.ktoropenapi.config.TagConfig import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe @@ -44,10 +44,10 @@ class TagsBuilderTest : StringSpec({ companion object { - private fun buildTagObject(name: String, builder: OpenApiTag.() -> Unit): Tag { + private fun buildTagObject(name: String, builder: TagConfig.() -> Unit): Tag { return TagBuilder( tagExternalDocumentationBuilder = TagExternalDocumentationBuilder() - ).build(OpenApiTag(name).apply(builder).build(TagData.DEFAULT)) + ).build(TagConfig(name).apply(builder).build(TagData.DEFAULT)) } } diff --git a/ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/misc/RouteDocumentationMergerTest.kt b/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/misc/RouteDocumentationMergerTest.kt similarity index 90% rename from ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/misc/RouteDocumentationMergerTest.kt rename to ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/misc/RouteDocumentationMergerTest.kt index 5f9095c6..7615d6a2 100644 --- a/ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/misc/RouteDocumentationMergerTest.kt +++ b/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/misc/RouteDocumentationMergerTest.kt @@ -1,8 +1,8 @@ package io.github.smiley4.ktorswaggerui.misc -import io.github.smiley4.ktorswaggerui.builder.route.RouteDocumentationMerger -import io.github.smiley4.ktorswaggerui.data.KTypeDescriptor -import io.github.smiley4.ktorswaggerui.dsl.routes.OpenApiRoute +import io.github.smiley4.ktoropenapi.builder.route.RouteDocumentationMerger +import io.github.smiley4.ktoropenapi.config.KTypeDescriptor +import io.github.smiley4.ktoropenapi.config.RouteConfig import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder @@ -38,7 +38,7 @@ class RouteDocumentationMergerTest : StringSpec({ "merge complete routes" { merge( route { - specId = "test-spec-a" + specName = "test-spec-a" tags = listOf("a1", "a2") summary = "Summary A" description = "Description A" @@ -61,7 +61,7 @@ class RouteDocumentationMergerTest : StringSpec({ } }, route { - specId = "test-spec-b" + specName = "test-spec-b" tags = listOf("b1", "b2") summary = "Summary B" description = "Description B" @@ -84,7 +84,7 @@ class RouteDocumentationMergerTest : StringSpec({ } } ).also { route -> - route.specId shouldBe "test-spec-a" + route.specName shouldBe "test-spec-a" route.tags shouldContainExactlyInAnyOrder listOf("a1", "a2", "b1", "b2") route.summary shouldBe "Summary A" route.description shouldBe "Description A" @@ -123,11 +123,11 @@ class RouteDocumentationMergerTest : StringSpec({ companion object { - fun route(builder: OpenApiRoute.() -> Unit): OpenApiRoute { - return OpenApiRoute().apply(builder) + fun route(builder: RouteConfig.() -> Unit): RouteConfig { + return RouteConfig().apply(builder) } - fun merge(a: OpenApiRoute, b: OpenApiRoute): OpenApiRoute { + fun merge(a: RouteConfig, b: RouteConfig): RouteConfig { return RouteDocumentationMerger().merge(a, b) } diff --git a/ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/misc/RoutingTests.kt b/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/misc/RoutingTests.kt similarity index 62% rename from ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/misc/RoutingTests.kt rename to ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/misc/RoutingTests.kt index c7fcc782..0cea9e03 100644 --- a/ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/misc/RoutingTests.kt +++ b/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/misc/RoutingTests.kt @@ -1,11 +1,9 @@ package io.github.smiley4.ktorswaggerui.misc -import io.github.smiley4.ktorswaggerui.SwaggerUI -import io.github.smiley4.ktorswaggerui.data.OutputFormat -import io.github.smiley4.ktorswaggerui.routing.openApiSpec -import io.github.smiley4.ktorswaggerui.routing.swaggerUI +import io.github.smiley4.ktoropenapi.OpenApi +import io.github.smiley4.ktoropenapi.config.OutputFormat +import io.github.smiley4.ktoropenapi.openApi import io.kotest.matchers.shouldBe -import io.kotest.matchers.string.shouldContain import io.kotest.matchers.string.shouldNotBeEmpty import io.kotest.matchers.string.shouldStartWith import io.ktor.client.HttpClient @@ -14,7 +12,6 @@ import io.ktor.client.statement.bodyAsText import io.ktor.http.ContentType import io.ktor.http.HttpStatusCode import io.ktor.http.contentType -import io.ktor.server.application.call import io.ktor.server.response.respondText import io.ktor.server.routing.get import io.ktor.server.routing.route @@ -24,7 +21,7 @@ import kotlin.test.Test class RoutingTests { @Test - fun basicRouting() = swaggerUITestApplication { + fun jsonSpec() = swaggerUITestApplication { get("hello").also { it.status shouldBe HttpStatusCode.OK it.body shouldBe "Hello Test" @@ -32,21 +29,6 @@ class RoutingTests { get("/").also { it.status shouldBe HttpStatusCode.NotFound } - get("/swagger").also { - it.status shouldBe HttpStatusCode.OK - it.contentType shouldBe ContentType.Text.Html - it.body.shouldNotBeEmpty() - } - get("/swagger/index.html").also { - it.status shouldBe HttpStatusCode.OK - it.contentType shouldBe ContentType.Text.Html - it.body.shouldNotBeEmpty() - } - get("/swagger/swagger-initializer.js").also { - it.status shouldBe HttpStatusCode.OK - it.contentType shouldBe ContentType.Application.JavaScript - it.body shouldContain "url: \"/api.json\"" - } get("/api.json").also { it.status shouldBe HttpStatusCode.OK it.contentType shouldBe ContentType.Application.Json @@ -57,7 +39,7 @@ class RoutingTests { @Test - fun basicRoutingYml() = swaggerUITestApplication(OutputFormat.YAML) { + fun yamlSpec() = swaggerUITestApplication(OutputFormat.YAML) { get("hello").also { it.status shouldBe HttpStatusCode.OK it.body shouldBe "Hello Test" @@ -65,21 +47,6 @@ class RoutingTests { get("/").also { it.status shouldBe HttpStatusCode.NotFound } - get("/swagger").also { - it.status shouldBe HttpStatusCode.OK - it.contentType shouldBe ContentType.Text.Html - it.body.shouldNotBeEmpty() - } - get("/swagger/index.html").also { - it.status shouldBe HttpStatusCode.OK - it.contentType shouldBe ContentType.Text.Html - it.body.shouldNotBeEmpty() - } - get("/swagger/swagger-initializer.js").also { - it.status shouldBe HttpStatusCode.OK - it.contentType shouldBe ContentType.Application.JavaScript - it.body shouldContain "url: \"/api.yml\"" - } get("/api.yml").also { it.status shouldBe HttpStatusCode.OK it.contentType shouldBe ContentType.Text.Plain.withParameter("charset", "utf-8") @@ -91,9 +58,9 @@ class RoutingTests { private fun swaggerUITestApplication(format: OutputFormat = OutputFormat.JSON, block: suspend TestContext.() -> Unit) { testApplication { val client = createClient { - this.followRedirects = followRedirects + this.followRedirects = false } - install(SwaggerUI) { + install(OpenApi) { outputFormat = format } routing { @@ -102,10 +69,7 @@ class RoutingTests { OutputFormat.YAML -> "yml" } route("api.$routeSuffix") { - openApiSpec() - } - route("swagger") { - swaggerUI("/api.$routeSuffix") + openApi() } get("hello") { call.respondText("Hello Test") diff --git a/ktor-swagger-ui/src/test/resources/logback.xml b/ktor-openapi/src/test/resources/logback.xml similarity index 100% rename from ktor-swagger-ui/src/test/resources/logback.xml rename to ktor-openapi/src/test/resources/logback.xml diff --git a/ktor-swagger-ui-examples/build.gradle.kts b/ktor-swagger-ui-examples/build.gradle.kts index 58bec51a..7527e9eb 100644 --- a/ktor-swagger-ui-examples/build.gradle.kts +++ b/ktor-swagger-ui-examples/build.gradle.kts @@ -13,14 +13,10 @@ repositories { } dependencies { - val versionKtor: String by project - val versionSwaggerParser: String by project - val versionSchemaKenerator: String by project - val versionKotlinLogging: String by project - val versionLogback: String by project - implementation(project(":ktor-swagger-ui")) + implementation(project(":ktor-openapi")) + val versionKtor: String by project implementation("io.ktor:ktor-server-netty-jvm:$versionKtor") implementation("io.ktor:ktor-server-content-negotiation:$versionKtor") implementation("io.ktor:ktor-serialization-jackson:$versionKtor") @@ -28,14 +24,20 @@ dependencies { implementation("io.ktor:ktor-server-call-logging:$versionKtor") implementation("io.ktor:ktor-server-test-host:$versionKtor") + val versionSchemaKenerator: String by project implementation("io.github.smiley4:schema-kenerator-core:$versionSchemaKenerator") implementation("io.github.smiley4:schema-kenerator-reflection:$versionSchemaKenerator") implementation("io.github.smiley4:schema-kenerator-serialization:$versionSchemaKenerator") implementation("io.github.smiley4:schema-kenerator-swagger:$versionSchemaKenerator") implementation("io.github.smiley4:schema-kenerator-jackson:$versionSchemaKenerator") + val versionSwaggerParser: String by project implementation("io.swagger.parser.v3:swagger-parser:$versionSwaggerParser") + + val versionKotlinLogging: String by project implementation("io.github.oshai:kotlin-logging-jvm:$versionKotlinLogging") + + val versionLogback: String by project implementation("ch.qos.logback:logback-classic:$versionLogback") } diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Authentication.kt b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Authentication.kt index 1faa78eb..1255adaa 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Authentication.kt +++ b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Authentication.kt @@ -1,13 +1,12 @@ package io.github.smiley4.ktorswaggerui.examples -import io.github.smiley4.ktorswaggerui.SwaggerUI -import io.github.smiley4.ktorswaggerui.data.AuthScheme -import io.github.smiley4.ktorswaggerui.data.AuthType -import io.github.smiley4.ktorswaggerui.dsl.routing.get -import io.github.smiley4.ktorswaggerui.routing.openApiSpec -import io.github.smiley4.ktorswaggerui.routing.swaggerUI +import io.github.smiley4.ktoropenapi.OpenApi +import io.github.smiley4.ktoropenapi.config.AuthScheme +import io.github.smiley4.ktoropenapi.config.AuthType +import io.github.smiley4.ktoropenapi.get +import io.github.smiley4.ktoropenapi.openApi +import io.github.smiley4.ktorswaggerui.swaggerUI 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 @@ -41,7 +40,7 @@ private fun Application.myModule() { } // Install and configure the "SwaggerUI"-Plugin - install(SwaggerUI) { + install(OpenApi) { schemas { } security { // configure a basic-auth security scheme @@ -66,7 +65,7 @@ private fun Application.myModule() { swaggerUI("/api.json") } route("api.json") { - openApiSpec() + openApi() } authenticate { diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Basics.kt b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Basics.kt index 40290b37..5dd373aa 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Basics.kt +++ b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Basics.kt @@ -1,12 +1,11 @@ package io.github.smiley4.ktorswaggerui.examples -import io.github.smiley4.ktorswaggerui.SwaggerUI -import io.github.smiley4.ktorswaggerui.dsl.routing.get -import io.github.smiley4.ktorswaggerui.routing.openApiSpec -import io.github.smiley4.ktorswaggerui.routing.swaggerUI +import io.github.smiley4.ktoropenapi.OpenApi +import io.github.smiley4.ktoropenapi.get +import io.github.smiley4.ktoropenapi.openApi +import io.github.smiley4.ktorswaggerui.swaggerUI import io.ktor.http.HttpStatusCode 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 @@ -20,8 +19,8 @@ fun main() { private fun Application.myModule() { - // Install and configure the "SwaggerUI"-Plugin - install(SwaggerUI) { + // Install and configure the "OpenApi"-Plugin + install(OpenApi) { // configure basic information about the api info { title = "Example API" @@ -53,7 +52,7 @@ private fun Application.myModule() { // Create a route for the openapi-spec file. // This route will not be included in the spec. route("api.json") { - openApiSpec() + openApi() } // a documented route diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/CompleteConfig.kt b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/CompleteConfig.kt index 16e17535..5f7184c1 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/CompleteConfig.kt +++ b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/CompleteConfig.kt @@ -1,23 +1,22 @@ package io.github.smiley4.ktorswaggerui.examples -import io.github.smiley4.ktorswaggerui.SwaggerUI -import io.github.smiley4.ktorswaggerui.data.AuthScheme -import io.github.smiley4.ktorswaggerui.data.AuthType -import io.github.smiley4.ktorswaggerui.data.SwaggerUiSort -import io.github.smiley4.ktorswaggerui.data.SwaggerUiSyntaxHighlight -import io.github.smiley4.ktorswaggerui.dsl.config.PluginConfigDsl -import io.github.smiley4.ktorswaggerui.dsl.routing.get -import io.github.smiley4.ktorswaggerui.routing.openApiSpec -import io.github.smiley4.ktorswaggerui.routing.swaggerUI +import io.github.smiley4.ktoropenapi.OpenApi +import io.github.smiley4.ktoropenapi.config.OpenApiPluginConfig +import io.github.smiley4.ktoropenapi.config.AuthScheme +import io.github.smiley4.ktoropenapi.config.AuthType +import io.github.smiley4.ktoropenapi.get +import io.github.smiley4.ktoropenapi.openApi +import io.github.smiley4.ktorswaggerui.config.SwaggerUISort +import io.github.smiley4.ktorswaggerui.config.SwaggerUISyntaxHighlight +import io.github.smiley4.ktorswaggerui.swaggerUI import io.github.smiley4.schemakenerator.reflection.processReflection import io.github.smiley4.schemakenerator.swagger.compileReferencingRoot import io.github.smiley4.schemakenerator.swagger.data.TitleType import io.github.smiley4.schemakenerator.swagger.generateSwaggerSchema -import io.github.smiley4.schemakenerator.swagger.withAutoTitle +import io.github.smiley4.schemakenerator.swagger.withTitle import io.ktor.http.ContentType import io.ktor.http.HttpStatusCode 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 @@ -35,13 +34,13 @@ class Greeting( val name: String ) + /** * A (nearly) complete - and mostly nonsensical - plugin configuration */ private fun Application.myModule() { - - install(SwaggerUI) { + install(OpenApi) { info { title = "Example API" version = "latest" @@ -80,13 +79,6 @@ private fun Application.myModule() { description = "the version of the server api" } } - swagger { - displayOperationId = true - showTagFilterInput = true - sort = SwaggerUiSort.HTTP_METHOD - syntaxHighlight = SwaggerUiSyntaxHighlight.MONOKAI - withCredentials = false - } security { defaultUnauthorizedResponse { description = "Username or password is invalid" @@ -116,7 +108,7 @@ private fun Application.myModule() { type .processReflection() .generateSwaggerSchema() - .withAutoTitle(TitleType.SIMPLE) + .withTitle(TitleType.SIMPLE) .compileReferencingRoot() } overwrite(Schema().also { @@ -135,7 +127,7 @@ private fun Application.myModule() { } } - specAssigner = { _, _ -> PluginConfigDsl.DEFAULT_SPEC_ID } + specAssigner = { _, _ -> OpenApiPluginConfig.DEFAULT_SPEC_ID } pathFilter = { _, url -> url.firstOrNull() != "hidden" } ignoredRouteSelectors = emptySet() ignoredRouteSelectorClassNames = emptySet() @@ -147,10 +139,16 @@ private fun Application.myModule() { // add the routes for swagger-ui and api-spec route("swagger") { - swaggerUI("/api.json") + swaggerUI("/api.json") { + displayOperationId = true + showTagFilterInput = true + sort = SwaggerUISort.HTTP_METHOD + syntaxHighlight = SwaggerUISyntaxHighlight.MONOKAI + withCredentials = false + } } route("api.json") { - openApiSpec() + openApi() } // a documented route @@ -159,7 +157,7 @@ private fun Application.myModule() { summary = "hello world route" description = "A Hello-World route as an example." tags("hello", "example") - specId = PluginConfigDsl.DEFAULT_SPEC_ID + specName = io.github.smiley4.ktoropenapi.config.OpenApiPluginConfig.DEFAULT_SPEC_ID deprecated = false hidden = false protected = false diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/CustomizedSchemaGenerator.kt b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/CustomizedSchemaGenerator.kt index 761a6a6e..832541e4 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/CustomizedSchemaGenerator.kt +++ b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/CustomizedSchemaGenerator.kt @@ -1,9 +1,9 @@ package io.github.smiley4.ktorswaggerui.examples -import io.github.smiley4.ktorswaggerui.SwaggerUI -import io.github.smiley4.ktorswaggerui.dsl.routing.get -import io.github.smiley4.ktorswaggerui.routing.openApiSpec -import io.github.smiley4.ktorswaggerui.routing.swaggerUI +import io.github.smiley4.ktoropenapi.OpenApi +import io.github.smiley4.ktoropenapi.get +import io.github.smiley4.ktoropenapi.openApi +import io.github.smiley4.ktorswaggerui.swaggerUI import io.github.smiley4.schemakenerator.serialization.processKotlinxSerialization import io.github.smiley4.schemakenerator.swagger.compileReferencingRoot import io.github.smiley4.schemakenerator.swagger.data.TitleType @@ -26,7 +26,7 @@ fun main() { private fun Application.myModule() { // Install and configure the "SwaggerUI"-Plugin - install(SwaggerUI) { + install(OpenApi) { schemas { // replace default schema-generator with customized one generator = { type -> @@ -52,7 +52,7 @@ private fun Application.myModule() { // Create a route for the openapi-spec file. // This route will not be included in the spec. route("api.json") { - openApiSpec() + openApi() } // a documented route diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Examples.kt b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Examples.kt index 4240c3ca..74efcd60 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Examples.kt +++ b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Examples.kt @@ -1,12 +1,11 @@ package io.github.smiley4.ktorswaggerui.examples -import io.github.smiley4.ktorswaggerui.SwaggerUI -import io.github.smiley4.ktorswaggerui.data.KTypeDescriptor -import io.github.smiley4.ktorswaggerui.dsl.routing.get -import io.github.smiley4.ktorswaggerui.routing.openApiSpec -import io.github.smiley4.ktorswaggerui.routing.swaggerUI +import io.github.smiley4.ktoropenapi.OpenApi +import io.github.smiley4.ktoropenapi.config.KTypeDescriptor +import io.github.smiley4.ktoropenapi.get +import io.github.smiley4.ktoropenapi.openApi +import io.github.smiley4.ktorswaggerui.swaggerUI 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 @@ -22,7 +21,7 @@ fun main() { private fun Application.myModule() { // Install and customize the "SwaggerUI"-Plugin - install(SwaggerUI) { + install(OpenApi) { examples { // specify two shared examples @@ -58,7 +57,7 @@ private fun Application.myModule() { swaggerUI("/api.json") } route("api.json") { - openApiSpec() + openApi() } diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/ExternalOpenApiSpec.kt b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/ExternalOpenApiSpec.kt new file mode 100644 index 00000000..31506856 --- /dev/null +++ b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/ExternalOpenApiSpec.kt @@ -0,0 +1,25 @@ +package io.github.smiley4.ktorswaggerui.examples + +import io.github.smiley4.ktorswaggerui.swaggerUI +import io.ktor.server.application.Application +import io.ktor.server.engine.embeddedServer +import io.ktor.server.netty.Netty +import io.ktor.server.routing.route +import io.ktor.server.routing.routing + +fun main() { + embeddedServer(Netty, port = 8080, host = "localhost", module = Application::myModule).start(wait = true) +} + +private fun Application.myModule() { + + routing { + + // Create a route for the swagger-ui using an external openapi-spec. + route("swagger") { + swaggerUI("https://petstore3.swagger.io/api/v3/openapi.json") + } + + } + +} diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/FileUpload.kt b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/FileUpload.kt index 189adae8..8abc3482 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/FileUpload.kt +++ b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/FileUpload.kt @@ -1,13 +1,12 @@ package io.github.smiley4.ktorswaggerui.examples -import io.github.smiley4.ktorswaggerui.SwaggerUI -import io.github.smiley4.ktorswaggerui.dsl.routing.post -import io.github.smiley4.ktorswaggerui.routing.openApiSpec -import io.github.smiley4.ktorswaggerui.routing.swaggerUI +import io.github.smiley4.ktoropenapi.OpenApi +import io.github.smiley4.ktoropenapi.post +import io.github.smiley4.ktoropenapi.openApi +import io.github.smiley4.ktorswaggerui.swaggerUI import io.ktor.http.ContentType import io.ktor.http.HttpStatusCode 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 @@ -24,7 +23,7 @@ fun main() { private fun Application.myModule() { // Install the "SwaggerUI"-Plugin and use the default configuration - install(SwaggerUI) { + install(OpenApi) { schemas { // overwrite type "File" with custom schema for binary data overwrite(Schema().also { @@ -41,7 +40,7 @@ private fun Application.myModule() { swaggerUI("/api.json") } route("api.json") { - openApiSpec() + openApi() } // upload a single file, either as png, jpeg or svg diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/KotlinxSerialization.kt b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/KotlinxSerialization.kt index c2c81992..ac2a2444 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/KotlinxSerialization.kt +++ b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/KotlinxSerialization.kt @@ -1,10 +1,10 @@ package io.github.smiley4.ktorswaggerui.examples -import io.github.smiley4.ktorswaggerui.SwaggerUI -import io.github.smiley4.ktorswaggerui.data.kotlinxExampleEncoder -import io.github.smiley4.ktorswaggerui.dsl.routing.get -import io.github.smiley4.ktorswaggerui.routing.openApiSpec -import io.github.smiley4.ktorswaggerui.routing.swaggerUI +import io.github.smiley4.ktoropenapi.OpenApi +import io.github.smiley4.ktoropenapi.config.kotlinxExampleEncoder +import io.github.smiley4.ktoropenapi.get +import io.github.smiley4.ktoropenapi.openApi +import io.github.smiley4.ktorswaggerui.swaggerUI import io.github.smiley4.schemakenerator.serialization.processKotlinxSerialization import io.github.smiley4.schemakenerator.swagger.compileReferencingRoot import io.github.smiley4.schemakenerator.swagger.data.TitleType @@ -26,7 +26,7 @@ fun main() { private fun Application.myModule() { - install(SwaggerUI) { + install(OpenApi) { schemas { // configure the schema generator to use kotlinx-serializer // (see https://github.com/SMILEY4/schema-kenerator/wiki for more information) @@ -51,7 +51,7 @@ private fun Application.myModule() { swaggerUI("/api.json") } route("api.json") { - openApiSpec() + openApi() } // a documented route diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Minimal.kt b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Minimal.kt index 81d105ed..1aef2c53 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Minimal.kt +++ b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Minimal.kt @@ -1,11 +1,10 @@ package io.github.smiley4.ktorswaggerui.examples -import io.github.smiley4.ktorswaggerui.SwaggerUI -import io.github.smiley4.ktorswaggerui.dsl.routing.get -import io.github.smiley4.ktorswaggerui.routing.openApiSpec -import io.github.smiley4.ktorswaggerui.routing.swaggerUI +import io.github.smiley4.ktoropenapi.OpenApi +import io.github.smiley4.ktoropenapi.get +import io.github.smiley4.ktoropenapi.openApi +import io.github.smiley4.ktorswaggerui.swaggerUI 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 @@ -20,7 +19,7 @@ fun main() { private fun Application.myModule() { // Install the "SwaggerUI"-Plugin and use the default configuration - install(SwaggerUI) + install(OpenApi) routing { @@ -32,7 +31,7 @@ private fun Application.myModule() { // Create a route for the openapi-spec file. // This route will not be included in the spec. route("api.json") { - openApiSpec() + openApi() } // a documented route diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/MultipleSpecs.kt b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/MultipleSpecs.kt index 00d0bc9d..55847d07 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/MultipleSpecs.kt +++ b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/MultipleSpecs.kt @@ -1,12 +1,11 @@ package io.github.smiley4.ktorswaggerui.examples -import io.github.smiley4.ktorswaggerui.SwaggerUI -import io.github.smiley4.ktorswaggerui.dsl.routing.get -import io.github.smiley4.ktorswaggerui.dsl.routing.route -import io.github.smiley4.ktorswaggerui.routing.openApiSpec -import io.github.smiley4.ktorswaggerui.routing.swaggerUI +import io.github.smiley4.ktoropenapi.OpenApi +import io.github.smiley4.ktoropenapi.get +import io.github.smiley4.ktoropenapi.route +import io.github.smiley4.ktoropenapi.openApi +import io.github.smiley4.ktorswaggerui.swaggerUI 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 @@ -21,7 +20,7 @@ fun main() { private fun Application.myModule() { // Install and configure the "SwaggerUI"-Plugin - install(SwaggerUI) { + install(OpenApi) { // "global" configuration for all specs info { title = "Example API" @@ -52,7 +51,7 @@ private fun Application.myModule() { } route("api.json") { // api-spec containing all routes assigned to "v1" - openApiSpec("version1") + openApi("version1") } } @@ -64,14 +63,14 @@ private fun Application.myModule() { } route("api.json") { // api-spec containing all routes assigned to "v2" - openApiSpec("version2") + openApi("version2") } } // version 1.0 routes route("v1", { - specId = "version1" + specName = "version1" }) { // "hello"-route in version 1.0 @@ -85,7 +84,7 @@ private fun Application.myModule() { // version 2.0 routes route("v2", { - specId = "version2" + specName = "version2" }) { // "hello"-route in version 2.0 diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Petstore.kt b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Petstore.kt index ab9e04df..bf415616 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Petstore.kt +++ b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Petstore.kt @@ -1,14 +1,13 @@ package io.github.smiley4.ktorswaggerui.examples -import io.github.smiley4.ktorswaggerui.SwaggerUI -import io.github.smiley4.ktorswaggerui.dsl.routing.delete -import io.github.smiley4.ktorswaggerui.dsl.routing.get -import io.github.smiley4.ktorswaggerui.dsl.routing.post -import io.github.smiley4.ktorswaggerui.routing.openApiSpec -import io.github.smiley4.ktorswaggerui.routing.swaggerUI +import io.github.smiley4.ktoropenapi.OpenApi +import io.github.smiley4.ktoropenapi.delete +import io.github.smiley4.ktoropenapi.get +import io.github.smiley4.ktoropenapi.post +import io.github.smiley4.ktoropenapi.openApi +import io.github.smiley4.ktorswaggerui.swaggerUI import io.ktor.http.HttpStatusCode 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 @@ -27,7 +26,7 @@ fun main() { */ private fun Application.myModule() { - install(SwaggerUI) { + install(OpenApi) { info { title = "Swagger Petstore" version = "1.0.0" @@ -53,7 +52,7 @@ private fun Application.myModule() { swaggerUI("/api.json") } route("api.json") { - openApiSpec() + openApi() } route("/pets") { diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/RequestResponse.kt b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/RequestResponse.kt index 50b02fee..e46a2330 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/RequestResponse.kt +++ b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/RequestResponse.kt @@ -3,14 +3,13 @@ package io.github.smiley4.ktorswaggerui.examples import com.fasterxml.jackson.core.util.DefaultIndenter import com.fasterxml.jackson.core.util.DefaultPrettyPrinter import com.fasterxml.jackson.databind.SerializationFeature -import io.github.smiley4.ktorswaggerui.SwaggerUI -import io.github.smiley4.ktorswaggerui.dsl.routing.post -import io.github.smiley4.ktorswaggerui.routing.openApiSpec -import io.github.smiley4.ktorswaggerui.routing.swaggerUI +import io.github.smiley4.ktoropenapi.OpenApi +import io.github.smiley4.ktoropenapi.post +import io.github.smiley4.ktoropenapi.openApi +import io.github.smiley4.ktorswaggerui.swaggerUI import io.ktor.http.HttpStatusCode import io.ktor.serialization.jackson.jackson 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 @@ -27,7 +26,7 @@ fun main() { private fun Application.myModule() { // Install the "SwaggerUI"-Plugin and use the default configuration - install(SwaggerUI) + install(OpenApi) install(ContentNegotiation) { jackson { @@ -46,7 +45,7 @@ private fun Application.myModule() { swaggerUI("/api.json") } route("api.json") { - openApiSpec() + openApi() } // a documented route diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Schemas.kt b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Schemas.kt index b078b9af..6b1e7b18 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Schemas.kt +++ b/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Schemas.kt @@ -1,13 +1,13 @@ package io.github.smiley4.ktorswaggerui.examples import com.fasterxml.jackson.annotation.JsonSubTypes -import io.github.smiley4.ktorswaggerui.SwaggerUI -import io.github.smiley4.ktorswaggerui.data.anyOf -import io.github.smiley4.ktorswaggerui.data.array -import io.github.smiley4.ktorswaggerui.data.ref -import io.github.smiley4.ktorswaggerui.dsl.routing.get -import io.github.smiley4.ktorswaggerui.routing.openApiSpec -import io.github.smiley4.ktorswaggerui.routing.swaggerUI +import io.github.smiley4.ktoropenapi.OpenApi +import io.github.smiley4.ktoropenapi.config.anyOf +import io.github.smiley4.ktoropenapi.config.array +import io.github.smiley4.ktoropenapi.config.ref +import io.github.smiley4.ktoropenapi.get +import io.github.smiley4.ktoropenapi.openApi +import io.github.smiley4.ktorswaggerui.swaggerUI import io.github.smiley4.schemakenerator.core.connectSubTypes import io.github.smiley4.schemakenerator.jackson.collectJacksonSubTypes import io.github.smiley4.schemakenerator.reflection.processReflection @@ -16,7 +16,6 @@ import io.github.smiley4.schemakenerator.swagger.data.TitleType import io.github.smiley4.schemakenerator.swagger.generateSwaggerSchema import io.github.smiley4.schemakenerator.swagger.withAutoTitle 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 @@ -33,7 +32,7 @@ fun main() { private fun Application.myModule() { // Install and customize the "SwaggerUI"-Plugin - install(SwaggerUI) { + install(OpenApi) { schemas { // add a swagger schema to the component-section of the api-spec with the id "swagger-schema" @@ -72,7 +71,7 @@ private fun Application.myModule() { swaggerUI("/api.json") } route("api.json") { - openApiSpec() + openApi() } diff --git a/ktor-swagger-ui/build.gradle.kts b/ktor-swagger-ui/build.gradle.kts index 60577456..c3852993 100644 --- a/ktor-swagger-ui/build.gradle.kts +++ b/ktor-swagger-ui/build.gradle.kts @@ -11,6 +11,7 @@ version = projectVersion plugins { kotlin("jvm") id("org.owasp.dependencycheck") + id("com.github.ben-manes.versions") id("io.gitlab.arturbosch.detekt") id("com.vanniktech.maven.publish") id("org.jetbrains.dokka") @@ -22,42 +23,22 @@ repositories { dependencies { val versionKtor: String by project - val versionSwaggerUI: String by project - val versionSwaggerParser: String by project - val versionSchemaKenerator: String by project - val versionKotlinLogging: String by project - val versionKotest: String by project - val versionKotlinTest: String by project - val versionMockk: String by project - val versionJackson: String by project - implementation("io.ktor:ktor-server-core-jvm:$versionKtor") - implementation("io.ktor:ktor-server-auth:$versionKtor") - implementation("io.ktor:ktor-server-resources:$versionKtor") - - implementation("org.webjars:swagger-ui:$versionSwaggerUI") - - implementation("io.swagger.parser.v3:swagger-parser:$versionSwaggerParser") - - implementation("com.fasterxml.jackson.module:jackson-module-kotlin:$versionJackson") - - implementation("io.github.smiley4:schema-kenerator-core:$versionSchemaKenerator") - implementation("io.github.smiley4:schema-kenerator-reflection:$versionSchemaKenerator") - implementation("io.github.smiley4:schema-kenerator-swagger:$versionSchemaKenerator") - - implementation("io.github.oshai:kotlin-logging-jvm:$versionKotlinLogging") - + implementation("io.ktor:ktor-server-content-negotiation:$versionKtor") testImplementation("io.ktor:ktor-server-netty-jvm:$versionKtor") testImplementation("io.ktor:ktor-server-content-negotiation:$versionKtor") testImplementation("io.ktor:ktor-serialization-jackson:$versionKtor") - testImplementation("io.ktor:ktor-server-auth:$versionKtor") - testImplementation("io.ktor:ktor-server-call-logging:$versionKtor") testImplementation("io.ktor:ktor-server-test-host:$versionKtor") + + val versionSwaggerUI: String by project + implementation("org.webjars:swagger-ui:$versionSwaggerUI") + + val versionKotest: String by project testImplementation("io.kotest:kotest-runner-junit5:$versionKotest") testImplementation("io.kotest:kotest-assertions-core:$versionKotest") - testImplementation("org.jetbrains.kotlin:kotlin-test:$versionKotlinTest") - testImplementation("io.mockk:mockk:$versionMockk") + val versionKotlinTest: String by project + testImplementation("org.jetbrains.kotlin:kotlin-test:$versionKotlinTest") } kotlin { @@ -87,9 +68,6 @@ tasks.withType().configureEach { mavenPublishing { val projectGroupId: String by project val projectVersion: String by project - val projectArtifactIdBase: String by project - val projectNameBase: String by project - val projectDescriptionBase: String by project val projectScmUrl: String by project val projectScmConnection: String by project val projectLicenseName: String by project @@ -100,10 +78,10 @@ mavenPublishing { configure(KotlinJvm(JavadocJar.Dokka("dokkaHtml"), true)) publishToMavenCentral(SonatypeHost.S01) signAllPublications() - coordinates(projectGroupId, projectArtifactIdBase, projectVersion) + coordinates(projectGroupId, "ktor-swagger-ui", projectVersion) pom { - name.set(projectNameBase) - description.set(projectDescriptionBase) + name.set("Ktor OpenApi") + description.set("Ktor plugin to provide a Swagger-UI") url.set(projectScmUrl) licenses { license { diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/ResourceContent.kt b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/ResourceContent.kt similarity index 86% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/ResourceContent.kt rename to ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/ResourceContent.kt index 81d63f1d..3f84fd2c 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/ResourceContent.kt +++ b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/ResourceContent.kt @@ -1,11 +1,11 @@ -package io.github.smiley4.ktorswaggerui.routing +package io.github.smiley4.ktorswaggerui 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() { +internal class ResourceContent(private val resource: URL) : OutgoingContent.ByteArrayContent() { private val contentTypes = mapOf( "html" to ContentType.Text.Html, diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt deleted file mode 100644 index 20276672..00000000 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt +++ /dev/null @@ -1,182 +0,0 @@ -package io.github.smiley4.ktorswaggerui - -import io.github.oshai.kotlinlogging.KotlinLogging -import io.github.smiley4.ktorswaggerui.builder.example.ExampleContext -import io.github.smiley4.ktorswaggerui.builder.example.ExampleContextImpl -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.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.SchemaContext -import io.github.smiley4.ktorswaggerui.builder.schema.SchemaContextImpl -import io.github.smiley4.ktorswaggerui.data.OutputFormat -import io.github.smiley4.ktorswaggerui.data.PluginConfigData -import io.github.smiley4.ktorswaggerui.dsl.config.PluginConfigDsl -import io.github.smiley4.ktorswaggerui.routing.ApiSpec -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.plugin -import io.ktor.server.routing.RoutingRoot -import io.swagger.v3.core.util.Json31 -import io.swagger.v3.core.util.Yaml31 - -/** - * This version must match the version of the gradle dependency - */ -internal const val SWAGGER_UI_WEBJARS_VERSION = "5.17.11" - -private val logger = KotlinLogging.logger {} - -val SwaggerUI = createApplicationPlugin(name = "SwaggerUI", createConfiguration = ::PluginConfigDsl) { - - val config = pluginConfig.build(PluginConfigData.DEFAULT) - - on(MonitoringEvent(ApplicationStarted)) { application -> - - try { - val routes = routes(application, config) - ApiSpec.setAll(buildOpenApiSpecs(config, routes)) - ApiSpec.swaggerUiConfig = config.swagger - } catch (e: Exception) { - logger.error(e) { "Error during application startup in swagger-ui-plugin" } - } - - } -} - -private fun buildOpenApiSpecs(config: PluginConfigData, routes: List): Map> { - val routesBySpec = buildMap> { - routes.forEach { route -> - val specName = - route.documentation.specId ?: config.specAssigner(route.path, route.documentation.tags.toList()) - computeIfAbsent(specName) { mutableListOf() }.add(route) - } - } - return buildMap { - routesBySpec.forEach { (specName, routes) -> - val specConfig = config.specConfigs[specName] ?: config - this[specName] = buildOpenApiSpec(specName, specConfig, routes) - } - } -} - -private fun buildOpenApiSpec(specName: String, pluginConfig: PluginConfigData, routes: List): Pair { - return try { - val schemaContext = SchemaContextImpl(pluginConfig.schemaConfig).also { - it.addGlobal(pluginConfig.schemaConfig) - it.add(routes) - } - val exampleContext = ExampleContextImpl(pluginConfig.exampleConfig.exampleEncoder).also { - it.addShared(pluginConfig.exampleConfig) - it.add(routes) - } - val openApi = builder(pluginConfig, schemaContext, exampleContext).build(routes) - pluginConfig.postBuild?.let { it(openApi, specName) } - when (pluginConfig.outputFormat) { - OutputFormat.JSON -> Json31.pretty(openApi) to pluginConfig.outputFormat - OutputFormat.YAML -> Yaml31.pretty(openApi) to pluginConfig.outputFormat - } - } catch (e: Exception) { - logger.error(e) { "Error during openapi-generation" } - return pluginConfig.outputFormat.empty to pluginConfig.outputFormat - } -} - -private fun routes(application: Application, config: PluginConfigData): List { - return RouteCollector(RouteDocumentationMerger()) - .collectRoutes({ application.plugin(RoutingRoot) }, config) - .map { it.copy(path = "${application.rootPath()}${it.path}") } - .toList() -} - - -/** - * fix [#97](https://github.com/SMILEY4/ktor-swagger-ui/pull/97) - * - * @receiver Application - * @return String - */ -private fun Application.rootPath(): String = - environment.config.propertyOrNull("ktor.deployment.rootPath")?.getString() ?: "" - -private fun builder( - config: PluginConfigData, - schemaContext: SchemaContext, - exampleContext: ExampleContext, -): OpenApiBuilder { - return OpenApiBuilder( - config = config, - schemaContext = schemaContext, - exampleContext = exampleContext, - infoBuilder = InfoBuilder( - contactBuilder = ContactBuilder(), - licenseBuilder = LicenseBuilder() - ), - externalDocumentationBuilder = ExternalDocumentationBuilder(), - serverBuilder = ServerBuilder(), - tagBuilder = TagBuilder( - tagExternalDocumentationBuilder = TagExternalDocumentationBuilder() - ), - pathsBuilder = PathsBuilder( - pathBuilder = PathBuilder( - operationBuilder = OperationBuilder( - operationTagsBuilder = OperationTagsBuilder(config), - parameterBuilder = ParameterBuilder( - schemaContext = schemaContext, - exampleContext = exampleContext, - ), - requestBodyBuilder = RequestBodyBuilder( - contentBuilder = ContentBuilder( - schemaContext = schemaContext, - exampleContext = exampleContext, - headerBuilder = HeaderBuilder(schemaContext) - ) - ), - responsesBuilder = ResponsesBuilder( - responseBuilder = ResponseBuilder( - headerBuilder = HeaderBuilder(schemaContext), - contentBuilder = ContentBuilder( - schemaContext = schemaContext, - exampleContext = exampleContext, - headerBuilder = HeaderBuilder(schemaContext) - ) - ), - config = config - ), - securityRequirementsBuilder = SecurityRequirementsBuilder(config), - externalDocumentationBuilder = ExternalDocumentationBuilder(), - serverBuilder = ServerBuilder() - ) - ) - ), - componentsBuilder = ComponentsBuilder( - config = config, - securitySchemesBuilder = SecuritySchemesBuilder( - oAuthFlowsBuilder = OAuthFlowsBuilder() - ) - ) - ) -} diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUI.kt b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUI.kt new file mode 100644 index 00000000..18aec1bc --- /dev/null +++ b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUI.kt @@ -0,0 +1,84 @@ +package io.github.smiley4.ktorswaggerui + +import io.github.smiley4.ktorswaggerui.config.SwaggerUIConfig +import io.github.smiley4.ktorswaggerui.config.SwaggerUISort +import io.github.smiley4.ktorswaggerui.config.SwaggerUISyntaxHighlight +import io.ktor.http.ContentType +import io.ktor.http.HttpStatusCode +import io.ktor.server.application.ApplicationCall +import io.ktor.server.request.uri +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 + + +/** + * Registers the route for serving all swagger-ui resources. The path to the OpenApi-file to use has to be given. + */ +fun Route.swaggerUI(openApiUrl: String, config: SwaggerUIConfig.() -> Unit = {}) { + val swaggerUIConfig = SwaggerUIConfig().apply(config) + markedSwaggerUI { + get { + call.respondRedirect("${call.request.uri}/index.html") + } + get("{filename}") { + SwaggerUI.serveStaticResource(call.parameters["filename"]!!, swaggerUIConfig, call) + } + get("swagger-initializer.js") { + SwaggerUI.serveSwaggerInitializer(call, swaggerUIConfig, openApiUrl) + } + } +} + +internal object SwaggerUI { + + internal suspend fun serveSwaggerInitializer(call: ApplicationCall, config: SwaggerUIConfig, openApiUrl: String) { + // see https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/configuration.md for reference + val propValidatorUrl = config.validatorUrl?.let { "validatorUrl: \"$it\"" } ?: "validatorUrl: false" + val propDisplayOperationId = "displayOperationId: ${config.displayOperationId}" + val propFilter = "filter: ${config.showTagFilterInput}" + val propSort = "operationsSorter: " + + if (config.sort == SwaggerUISort.NONE) "undefined" + else "\"${config.sort.value}\"" + val propSyntaxHighlight = "syntaxHighlight: " + + if (config.syntaxHighlight == SwaggerUISyntaxHighlight.DISABLED) "false" + else "{ theme: \"${config.syntaxHighlight.value}\" }" + val content = """ + window.onload = function() { + window.ui = SwaggerUIBundle({ + url: "$openApiUrl", + dom_id: '#swagger-ui', + deepLinking: true, + presets: [ + SwaggerUIBundle.presets.apis, + SwaggerUIStandalonePreset + ], + plugins: [ + SwaggerUIBundle.plugins.DownloadUrl + ], + layout: "StandaloneLayout", + withCredentials: ${config.withCredentials}, + $propValidatorUrl, + $propDisplayOperationId, + $propFilter, + $propSort, + $propSyntaxHighlight + }); + }; + """.trimIndent() + call.respondText(ContentType.Application.JavaScript, HttpStatusCode.OK) { content } + } + + internal suspend fun serveStaticResource(filename: String, config: SwaggerUIConfig, call: ApplicationCall) { + val resourceName = "${config.staticResourcesPath}/$filename" + val resource = SwaggerUI::class.java.getResource(resourceName) + if (resource != null) { + call.respond(ResourceContent(resource)) + } else { + call.respond(HttpStatusCode.NotFound, "'$filename' could not be found.") + } + } + +} diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIRouteSelector.kt b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIRouteSelector.kt new file mode 100644 index 00000000..0cc4780f --- /dev/null +++ b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIRouteSelector.kt @@ -0,0 +1,14 @@ +package io.github.smiley4.ktorswaggerui + +import io.ktor.server.routing.Route +import io.ktor.server.routing.RouteSelector +import io.ktor.server.routing.RouteSelectorEvaluation +import io.ktor.server.routing.RoutingResolveContext + +internal class SwaggerUIRouteSelector : RouteSelector() { + override suspend fun evaluate(context: RoutingResolveContext, segmentIndex: Int) = RouteSelectorEvaluation.Transparent +} + +internal fun Route.markedSwaggerUI(build: Route.() -> Unit): Route { + return createChild(SwaggerUIRouteSelector()).also(build) +} diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/route/RouteMeta.kt b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/route/RouteMeta.kt deleted file mode 100644 index b9d88239..00000000 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/builder/route/RouteMeta.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.smiley4.ktorswaggerui.builder.route - -import io.github.smiley4.ktorswaggerui.data.OpenApiRouteData -import io.ktor.http.HttpMethod - -/** - * Information about a route - */ -data class RouteMeta( - val path: String, - val method: HttpMethod, - val documentation: OpenApiRouteData, - val protected: Boolean -) diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/config/SwaggerUIConfig.kt b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/config/SwaggerUIConfig.kt new file mode 100644 index 00000000..4e1a00a0 --- /dev/null +++ b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/config/SwaggerUIConfig.kt @@ -0,0 +1,53 @@ +package io.github.smiley4.ktorswaggerui.config + +class SwaggerUIConfig { + + /** + * Path to the static resources for swagger-ui in the jar-file. + * Version must match the version of the swagger-ui-webjars dependency. + */ + internal val staticResourcesPath: String = "/META-INF/resources/webjars/swagger-ui/5.17.11" + + /** + * Swagger UI can attempt to validate specs against swagger.io's online validator. + * You can use this parameter to set a different validator URL, for example for locally deployed validators. + * Set to "null" to disable validation. + * 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) + */ + var validatorUrl: String? = null + + fun disableSpecValidator() { + validatorUrl = null + } + + fun onlineSpecValidator() { + validatorUrl = "https://validator.swagger.io/validator" + } + + /** + * Whether to show the operation-id of endpoints in the list + */ + var displayOperationId: Boolean = false + + /** + * Whether the top bar will show an edit box that you can use to filter the tagged operations. + */ + var showTagFilterInput: Boolean = false + + /** + * Apply a sort to the operation list of each API + */ + var sort: SwaggerUISort = SwaggerUISort.NONE + + /** + * Syntax coloring theme to use + */ + var syntaxHighlight: SwaggerUISyntaxHighlight = SwaggerUISyntaxHighlight.AGATE + + /** + * Bring cookies when initiating network requests + */ + var withCredentials: Boolean = false + +} diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUiSort.kt b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/config/SwaggerUISort.kt similarity index 77% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUiSort.kt rename to ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/config/SwaggerUISort.kt index 8dccbc57..fd7c2871 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUiSort.kt +++ b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/config/SwaggerUISort.kt @@ -1,9 +1,9 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktorswaggerui.config /** * Determines the order to sort the operations in the swagger-ui. */ -enum class SwaggerUiSort(val value: String) { +enum class SwaggerUISort(val value: String) { /** * The order returned by the server unchanged */ diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUiSyntaxHighlight.kt b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/config/SwaggerUISyntaxHighlight.kt similarity index 53% rename from ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUiSyntaxHighlight.kt rename to ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/config/SwaggerUISyntaxHighlight.kt index 07c17dbc..6a29baeb 100644 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUiSyntaxHighlight.kt +++ b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/config/SwaggerUISyntaxHighlight.kt @@ -1,9 +1,9 @@ -package io.github.smiley4.ktorswaggerui.data +package io.github.smiley4.ktorswaggerui.config /** - * The syntax-highlight theme to use for code-blocks in swagger-ui. + * The theme to use for syntax highlighting code-blocks in swagger-ui. */ -enum class SwaggerUiSyntaxHighlight(val value: String) { +enum class SwaggerUISyntaxHighlight(val value: String) { DISABLED("disabled"), AGATE("agate"), ARTA("arta"), diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ExampleConfigData.kt b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ExampleConfigData.kt deleted file mode 100644 index fcef6255..00000000 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/ExampleConfigData.kt +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.smiley4.ktorswaggerui.data - -import com.fasterxml.jackson.core.type.TypeReference -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import kotlinx.serialization.json.Json -import kotlinx.serialization.serializer - -/** - * Encoder to produce the final example value. - * Return the unmodified example to fall back to the default encoder. - */ -typealias ExampleEncoder = (type: TypeDescriptor?, example: Any?) -> Any? - -/** - * [ExampleEncoder] using kotlinx-serialization to encode example objects. - */ -val kotlinxExampleEncoder: ExampleEncoder = { type, example -> - if (type is KTypeDescriptor) { - val jsonString = Json.encodeToString(serializer(type.type), example) - val jsonObj = jacksonObjectMapper().readValue(jsonString, object : TypeReference() {}) - jsonObj - } else { - example - } -} - -class ExampleConfigData( - val sharedExamples: Map, - val securityExamples: OpenApiSimpleBodyData?, - val exampleEncoder: ExampleEncoder? -) { - - companion object { - val DEFAULT = ExampleConfigData( - sharedExamples = emptyMap(), - securityExamples = null, - exampleEncoder = null - ) - } - -} diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenApiRequestData.kt b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenApiRequestData.kt deleted file mode 100644 index 6405acc4..00000000 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenApiRequestData.kt +++ /dev/null @@ -1,9 +0,0 @@ -package io.github.smiley4.ktorswaggerui.data - -/** - * Information about a request - */ -data class OpenApiRequestData( - val parameters: List, - val body: OpenApiBaseBodyData?, -) diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenApiResponseData.kt b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenApiResponseData.kt deleted file mode 100644 index 6693df3a..00000000 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/OpenApiResponseData.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.smiley4.ktorswaggerui.data - -/** - * Information about a response for a status-code. - */ -data class OpenApiResponseData( - val statusCode: String, - val description: String?, - val headers: Map, - val body: OpenApiBaseBodyData?, -) diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PostBuild.kt b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PostBuild.kt deleted file mode 100644 index 8c267510..00000000 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/PostBuild.kt +++ /dev/null @@ -1,9 +0,0 @@ -package io.github.smiley4.ktorswaggerui.data - -import io.swagger.v3.oas.models.OpenAPI - -/** - * Function executed after building the openapi-spec. - * @author yuefeng in 2024/3/25. - */ -typealias PostBuild = (openApi: OpenAPI, specId: String) -> Unit diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUIData.kt b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUIData.kt deleted file mode 100644 index c57dfb2d..00000000 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/data/SwaggerUIData.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.smiley4.ktorswaggerui.data - -/** - * Common configuration for the swagger-ui. - */ -data class SwaggerUIData( - val validatorUrl: String?, - val displayOperationId: Boolean, - val showTagFilterInput: Boolean, - val sort: SwaggerUiSort, - val syntaxHighlight: SwaggerUiSyntaxHighlight, - val withCredentials: Boolean, -) { - - companion object { - val DEFAULT = SwaggerUIData( - validatorUrl = null, - displayOperationId = false, - showTagFilterInput = false, - sort = SwaggerUiSort.NONE, - syntaxHighlight = SwaggerUiSyntaxHighlight.AGATE, - withCredentials = false, - ) - } - -} diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/SwaggerUIDsl.kt b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/SwaggerUIDsl.kt deleted file mode 100644 index 9237a0ec..00000000 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/config/SwaggerUIDsl.kt +++ /dev/null @@ -1,80 +0,0 @@ -package io.github.smiley4.ktorswaggerui.dsl.config - -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 -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker - -/** - * Configuration for the swagger-ui - */ -@OpenApiDslMarker -class SwaggerUIDsl { - - /** - * Swagger UI can attempt to validate specs against swagger.io's online validator. - * You can use this parameter to set a different validator URL, for example for locally deployed validators. - * Set to "null" to disable validation. - * 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? = SwaggerUIData.DEFAULT.validatorUrl - - fun disableSpecValidator() { - validatorUrl = null - } - - fun specValidator(url: String) { - validatorUrl = url - } - - fun onlineSpecValidator() { - specValidator("https://validator.swagger.io/validator") - } - - /** - * Whether to show the operation-id of endpoints in the list - */ - 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 = SwaggerUIData.DEFAULT.showTagFilterInput - - - /** - * Apply a sort to the operation list of each API - */ - var sort = SwaggerUIData.DEFAULT.sort - - - /** - * Syntax coloring theme to use - */ - var syntaxHighlight = SwaggerUIData.DEFAULT.syntaxHighlight - - /** - * Bring cookies when initiating network requests - */ - var withCredentials: Boolean = false - - /** - * Build the data object for this config. - * @param base the base config to "inherit" from. Values from the base should be copied, replaced or merged together. - */ - internal fun build(base: SwaggerUIData): SwaggerUIData { - return SwaggerUIData( - 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), - withCredentials = mergeBoolean(base.withCredentials, this.withCredentials) - ) - } - -} - diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiMultipartBody.kt b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiMultipartBody.kt deleted file mode 100644 index fc89dee3..00000000 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiMultipartBody.kt +++ /dev/null @@ -1,56 +0,0 @@ -package io.github.smiley4.ktorswaggerui.dsl.routes - -import io.github.smiley4.ktorswaggerui.data.KTypeDescriptor -import io.github.smiley4.ktorswaggerui.data.OpenApiMultipartBodyData -import io.github.smiley4.ktorswaggerui.data.SwaggerTypeDescriptor -import io.github.smiley4.ktorswaggerui.data.TypeDescriptor -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker -import io.swagger.v3.oas.models.media.Schema -import kotlin.reflect.KType -import kotlin.reflect.typeOf - - -/** - * Describes a single request/response body with multipart content. - * See https://swagger.io/docs/specification/describing-request-body/multipart-requests/ for more info - */ -@OpenApiDslMarker -class OpenApiMultipartBody : OpenApiBaseBody() { - - private val parts = mutableListOf() - - - /** - * One part of a multipart-body - */ - fun part(name: String, type: TypeDescriptor, block: OpenApiMultipartPart.() -> Unit = {}) { - parts.add(OpenApiMultipartPart(name, type).apply(block)) - } - - - /** - * One part of a multipart-body - */ - fun part(name: String, type: Schema<*>, block: OpenApiMultipartPart.() -> Unit = {}) = part(name, SwaggerTypeDescriptor(type), block) - - - /** - * One part of a multipart-body - */ - fun part(name: String, type: KType, block: OpenApiMultipartPart.() -> Unit = {}) = part(name, KTypeDescriptor(type), block) - - - /** - * One part of a multipart-body - */ - inline fun part(name: String, noinline block: OpenApiMultipartPart.() -> Unit = {}) = - part(name, KTypeDescriptor(typeOf()), block) - - - override fun build() = OpenApiMultipartBodyData( - description = description, - required = required ?: false, - mediaTypes = mediaTypes.toSet(), - parts = parts.map { it.build() } - ) -} diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiResponse.kt b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiResponse.kt deleted file mode 100644 index 24c8c91f..00000000 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiResponse.kt +++ /dev/null @@ -1,101 +0,0 @@ -package io.github.smiley4.ktorswaggerui.dsl.routes - -import io.github.smiley4.ktorswaggerui.data.KTypeDescriptor -import io.github.smiley4.ktorswaggerui.data.OpenApiResponseData -import io.github.smiley4.ktorswaggerui.data.SwaggerTypeDescriptor -import io.github.smiley4.ktorswaggerui.data.TypeDescriptor -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker -import io.swagger.v3.oas.models.media.Schema -import kotlin.reflect.KType -import kotlin.reflect.typeOf - -/** - * A container for the expected responses of an operation. The container maps an HTTP response code to the expected response. - * A response code can only have one response object. - */ -@OpenApiDslMarker -class OpenApiResponse(val statusCode: String) { - - /** - * A short description of the response - */ - var description: String? = null - - val headers = mutableMapOf() - - - /** - * Possible headers returned with this response - */ - fun header(name: String, type: TypeDescriptor, block: OpenApiHeader.() -> Unit = {}) { - headers[name] = OpenApiHeader().apply(block).apply { - this.type = type - } - } - - - /** - * Possible headers returned with this response - */ - fun header(name: String, type: Schema<*>, block: OpenApiHeader.() -> Unit = {}) = header(name, SwaggerTypeDescriptor(type), block) - - - /** - * Possible headers returned with this response - */ - fun header(name: String, type: KType, block: OpenApiHeader.() -> Unit = {}) = header(name, KTypeDescriptor(type), block) - - - /** - * Possible headers returned with this response - */ - inline fun header(name: String, noinline block: OpenApiHeader.() -> Unit = {}) = - header(name, KTypeDescriptor(typeOf()), block) - - - private var body: OpenApiBaseBody? = null - - - /** - * The body returned with this response - */ - fun body(type: TypeDescriptor, block: OpenApiSimpleBody.() -> Unit = {}) { - body = OpenApiSimpleBody(type).apply(block) - } - - /** - * The body returned with this response - */ - fun body(type: Schema<*>, block: OpenApiSimpleBody.() -> Unit = {}) = body(SwaggerTypeDescriptor(type), block) - - /** - * The body returned with this response - */ - fun body(type: KType, block: OpenApiSimpleBody.() -> Unit = {}) = body(KTypeDescriptor(type), block) - - /** - * The body returned with this response - */ - inline fun body(noinline block: OpenApiSimpleBody.() -> Unit = {}) = body(KTypeDescriptor(typeOf()), block) - - - - - /** - * The multipart-body returned with this response - */ - fun multipartBody(block: OpenApiMultipartBody.() -> Unit) { - body = OpenApiMultipartBody().apply(block) - } - - /** - * Build the data object for this config. - */ - fun build() = OpenApiResponseData( - statusCode = statusCode, - description = description, - headers = headers.mapValues { it.value.build() }, - body = body?.build() - ) - -} diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiResponses.kt b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiResponses.kt deleted file mode 100644 index 4e754072..00000000 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/routes/OpenApiResponses.kt +++ /dev/null @@ -1,57 +0,0 @@ -package io.github.smiley4.ktorswaggerui.dsl.routes - -import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker -import io.ktor.http.HttpStatusCode - -/** - * All possible responses of an operation - */ -@OpenApiDslMarker -class OpenApiResponses { - - private val responses = mutableMapOf() - - - /** - * Information of response for a given http status code - */ - infix fun String.to(block: OpenApiResponse.() -> Unit) { - responses[this] = OpenApiResponse(this).apply(block) - } - - - /** - * Information of response for a given http status code - */ - infix fun HttpStatusCode.to(block: OpenApiResponse.() -> Unit) = this.value.toString() to block - - /** - * Information of response for a given http status code - */ - fun code(statusCode: String, block: OpenApiResponse.() -> Unit) { - responses[statusCode] = OpenApiResponse(statusCode).apply(block) - } - - /** - * Information of response for a given http status code - */ - fun code(statusCode: HttpStatusCode, block: OpenApiResponse.() -> Unit) = code(statusCode.value.toString(), block) - - - /** - * Information of the default response - */ - fun default(block: OpenApiResponse.() -> Unit) = "default" to block - - - /** - * Add the given response. Intended for internal use. - */ - fun addResponse(response: OpenApiResponse) { - responses[response.statusCode] = response - } - - - fun getResponses() = responses.values.toList() - -} diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/ApiSpec.kt b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/ApiSpec.kt deleted file mode 100644 index f94d403f..00000000 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/ApiSpec.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.smiley4.ktorswaggerui.routing - -import io.github.smiley4.ktorswaggerui.data.OutputFormat -import io.github.smiley4.ktorswaggerui.data.SwaggerUIData - -object ApiSpec { - - var swaggerUiConfig: SwaggerUIData = SwaggerUIData.DEFAULT - - private val apiSpecs = mutableMapOf>() - - fun setAll(specs: Map>) { - apiSpecs.clear() - apiSpecs.putAll(specs) - } - - fun set(name: String, spec: Pair) { - apiSpecs[name] = spec - } - - fun get(name: String): String { - return apiSpecs[name]?.first ?: throw NoSuchElementException("No api-spec with name '$name' registered.") - } - - fun getFormat(name: String): OutputFormat { - return apiSpecs[name]?.second ?: throw NoSuchElementException("No api-spec with name '$name' registered.") - } - - fun getAll(): Map { - return apiSpecs.mapValues { it.value.first } - } - -} diff --git a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/routing.kt b/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/routing.kt deleted file mode 100644 index 26df6ec0..00000000 --- a/ktor-swagger-ui/src/main/kotlin/io/github/smiley4/ktorswaggerui/routing/routing.kt +++ /dev/null @@ -1,94 +0,0 @@ -package io.github.smiley4.ktorswaggerui.routing - -import io.github.smiley4.ktorswaggerui.SWAGGER_UI_WEBJARS_VERSION -import io.github.smiley4.ktorswaggerui.SwaggerUI -import io.github.smiley4.ktorswaggerui.data.OutputFormat -import io.github.smiley4.ktorswaggerui.data.SwaggerUIData -import io.github.smiley4.ktorswaggerui.data.SwaggerUiSort -import io.github.smiley4.ktorswaggerui.data.SwaggerUiSyntaxHighlight -import io.github.smiley4.ktorswaggerui.dsl.config.PluginConfigDsl -import io.github.smiley4.ktorswaggerui.dsl.routing.route -import io.ktor.http.* -import io.ktor.server.application.* -import io.ktor.server.request.* -import io.ktor.server.response.* -import io.ktor.server.routing.* - -/** - * Registers the route for serving an openapi-spec. When multiple specs are configured, the id of the one to serve has to be provided. - */ -fun Route.openApiSpec(specId: String = PluginConfigDsl.DEFAULT_SPEC_ID) { - route({ hidden = true }) { - get { - val contentType = when(ApiSpec.getFormat(specId)) { - OutputFormat.JSON -> ContentType.Application.Json - OutputFormat.YAML -> ContentType.Text.Plain - } - call.respondText(contentType, HttpStatusCode.OK) { ApiSpec.get(specId) } - } - } -} - -/** - * Registers the route for serving all swagger-ui resources. The path to the openapi-spec file to use has to be given. - */ -fun Route.swaggerUI(apiUrl: String) { - route({ hidden = true }) { - get { - call.respondRedirect("${call.request.uri}/index.html") - } - get("{filename}") { - serveStaticResource(call.parameters["filename"]!!, SWAGGER_UI_WEBJARS_VERSION, call) - } - get("swagger-initializer.js") { - serveSwaggerInitializer(call, ApiSpec.swaggerUiConfig, apiUrl) - } - } -} - -private suspend fun serveSwaggerInitializer(call: ApplicationCall, swaggerUiConfig: SwaggerUIData, apiUrl: String) { - // see https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/configuration.md for reference - val propValidatorUrl = swaggerUiConfig.validatorUrl?.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: " + - if(swaggerUiConfig.syntaxHighlight == SwaggerUiSyntaxHighlight.DISABLED) "false" - else "{ theme: \"${swaggerUiConfig.syntaxHighlight.value}\" }" - val content = """ - window.onload = function() { - window.ui = SwaggerUIBundle({ - url: "$apiUrl", - dom_id: '#swagger-ui', - deepLinking: true, - presets: [ - SwaggerUIBundle.presets.apis, - SwaggerUIStandalonePreset - ], - plugins: [ - SwaggerUIBundle.plugins.DownloadUrl - ], - layout: "StandaloneLayout", - withCredentials: ${swaggerUiConfig.withCredentials}, - $propValidatorUrl, - $propDisplayOperationId, - $propFilter, - $propSort, - $propSyntaxHighlight - }); - }; - """.trimIndent() - call.respondText(ContentType.Application.JavaScript, HttpStatusCode.OK) { content } -} - -private suspend fun serveStaticResource(filename: String, swaggerWebjarVersion: String, call: ApplicationCall) { - val resourceName = "/META-INF/resources/webjars/swagger-ui/$swaggerWebjarVersion/$filename" - val resource = SwaggerUI::class.java.getResource(resourceName) - if (resource != null) { - call.respond(ResourceContent(resource)) - } else { - call.respond(HttpStatusCode.NotFound, "$filename could not be found") - } -} diff --git a/ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/RoutingTests.kt b/ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/RoutingTests.kt new file mode 100644 index 00000000..549f4acb --- /dev/null +++ b/ktor-swagger-ui/src/test/kotlin/io/github/smiley4/ktorswaggerui/RoutingTests.kt @@ -0,0 +1,136 @@ +package io.github.smiley4.ktorswaggerui + +import io.github.smiley4.ktorswaggerui.config.SwaggerUIConfig +import io.github.smiley4.ktorswaggerui.config.SwaggerUISort +import io.github.smiley4.ktorswaggerui.config.SwaggerUISyntaxHighlight +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain +import io.kotest.matchers.string.shouldNotBeEmpty +import io.ktor.client.HttpClient +import io.ktor.client.request.* +import io.ktor.client.statement.bodyAsText +import io.ktor.http.ContentType +import io.ktor.http.HttpStatusCode +import io.ktor.http.contentType +import io.ktor.server.response.respondText +import io.ktor.server.routing.get +import io.ktor.server.routing.route +import io.ktor.server.testing.testApplication +import kotlin.test.Test + +class RoutingTests { + + @Test + fun defaultConfig() = swaggerUITestApplication { + get("hello").also { + it.status shouldBe HttpStatusCode.OK + it.body shouldBe "Hello Test" + } + get("/swagger").also { + it.status shouldBe HttpStatusCode.Found + it.redirect shouldBe "/swagger/index.html" + } + get("/swagger/index.html").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Text.Html + it.body.shouldNotBeEmpty() + } + get("/swagger/swagger-initializer.js").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Application.JavaScript + it.body.shouldNotBeEmpty() + it.body shouldContain "url: \"api.json\"" + it.body shouldContain "withCredentials: false" + it.body shouldContain "validatorUrl: false" + it.body shouldContain "displayOperationId: false" + it.body shouldContain "filter: false" + it.body shouldContain "operationsSorter: undefined" + it.body shouldContain "syntaxHighlight: { theme: \"agate\" }" + } + } + + @Test + fun fullConfig() = swaggerUITestApplication({ + onlineSpecValidator() + displayOperationId = true + showTagFilterInput = true + sort = SwaggerUISort.ALPHANUMERICALLY + syntaxHighlight = SwaggerUISyntaxHighlight.MONOKAI + withCredentials = true + + }) { + get("hello").also { + it.status shouldBe HttpStatusCode.OK + it.body shouldBe "Hello Test" + } + get("/swagger").also { + it.status shouldBe HttpStatusCode.Found + it.redirect shouldBe "/swagger/index.html" + } + get("/swagger/index.html").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Text.Html + it.body.shouldNotBeEmpty() + } + get("/swagger/swagger-initializer.js").also { + it.status shouldBe HttpStatusCode.OK + it.contentType shouldBe ContentType.Application.JavaScript + it.body.shouldNotBeEmpty() + it.body shouldContain "url: \"api.json\"" + it.body shouldContain "withCredentials: true" + it.body shouldContain "validatorUrl: \"https://validator.swagger.io/validator\"" + it.body shouldContain "displayOperationId: true" + it.body shouldContain "filter: true" + it.body shouldContain "operationsSorter: \"alpha\"" + it.body shouldContain "syntaxHighlight: { theme: \"monokai\" }" + } + } + + private fun swaggerUITestApplication(config: SwaggerUIConfig.() -> Unit = {}, block: suspend TestContext.() -> Unit = {}) { + testApplication { + val client = createClient { + this.followRedirects = false + } + routing { + route("swagger") { + swaggerUI("api.json", config) + } + get("hello") { + call.respondText("Hello Test") + } + } + TestContext(client).apply { block() } + } + } + + class TestContext(private val client: HttpClient) { + + suspend fun get(path: String): GetResult { + return client.get(path) + .let { + GetResult( + path = path, + status = it.status, + contentType = it.contentType(), + body = it.bodyAsText(), + redirect = it.headers["Location"] + ) + } + .also { it.print() } + } + + + private fun GetResult.print() { + println("GET ${this.path} => ${this.status} (${this.contentType}): ${this.body}") + } + } + + data class GetResult( + val path: String, + val status: HttpStatusCode, + val contentType: ContentType?, + val body: String, + val redirect: String? + ) + +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 646ddeae..a5af31c8 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,7 +1,9 @@ -rootProject.name = "ktor-swagger-ui" +rootProject.name = "ktor-openapi" + +include("ktor-openapi") -include("ktor-swagger-ui-examples") include("ktor-swagger-ui") +include("ktor-swagger-ui-examples") pluginManagement { repositories {