diff --git a/ktor-swagger-ui-examples/build.gradle.kts b/examples/build.gradle.kts similarity index 97% rename from ktor-swagger-ui-examples/build.gradle.kts rename to examples/build.gradle.kts index 7527e9eb..29ec2991 100644 --- a/ktor-swagger-ui-examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -13,8 +13,9 @@ repositories { } dependencies { - implementation(project(":ktor-swagger-ui")) implementation(project(":ktor-openapi")) + implementation(project(":ktor-swagger-ui")) + implementation(project(":ktor-redoc")) val versionKtor: String by project implementation("io.ktor:ktor-server-netty-jvm:$versionKtor") diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Authentication.kt b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/Authentication.kt similarity index 94% rename from ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Authentication.kt rename to examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/Authentication.kt index 1255adaa..9cbbb602 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Authentication.kt +++ b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/Authentication.kt @@ -1,10 +1,11 @@ -package io.github.smiley4.ktorswaggerui.examples +package io.github.smiley4.ktoropenapi.examples 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.ktorredoc.redoc import io.github.smiley4.ktorswaggerui.swaggerUI import io.ktor.server.application.Application import io.ktor.server.application.install @@ -60,13 +61,16 @@ private fun Application.myModule() { routing { - // add the routes for swagger-ui and api-spec + // add the routes for the api-spec, swagger-ui and redoc route("swagger") { swaggerUI("/api.json") } route("api.json") { openApi() } + route("redoc") { + redoc("/api.json") + } authenticate { // route is in an "authenticate"-block -> default security-scheme will be used (if not specified otherwise) diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Basics.kt b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/Basics.kt similarity index 90% rename from ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Basics.kt rename to examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/Basics.kt index 5dd373aa..0c6bdc82 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Basics.kt +++ b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/Basics.kt @@ -1,8 +1,9 @@ -package io.github.smiley4.ktorswaggerui.examples +package io.github.smiley4.ktoropenapi.examples import io.github.smiley4.ktoropenapi.OpenApi import io.github.smiley4.ktoropenapi.get import io.github.smiley4.ktoropenapi.openApi +import io.github.smiley4.ktorredoc.redoc import io.github.smiley4.ktorswaggerui.swaggerUI import io.ktor.http.HttpStatusCode import io.ktor.server.application.Application @@ -44,15 +45,20 @@ private fun Application.myModule() { routing { + // Create a route for the openapi-spec file. + // This route will not be included in the spec. + route("api.json") { + openApi() + } // Create a route for the swagger-ui using the openapi-spec at "/api.json". // This route will not be included in the spec. route("swagger") { swaggerUI("/api.json") } - // Create a route for the openapi-spec file. + // Create a route for redoc using the openapi-spec at "/api.json". // This route will not be included in the spec. - route("api.json") { - openApi() + route("redoc") { + redoc("/api.json") } // a documented route diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/CompleteConfig.kt b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/CompleteConfig.kt similarity index 78% rename from ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/CompleteConfig.kt rename to examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/CompleteConfig.kt index 220a5504..7a313212 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/CompleteConfig.kt +++ b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/CompleteConfig.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.examples +package io.github.smiley4.ktoropenapi.examples import io.github.smiley4.ktoropenapi.OpenApi import io.github.smiley4.ktoropenapi.config.OpenApiPluginConfig @@ -6,6 +6,7 @@ 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.ktorredoc.redoc import io.github.smiley4.ktorswaggerui.config.SwaggerUISort import io.github.smiley4.ktorswaggerui.config.SwaggerUISyntaxHighlight import io.github.smiley4.ktorswaggerui.swaggerUI @@ -112,7 +113,7 @@ private fun Application.myModule() { .compileReferencingRoot() } overwrite(Schema().also { - it.types = setOf("string") + it.type = "string" it.format = "binary" }) } @@ -137,7 +138,9 @@ private fun Application.myModule() { routing { - // add the routes for swagger-ui and api-spec + route("api.json") { + openApi() + } route("swagger") { swaggerUI("/api.json") { displayOperationId = true @@ -146,9 +149,48 @@ private fun Application.myModule() { syntaxHighlight = SwaggerUISyntaxHighlight.MONOKAI withCredentials = false } - } - route("api.json") { - openApi() + route("redoc") { + redoc("/api.json") { + disableSearch = false + minCharacterLengthToInitSearch = 1 + expandResponses = listOf("all") + expandSingleSchemaField = true + hideDownloadButton = false + hideHostname = false + hideLoading = false + hideRequestPayloadSample = true + hideOneOfDescription = false + hideSchemaPattern = false + hideSchemaTitles = true + hideSecuritySection = false + hideSingleRequestSampleTab = true + jsonSampleExpandLevel = "1" + maxDisplayedEnumValues = 3 + menuToggle = true + nativeScrollbars = true + onlyRequiredInSamples = false + pathInMiddlePanel = true + requiredPropsFirst = true + schemaExpansionLevel = "all" + showObjectSchemaExamples = true + showWebhookVerb = true + simpleOneOfTypeLabel = true + sortEnumValuesAlphabetically = true + sortOperationsAlphabetically = true + sortPropsAlphabetically = true + sortTagsAlphabetically = true + theme = """ + { + "sidebar": { + "backgroundColor": "lightblue" + }, + "rightPanel": { + "backgroundColor": "darkblue" + } + } + """.trimIndent() + } + } } // a documented route diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/CustomizedSchemaGenerator.kt b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/CustomizedSchemaGenerator.kt similarity index 89% rename from ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/CustomizedSchemaGenerator.kt rename to examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/CustomizedSchemaGenerator.kt index 832541e4..8c14f1f2 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/CustomizedSchemaGenerator.kt +++ b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/CustomizedSchemaGenerator.kt @@ -1,8 +1,9 @@ -package io.github.smiley4.ktorswaggerui.examples +package io.github.smiley4.ktoropenapi.examples import io.github.smiley4.ktoropenapi.OpenApi import io.github.smiley4.ktoropenapi.get import io.github.smiley4.ktoropenapi.openApi +import io.github.smiley4.ktorredoc.redoc import io.github.smiley4.ktorswaggerui.swaggerUI import io.github.smiley4.schemakenerator.serialization.processKotlinxSerialization import io.github.smiley4.schemakenerator.swagger.compileReferencingRoot @@ -44,16 +45,16 @@ private fun Application.myModule() { routing { - // Create a route for the swagger-ui using the openapi-spec at "/api.json". - // This route will not be included in the spec. + // add the routes for the api-spec, swagger-ui and redoc route("swagger") { swaggerUI("/api.json") } - // Create a route for the openapi-spec file. - // This route will not be included in the spec. route("api.json") { openApi() } + route("redoc") { + redoc("/api.json") + } // a documented route get("hello", { diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Examples.kt b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/Examples.kt similarity index 94% rename from ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Examples.kt rename to examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/Examples.kt index 74efcd60..eb533f91 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Examples.kt +++ b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/Examples.kt @@ -1,9 +1,10 @@ -package io.github.smiley4.ktorswaggerui.examples +package io.github.smiley4.ktoropenapi.examples 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.ktorredoc.redoc import io.github.smiley4.ktorswaggerui.swaggerUI import io.ktor.server.application.Application import io.ktor.server.application.install @@ -52,13 +53,16 @@ private fun Application.myModule() { routing { - // add the routes for swagger-ui and api-spec + // add the routes for the api-spec, swagger-ui and redoc route("swagger") { swaggerUI("/api.json") } route("api.json") { openApi() } + route("redoc") { + redoc("/api.json") + } get("basic", { diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/ExternalOpenApiSpec.kt b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/ExternalOpenApiSpec.kt similarity index 70% rename from ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/ExternalOpenApiSpec.kt rename to examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/ExternalOpenApiSpec.kt index 31506856..a2b3d662 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/ExternalOpenApiSpec.kt +++ b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/ExternalOpenApiSpec.kt @@ -1,5 +1,6 @@ -package io.github.smiley4.ktorswaggerui.examples +package io.github.smiley4.ktoropenapi.examples +import io.github.smiley4.ktorredoc.redoc import io.github.smiley4.ktorswaggerui.swaggerUI import io.ktor.server.application.Application import io.ktor.server.engine.embeddedServer @@ -20,6 +21,11 @@ private fun Application.myModule() { swaggerUI("https://petstore3.swagger.io/api/v3/openapi.json") } + // Create a route for redoc using an external openapi-spec. + route("redoc") { + redoc("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/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/FileUpload.kt similarity index 70% rename from ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/FileUpload.kt rename to examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/FileUpload.kt index 70f76815..c1aefcbe 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/FileUpload.kt +++ b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/FileUpload.kt @@ -1,14 +1,10 @@ -package io.github.smiley4.ktorswaggerui.examples +package io.github.smiley4.ktoropenapi.examples import io.github.smiley4.ktoropenapi.OpenApi import io.github.smiley4.ktoropenapi.post import io.github.smiley4.ktoropenapi.openApi +import io.github.smiley4.ktorredoc.redoc import io.github.smiley4.ktorswaggerui.swaggerUI -import io.github.smiley4.ktorswaggerui.SwaggerUI -import io.github.smiley4.ktorswaggerui.data.array -import io.github.smiley4.ktorswaggerui.dsl.routing.post -import io.github.smiley4.ktorswaggerui.routing.openApiSpec -import io.github.smiley4.ktorswaggerui.routing.swaggerUI import io.ktor.http.ContentType import io.ktor.http.HttpStatusCode import io.ktor.server.application.Application @@ -32,7 +28,7 @@ private fun Application.myModule() { schemas { // overwrite type "File" with custom schema for binary data overwrite(Schema().also { - it.types = setOf("string") + it.type = "string" it.format = "binary" }) } @@ -40,13 +36,16 @@ private fun Application.myModule() { routing { - // add the routes for swagger-ui and api-spec + // add the routes for the api-spec, swagger-ui and redoc route("swagger") { swaggerUI("/api.json") } route("api.json") { openApi() } + route("redoc") { + redoc("/api.json") + } // upload a single file, either as png, jpeg or svg post("single", { @@ -63,7 +62,7 @@ private fun Application.myModule() { call.respond(HttpStatusCode.NotImplemented, "...") } - // upload multiple (two) files + // upload multiple files post("multipart", { request { multipartBody { @@ -88,24 +87,6 @@ private fun Application.myModule() { call.respond(HttpStatusCode.NotImplemented, "...") } - // upload multiple (any amount of) files - post("list", { - request { - multipartBody { - mediaTypes(ContentType.MultiPart.FormData) - part("images", array()) { - mediaTypes( - ContentType.Image.PNG, - ContentType.Image.JPEG, - ContentType.Image.SVG - ) - } - } - } - }) { - call.respond(HttpStatusCode.NotImplemented, "...") - } - } } diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/KotlinxSerialization.kt b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/KotlinxSerialization.kt similarity index 93% rename from ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/KotlinxSerialization.kt rename to examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/KotlinxSerialization.kt index ac2a2444..97daf016 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/KotlinxSerialization.kt +++ b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/KotlinxSerialization.kt @@ -1,9 +1,10 @@ -package io.github.smiley4.ktorswaggerui.examples +package io.github.smiley4.ktoropenapi.examples 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.ktorredoc.redoc import io.github.smiley4.ktorswaggerui.swaggerUI import io.github.smiley4.schemakenerator.serialization.processKotlinxSerialization import io.github.smiley4.schemakenerator.swagger.compileReferencingRoot @@ -46,13 +47,16 @@ private fun Application.myModule() { routing { - // add the routes for swagger-ui and api-spec + // add the routes for the api-spec, swagger-ui and redoc route("swagger") { swaggerUI("/api.json") } route("api.json") { openApi() } + route("redoc") { + redoc("/api.json") + } // a documented route get("hello", { diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Minimal.kt b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/Minimal.kt similarity index 82% rename from ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Minimal.kt rename to examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/Minimal.kt index 1aef2c53..dc7126f3 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Minimal.kt +++ b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/Minimal.kt @@ -1,8 +1,9 @@ -package io.github.smiley4.ktorswaggerui.examples +package io.github.smiley4.ktoropenapi.examples import io.github.smiley4.ktoropenapi.OpenApi import io.github.smiley4.ktoropenapi.get import io.github.smiley4.ktoropenapi.openApi +import io.github.smiley4.ktorredoc.redoc import io.github.smiley4.ktorswaggerui.swaggerUI import io.ktor.server.application.Application import io.ktor.server.application.install @@ -23,15 +24,20 @@ private fun Application.myModule() { routing { + // Create a route for the openapi-spec file. + // This route will not be included in the spec. + route("api.json") { + openApi() + } // Create a route for the swagger-ui using the openapi-spec at "/api.json". // This route will not be included in the spec. route("swagger") { swaggerUI("/api.json") } - // Create a route for the openapi-spec file. + // Create a route for redoc using the openapi-spec at "/api.json". // This route will not be included in the spec. - route("api.json") { - openApi() + route("redoc") { + redoc("/api.json") } // a documented route diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/MultipleSpecs.kt b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/MultipleSpecs.kt similarity index 83% rename from ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/MultipleSpecs.kt rename to examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/MultipleSpecs.kt index cd49f5a8..61ee5128 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/MultipleSpecs.kt +++ b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/MultipleSpecs.kt @@ -1,9 +1,10 @@ -package io.github.smiley4.ktorswaggerui.examples +package io.github.smiley4.ktoropenapi.examples 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.ktoropenapi.route +import io.github.smiley4.ktorredoc.redoc import io.github.smiley4.ktorswaggerui.swaggerUI import io.ktor.server.application.Application import io.ktor.server.application.install @@ -45,25 +46,33 @@ private fun Application.myModule() { // add routes for "v1"-spec and swagger-ui route("v1") { + // api-spec containing all routes assigned to "v1" + route("api.json") { + openApi("version1") + } + // swagger-ui using '/v1/api.json' route("swagger") { - // swagger-ui using '/v1/api.json' swaggerUI("/v1/api.json") } - route("api.json") { - // api-spec containing all routes assigned to "v1" - openApi("version1") + // redoc using '/v1/api.json' + route("redoc") { + redoc("/v1/api.json") } } // add routes for "v2"-spec and swagger-ui route("v2") { + // api-spec containing all routes assigned to "v2" + route("api.json") { + openApi("version2") + } + // swagger-ui using '/v2/api.json' route("swagger") { - // swagger-ui using '/v2/api.json' swaggerUI("/v2/api.json") } - route("api.json") { - // api-spec containing all routes assigned to "v2" - openApi("version2") + // redoc using '/v2/api.json' + route("swagger") { + redoc("/v2/api.json") } } @@ -75,9 +84,6 @@ private fun Application.myModule() { // "hello"-route in version 1.0 get("hello", { description = "Version 1 'Hello World'" - request { - queryParameter>("name") - } }) { call.respondText("Hello World!") } @@ -92,9 +98,6 @@ private fun Application.myModule() { // "hello"-route in version 2.0 get("hello", { description = "Version 2 'Hello World'" - request { - queryParameter>("name") - } }) { call.respondText("Hello World! (improved)") } @@ -104,9 +107,6 @@ private fun Application.myModule() { // unassigned route get("greet", { description = "Alternative route not manually assigned to any spec." - request { - queryParameter>("name") - } }) { call.respondText("Alternative Hello World!") } diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Petstore.kt b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/Petstore.kt similarity index 97% rename from ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Petstore.kt rename to examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/Petstore.kt index bf415616..2c0b582a 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Petstore.kt +++ b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/Petstore.kt @@ -1,10 +1,11 @@ -package io.github.smiley4.ktorswaggerui.examples +package io.github.smiley4.ktoropenapi.examples 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.ktorredoc.redoc import io.github.smiley4.ktorswaggerui.swaggerUI import io.ktor.http.HttpStatusCode import io.ktor.server.application.Application @@ -48,12 +49,16 @@ private fun Application.myModule() { routing { + // add the routes for the api-spec, swagger-ui and redoc route("swagger") { swaggerUI("/api.json") } route("api.json") { openApi() } + route("redoc") { + redoc("/api.json") + } route("/pets") { diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/RequestResponse.kt b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/RequestResponse.kt similarity index 95% rename from ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/RequestResponse.kt rename to examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/RequestResponse.kt index e46a2330..53e18a3f 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/RequestResponse.kt +++ b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/RequestResponse.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.examples +package io.github.smiley4.ktoropenapi.examples import com.fasterxml.jackson.core.util.DefaultIndenter import com.fasterxml.jackson.core.util.DefaultPrettyPrinter @@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.SerializationFeature import io.github.smiley4.ktoropenapi.OpenApi import io.github.smiley4.ktoropenapi.post import io.github.smiley4.ktoropenapi.openApi +import io.github.smiley4.ktorredoc.redoc import io.github.smiley4.ktorswaggerui.swaggerUI import io.ktor.http.HttpStatusCode import io.ktor.serialization.jackson.jackson @@ -40,13 +41,16 @@ private fun Application.myModule() { routing { - // add the routes for swagger-ui and api-spec + // add the routes for the api-spec, swagger-ui and redoc route("swagger") { swaggerUI("/api.json") } route("api.json") { openApi() } + route("redoc") { + redoc("/api.json") + } // a documented route post("calculate", { diff --git a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Schemas.kt b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/Schemas.kt similarity index 94% rename from ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Schemas.kt rename to examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/Schemas.kt index 03eb8ce9..45da9a79 100644 --- a/ktor-swagger-ui-examples/src/main/kotlin/io/github/smiley4/ktorswaggerui/examples/Schemas.kt +++ b/examples/src/main/kotlin/io/github/smiley4/ktoropenapi/examples/Schemas.kt @@ -1,4 +1,4 @@ -package io.github.smiley4.ktorswaggerui.examples +package io.github.smiley4.ktoropenapi.examples import com.fasterxml.jackson.annotation.JsonSubTypes import io.github.smiley4.ktoropenapi.OpenApi @@ -7,6 +7,7 @@ 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.ktorredoc.redoc import io.github.smiley4.ktorswaggerui.swaggerUI import io.github.smiley4.schemakenerator.core.connectSubTypes import io.github.smiley4.schemakenerator.jackson.collectJacksonSubTypes @@ -37,7 +38,7 @@ private fun Application.myModule() { // add a swagger schema to the component-section of the api-spec with the id "swagger-schema" schema("swagger-schema", Schema().also { - it.types = setOf("number") + it.type = "number" it.title = "Custom Type" }) @@ -47,7 +48,7 @@ private fun Application.myModule() { // overwrite 'LocalDateTime' with custom schema (root only) overwrite(Schema().also { it.title = "timestamp" - it.types = setOf("integer") + it.type = "integer" }) // customized schema generation pipeline @@ -66,13 +67,16 @@ private fun Application.myModule() { routing { - // add the routes for swagger-ui and api-spec + // add the routes for the api-spec, swagger-ui and redoc route("swagger") { swaggerUI("/api.json") } route("api.json") { openApi() } + route("redoc") { + redoc("/api.json") + } get("basic", { diff --git a/ktor-swagger-ui-examples/src/main/resources/logback.xml b/examples/src/main/resources/logback.xml similarity index 100% rename from ktor-swagger-ui-examples/src/main/resources/logback.xml rename to examples/src/main/resources/logback.xml diff --git a/gradle.properties b/gradle.properties index ce599b6c..185c720f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,4 +22,5 @@ versionKotest=5.8.0 versionKotlinTest=2.0.21 versionMockk=1.13.12 versionLogback=1.5.6 -versionJackson=2.18.1 \ No newline at end of file +versionJackson=2.18.1 +versionRedoc=2.1.5 \ No newline at end of file diff --git a/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ContentBuilder.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ContentBuilder.kt index 5a38e266..07face68 100644 --- a/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ContentBuilder.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/openapi/ContentBuilder.kt @@ -66,7 +66,7 @@ internal class ContentBuilder( private fun buildMultipartMediaType(body: MultipartBodyData): MediaType { return MediaType().also { mediaType -> mediaType.schema = Schema().also { schema -> - schema.types = setOf("object") + schema.type = "object" schema.properties = mutableMapOf?>().also { props -> body.parts.forEach { part -> props[part.name] = schemaContext.getSchema(part.type) diff --git a/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/route/RouteCollector.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/route/RouteCollector.kt index bb708888..d9d6d4e1 100644 --- a/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/route/RouteCollector.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/builder/route/RouteCollector.kt @@ -23,7 +23,8 @@ internal class RouteCollector { private val routeDocumentationMerger = RouteDocumentationMerger() private val hiddenRouteMarkers = setOf( - "io.github.smiley4.ktorswaggerui.SwaggerUIRouteSelector" + "io.github.smiley4.ktorswaggerui.SwaggerUIRouteSelector", + "io.github.smiley4.ktorredoc.RedocRouteSelector" ) diff --git a/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/OpenApiPluginConfig.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/OpenApiPluginConfig.kt index 2ea6e399..8c107591 100644 --- a/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/OpenApiPluginConfig.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/OpenApiPluginConfig.kt @@ -1,7 +1,6 @@ package io.github.smiley4.ktoropenapi.config import io.github.smiley4.ktoropenapi.data.DataUtils.merge -import io.github.smiley4.ktoropenapi.data.DataUtils.mergeDefault import io.github.smiley4.ktoropenapi.data.OpenApiPluginData import io.github.smiley4.ktoropenapi.data.ServerData import io.ktor.server.routing.RouteSelector @@ -159,7 +158,7 @@ class OpenApiPluginConfig { }, securityConfig = securityConfig, tagsConfig = tags.build(base.tagsConfig), - schemaConfig = schemaConfig.build(base.schemaConfig, securityConfig), + schemaConfig = schemaConfig.build(securityConfig), exampleConfig = exampleConfig.build(securityConfig), specAssigner = merge(base.specAssigner, specAssigner) ?: OpenApiPluginData.DEFAULT.specAssigner, pathFilter = merge(base.pathFilter, pathFilter) ?: OpenApiPluginData.DEFAULT.pathFilter, @@ -173,7 +172,7 @@ class OpenApiPluginConfig { }, specConfigs = mutableMapOf(), postBuild = merge(base.postBuild, postBuild), - outputFormat = mergeDefault(base.outputFormat, outputFormat, OpenApiPluginData.DEFAULT.outputFormat), + outputFormat = outputFormat, rootPath = merge(rootPath ?: ktorRootPath, base.rootPath) ).also { specConfigs.forEach { (specName, config) -> diff --git a/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/SchemaConfig.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/SchemaConfig.kt index d5fb17fc..ad9d67ee 100644 --- a/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/SchemaConfig.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/SchemaConfig.kt @@ -1,7 +1,6 @@ package io.github.smiley4.ktoropenapi.config import io.github.smiley4.ktoropenapi.data.* -import io.github.smiley4.ktoropenapi.data.DataUtils.mergeDefault import io.github.smiley4.schemakenerator.swagger.data.CompiledSwaggerSchema import io.swagger.v3.oas.models.media.Schema import kotlin.reflect.KType @@ -87,16 +86,10 @@ class SchemaConfig { * Build the data object for this config. * @param securityConfig configuration that might contain additional schemas */ - internal fun build(base: SchemaConfigData, securityConfig: SecurityData) = SchemaConfigData( - generator = mergeDefault(base.generator, generator, SchemaConfigData.DEFAULT.generator), - schemas = mutableMapOf().apply { - this.putAll(base.schemas) - this.putAll(schemas) - }, - overwrite = mutableMapOf().apply { - this.putAll(base.overwrite) - this.putAll(overwrite) - }, + internal fun build(securityConfig: SecurityData) = SchemaConfigData( + generator = generator, + schemas = schemas, + overwrite = overwrite, securitySchemas = securityConfig.defaultUnauthorizedResponse?.body?.let { body -> when (body) { is SimpleBodyData -> listOf(body.type) diff --git a/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/TagsConfig.kt b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/TagsConfig.kt index cadb8575..897b42d8 100644 --- a/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/TagsConfig.kt +++ b/ktor-openapi/src/main/kotlin/io/github/smiley4/ktoropenapi/config/TagsConfig.kt @@ -1,6 +1,6 @@ package io.github.smiley4.ktoropenapi.config -import io.github.smiley4.ktoropenapi.data.DataUtils.mergeDefault +import io.github.smiley4.ktoropenapi.data.DataUtils.merge import io.github.smiley4.ktoropenapi.data.TagData import io.github.smiley4.ktoropenapi.data.TagsData @@ -36,7 +36,7 @@ class TagsConfig { addAll(base.tags) addAll(tags.map { it.build(TagData.DEFAULT) }) }, - generator = mergeDefault(base.generator, tagGenerator, TagsData.DEFAULT.generator) + generator = merge(base.generator, tagGenerator) ?: TagsData.DEFAULT.generator, ) } diff --git a/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/OperationBuilderTest.kt b/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/OperationBuilderTest.kt index e87f515b..e1404622 100644 --- a/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/OperationBuilderTest.kt +++ b/ktor-openapi/src/test/kotlin/io/github/smiley4/ktorswaggerui/builder/OperationBuilderTest.kt @@ -573,7 +573,7 @@ class OperationBuilderTest : StringSpec({ mediaType.schema .also { it.shouldNotBeNull() } ?.also { schema -> - schema.types.shouldContainExactlyInAnyOrder("object") + schema.type shouldBe "object" schema.properties.keys shouldContainExactlyInAnyOrder listOf( "image", "data" @@ -975,7 +975,7 @@ class OperationBuilderTest : StringSpec({ mediaType.schema .also { it.shouldNotBeNull() } ?.also { schema -> - schema.types.shouldContainExactlyInAnyOrder("object") + schema.type shouldBe "object" schema.properties.keys shouldContainExactlyInAnyOrder listOf("customData") schema.properties["customData"]!!.`$ref` shouldBe "#/components/schemas/myCustomSchema" } diff --git a/ktor-redoc/build.gradle.kts b/ktor-redoc/build.gradle.kts new file mode 100644 index 00000000..1e9567dd --- /dev/null +++ b/ktor-redoc/build.gradle.kts @@ -0,0 +1,105 @@ +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-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-test-host:$versionKtor") + + val versionRedoc: String by project + implementation("org.webjars:redoc:$versionRedoc") + + 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") +} + +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-redoc", projectVersion) + pom { + name.set("Ktor Redoc") + description.set("Ktor plugin to provide Redoc") + 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-redoc/src/main/kotlin/io/github/smiley4/ktorredoc/Redoc.kt b/ktor-redoc/src/main/kotlin/io/github/smiley4/ktorredoc/Redoc.kt new file mode 100644 index 00000000..eae7bbec --- /dev/null +++ b/ktor-redoc/src/main/kotlin/io/github/smiley4/ktorredoc/Redoc.kt @@ -0,0 +1,112 @@ +package io.github.smiley4.ktorredoc + +import io.github.smiley4.ktorredoc.config.RedocConfig +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 redoc resources. The path to the OpenApi-file to use has to be given. + */ +fun Route.redoc(openApiUrl: String, config: RedocConfig.() -> Unit = {}) { + val redocConfig = RedocConfig().apply(config) + markedRedoc { + get { + call.respondRedirect("${call.request.uri}/index.html") + } + get("{filename}") { + Redoc.serveStaticResource(call.parameters["filename"]!!, redocConfig, call) + } + get("index.html") { + Redoc.serveIndexHtml(call, redocConfig, openApiUrl) + } + } +} + +internal object Redoc { + + internal suspend fun serveIndexHtml(call: ApplicationCall, config: RedocConfig, openApiUrl: String) { + val redocProperties = buildMap { + this["spec-url"] = "'$openApiUrl'" + config.disableSearch?.also { this["disable-search"] = it.toString() } + config.minCharacterLengthToInitSearch?.also { this["min-character-length-to-init-search"] = it.toString() } + config.expandDefaultServerVariables?.also { this["expand-default-server-variables"] = it.toString() } + config.expandResponses?.also { + val values = it.toSet() + if (values.any { v -> v.equals("all", ignoreCase = true) }) { + this["expand-responses"] = "'all'" + } else { + this["expand-responses"] = "'${values.joinToString(",")}'" + } + } + config.expandSingleSchemaField?.also { this["expand-single-schema-field"] = it.toString() } + config.hideDownloadButton?.also { this["hide-download-button"] = it.toString() } + config.hideHostname?.also { this["hide-hostname"] = it.toString() } + config.hideLoading?.also { this["hide-loading"] = it.toString() } + config.hideRequestPayloadSample?.also { this["hide-request-payload-sample"] = it.toString() } + config.hideOneOfDescription?.also { this["hide-one-of-description"] = it.toString() } + config.hideSchemaPattern?.also { this["hide-schema-pattern"] = it.toString() } + config.hideSchemaTitles?.also { this["hide-schema-titles"] = it.toString() } + config.hideSecuritySection?.also { this["hide-security-section"] = it.toString() } + config.hideSingleRequestSampleTab?.also { this["hide-single-request-sample-tab"] = it.toString() } + config.htmlTemplate?.also { this["html-template"] = "'$it'" } + config.jsonSampleExpandLevel?.also { this["json-sample-expand-level"] = "'$it'" } + config.maxDisplayedEnumValues?.also { this["max-displayed-enum-values"] = it.toString() } + config.menuToggle?.also { this["menu-toggle"] = it.toString() } + config.nativeScrollbars?.also { this["native-scrollbars"] = it.toString() } + config.onlyRequiredInSamples?.also { this["only-required-in-samples"] = it.toString() } + config.pathInMiddlePanel?.also { this["path-in-middle-panel"] = it.toString() } + config.payloadSampleIdx?.also { this["payload-sample-idx"] = it.toString() } + config.requiredPropsFirst?.also { this["required-props-first"] = it.toString() } + config.schemaExpansionLevel?.also { this["schema-expansion-level"] = "'$it'" } + config.showObjectSchemaExamples?.also { this["show-object-schema-examples"] = it.toString() } + config.showWebhookVerb?.also { this["show-webhook-verb"] = it.toString() } + config.simpleOneOfTypeLabel?.also { this["simple-one-of-type-label"] = it.toString() } + config.sortEnumValuesAlphabetically?.also { this["sort-enum-values-alphabetically"] = it.toString() } + config.sortOperationsAlphabetically?.also { this["sort-operations-alphabetically"] = it.toString() } + config.sortPropsAlphabetically?.also { this["sort-props-alphabetically"] = it.toString() } + config.sortTagsAlphabetically?.also { this["sort-tags-alphabetically"] = it.toString() } + config.untrustedDefinition?.also { this["untrusted-definition"] = it.toString() } + config.theme?.also { this["theme"] = "'$it'" } + } + val content = """ + + + + Redoc + + + + + + + "$key=$value" }}> + + + + """.trimIndent() + call.respondText(ContentType.Text.Html, HttpStatusCode.OK) { content } + } + + internal suspend fun serveStaticResource(filename: String, config: RedocConfig, call: ApplicationCall) { + val resourceName = "${config.staticResourcesPath}/$filename" + val resource = Redoc::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-redoc/src/main/kotlin/io/github/smiley4/ktorredoc/RedocRouteSelector.kt b/ktor-redoc/src/main/kotlin/io/github/smiley4/ktorredoc/RedocRouteSelector.kt new file mode 100644 index 00000000..2dda1d81 --- /dev/null +++ b/ktor-redoc/src/main/kotlin/io/github/smiley4/ktorredoc/RedocRouteSelector.kt @@ -0,0 +1,14 @@ +package io.github.smiley4.ktorredoc + +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 RedocRouteSelector : RouteSelector() { + override suspend fun evaluate(context: RoutingResolveContext, segmentIndex: Int) = RouteSelectorEvaluation.Transparent +} + +internal fun Route.markedRedoc(build: Route.() -> Unit): Route { + return createChild(RedocRouteSelector()).also(build) +} diff --git a/ktor-redoc/src/main/kotlin/io/github/smiley4/ktorredoc/ResourceContent.kt b/ktor-redoc/src/main/kotlin/io/github/smiley4/ktorredoc/ResourceContent.kt new file mode 100644 index 00000000..9deeef82 --- /dev/null +++ b/ktor-redoc/src/main/kotlin/io/github/smiley4/ktorredoc/ResourceContent.kt @@ -0,0 +1,32 @@ +package io.github.smiley4.ktorredoc + +import io.ktor.http.ContentType +import io.ktor.http.content.OutgoingContent +import io.ktor.http.withCharset +import java.net.URL + +internal class ResourceContent(private val resource: URL) : OutgoingContent.ByteArrayContent() { + + private val contentTypes = mapOf( + "html" to ContentType.Text.Html, + "css" to ContentType.Text.CSS, + "js" to ContentType.Application.JavaScript, + "json" to ContentType.Application.Json, + "png" to ContentType.Image.PNG + ) + + private val bytes by lazy { resource.readBytes() } + + override val contentType: ContentType? by lazy { + val extension = resource.file.substring(resource.file.lastIndexOf('.') + 1) + contentTypes[extension] ?: ContentType.Text.Html + } + + override val contentLength: Long? by lazy { + bytes.size.toLong() + } + + override fun bytes(): ByteArray = bytes + + override fun toString() = "ResourceContent \"$resource\"" +} diff --git a/ktor-redoc/src/main/kotlin/io/github/smiley4/ktorredoc/config/RedocConfig.kt b/ktor-redoc/src/main/kotlin/io/github/smiley4/ktorredoc/config/RedocConfig.kt new file mode 100644 index 00000000..030af4e7 --- /dev/null +++ b/ktor-redoc/src/main/kotlin/io/github/smiley4/ktorredoc/config/RedocConfig.kt @@ -0,0 +1,256 @@ +package io.github.smiley4.ktorredoc.config + +class RedocConfig { + + /** + * Path to the static resources for redoc in the jar-file. + * Version must match the version of the redoc-webjars dependency. + */ + internal val staticResourcesPath: String = "/META-INF/resources/webjars/redoc/2.1.5" + + + /** + * Disables search indexing and hides the search box from the API documentation page. + * https://redocly.com/docs/redoc/config#disablesearch + */ + var disableSearch: Boolean? = null + + + /** + * Sets the minimum amount of characters that need to be typed into the search dialog to initiate the search. + * https://redocly.com/docs/redoc/config#mincharacterlengthtoinitsearch + */ + var minCharacterLengthToInitSearch: Int? = null + + + /** + * Enables or disables expanding default server variables. + * https://redocly.com/docs/redoc/config#expanddefaultservervariables + */ + var expandDefaultServerVariables: Boolean? = null + + + /** + * Controls which responses to expand by default. + * Specify one or more responses by providing their response codes as a list, for example expandResponses=["200","201"]. + * Special value 'all' expands all responses by default. + * Be careful: this option can slow down documentation rendering time. + * https://redocly.com/docs/redoc/config#expandresponses + */ + var expandResponses: Collection? = null + + + /** + * Automatically expands the single field in a schema. + * https://redocly.com/docs/redoc/config#expandsingleschemafield + */ + var expandSingleSchemaField: Boolean? = null + + + /** + * Hides the 'Download' button for saving the API definition source file. + * This setting does not make the API definition private; it just hides the button. + * https://redocly.com/docs/redoc/config#hidedownloadbutton + */ + var hideDownloadButton: Boolean? = null + + + /** + * If set to true, the protocol and hostname are not shown in the operation definition. + * https://redocly.com/docs/redoc/config#hidehostname + */ + var hideHostname: Boolean? = null + + + /** + * Hides the loading animation. + * https://redocly.com/docs/redoc/config#hideloading + */ + var hideLoading: Boolean? = null + + + /** + * Hides request payload examples. + * https://redocly.com/docs/redoc/config#hiderequestpayloadsample + */ + var hideRequestPayloadSample: Boolean? = null + + + /** + * If set to true, the description for oneOf/anyOf object is not shown in the schema. + * https://redocly.com/docs/redoc/config#hideoneofdescription + */ + var hideOneOfDescription: Boolean? = null + + + /** + * If set to true, the pattern is not shown in the schema. + * https://redocly.com/docs/redoc/config#hideschemapattern + */ + var hideSchemaPattern: Boolean? = null + + + /** + * Hides the schema title next to the type. + * https://redocly.com/docs/redoc/config#hideschematitles + */ + var hideSchemaTitles: Boolean? = null + + + /** + * Hides the Security panel section. + * https://redocly.com/docs/redoc/config#hidesecuritysection + */ + var hideSecuritySection: Boolean? = null + + + /** + * Hides the request sample tab for requests with only one sample. + * https://redocly.com/docs/redoc/config#hidesinglerequestsampletab + */ + var hideSingleRequestSampleTab: Boolean? = null + + + /** + * Sets the path to the optional HTML file used to modify the layout of the reference docs page. + * https://redocly.com/docs/redoc/config#htmltemplate + */ + var htmlTemplate: String? = null + + + /** + * Sets the default expand level for JSON payload samples (response and request body). + * The default value is "2", and the maximum supported value is '+Infinity'. + * It can also be configured with the special value "all" that expands all levels. + * https://redocly.com/docs/redoc/config#jsonsampleexpandlevel + */ + var jsonSampleExpandLevel: String? = null + + + /** + * Displays only the specified number of enum values. The remaining values are hidden in an expandable area. + * If not set, all values are displayed. + * https://redocly.com/docs/redoc/config#maxdisplayedenumvalues + */ + var maxDisplayedEnumValues: Int? = null + + + /** + * If set to true, selecting an expanded item in the sidebar twice collapses it. + * https://redocly.com/docs/redoc/config#menutoggle + */ + var menuToggle: Boolean? = null + + + /** + * If set to true, the sidebar uses the native scrollbar instead of perfect-scroll. + * This setting is a scrolling performance optimization for big API definitions. + * https://redocly.com/docs/redoc/config#nativescrollbars + */ + var nativeScrollbars: Boolean? = null + + + /** + * Shows only required fields in request samples. + * https://redocly.com/docs/redoc/config#onlyrequiredinsamples + */ + var onlyRequiredInSamples: Boolean? = null + + + /** + * Shows the path link and HTTP verb in the middle panel instead of the right panel. + * https://redocly.com/docs/redoc/config#pathinmiddlepanel + */ + var pathInMiddlePanel: Boolean? = null + + + /** + * If set, the payload sample is inserted at the specified index. + * If there are N payload samples and the value configured here is bigger than N, the payload sample is inserted last. Indexes start from 0. + * https://redocly.com/docs/redoc/config#payloadsampleidx + */ + var payloadSampleIdx: Int? = null + + + /** + * Shows required properties in schemas first, ordered in the same order as in the required array. + * https://redocly.com/docs/redoc/config#requiredpropsfirst + */ + var requiredPropsFirst: Boolean? = null + + + /** + * Specifies whether to automatically expand schemas in Reference docs. + * Set it to "all" to expand all schemas regardless of their level, or set it to a number to expand schemas up to the specified level. + * For example, schemaExpansionLevel: "3" expands schemas up to three levels deep. + * The default value is 0, meaning no schemas are expanded automatically. + * https://redocly.com/docs/redoc/config#schemaexpansionlevel + */ + var schemaExpansionLevel: String? = null + + + /** + * Shows object schema example in the properties; default false. + * https://redocly.com/docs/redoc/config#showobjectschemaexamples + */ + var showObjectSchemaExamples: Boolean? = null + + + /** + * When set to true, shows the HTTP request method for webhooks in operations and in the sidebar. + * https://redocly.com/docs/redoc/config#showwebhookverb + */ + var showWebhookVerb: Boolean? = null + + + /** + * Shows only unique oneOf types in the label without titles. + * https://redocly.com/docs/redoc/config#simpleoneoftypelabel + */ + var simpleOneOfTypeLabel: Boolean? = null + + + /** + * When set to true, sorts all enum values in all schemas alphabetically. + * https://redocly.com/docs/redoc/config#sortenumvaluesalphabetically + */ + var sortEnumValuesAlphabetically: Boolean? = null + + + /** + * When set to true, sorts operations in the navigation sidebar and in the middle panel alphabetically. + * https://redocly.com/docs/redoc/config#sortoperationsalphabetically + */ + var sortOperationsAlphabetically: Boolean? = null + + + /** + * When set to true, sorts properties in all schemas alphabetically. + * https://redocly.com/docs/redoc/config#sortpropsalphabetically + */ + var sortPropsAlphabetically: Boolean? = null + + + /** + * When set to true, sorts tags in the navigation sidebar and in the middle panel alphabetically. + * Note that only tags are sorted alphabetically in the middle panel, not the operations associated with each tag. + * To sort operations alphabetically as well, you must set the sortOperationsAlphabetically setting to true. + * https://redocly.com/docs/redoc/config#sorttagsalphabetically + */ + var sortTagsAlphabetically: Boolean? = null + + + /** + * If set to true, the API definition is considered untrusted and all HTML/Markdown is sanitized to prevent XSS. + * https://redocly.com/docs/redoc/config#untrusteddefinition + */ + var untrustedDefinition: Boolean? = null + + + /** + * The theme configuration setting is more complex since it represents many nested options, you can supply these as a JSON string + * https://redocly.com/docs/redoc/config#theme-settings + */ + var theme: String? = null + +} diff --git a/ktor-swagger-ui/build.gradle.kts b/ktor-swagger-ui/build.gradle.kts index c3852993..cccecf3f 100644 --- a/ktor-swagger-ui/build.gradle.kts +++ b/ktor-swagger-ui/build.gradle.kts @@ -80,7 +80,7 @@ mavenPublishing { signAllPublications() coordinates(projectGroupId, "ktor-swagger-ui", projectVersion) pom { - name.set("Ktor OpenApi") + name.set("Ktor Swagger-UI") description.set("Ktor plugin to provide a Swagger-UI") url.set(projectScmUrl) licenses { diff --git a/settings.gradle.kts b/settings.gradle.kts index a5af31c8..c4f3be9f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,9 +1,10 @@ rootProject.name = "ktor-openapi" include("ktor-openapi") - include("ktor-swagger-ui") -include("ktor-swagger-ui-examples") +include("ktor-redoc") + +include("examples") pluginManagement { repositories {