-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
- Loading branch information
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
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) | ||
} |
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" | ||
} |
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, | ||
) | ||
} | ||
} |
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; | ||
} |
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> |
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> |
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> | ||
}; |
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.
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.
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.