Не подтверждена Коммит 2cd02baf создал по автору Leonid Stashevsky's avatar Leonid Stashevsky Зафиксировано автором GitHub
Просмотр файлов

KTOR-774 Add support for OpenAPI and Swagger endpoints (#3147)

владелец 20c31ae1
public final class io/ktor/server/plugins/openapi/OpenAPIConfig {
public fun <init> ()V
public final fun getCodegen ()Lio/swagger/codegen/v3/CodegenConfig;
public final fun getGenerator ()Lio/swagger/codegen/v3/Generator;
public final fun getOptions ()Lio/swagger/v3/parser/core/models/ParseOptions;
public final fun getOpts ()Lio/swagger/codegen/v3/ClientOptInput;
public final fun getParser ()Lio/swagger/parser/OpenAPIParser;
public final fun setCodegen (Lio/swagger/codegen/v3/CodegenConfig;)V
public final fun setGenerator (Lio/swagger/codegen/v3/Generator;)V
public final fun setOptions (Lio/swagger/v3/parser/core/models/ParseOptions;)V
public final fun setOpts (Lio/swagger/codegen/v3/ClientOptInput;)V
public final fun setParser (Lio/swagger/parser/OpenAPIParser;)V
}
public final class io/ktor/server/plugins/openapi/OpenAPIKt {
public static final fun openAPI (Lio/ktor/server/routing/Routing;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
public static synthetic fun openAPI$default (Lio/ktor/server/routing/Routing;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
}
/*
* Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
kotlin {
sourceSets {
jvmMain {
dependencies {
implementation(project(":ktor-server:ktor-server-plugins:ktor-server-html-builder"))
implementation("io.swagger.codegen.v3:swagger-codegen:3.0.35")
implementation("io.swagger.codegen.v3:swagger-codegen-generators:1.0.35")
implementation("io.swagger.parser.v3:swagger-parser:2.1.1")
}
}
}
}
/*
* Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
package io.ktor.server.plugins.openapi
import io.ktor.server.http.content.*
import io.ktor.server.routing.*
import io.swagger.codegen.v3.*
import io.swagger.codegen.v3.generators.html.*
import java.io.*
/**
* Creates a `get` endpoint at [path] with documentation rendered from the OpenAPI file.
*
* This method tries to lookup [swaggerFile] in the resources first, and if it's not found, it will try to read it from
* the file system using [java.io.File].
*
* The documentation is generated using [StaticHtml2Codegen] by default. It can be customized using config in [block].
* See [OpenAPIConfig] for more details.
*/
public fun Routing.openAPI(
path: String,
swaggerFile: String = "documentation.json",
block: OpenAPIConfig.() -> Unit = {}
) {
val resource = application.environment.classLoader.getResource(swaggerFile)
val file = if (resource != null) File(resource.toURI()) else File(swaggerFile)
if (!file.exists()) {
throw FileNotFoundException("Swagger file not found: $swaggerFile")
}
val config = OpenAPIConfig()
with(config) {
val swagger = parser.readContents(File(swaggerFile).readText(), null, options)
opts.apply {
config(codegen)
opts(ClientOpts())
openAPI(swagger.openAPI)
}
block(this)
generator.opts(opts)
generator.generate()
static(path) {
staticRootFolder = File("docs")
files(".")
default("index.html")
}
}
}
/*
* Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
package io.ktor.server.plugins.openapi
import io.swagger.codegen.v3.*
import io.swagger.codegen.v3.generators.html.*
import io.swagger.parser.*
import io.swagger.v3.parser.core.models.*
/**
* Configuration for OpenAPI endpoint.
*/
public class OpenAPIConfig {
/**
* Specifies a parser used to parse OpenAPI.
*/
public var parser: OpenAPIParser = OpenAPIParser()
/**
* Specifies options of the OpenAPI generator.
*/
public var opts: ClientOptInput = ClientOptInput()
/**
* Specifies a generator used to generate OpenAPI.
*/
public var generator: Generator = DefaultGenerator()
/**
* Specifies a code generator for [OpenAPIConfig].
*
* See also [StaticHtml2Codegen], [StaticHtmlCodegen] and etc.
*/
public var codegen: CodegenConfig = StaticHtml2Codegen()
/**
* Provides access to options of the OpenAPI format parser.
*/
public var options: ParseOptions = ParseOptions()
}
public final class io/ktor/server/swagger/SwaggerConfig {
public fun <init> ()V
public final fun customStyle (Ljava/lang/String;)V
public final fun getPackageLocation ()Ljava/lang/String;
public final fun getVersion ()Ljava/lang/String;
public final fun setPackageLocation (Ljava/lang/String;)V
public final fun setVersion (Ljava/lang/String;)V
}
public final class io/ktor/server/swagger/SwaggerKt {
public static final fun swaggerUI (Lio/ktor/server/routing/Routing;Ljava/lang/String;Ljava/io/File;Lkotlin/jvm/functions/Function1;)V
public static final fun swaggerUI (Lio/ktor/server/routing/Routing;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
public static synthetic fun swaggerUI$default (Lio/ktor/server/routing/Routing;Ljava/lang/String;Ljava/io/File;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public static synthetic fun swaggerUI$default (Lio/ktor/server/routing/Routing;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
}
/*
* Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
kotlin {
sourceSets {
jvmMain {
dependencies {
implementation(project(":ktor-server:ktor-server-plugins:ktor-server-html-builder"))
}
}
}
}
package io.ktor.server.swagger
import io.ktor.server.application.*
import io.ktor.server.html.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.html.*
import java.io.*
/**
* Creates a `get` endpoint with [SwaggerUI] at [path] rendered from the OpenAPI file located at [swaggerFile].
*
* This method tries to lookup [swaggerFile] in the resources first, and if it's not found, it will try to read it from
* the file system using [java.io.File].
*
*/
public fun Routing.swaggerUI(
path: String,
swaggerFile: String = "documentation.json",
block: SwaggerConfig.() -> Unit = {}
) {
val resource = application.environment.classLoader.getResource(swaggerFile)
if (resource != null) {
swaggerUI(path, File(resource.toURI()), block)
return
}
val file = File(swaggerFile)
if (!file.exists()) {
throw FileNotFoundException("Swagger file not found: $swaggerFile")
}
swaggerUI(path, file, block)
}
/**
* Creates a `get` endpoint with [SwaggerUI] at [path] rendered from the [apiFile].
*/
public fun Routing.swaggerUI(path: String, apiFile: File, block: SwaggerConfig.() -> Unit = {}) {
if (!apiFile.exists()) {
throw FileNotFoundException("Swagger file not found: ${apiFile.absolutePath}")
}
val fileName = apiFile.name
val config = SwaggerConfig().apply(block)
route(path) {
get(apiFile.name) {
call.respondFile(apiFile)
}
get {
val fullPath = call.request.path()
call.respondHtml {
head {
title { +"Swagger UI" }
link(
href = "${config.packageLocation}@${config.version}/swagger-ui.css",
rel = "stylesheet"
)
config.customStyle?.let {
link(href = it, rel = "stylesheet")
}
}
body {
div { id = "swagger-ui" }
script(src = "${config.packageLocation}@${config.version}/swagger-ui-bundle.js") {
attributes["crossorigin"] = "anonymous"
}
val src = "${config.packageLocation}@${config.version}/swagger-ui-standalone-preset.js"
script(src = src) {
attributes["crossorigin"] = "anonymous"
}
script {
unsafe {
+"""
window.onload = function() {
window.ui = SwaggerUIBundle({
url: '$fullPath/$fileName',
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
layout: 'StandaloneLayout'
});
}
""".trimIndent()
}
}
}
}
}
}
}
/*
* Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
package io.ktor.server.swagger
/**
* A configuration for the Swagger UI endpoint.
*/
public class SwaggerConfig {
internal var customStyle: String? = null
/**
* Specifies a Swagger UI version to use.
*/
public var version: String = "4.14.0"
/**
* Specifies a URL for a custom CSS applied to a Swagger UI.
*
* Example: https://unpkg.com/swagger-ui-themes@3.0.1/themes/3.x/theme-monokai.css
*/
public fun customStyle(path: String?) {
customStyle = path
}
/**
* Swagger package location
*/
public var packageLocation: String = "https://unpkg.com/swagger-ui-dist"
}
/*
* Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
package io.ktor.server.swagger
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.server.testing.*
import kotlin.test.*
class SwaggerTest {
@Test
fun testSwaggerFromResources() = testApplication {
routing {
swaggerUI("swagger")
}
val response = client.get("/swagger").bodyAsText()
assertEquals(
"""
<!DOCTYPE html>
<html>
<head>
<title>Swagger UI</title>
<link href="https://unpkg.com/swagger-ui-dist@4.14.0/swagger-ui.css" rel="stylesheet">
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://unpkg.com/swagger-ui-dist@4.14.0/swagger-ui-bundle.js" crossorigin="anonymous"></script>
<script src="https://unpkg.com/swagger-ui-dist@4.14.0/swagger-ui-standalone-preset.js" crossorigin="anonymous"></script>
<script>window.onload = function() {
window.ui = SwaggerUIBundle({
url: '/swagger/documentation.json',
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
layout: 'StandaloneLayout'
});
}</script>
</body>
</html>
""".trimIndent(),
response
)
}
@Test
fun testSwaggerFileIsServed() = testApplication {
routing {
swaggerUI("swagger")
}
val response = client.get("/swagger/documentation.json").bodyAsText()
assertEquals("""{"hello":"world"}""".filter { it.isLetterOrDigit() }, response.filter { it.isLetterOrDigit() })
}
}
...@@ -125,6 +125,8 @@ include(":ktor-server:ktor-server-plugins:ktor-server-velocity") ...@@ -125,6 +125,8 @@ include(":ktor-server:ktor-server-plugins:ktor-server-velocity")
include(":ktor-server:ktor-server-plugins:ktor-server-webjars") include(":ktor-server:ktor-server-plugins:ktor-server-webjars")
include(":ktor-server:ktor-server-plugins:ktor-server-websockets") include(":ktor-server:ktor-server-plugins:ktor-server-websockets")
include(":ktor-server:ktor-server-plugins:ktor-server-method-override") include(":ktor-server:ktor-server-plugins:ktor-server-method-override")
include(":ktor-server:ktor-server-plugins:ktor-server-openapi")
include(":ktor-server:ktor-server-plugins:ktor-server-swagger")
include(":ktor-server:ktor-server-plugins") include(":ktor-server:ktor-server-plugins")
include(":ktor-http") include(":ktor-http")
include(":ktor-http:ktor-http-cio") include(":ktor-http:ktor-http-cio")
......
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать