From a11773838f1fced0d0dcc935b59b9666f1cb23a6 Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Tue, 22 Jan 2019 11:16:27 -0600 Subject: [PATCH] feat(http): host swagger docs at /docs and /api/v2/swagger.json --- http/Makefile | 8 ++++++-- http/api_handler.go | 9 +++++++++ http/no_assets.go | 15 +++++++++++++++ http/platform_handler.go | 8 ++++++++ http/redoc.go | 39 +++++++++++++++++++++++++++++++++++++++ http/swagger.go | 34 ++++++++++++++++++++++++++++++++++ 6 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 http/no_assets.go create mode 100644 http/redoc.go create mode 100644 http/swagger.go diff --git a/http/Makefile b/http/Makefile index 5aeb99f057..910531c251 100644 --- a/http/Makefile +++ b/http/Makefile @@ -3,10 +3,10 @@ TARGETS = ../ui/src/api/api.ts # List any source files used to generate the targets here SOURCES = cur_swagger.yml # List any directories that have their own Makefile here -SUBDIRS = +SUBDIRS = # Default target -all: $(SUBDIRS) $(TARGETS) +all: $(SUBDIRS) $(TARGETS) swagger_gen.go # Recurse into subdirs for same make goal $(SUBDIRS): @@ -15,7 +15,11 @@ $(SUBDIRS): # Clean all targets recursively clean: $(SUBDIRS) rm -f $(TARGETS) + rm -f swagger_gen.go +swagger_gen.go: swagger.go redoc.go swagger.yml + go generate -x + echo '//lint:file-ignore ST1005 Ignore error strings should not be capitalized' >> swagger_gen.go GO_RUN := env GO111MODULE=on go run diff --git a/http/api_handler.go b/http/api_handler.go index 0fd4baf31e..8f9501e2e9 100644 --- a/http/api_handler.go +++ b/http/api_handler.go @@ -32,6 +32,7 @@ type APIHandler struct { WriteHandler *WriteHandler SetupHandler *SetupHandler SessionHandler *SessionHandler + SwaggerHandler http.HandlerFunc } // APIBackend is all services and associated parameters required to construct @@ -153,6 +154,8 @@ func NewAPIHandler(b *APIBackend) *APIHandler { h.ChronografHandler = NewChronografHandler(b.ChronografService) + h.SwaggerHandler = SwaggerHandler() + return h } @@ -182,6 +185,7 @@ var apiLinks = map[string]interface{}{ "signout": "/api/v2/signout", "sources": "/api/v2/sources", "scrapers": "/api/v2/scrapers", + "swagger": "/api/v2/swagger.json", "system": map[string]string{ "metrics": "/metrics", "debug": "/debug/pprof", @@ -304,5 +308,10 @@ func (h *APIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } + if r.URL.Path == "/api/v2/swagger.json" { + h.SwaggerHandler.ServeHTTP(w, r) + return + } + notFoundHandler(w, r) } diff --git a/http/no_assets.go b/http/no_assets.go new file mode 100644 index 0000000000..ee895a248c --- /dev/null +++ b/http/no_assets.go @@ -0,0 +1,15 @@ +// +build !assets + +package http + +import ( + "errors" +) + +// The functions defined in this file are placeholders when the binary is compiled +// without assets. + +// Asset returns an error stating no assets were included in the binary. +func Asset(string) ([]byte, error) { + return nil, errors.New("no assets included in binary") +} diff --git a/http/platform_handler.go b/http/platform_handler.go index 4f56546de7..ae71719b53 100644 --- a/http/platform_handler.go +++ b/http/platform_handler.go @@ -10,6 +10,7 @@ import ( // PlatformHandler is a collection of all the service handlers. type PlatformHandler struct { AssetHandler *AssetHandler + DocsHandler http.HandlerFunc APIHandler http.Handler } @@ -33,12 +34,14 @@ func NewPlatformHandler(b *APIBackend) *PlatformHandler { h.RegisterNoAuthRoute("POST", "/api/v2/signout") h.RegisterNoAuthRoute("POST", "/api/v2/setup") h.RegisterNoAuthRoute("GET", "/api/v2/setup") + h.RegisterNoAuthRoute("GET", "/api/v2/swagger.json") assetHandler := NewAssetHandler() assetHandler.DeveloperMode = b.DeveloperMode return &PlatformHandler{ AssetHandler: assetHandler, + DocsHandler: Redoc("/api/v2/swagger.json"), APIHandler: h, } } @@ -50,6 +53,11 @@ func (h *PlatformHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } + if strings.HasPrefix(r.URL.Path, "/docs") { + h.DocsHandler.ServeHTTP(w, r) + return + } + // Serve the chronograf assets for any basepath that does not start with addressable parts // of the platform API. if !strings.HasPrefix(r.URL.Path, "/v1") && diff --git a/http/redoc.go b/http/redoc.go new file mode 100644 index 0000000000..9bf1874778 --- /dev/null +++ b/http/redoc.go @@ -0,0 +1,39 @@ +package http + +import ( + "fmt" + "net/http" +) + +const index = ` + + + Chronograf API + + + + + + + + + + +` + +// Redoc servers the swagger JSON using the redoc package. +func Redoc(swagger string) http.HandlerFunc { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + w.WriteHeader(http.StatusOK) + + _, _ = w.Write([]byte(fmt.Sprintf(index, swagger))) + }) +} diff --git a/http/swagger.go b/http/swagger.go new file mode 100644 index 0000000000..f93ef3ea4b --- /dev/null +++ b/http/swagger.go @@ -0,0 +1,34 @@ +package http + +//go:generate env GO111MODULE=on go run github.com/kevinburke/go-bindata/go-bindata -o swagger_gen.go -tags assets -ignore go -nocompress -pkg http . + +import ( + "context" + "net/http" + + "github.com/ghodss/yaml" + "github.com/influxdata/influxdb" +) + +// SwaggerHandler servers the swagger.json file from bindata +func SwaggerHandler() http.HandlerFunc { + swagger, err := Asset("swagger.yml") + var json []byte + if err == nil { + json, err = yaml.YAMLToJSON(swagger) + } + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if err != nil { + EncodeError(context.Background(), &influxdb.Error{ + Err: err, + Msg: "this developer binary not built with assets", + Code: influxdb.EInternal, + }, w) + return + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write(json) + }) +}