Skip to content

Commit

Permalink
feat: allow named instance registration (#65)
Browse files Browse the repository at this point in the history
  • Loading branch information
ubogdan authored Apr 22, 2022
1 parent 55f5380 commit 03737ae
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 34 deletions.
91 changes: 58 additions & 33 deletions swagger.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,61 +11,70 @@ import (
"github.com/swaggo/swag"
)

// WrapHandler wraps swaggerFiles.Handler and returns http.HandlerFunc
// WrapHandler wraps swaggerFiles.Handler and returns http.HandlerFunc.
var WrapHandler = Handler()

// Config stores httpSwagger configuration variables.
type Config struct {
// The url pointing to API definition (normally swagger.json or swagger.yaml). Default is `doc.json`.
URL string
DeepLinking bool
DocExpansion string
DomID string
PersistAuthorization bool
Plugins []template.JS
UIConfig map[template.JS]template.JS
InstanceName string
BeforeScript template.JS
AfterScript template.JS
Plugins []template.JS
UIConfig map[template.JS]template.JS
DeepLinking bool
PersistAuthorization bool
}

// URL presents the url pointing to API definition (normally swagger.json or swagger.yaml).
func URL(url string) func(c *Config) {
func URL(url string) func(*Config) {
return func(c *Config) {
c.URL = url
}
}

// DeepLinking true, false.
func DeepLinking(deepLinking bool) func(c *Config) {
func DeepLinking(deepLinking bool) func(*Config) {
return func(c *Config) {
c.DeepLinking = deepLinking
}
}

// DocExpansion list, full, none.
func DocExpansion(docExpansion string) func(c *Config) {
func DocExpansion(docExpansion string) func(*Config) {
return func(c *Config) {
c.DocExpansion = docExpansion
}
}

// DomID #swagger-ui.
func DomID(domID string) func(c *Config) {
func DomID(domID string) func(*Config) {
return func(c *Config) {
c.DomID = domID
}
}

// If set to true, it persists authorization data and it would not be lost on browser close/refresh
// Defaults to false
func PersistAuthorization(persistAuthorization bool) func(c *Config) {
// InstanceName set the instance name that was used to generate the swagger documents
// Defaults to swag.Name ("swagger").
func InstanceName(name string) func(*Config) {
return func(c *Config) {
c.InstanceName = name
}
}

// PersistAuthorization Persist authorization information over browser close/refresh.
// Defaults to false.
func PersistAuthorization(persistAuthorization bool) func(*Config) {
return func(c *Config) {
c.PersistAuthorization = persistAuthorization
}
}

// Plugins specifies additional plugins to load into Swagger UI.
func Plugins(plugins []string) func(c *Config) {
func Plugins(plugins []string) func(*Config) {
return func(c *Config) {
vs := make([]template.JS, len(plugins))
for i, v := range plugins {
Expand All @@ -76,7 +85,7 @@ func Plugins(plugins []string) func(c *Config) {
}

// UIConfig specifies additional SwaggerUIBundle config object properties.
func UIConfig(props map[string]string) func(c *Config) {
func UIConfig(props map[string]string) func(*Config) {
return func(c *Config) {
vs := make(map[template.JS]template.JS, len(props))
for k, v := range props {
Expand All @@ -87,51 +96,66 @@ func UIConfig(props map[string]string) func(c *Config) {
}

// BeforeScript holds JavaScript to be run right before the Swagger UI object is created.
func BeforeScript(js string) func(c *Config) {
func BeforeScript(js string) func(*Config) {
return func(c *Config) {
c.BeforeScript = template.JS(js)
}
}

// AfterScript holds JavaScript to be run right after the Swagger UI object is created
// and set on the window.
func AfterScript(js string) func(c *Config) {
func AfterScript(js string) func(*Config) {
return func(c *Config) {
c.AfterScript = template.JS(js)
}
}

func newConfig(configFns ...func(*Config)) *Config {
config := Config{
URL: "doc.json",
DocExpansion: "list",
DomID: "swagger-ui",
InstanceName: "swagger",
DeepLinking: true,
PersistAuthorization: false,
}

for _, fn := range configFns {
fn(&config)
}

if config.InstanceName == "" {
config.InstanceName = swag.Name
}

return &config
}

// Handler wraps `http.Handler` into `http.HandlerFunc`.
func Handler(configFns ...func(*Config)) http.HandlerFunc {
var once sync.Once

config := &Config{
URL: "doc.json",
DeepLinking: true,
DocExpansion: "list",
DomID: "#swagger-ui",
}
for _, configFn := range configFns {
configFn(config)
}
config := newConfig(configFns...)

// create a template with name
t := template.New("swagger_index.html")
index, _ := t.Parse(indexTempl)
index, _ := template.New("swagger_index.html").Parse(indexTempl)

var re = regexp.MustCompile(`^(.*/)([^?].*)?[?|.]*$`)
re := regexp.MustCompile(`^(.*/)([^?].*)?[?|.]*$`)

return func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)

return
}

matches := re.FindStringSubmatch(r.RequestURI)

path := matches[2]

h := swaggerFiles.Handler
handler := swaggerFiles.Handler
once.Do(func() {
h.Prefix = matches[1]
handler.Prefix = matches[1]
})

switch filepath.Ext(path) {
Expand All @@ -151,17 +175,18 @@ func Handler(configFns ...func(*Config)) http.HandlerFunc {
case "index.html":
_ = index.Execute(w, config)
case "doc.json":
doc, err := swag.ReadDoc()
doc, err := swag.ReadDoc(config.InstanceName)
if err != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)

return
}

_, _ = w.Write([]byte(doc))
case "":
http.Redirect(w, r, h.Prefix+"index.html", http.StatusMovedPermanently)
http.Redirect(w, r, handler.Prefix+"index.html", http.StatusMovedPermanently)
default:
h.ServeHTTP(w, r)
handler.ServeHTTP(w, r)
}
}
}
Expand Down
26 changes: 25 additions & 1 deletion swagger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package httpSwagger
import (
"bytes"
"html/template"
"io/ioutil"
"net/http"
"net/http/httptest"
"strings"
Expand Down Expand Up @@ -49,11 +50,17 @@ func TestWrapHandler(t *testing.T) {

assert.Equal(t, http.StatusInternalServerError, performRequest(http.MethodGet, "/doc.json", router).Code)

swag.Register(swag.Name, &mockedSwag{})
doc := &mockedSwag{}
swag.Register(swag.Name, doc)
w2 := performRequest(http.MethodGet, "/doc.json", router)
assert.Equal(t, http.StatusOK, w2.Code)
assert.Equal(t, "application/json; charset=utf-8", w2.Header().Get("content-type"))

// Perform body rendering validation
w2Body, err := ioutil.ReadAll(w2.Body)
assert.NoError(t, err)
assert.Equal(t, doc.ReadDoc(), string(w2Body))

w3 := performRequest(http.MethodGet, "/favicon-16x16.png", router)
assert.Equal(t, http.StatusOK, w3.Code)
assert.Equal(t, w3.Header()["Content-Type"][0], "image/png")
Expand Down Expand Up @@ -116,6 +123,23 @@ func TestDomID(t *testing.T) {
assert.Equal(t, expected, cfg.DomID)
}

func TestInstanceName(t *testing.T) {
var cfg Config

assert.Equal(t, "", cfg.InstanceName)

expected := swag.Name
InstanceName(expected)(&cfg)
assert.Equal(t, expected, cfg.InstanceName)

expected = "custom_name"
InstanceName(expected)(&cfg)
assert.Equal(t, expected, cfg.InstanceName)

newCfg := newConfig(InstanceName(""))
assert.Equal(t, swag.Name, newCfg.InstanceName)
}

func TestPersistAuthorization(t *testing.T) {
expected := true
cfg := Config{}
Expand Down

0 comments on commit 03737ae

Please sign in to comment.