Skip to content

Commit

Permalink
Add Swagger UI lib
Browse files Browse the repository at this point in the history
  • Loading branch information
turchenkoalex committed Nov 28, 2024
1 parent a724899 commit d54d69f
Show file tree
Hide file tree
Showing 22 changed files with 294 additions and 0 deletions.
1 change: 1 addition & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions swagger-ui/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
plugins {
alias(libs.plugins.kotlin.jvm)
}

dependencies {
implementation(project(":core"))
compileOnly(libs.jakarta.api)

testImplementation(libs.kotlin.test)
testImplementation(libs.mockk)
testImplementation(libs.jakarta.api)
}

tasks.test {
useJUnitPlatform()
}

kotlin {
jvmToolchain(21)
}
88 changes: 88 additions & 0 deletions swagger-ui/src/main/kotlin/swagger/ui/SwaggerUI.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package swagger.ui

import kotlet.Handler
import kotlet.HttpCall
import kotlet.Routing

/**
* Install OpenAPI endpoint to the routing
*
* @param configure Configuration of the OpenAPI endpoint
*/
fun Routing.installSwaggerUIEndpoint(configure: SwaggerUIConfigBuilder.() -> Unit) {
val builder = SwaggerUIConfigBuilder()
builder.configure()
val config = builder.build()

fun serveResource(path: String, contentType: String) {
get("${config.path}/$path", SwaggerUIResourceHandler("/ui/$path", contentType))
}

// Serve swagger ui static files
get(config.path, RedirectHandler("${config.path}/index.html"))
serveResource("index.html", MediaTypes.TEXT_HTML)
serveResource("index.css", MediaTypes.TEXT_CSS)
serveResource("swagger-ui.css", MediaTypes.TEXT_CSS)
serveResource("swagger-ui-standalone-preset.js", MediaTypes.JAVASCRIPT)
serveResource("swagger-ui-bundle.js", MediaTypes.JAVASCRIPT)
get("${config.path}/swagger-initializer.js", SwaggerUIInitializerHandler(openAPIPath = config.openAPIPath))
}

private class RedirectHandler(private val location: String) : Handler {
override fun invoke(call: HttpCall) {
call.rawResponse.sendRedirect(location)
}
}

private class SwaggerUIResourceHandler(
resourcePath: String,
private val contentType: String,
) : Handler {
private val bytes: ByteArray = loadSwaggerUIResource(resourcePath)

override fun invoke(call: HttpCall) {
call.rawResponse.contentType = contentType
call.respondBytes(bytes)
}
}

private fun loadSwaggerUIResource(path: String): ByteArray {
val resourceStream = requireNotNull(SwaggerUIResourceHandler::class.java.getResourceAsStream(path)) {
"Resource not found: $path"
}
return resourceStream.readBytes()
}

private class SwaggerUIInitializerHandler(openAPIPath: String) : Handler {
private val bytes = """
window.onload = function() {
window.ui = SwaggerUIBundle({
url: "$openAPIPath",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
});
};
""".trimIndent().toByteArray()

override fun invoke(call: HttpCall) {
call.rawResponse.contentType = MediaTypes.JAVASCRIPT
call.respondBytes(bytes)
}
}

/**
* Media types for Swagger UI resources
*/
private object MediaTypes {
const val TEXT_HTML = "text/html"
const val TEXT_CSS = "text/css"
const val JAVASCRIPT = "application/javascript"
}
31 changes: 31 additions & 0 deletions swagger-ui/src/main/kotlin/swagger/ui/SwaggerUIConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package swagger.ui

/**
* Configuration for the Swagger UI endpoint
*/
internal data class SwaggerUIConfig(
val path: String,
val openAPIPath: String,
)

/**
* Builder for [SwaggerUIConfig].
*/
class SwaggerUIConfigBuilder internal constructor() {
/**
* Path to the Swagger UI endpoint
*/
var path: String = "/swagger/ui"

/**
* Path to the OpenAPI endpoint
*/
var openAPIPath: String = "/openapi.json"

internal fun build(): SwaggerUIConfig {
return SwaggerUIConfig(
path = path,
openAPIPath = openAPIPath,
)
}
}
Binary file added swagger-ui/src/main/resources/ui/favicon-16x16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added swagger-ui/src/main/resources/ui/favicon-32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions swagger-ui/src/main/resources/ui/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
html {
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}

*,
*:before,
*:after {
box-sizing: inherit;
}

body {
margin: 0;
background: #fafafa;
}
19 changes: 19 additions & 0 deletions swagger-ui/src/main/resources/ui/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" />
<link rel="stylesheet" type="text/css" href="./index.css" />
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
</head>

<body>
<div id="swagger-ui"></div>
<script src="./swagger-ui-bundle.js" charset="UTF-8"> </script>
<script src="./swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
<script src="./swagger-initializer.js" charset="UTF-8"> </script>
</body>
</html>
79 changes: 79 additions & 0 deletions swagger-ui/src/main/resources/ui/oauth2-redirect.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<!doctype html>
<html lang="en-US">
<head>
<title>Swagger UI: OAuth2 Redirect</title>
</head>
<body>
<script>
'use strict';
function run () {
var oauth2 = window.opener.swaggerUIRedirectOauth2;
var sentState = oauth2.state;
var redirectUrl = oauth2.redirectUrl;
var isValid, qp, arr;

if (/code|token|error/.test(window.location.hash)) {
qp = window.location.hash.substring(1).replace('?', '&');
} else {
qp = location.search.substring(1);
}

arr = qp.split("&");
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';});
qp = qp ? JSON.parse('{' + arr.join() + '}',
function (key, value) {
return key === "" ? value : decodeURIComponent(value);
}
) : {};

isValid = qp.state === sentState;

if ((
oauth2.auth.schema.get("flow") === "accessCode" ||
oauth2.auth.schema.get("flow") === "authorizationCode" ||
oauth2.auth.schema.get("flow") === "authorization_code"
) && !oauth2.auth.code) {
if (!isValid) {
oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "warning",
message: "Authorization may be unsafe, passed state was changed in server. The passed state wasn't returned from auth server."
});
}

if (qp.code) {
delete oauth2.state;
oauth2.auth.code = qp.code;
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
} else {
let oauthErrorMsg;
if (qp.error) {
oauthErrorMsg = "["+qp.error+"]: " +
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
(qp.error_uri ? "More info: "+qp.error_uri : "");
}

oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "error",
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server."
});
}
} else {
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
}
window.close();
}

if (document.readyState !== 'loading') {
run();
} else {
document.addEventListener('DOMContentLoaded', function () {
run();
});
}
</script>
</body>
</html>
20 changes: 20 additions & 0 deletions swagger-ui/src/main/resources/ui/swagger-initializer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
window.onload = function() {
//<editor-fold desc="Changeable Configuration Block">

// the following lines will be replaced by docker/configurator, when it runs in a docker-container
window.ui = SwaggerUIBundle({
url: "https://petstore.swagger.io/v2/swagger.json",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
});

//</editor-fold>
};
2 changes: 2 additions & 0 deletions swagger-ui/src/main/resources/ui/swagger-ui-bundle.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions swagger-ui/src/main/resources/ui/swagger-ui-bundle.js.map

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions swagger-ui/src/main/resources/ui/swagger-ui-es-bundle-core.js

Large diffs are not rendered by default.

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions swagger-ui/src/main/resources/ui/swagger-ui-es-bundle.js

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions swagger-ui/src/main/resources/ui/swagger-ui.css

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions swagger-ui/src/main/resources/ui/swagger-ui.css.map

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions swagger-ui/src/main/resources/ui/swagger-ui.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions swagger-ui/src/main/resources/ui/swagger-ui.js.map

Large diffs are not rendered by default.

0 comments on commit d54d69f

Please sign in to comment.