issue-155: add swagger-ui handler #157
12
handler/generate.go
Normal file
12
handler/generate.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
// import required packages
|
||||||
|
_ "go.unistack.org/micro-proto/v3/openapiv3"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate sh -c "curl -L https://github.com/swagger-api/swagger-ui/archive/refs/tags/v4.18.3.zip -o - | bsdtar -C swagger-ui --strip-components=2 -xv swagger-ui-4.18.3/dist && rm swagger-ui/*.map swagger-ui/*-es-*.js swagger-ui/swagger-ui.js swagger-ui/swagger-initializer.js"
|
||||||
|
|
||||||
|
//go:generate sh -c "protoc -I./ -I$(go list -f '{{ .Dir }}' -m go.unistack.org/micro-proto/v3) --go-micro_out='components=micro|http|server',standalone=false,debug=true,paths=source_relative:./ ./meter/meter.proto"
|
||||||
|
|
||||||
|
//go:generate sh -c "protoc -I./ -I$(go list -f '{{ .Dir }}' -m go.unistack.org/micro-proto/v3) --go-micro_out='components=micro|http|server',standalone=false,debug=true,paths=source_relative:./ ./health/health.proto"
|
@ -1,8 +0,0 @@
|
|||||||
package health
|
|
||||||
|
|
||||||
//go:generate sh -c "protoc -I./ -I$(go list -f '{{ .Dir }}' -m go.unistack.org/micro-proto/v3) --go-micro_out='components=micro|http|server',standalone=false,debug=true,paths=source_relative:./ health.proto"
|
|
||||||
|
|
||||||
import (
|
|
||||||
// import required packages
|
|
||||||
_ "go.unistack.org/micro-proto/v3/openapiv3"
|
|
||||||
)
|
|
@ -1,8 +0,0 @@
|
|||||||
package meter
|
|
||||||
|
|
||||||
//go:generate sh -c "protoc -I./ -I$(go list -f '{{ .Dir }}' -m go.unistack.org/micro-proto/v3) --go-micro_out='components=micro|http|server',standalone=false,debug=true,paths=source_relative:./ meter.proto"
|
|
||||||
|
|
||||||
import (
|
|
||||||
// import required packages
|
|
||||||
_ "go.unistack.org/micro-proto/v3/openapiv3"
|
|
||||||
)
|
|
BIN
handler/swagger-ui/favicon-16x16.png
Normal file
BIN
handler/swagger-ui/favicon-16x16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 665 B |
BIN
handler/swagger-ui/favicon-32x32.png
Normal file
BIN
handler/swagger-ui/favicon-32x32.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 628 B |
16
handler/swagger-ui/index.css
Normal file
16
handler/swagger-ui/index.css
Normal file
@ -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
handler/swagger-ui/index.html
Normal file
19
handler/swagger-ui/index.html
Normal file
@ -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
handler/swagger-ui/oauth2-redirect.html
Normal file
79
handler/swagger-ui/oauth2-redirect.html
Normal file
@ -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
handler/swagger-ui/swagger-initializer.js
Normal file
20
handler/swagger-ui/swagger-initializer.js
Normal file
@ -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: "{{ .SWAGGER }}",
|
||||||
|
dom_id: '#swagger-ui',
|
||||||
|
deepLinking: true,
|
||||||
|
presets: [
|
||||||
|
SwaggerUIBundle.presets.apis,
|
||||||
|
SwaggerUIStandalonePreset
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
SwaggerUIBundle.plugins.DownloadUrl
|
||||||
|
],
|
||||||
|
layout: "StandaloneLayout"
|
||||||
|
});
|
||||||
|
|
||||||
|
//</editor-fold>
|
||||||
|
};
|
3
handler/swagger-ui/swagger-ui-bundle.js
Normal file
3
handler/swagger-ui/swagger-ui-bundle.js
Normal file
File diff suppressed because one or more lines are too long
3
handler/swagger-ui/swagger-ui-standalone-preset.js
Normal file
3
handler/swagger-ui/swagger-ui-standalone-preset.js
Normal file
File diff suppressed because one or more lines are too long
3
handler/swagger-ui/swagger-ui.css
Normal file
3
handler/swagger-ui/swagger-ui.css
Normal file
File diff suppressed because one or more lines are too long
142
handler/swagger-ui/swaggerui.go
Normal file
142
handler/swagger-ui/swaggerui.go
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
package swaggerui // import "go.unistack.org/micro-server-http/v3/handler/swagger-ui"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"html/template"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed *.js *.css *.html *.png
|
||||||
|
var assets embed.FS
|
||||||
|
|
||||||
|
var (
|
||||||
|
Handler = func(prefix string) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != http.MethodGet || path.Base(r.URL.Path) != "swagger-initializer.js" {
|
||||||
|
http.StripPrefix(prefix, http.FileServer(http.FS(assets))).ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tpl := template.New("swagger-initializer.js").Funcs(TemplateFuncs)
|
||||||
|
ptpl, err := tpl.Parse(Template)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
_, _ = w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := ptpl.Execute(w, Config); err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
_, _ = w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TemplateFuncs = template.FuncMap{
|
||||||
|
"isInt": func(i interface{}) bool {
|
||||||
|
v := reflect.ValueOf(i)
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isBool": func(i interface{}) bool {
|
||||||
|
v := reflect.ValueOf(i)
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isString": func(i interface{}) bool {
|
||||||
|
v := reflect.ValueOf(i)
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isSlice": func(i interface{}) bool {
|
||||||
|
v := reflect.ValueOf(i)
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Slice:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isMap": func(i interface{}) bool {
|
||||||
|
v := reflect.ValueOf(i)
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Map:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Template = `
|
||||||
|
window.onload = function() {
|
||||||
|
//<editor-fold desc="Changeable Configuration Block">
|
||||||
|
|
||||||
|
window.ui = SwaggerUIBundle({
|
||||||
|
{{- range $k, $v := . }}
|
||||||
|
{{- if (eq (printf "%s" $v) "") -}}
|
||||||
|
{{- continue -}}
|
||||||
|
{{ end }}
|
||||||
|
{{ $k }}: {{ if isBool $v -}}
|
||||||
|
{{- $v -}},
|
||||||
|
{{- else if isInt $v -}}
|
||||||
|
{{- $v -}},
|
||||||
|
{{- else if isString $v -}}
|
||||||
|
"{{- $v -}}",
|
||||||
|
{{- else if and (isSlice $v) (or (eq (printf "%s" $k) "presets") (eq (printf "%s" $k) "plugins")) -}}
|
||||||
|
[
|
||||||
|
{{- range $v }}
|
||||||
|
{{ . }},
|
||||||
|
{{- end }}
|
||||||
|
],
|
||||||
|
{{- end -}}
|
||||||
|
{{ end }}
|
||||||
|
});
|
||||||
|
|
||||||
|
//</editor-fold>
|
||||||
|
};`
|
||||||
|
Config = map[string]interface{}{
|
||||||
|
"configUrl": "",
|
||||||
|
"dom_id": "#swagger-ui",
|
||||||
|
/*
|
||||||
|
"domNode": "",
|
||||||
|
"spec": "",
|
||||||
|
"urls": []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"url": "",
|
||||||
|
"name": "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
"url": "https://petstore.swagger.io/v2/swagger.json",
|
||||||
|
"deepLinking": true,
|
||||||
|
"displayOperationId": false,
|
||||||
|
"defaultModelsExpandDepth": 1,
|
||||||
|
"defaultModelExpandDepth": 1,
|
||||||
|
"displayRequestDuration": true,
|
||||||
|
"filter": true,
|
||||||
|
"operationsSorter": "alpha",
|
||||||
|
"showExtensions": true,
|
||||||
|
"tryItOutEnabled": true,
|
||||||
|
"presets": []string{
|
||||||
|
"SwaggerUIBundle.presets.apis",
|
||||||
|
"SwaggerUIStandalonePreset",
|
||||||
|
},
|
||||||
|
"plugins": []string{
|
||||||
|
"SwaggerUIBundle.plugins.DownloadUrl",
|
||||||
|
},
|
||||||
|
"layout": "StandaloneLayout",
|
||||||
|
}
|
||||||
|
)
|
15
handler/swagger-ui/swaggerui_test.go
Normal file
15
handler/swagger-ui/swaggerui_test.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package swaggerui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTemplate(t *testing.T) {
|
||||||
|
t.Skip()
|
||||||
|
h := http.NewServeMux()
|
||||||
|
h.HandleFunc("/", Handler(""))
|
||||||
|
if err := http.ListenAndServe(":8080", h); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user