#19 Retrieve bathPath automatically

This commit is contained in:
elvinchan 2019-05-11 22:27:52 +08:00
parent 27beb50b64
commit cb60195b27
12 changed files with 150 additions and 125 deletions

View File

@ -56,10 +56,8 @@ func createUser(c echo.Context) error {
## Usage ## Usage
#### Create a `ApiRoot` with `New()`, which is a wrapper of `echo.New()` #### Create a `ApiRoot` with `New()`, which is a wrapper of `echo.New()`
``` ```
r := echoswagger.New(echo.New(), "/v1", "doc/", nil) r := echoswagger.New(echo.New(), "doc/", nil)
``` ```
> Note: The parameter `basePath` is generally used when the access root path is not the root directory of the website after application is deployed. For example, the URL of an API in the program running locally is: `http://localhost:1323/users`, the actual URL after deployed to server is: `https://www.xxx.com/legacy-api/users`, then, when running locally, `basePath` should be `/`, when running on server, `basePath` should be `/legacy-api`.
You can use the result `ApiRoot` instance to: You can use the result `ApiRoot` instance to:
- Setup Security definitions, request/response Content-Types, UI options, Scheme, etc. - Setup Security definitions, request/response Content-Types, UI options, Scheme, etc.
``` ```

View File

@ -58,8 +58,6 @@ func createUser(c echo.Context) error {
``` ```
r := echoswagger.New(echo.New(), "/v1", "doc/", nil) r := echoswagger.New(echo.New(), "/v1", "doc/", nil)
``` ```
> 注意:参数`basePath`一般用于程序部署后访问路径并非网站根目录时的情况比如程序运行在本地的某个API的URL为`http://localhost:1323/users`部署至服务器后的实际URL为`https://www.xxx.com/legacy-api/users`,则本地运行时,`basePath`应该传入`/`, 部署至服务器时,`basePath`应该传入`/legacy-api`。
你可以用这个`ApiRoot`来: 你可以用这个`ApiRoot`来:
- 设置Security定义, 请求/响应Content-TypeUI选项Scheme等。 - 设置Security定义, 请求/响应Content-TypeUI选项Scheme等。
``` ```

View File

@ -47,10 +47,13 @@ const SwaggerUIContent = `{{define "swagger"}}
<script src="{{.cdn}}/swagger-ui-standalone-preset.js" crossorigin="anonymous"></script> <script src="{{.cdn}}/swagger-ui-standalone-preset.js" crossorigin="anonymous"></script>
<script> <script>
window.onload = function() { window.onload = function() {
var specPath = "{{.specName}}"
if (!window.location.pathname.endsWith("/")) {
specPath = "/" + specPath
}
// Build a system // Build a system
const ui = SwaggerUIBundle({ const ui = SwaggerUIBundle({
url: this.window.location.origin+{{.path}}, url: window.location.origin+window.location.pathname+specPath,
dom_id: '#swagger-ui', dom_id: '#swagger-ui',
deepLinking: true, deepLinking: true,
presets: [ presets: [

View File

@ -13,7 +13,7 @@ func main() {
func initServer() echoswagger.ApiRoot { func initServer() echoswagger.ApiRoot {
e := echo.New() e := echo.New()
se := echoswagger.New(e, "", "doc/", &echoswagger.Info{ se := echoswagger.New(e, "doc/", &echoswagger.Info{
Title: "Swagger Petstore", Title: "Swagger Petstore",
Description: "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", Description: "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.",
Version: "1.0.0", Version: "1.0.0",

View File

@ -18,7 +18,7 @@ func TestMain(t *testing.T) {
c := e.Echo().NewContext(req, rec) c := e.Echo().NewContext(req, rec)
b, err := ioutil.ReadFile("./swagger.json") b, err := ioutil.ReadFile("./swagger.json")
assert.Nil(t, err) assert.Nil(t, err)
if assert.NoError(t, e.(*echoswagger.Root).Spec(c)) { if assert.NoError(t, e.(*echoswagger.Root).SpecHandler("/doc")(c)) {
assert.Equal(t, http.StatusOK, rec.Code) assert.Equal(t, http.StatusOK, rec.Code)
assert.JSONEq(t, string(b), rec.Body.String()) assert.JSONEq(t, string(b), rec.Body.String())
} }

View File

@ -31,7 +31,7 @@ type RawDefine struct {
Schema *JSONSchema Schema *JSONSchema
} }
func (r *Root) docHandler(realSpecPath string) echo.HandlerFunc { func (r *Root) docHandler() echo.HandlerFunc {
t, err := template.New("swagger").Parse(SwaggerUIContent) t, err := template.New("swagger").Parse(SwaggerUIContent)
if err != nil { if err != nil {
panic(err) panic(err)
@ -45,8 +45,8 @@ func (r *Root) docHandler(realSpecPath string) echo.HandlerFunc {
t.Execute(buf, map[string]interface{}{ t.Execute(buf, map[string]interface{}{
"title": r.spec.Info.Title, "title": r.spec.Info.Title,
"hideTop": r.ui.HideTop, "hideTop": r.ui.HideTop,
"path": realSpecPath,
"cdn": cdn, "cdn": cdn,
"specName": SpecName,
}) })
return c.HTMLBlob(http.StatusOK, buf.Bytes()) return c.HTMLBlob(http.StatusOK, buf.Bytes())
} }

View File

@ -9,7 +9,7 @@ import (
) )
func TestSecurity(t *testing.T) { func TestSecurity(t *testing.T) {
r := New(echo.New(), "/", "doc/", nil) r := New(echo.New(), "doc/", nil)
scope := map[string]string{ scope := map[string]string{
"read:users": "read users", "read:users": "read users",
"write:users": "modify users", "write:users": "modify users",
@ -194,7 +194,7 @@ func TestSecurity(t *testing.T) {
} }
func TestSecurityRepeat(t *testing.T) { func TestSecurityRepeat(t *testing.T) {
r := New(echo.New(), "/", "doc/", nil) r := New(echo.New(), "doc/", nil)
scope := map[string]string{ scope := map[string]string{
"read:users": "read users", "read:users": "read users",
"write:users": "modify users", "write:users": "modify users",

View File

@ -12,9 +12,11 @@ import (
const ( const (
DefPrefix = "#/definitions/" DefPrefix = "#/definitions/"
SwaggerVersion = "2.0" SwaggerVersion = "2.0"
SpecName = "swagger.json"
) )
func (r *Root) Spec(c echo.Context) error { func (r *Root) SpecHandler(docPath string) echo.HandlerFunc {
return func(c echo.Context) error {
r.once.Do(func() { r.once.Do(func() {
r.err = r.genSpec(c) r.err = r.genSpec(c)
r.cleanUp() r.cleanUp()
@ -22,13 +24,18 @@ func (r *Root) Spec(c echo.Context) error {
if r.err != nil { if r.err != nil {
return c.String(http.StatusInternalServerError, r.err.Error()) return c.String(http.StatusInternalServerError, r.err.Error())
} }
var basePath string
if uri, err := url.ParseRequestURI(c.Request().Referer()); err == nil { if uri, err := url.ParseRequestURI(c.Request().Referer()); err == nil {
basePath = trimSuffixSlash(uri.Path, docPath)
r.spec.Host = uri.Host r.spec.Host = uri.Host
} else { } else {
basePath = trimSuffixSlash(c.Request().URL.Path, connectPath(docPath, SpecName))
r.spec.Host = c.Request().Host r.spec.Host = c.Request().Host
} }
r.spec.BasePath = connectPath(basePath)
return c.JSON(http.StatusOK, r.spec) return c.JSON(http.StatusOK, r.spec)
} }
}
func (r *Root) genSpec(c echo.Context) error { func (r *Root) genSpec(c echo.Context) error {
r.spec.Swagger = SwaggerVersion r.spec.Swagger = SwaggerVersion

View File

@ -21,7 +21,7 @@ func TestSpec(t *testing.T) {
rec := httptest.NewRecorder() rec := httptest.NewRecorder()
c := e.NewContext(req, rec) c := e.NewContext(req, rec)
j := `{"swagger":"2.0","info":{"title":"Project APIs","version":""},"host":"example.com","basePath":"/","paths":{}}` j := `{"swagger":"2.0","info":{"title":"Project APIs","version":""},"host":"example.com","basePath":"/","paths":{}}`
if assert.NoError(t, r.(*Root).Spec(c)) { if assert.NoError(t, r.(*Root).SpecHandler("/doc/")(c)) {
assert.Equal(t, http.StatusOK, rec.Code) assert.Equal(t, http.StatusOK, rec.Code)
assert.JSONEq(t, j, rec.Body.String()) assert.JSONEq(t, j, rec.Body.String())
} }
@ -41,7 +41,7 @@ func TestSpec(t *testing.T) {
req := httptest.NewRequest(echo.GET, "/doc/swagger.json", nil) req := httptest.NewRequest(echo.GET, "/doc/swagger.json", nil)
rec := httptest.NewRecorder() rec := httptest.NewRecorder()
c := e.NewContext(req, rec) c := e.NewContext(req, rec)
if assert.NoError(t, r.(*Root).Spec(c)) { if assert.NoError(t, r.(*Root).SpecHandler("/doc")(c)) {
assert.Equal(t, http.StatusOK, rec.Code) assert.Equal(t, http.StatusOK, rec.Code)
s := r.(*Root).spec s := r.(*Root).spec
assert.Len(t, s.Paths, 1) assert.Len(t, s.Paths, 1)
@ -63,7 +63,7 @@ func TestSpec(t *testing.T) {
req := httptest.NewRequest(echo.GET, "/doc/swagger.json", nil) req := httptest.NewRequest(echo.GET, "/doc/swagger.json", nil)
rec := httptest.NewRecorder() rec := httptest.NewRecorder()
c := e.NewContext(req, rec) c := e.NewContext(req, rec)
if assert.NoError(t, r.(*Root).Spec(c)) { if assert.NoError(t, r.(*Root).SpecHandler("/doc")(c)) {
assert.Equal(t, http.StatusInternalServerError, rec.Code) assert.Equal(t, http.StatusInternalServerError, rec.Code)
} }
}) })
@ -76,7 +76,7 @@ func TestSpec(t *testing.T) {
req := httptest.NewRequest(echo.GET, "/doc/swagger.json", nil) req := httptest.NewRequest(echo.GET, "/doc/swagger.json", nil)
rec := httptest.NewRecorder() rec := httptest.NewRecorder()
c := e.NewContext(req, rec) c := e.NewContext(req, rec)
if assert.NoError(t, r.(*Root).Spec(c)) { if assert.NoError(t, r.(*Root).SpecHandler("/doc")(c)) {
assert.Equal(t, http.StatusInternalServerError, rec.Code) assert.Equal(t, http.StatusInternalServerError, rec.Code)
} }
}) })
@ -96,7 +96,7 @@ func TestSpec(t *testing.T) {
rec := httptest.NewRecorder() rec := httptest.NewRecorder()
c := e.NewContext(req, rec) c := e.NewContext(req, rec)
j := `{"swagger":"2.0","info":{"title":"Project APIs","version":""},"host":"example.com","basePath":"/","paths":{"/ping":{"get":{"responses":{"default":{"description":"successful operation"}}}},"/users/{id}":{"delete":{"tags":["Users"],"responses":{"default":{"description":"successful operation"}}}}},"tags":[{"name":"Users"}]}` j := `{"swagger":"2.0","info":{"title":"Project APIs","version":""},"host":"example.com","basePath":"/","paths":{"/ping":{"get":{"responses":{"default":{"description":"successful operation"}}}},"/users/{id}":{"delete":{"tags":["Users"],"responses":{"default":{"description":"successful operation"}}}}},"tags":[{"name":"Users"}]}`
if assert.NoError(t, r.(*Root).Spec(c)) { if assert.NoError(t, r.(*Root).SpecHandler("/doc")(c)) {
assert.Equal(t, http.StatusOK, rec.Code) assert.Equal(t, http.StatusOK, rec.Code)
assert.JSONEq(t, j, rec.Body.String()) assert.JSONEq(t, j, rec.Body.String())
} }
@ -108,29 +108,72 @@ func TestSpec(t *testing.T) {
}) })
} }
func TestRefererHost(t *testing.T) { func TestReferer(t *testing.T) {
tests := []struct { tests := []struct {
name, referer, host string name, referer, host, docPath, basePath string
}{ }{
{ {
referer: "http://localhost:1323/doc", referer: "http://localhost:1323/doc",
host: "localhost:1323", host: "localhost:1323",
docPath: "/doc",
name: "A", name: "A",
basePath: "/",
},
{
referer: "http://localhost:1323/doc",
host: "localhost:1323",
docPath: "/doc/",
name: "B",
basePath: "/",
},
{
referer: "http://localhost:1323/doc/",
host: "localhost:1323",
docPath: "/doc",
name: "C",
basePath: "/",
},
{
referer: "http://localhost:1323/api/v1/doc",
host: "localhost:1323",
docPath: "/doc",
name: "D",
basePath: "/api/v1",
}, },
{ {
referer: "1/doc", referer: "1/doc",
host: "127.0.0.1", host: "127.0.0.1",
name: "B", docPath: "/doc",
name: "E",
basePath: "/",
}, },
{ {
referer: "http://user:pass@github.com", referer: "http://user:pass@github.com",
host: "github.com", host: "github.com",
name: "C", docPath: "/",
name: "F",
basePath: "/",
}, },
{ {
referer: "https://www.github.com?q=1", referer: "https://www.github.com/v1/docs/?q=1",
host: "www.github.com", host: "www.github.com",
name: "D", docPath: "/docs/",
name: "G",
basePath: "/v1",
},
{
referer: "https://www.github.com/?q=1#tag=TAG",
host: "www.github.com",
docPath: "",
name: "H",
basePath: "/",
},
{
referer: "https://www.github.com/",
host: "www.github.com",
docPath: "/doc",
name: "I",
basePath: "/",
}, },
} }
for _, tt := range tests { for _, tt := range tests {
@ -141,14 +184,16 @@ func TestRefererHost(t *testing.T) {
req.Header.Add("referer", tt.referer) req.Header.Add("referer", tt.referer)
rec := httptest.NewRecorder() rec := httptest.NewRecorder()
c := e.NewContext(req, rec) c := e.NewContext(req, rec)
if assert.NoError(t, r.(*Root).Spec(c)) { if assert.NoError(t, r.(*Root).SpecHandler(tt.docPath)(c)) {
assert.Equal(t, http.StatusOK, rec.Code) assert.Equal(t, http.StatusOK, rec.Code)
var v struct { var v struct {
Host string `json:"host"` Host string `json:"host"`
BasePath string `json:"basePath"`
} }
err := json.Unmarshal(rec.Body.Bytes(), &v) err := json.Unmarshal(rec.Body.Bytes(), &v)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, tt.host, v.Host) assert.Equal(t, tt.host, v.Host)
assert.Equal(t, tt.basePath, v.BasePath)
} }
}) })
} }
@ -181,7 +226,7 @@ func TestAddDefinition(t *testing.T) {
req := httptest.NewRequest(echo.GET, "/doc/swagger.json", nil) req := httptest.NewRequest(echo.GET, "/doc/swagger.json", nil)
rec := httptest.NewRecorder() rec := httptest.NewRecorder()
c := e.NewContext(req, rec) c := e.NewContext(req, rec)
if assert.NoError(t, r.(*Root).Spec(c)) { if assert.NoError(t, r.(*Root).SpecHandler("/doc")(c)) {
assert.Equal(t, http.StatusOK, rec.Code) assert.Equal(t, http.StatusOK, rec.Code)
assert.Len(t, r.(*Root).spec.Definitions, 2) assert.Len(t, r.(*Root).spec.Definitions, 2)
} }

View File

@ -1,6 +1,9 @@
package echoswagger package echoswagger
import "reflect" import (
"reflect"
"strings"
)
func contains(list []string, s string) bool { func contains(list []string, s string) bool {
for _, t := range list { for _, t := range list {
@ -85,3 +88,19 @@ func connectPath(paths ...string) string {
} }
return result return result
} }
func removeTrailingSlash(path string) string {
l := len(path) - 1
if l > 0 && strings.HasSuffix(path, "/") {
path = path[:l]
}
return path
}
func trimSuffixSlash(s, suffix string) string {
s = connectPath(s)
suffix = connectPath(suffix)
s = removeTrailingSlash(s)
suffix = removeTrailingSlash(suffix)
return strings.TrimSuffix(s, suffix)
}

View File

@ -209,7 +209,7 @@ type api struct {
// New creates ApiRoot instance. // New creates ApiRoot instance.
// Multiple ApiRoot are allowed in one project. // Multiple ApiRoot are allowed in one project.
func New(e *echo.Echo, basePath, docPath string, i *Info) ApiRoot { func New(e *echo.Echo, docPath string, i *Info) ApiRoot {
if e == nil { if e == nil {
panic("echoswagger: invalid Echo instance") panic("echoswagger: invalid Echo instance")
} }
@ -225,7 +225,6 @@ func New(e *echo.Echo, basePath, docPath string, i *Info) ApiRoot {
spec: &Swagger{ spec: &Swagger{
Info: i, Info: i,
SecurityDefinitions: make(map[string]*SecurityDefinition), SecurityDefinitions: make(map[string]*SecurityDefinition),
BasePath: connectPath(basePath),
Definitions: make(map[string]*JSONSchema), Definitions: make(map[string]*JSONSchema),
}, },
routers: routers{ routers: routers{
@ -233,10 +232,8 @@ func New(e *echo.Echo, basePath, docPath string, i *Info) ApiRoot {
}, },
} }
specPath := connectPath(docPath, "swagger.json") e.GET(connectPath(docPath), r.docHandler())
realSpecPath := connectPath(basePath, specPath) e.GET(connectPath(docPath, SpecName), r.SpecHandler(docPath))
e.GET(connectPath(docPath), r.docHandler(realSpecPath))
e.GET(specPath, r.Spec)
return r return r
} }

View File

@ -12,7 +12,7 @@ import (
) )
func prepareApiRoot() ApiRoot { func prepareApiRoot() ApiRoot {
return New(echo.New(), "/", "doc/", nil) return New(echo.New(), "doc/", nil)
} }
func prepareApiGroup() ApiGroup { func prepareApiGroup() ApiGroup {
@ -29,7 +29,6 @@ func prepareApi() Api {
func TestNew(t *testing.T) { func TestNew(t *testing.T) {
tests := []struct { tests := []struct {
echo *echo.Echo echo *echo.Echo
basePath string
docPath string docPath string
info *Info info *Info
expectPaths []string expectPaths []string
@ -38,7 +37,6 @@ func TestNew(t *testing.T) {
}{ }{
{ {
echo: echo.New(), echo: echo.New(),
basePath: "/",
docPath: "doc/", docPath: "doc/",
info: nil, info: nil,
expectPaths: []string{"/doc/", "/doc/swagger.json"}, expectPaths: []string{"/doc/", "/doc/swagger.json"},
@ -47,7 +45,6 @@ func TestNew(t *testing.T) {
}, },
{ {
echo: echo.New(), echo: echo.New(),
basePath: "/",
docPath: "doc", docPath: "doc",
info: &Info{ info: &Info{
Title: "Test project", Title: "Test project",
@ -69,16 +66,15 @@ func TestNew(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
if tt.panic { if tt.panic {
assert.Panics(t, func() { assert.Panics(t, func() {
New(tt.echo, tt.basePath, tt.docPath, tt.info) New(tt.echo, tt.docPath, tt.info)
}) })
} else { } else {
apiRoot := New(tt.echo, tt.basePath, tt.docPath, tt.info) apiRoot := New(tt.echo, tt.docPath, tt.info)
assert.NotNil(t, apiRoot.(*Root)) assert.NotNil(t, apiRoot.(*Root))
r := apiRoot.(*Root) r := apiRoot.(*Root)
assert.NotNil(t, r.spec) assert.NotNil(t, r.spec)
assert.Equal(t, r.spec.BasePath, tt.basePath)
if tt.info == nil { if tt.info == nil {
assert.Equal(t, r.spec.Info.Title, "Project APIs") assert.Equal(t, r.spec.Info.Title, "Project APIs")
} else { } else {
@ -97,74 +93,36 @@ func TestNew(t *testing.T) {
func TestPath(t *testing.T) { func TestPath(t *testing.T) {
tests := []struct { tests := []struct {
baseInput, docInput string docInput string
baseOutput, docOutput, specOutput, realSpecOutput string docOutput, specOutput string
name string name string
}{ }{
{ {
baseInput: "/",
docInput: "doc/", docInput: "doc/",
baseOutput: "/",
docOutput: "/doc/", docOutput: "/doc/",
specOutput: "/doc/swagger.json", specOutput: "/doc/swagger.json",
realSpecOutput: "/doc/swagger.json",
name: "A", name: "A",
}, { }, {
baseInput: "",
docInput: "", docInput: "",
baseOutput: "/",
docOutput: "/", docOutput: "/",
specOutput: "/swagger.json", specOutput: "/swagger.json",
realSpecOutput: "/swagger.json",
name: "B", name: "B",
}, { }, {
baseInput: "/omni-api",
docInput: "/doc", docInput: "/doc",
baseOutput: "/omni-api",
docOutput: "/doc", docOutput: "/doc",
specOutput: "/doc/swagger.json", specOutput: "/doc/swagger.json",
realSpecOutput: "/omni-api/doc/swagger.json",
name: "C", name: "C",
}, {
baseInput: "/omni-api/",
docInput: "",
baseOutput: "/omni-api/",
docOutput: "/",
specOutput: "/swagger.json",
realSpecOutput: "/omni-api/swagger.json",
name: "D",
}, {
baseInput: "/omni-api",
docInput: "doc/",
baseOutput: "/omni-api",
docOutput: "/doc/",
specOutput: "/doc/swagger.json",
realSpecOutput: "/omni-api/doc/swagger.json",
name: "F",
}, {
baseInput: "omni-api",
docInput: "doc/",
baseOutput: "/omni-api",
docOutput: "/doc/",
specOutput: "/doc/swagger.json",
realSpecOutput: "/omni-api/doc/swagger.json",
name: "G",
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
apiRoot := New(echo.New(), tt.baseInput, tt.docInput, nil) apiRoot := New(echo.New(), tt.docInput, nil)
r := apiRoot.(*Root) r := apiRoot.(*Root)
assert.Equal(t, r.spec.BasePath, tt.baseOutput)
assert.NotNil(t, r.echo) assert.NotNil(t, r.echo)
assert.Len(t, r.echo.Routes(), 2) assert.Len(t, r.echo.Routes(), 2)
res := r.echo.Routes() res := r.echo.Routes()
paths := []string{res[0].Path, res[1].Path} paths := []string{res[0].Path, res[1].Path}
assert.ElementsMatch(t, paths, []string{tt.docOutput, tt.specOutput}) assert.ElementsMatch(t, paths, []string{tt.docOutput, tt.specOutput})
assert.Equal(t, tt.realSpecOutput, connectPath(tt.baseOutput, tt.specOutput))
}) })
} }
} }
@ -367,12 +325,12 @@ func TestAddResponse(t *testing.T) {
func TestUI(t *testing.T) { func TestUI(t *testing.T) {
t.Run("DefaultCDN", func(t *testing.T) { t.Run("DefaultCDN", func(t *testing.T) {
r := New(echo.New(), "/", "doc/", nil) r := New(echo.New(), "doc/", nil)
se := r.(*Root) se := r.(*Root)
req := httptest.NewRequest(echo.GET, "/doc/", nil) req := httptest.NewRequest(echo.GET, "/doc/", nil)
rec := httptest.NewRecorder() rec := httptest.NewRecorder()
c := se.echo.NewContext(req, rec) c := se.echo.NewContext(req, rec)
h := se.docHandler("/doc/swagger.json") h := se.docHandler()
if assert.NoError(t, h(c)) { if assert.NoError(t, h(c)) {
assert.Equal(t, http.StatusOK, rec.Code) assert.Equal(t, http.StatusOK, rec.Code)
@ -381,12 +339,12 @@ func TestUI(t *testing.T) {
}) })
t.Run("SetUI", func(t *testing.T) { t.Run("SetUI", func(t *testing.T) {
r := New(echo.New(), "/", "doc/", nil) r := New(echo.New(), "doc/", nil)
se := r.(*Root) se := r.(*Root)
req := httptest.NewRequest(echo.GET, "/doc/", nil) req := httptest.NewRequest(echo.GET, "/doc/", nil)
rec := httptest.NewRecorder() rec := httptest.NewRecorder()
c := se.echo.NewContext(req, rec) c := se.echo.NewContext(req, rec)
h := se.docHandler("/doc/swagger.json") h := se.docHandler()
cdn := "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/3.18.0" cdn := "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/3.18.0"
r.SetUI(UISetting{ r.SetUI(UISetting{