- removed serving of `index.html` since it was not used

- moved serving of UI template to the `SwaggerBase`
- added `ui.initOAuth` UI call
- added support for `Oauth2ClientID` and `Oauth2AppName`
- added config helpers
- minor refactoring
- fixed tests since they were assuming that UI is always served from the
  root
This commit is contained in:
Volodymyr Khrestin 2019-12-06 21:07:36 +02:00
parent 362f7af176
commit 11a6cb395c
5 changed files with 81 additions and 42 deletions

View File

@ -64,7 +64,7 @@ import (
func main() {
r := gin.New()
swaggerBase := ginSwagger.SwaggerBase("docs/") // default `swagger/`
swaggerBase := ginSwagger.SwaggerBase("/api/v2/docs/") // default `swagger/`
specFileName := ginSwagger.SpecFileName("swagger.json") // default `doc.json`
apiGroup := r.Group("/api/v2")
@ -75,7 +75,7 @@ func main() {
}
```
5. Run it, and browser to http://localhost:8080/swagger/index.html, you can see Swagger 2.0 Api documents.
5. Run it, and browse to http://localhost:8080/api/v2/docs/, you can see Swagger 2.0 Api documents.
![swagger_index.html](https://user-images.githubusercontent.com/8943871/60704329-b7ab0680-9f36-11e9-9184-5c638c05e9c5.png)

3
go.mod
View File

@ -7,6 +7,7 @@ require (
github.com/kr/pretty v0.1.0 // indirect
github.com/mattn/go-isatty v0.0.8 // indirect
github.com/stretchr/testify v1.3.0
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14
github.com/swaggo/swag v1.5.1
github.com/ugorji/go v1.1.5-pre // indirect
golang.org/x/net v0.0.0-20190611141213-3f473d35a33a
@ -15,3 +16,5 @@ require (
golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
)
go 1.13

4
go.sum
View File

@ -53,6 +53,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 h1:PyYN9JH5jY9j6av01SpfRMb+1DWg/i3MbGOKPxJ2wjM=
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=
github.com/swaggo/swag v1.5.1 h1:2Agm8I4K5qb00620mHq0VJ05/KT4FtmALPIcQR9lEZM=
github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
@ -69,6 +71,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190611141213-3f473d35a33a h1:+KkCgOMgnKSgenxTBoiwkMqTiouMIy/3o8RLdmSbGoY=
golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191204025024-5ee1b9f4859a h1:+HHJiFUXVOIS9mr1ThqkQD1N8vpFCfCShqADBM12KTc=
golang.org/x/net v0.0.0-20191206103017-1ddd1de85cb0 h1:LxY/gQN/MrcW24/46nLyiip1GhN/Yi14QPbeNskTvQA=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

View File

@ -2,6 +2,7 @@ package ginSwagger
import (
"html/template"
"net/http"
"os"
"path/filepath"
"regexp"
@ -16,9 +17,11 @@ import (
// Config stores ginSwagger configuration variables.
type Config struct {
//The url pointing to API definition (normally swagger.json or swagger.yaml). Default is `doc.json`.
SpecFileName string
SwaggerBase string
DeepLinking bool
SpecFileName string
SwaggerBase string
DeepLinking bool
Oauth2ClientID string
Oauth2AppName string
}
// SwaggerBase sets the subpath of swagger router. Default is `swagger/`.
@ -42,12 +45,28 @@ func DeepLinking(deepLinking bool) func(c *Config) {
}
}
// Oauth2ClientID sets OAuth2 client ID
func Oauth2ClientID(clientID string) func(c *Config) {
return func(c *Config) {
c.Oauth2ClientID = clientID
}
}
// Oauth2AppName sets OAuth2 application name
func Oauth2AppName(appName string) func(c *Config) {
return func(c *Config) {
c.Oauth2AppName = appName
}
}
// WrapHandler wraps `http.Handler` into `gin.HandlerFunc`.
func WrapHandler(h *webdav.Handler, confs ...func(c *Config)) gin.HandlerFunc {
defaultConfig := &Config{
SpecFileName: "doc.json",
SwaggerBase: "swagger/",
DeepLinking: true,
SpecFileName: "doc.json",
SwaggerBase: "/swagger/",
DeepLinking: true,
Oauth2ClientID: "your-client-id",
Oauth2AppName: "your-app-name",
}
for _, c := range confs {
@ -69,25 +88,39 @@ func CustomWrapHandler(config *Config, h *webdav.Handler) gin.HandlerFunc {
}
specRegexStr := strings.Replace(specFileName, ".", "\\.", -1)
var rexp = regexp.MustCompile(`(.*)(index\.html|` + specRegexStr + `|favicon-16x16\.png|favicon-32x32\.png|/oauth2-redirect\.html|swagger-ui\.css|swagger-ui\.css\.map|swagger-ui\.js|swagger-ui\.js\.map|swagger-ui-bundle\.js|swagger-ui-bundle\.js\.map|swagger-ui-standalone-preset\.js|swagger-ui-standalone-preset\.js\.map)[\?|.]*`)
var rexp = regexp.MustCompile(`(.*)(` + specRegexStr + `|favicon-16x16\.png|favicon-32x32\.png|/oauth2-redirect\.html|swagger-ui\.css|swagger-ui\.css\.map|swagger-ui\.js|swagger-ui\.js\.map|swagger-ui-bundle\.js|swagger-ui-bundle\.js\.map|swagger-ui-standalone-preset\.js|swagger-ui-standalone-preset\.js\.map)[\?|.]*`)
return func(c *gin.Context) {
type swaggerUIBundle struct {
URL string
Oauth2RedirectURL template.JS
DeepLinking bool
Oauth2RedirectURL template.JS
Oauth2ClientID string
Oauth2AppName string
}
if c.Request.RequestURI == config.SwaggerBase {
c.Header("Content-Type", "text/html; charset=utf-8")
index.Execute(c.Writer, &swaggerUIBundle{
URL: filepath.Join(config.SwaggerBase, specFileName),
DeepLinking: config.DeepLinking,
Oauth2RedirectURL: template.JS("`${window.location.protocol}//${window.location.host}" + filepath.Join(config.SwaggerBase, "oauth2-redirect.html") + "`"),
Oauth2ClientID: config.Oauth2ClientID,
Oauth2AppName: config.Oauth2AppName,
})
return
}
var matches []string
if matches = rexp.FindStringSubmatch(c.Request.RequestURI); len(matches) != 3 {
c.Status(404)
c.Status(http.StatusNotFound)
c.Writer.Write([]byte("404 page not found"))
return
}
h.Prefix = matches[1]
path := matches[2]
prefix := matches[1]
h.Prefix = prefix
if strings.HasSuffix(path, ".html") {
c.Header("Content-Type", "text/html; charset=utf-8")
@ -99,39 +132,32 @@ func CustomWrapHandler(config *Config, h *webdav.Handler) gin.HandlerFunc {
c.Header("Content-Type", "application/json")
}
switch path {
case "index.html":
index.Execute(c.Writer, &swaggerUIBundle{
URL: filepath.Join(config.SwaggerBase, specFileName),
Oauth2RedirectURL: template.JS("`${window.location.protocol}//${window.location.host}" + filepath.Join(config.SwaggerBase, "oauth2-redirect.html") + "`"),
DeepLinking: config.DeepLinking,
})
case specFileName:
if path == specFileName {
doc, err := swag.ReadDoc()
if err != nil {
panic(err)
}
c.Writer.Write([]byte(doc))
return
default:
h.ServeHTTP(c.Writer, c.Request)
}
h.ServeHTTP(c.Writer, c.Request)
}
}
// DisablingWrapHandler turn handler off
// if specified environment variable passed
func DisablingWrapHandler(h *webdav.Handler, envName string) gin.HandlerFunc {
func DisablingWrapHandler(h *webdav.Handler, envName string, confs ...func(c *Config)) gin.HandlerFunc {
eFlag := os.Getenv(envName)
if eFlag != "" {
return func(c *gin.Context) {
// Simulate behavior when route unspecified and
// return 404 HTTP code
c.String(404, "")
c.String(http.StatusNotFound, "")
}
}
return WrapHandler(h)
return WrapHandler(h, confs...)
}
// DisablingCustomWrapHandler turn handler off
@ -142,7 +168,7 @@ func DisablingCustomWrapHandler(config *Config, h *webdav.Handler, envName strin
return func(c *gin.Context) {
// Simulate behavior when route unspecified and
// return 404 HTTP code
c.String(404, "")
c.String(http.StatusNotFound, "")
}
}
@ -236,7 +262,13 @@ window.onload = function() {
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout",
deepLinking: {{.DeepLinking}}
deepLinking: {{.DeepLinking}},
showExtensions: true
})
ui.initOAuth({
clientId: "localhost.vela.care",
appName: "notifications"
})
window.ui = ui

View File

@ -18,9 +18,9 @@ func TestWrapHandler(t *testing.T) {
gin.SetMode(gin.TestMode)
router := gin.New()
router.GET("/*any", WrapHandler(swaggerFiles.Handler))
router.GET("/swagger/*any", WrapHandler(swaggerFiles.Handler))
w1 := performRequest("GET", "/index.html", router)
w1 := performRequest("GET", "/swagger/", router)
assert.Equal(t, 200, w1.Code)
}
@ -28,9 +28,9 @@ func TestCustomWrapHandler(t *testing.T) {
gin.SetMode(gin.TestMode)
router := gin.New()
router.GET("/*any", CustomWrapHandler(&Config{}, swaggerFiles.Handler))
router.GET("/*any", CustomWrapHandler(&Config{SwaggerBase: "/"}, swaggerFiles.Handler))
w1 := performRequest("GET", "/index.html", router)
w1 := performRequest("GET", "/", router)
assert.Equal(t, 200, w1.Code)
w2 := performRequest("GET", "/doc.json", router)
@ -52,9 +52,9 @@ func TestDisablingWrapHandler(t *testing.T) {
router := gin.New()
disablingKey := "SWAGGER_DISABLE"
router.GET("/simple/*any", DisablingWrapHandler(swaggerFiles.Handler, disablingKey))
router.GET("/simple/*any", DisablingWrapHandler(swaggerFiles.Handler, disablingKey, SwaggerBase("/simple/")))
w1 := performRequest("GET", "/simple/index.html", router)
w1 := performRequest("GET", "/simple/", router)
assert.Equal(t, 200, w1.Code)
w2 := performRequest("GET", "/simple/doc.json", router)
@ -71,9 +71,9 @@ func TestDisablingWrapHandler(t *testing.T) {
os.Setenv(disablingKey, "true")
router.GET("/disabling/*any", DisablingWrapHandler(swaggerFiles.Handler, disablingKey))
router.GET("/disabling/*any", DisablingWrapHandler(swaggerFiles.Handler, disablingKey, SwaggerBase("/disabling/")))
w11 := performRequest("GET", "/disabling/index.html", router)
w11 := performRequest("GET", "/disabling/", router)
assert.Equal(t, 404, w11.Code)
w22 := performRequest("GET", "/disabling/doc.json", router)
@ -95,16 +95,16 @@ func TestDisablingCustomWrapHandler(t *testing.T) {
router := gin.New()
disablingKey := "SWAGGER_DISABLE2"
router.GET("/simple/*any", DisablingCustomWrapHandler(&Config{}, swaggerFiles.Handler, disablingKey))
router.GET("/simple/*any", DisablingCustomWrapHandler(&Config{SwaggerBase: "/simple/"}, swaggerFiles.Handler, disablingKey))
w1 := performRequest("GET", "/simple/index.html", router)
w1 := performRequest("GET", "/simple/", router)
assert.Equal(t, 200, w1.Code)
os.Setenv(disablingKey, "true")
router.GET("/disabling/*any", DisablingCustomWrapHandler(&Config{}, swaggerFiles.Handler, disablingKey))
router.GET("/disabling/*any", DisablingCustomWrapHandler(&Config{SwaggerBase: "/disabling/"}, swaggerFiles.Handler, disablingKey))
w11 := performRequest("GET", "/disabling/index.html", router)
w11 := performRequest("GET", "/disabling/", router)
assert.Equal(t, 404, w11.Code)
}
@ -114,9 +114,9 @@ func TestWithGzipMiddleware(t *testing.T) {
router.Use(gzip.Gzip(gzip.BestSpeed))
router.GET("/*any", WrapHandler(swaggerFiles.Handler))
router.GET("/*any", WrapHandler(swaggerFiles.Handler, SwaggerBase("/")))
w1 := performRequest("GET", "/index.html", router)
w1 := performRequest("GET", "/", router)
assert.Equal(t, 200, w1.Code)
assert.Equal(t, w1.Header()["Content-Type"][0], "text/html; charset=utf-8")