Skip to content

Commit

Permalink
[OpenAPI] Add a way to add middleware to openAPI handlers & customisa…
Browse files Browse the repository at this point in the history
…tion
  • Loading branch information
danysousa committed Nov 30, 2022
1 parent d57b340 commit d7e2f9e
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 28 deletions.
22 changes: 20 additions & 2 deletions openapi/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
const (
// DefaultDocURI is the URI used to host documentation UI
DefaultDocURI = "/"
// DocPageTitle is the URI used to host documentation UI
DocPageTitle = "Doc | API"
// DefaultSpecOpenAPIURI is the URI used to host the openapi file generated by this package
DefaultSpecOpenAPIURI = "/openapi.json"
)
Expand All @@ -27,8 +29,10 @@ type config struct {
SpecOpenAPIURI string
// IgnoredPaths are the paths that shouldn't be included in the documentation.
IgnoredPaths []string
// Auth is your auth system to protect your documentation.
Auth Authorization
// DocPageTitle is used as doc title
DocPageTitle string
// FaviconURL is used for web doc UI
FaviconURL string
}

// GetDocURI return config.DocURI if a values was set otherwise DefaultDocURI is returned
Expand Down Expand Up @@ -63,3 +67,17 @@ func (c config) ignoredPaths() []string {

return append(ignoredPaths, c.IgnoredPaths...)
}

// GetDocPageTitle return config.DocPageTitle if a values was set otherwise DocPageTitle is returned
func (c config) GetDocPageTitle() string {
if c.DocPageTitle != "" {
return c.DocPageTitle
}

return DocPageTitle
}

// GetFaviconURL return config.FaviconURL
func (c config) GetFaviconURL() string {
return c.FaviconURL
}
23 changes: 9 additions & 14 deletions openapi/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,27 @@ import (

"github.com/gorilla/mux"
"github.com/swaggest/openapi-go/openapi3"

"github.com/mwm-io/gapi/handler"
)

// SpecOpenAPIHandler is a server.SpecOpenAPIHandler that will return a json openapi definition of the given reflector.
type SpecOpenAPIHandler struct {
handler.WithMiddlewares
_reflector *openapi3.Reflector
reflectorOnce sync.Once
computeDocError error
router *mux.Router
// auth Authorization
}

// NewSpecOpenAPIHandler builds a new SpecOpenAPIHandler, serving the api definition from the openapi3.Reflector,
// and checking auth access with the given Authorization.
func NewSpecOpenAPIHandler(router *mux.Router) *SpecOpenAPIHandler {
func NewSpecOpenAPIHandler(router *mux.Router, middlewares ...handler.Middleware) *SpecOpenAPIHandler {
return &SpecOpenAPIHandler{
router: router,
// auth: auth, TODO : change it by a middleware
WithMiddlewares: handler.WithMiddlewares{
MiddlewareList: middlewares,
},
}
}

Expand All @@ -32,24 +36,15 @@ func NewSpecOpenAPIHandler(router *mux.Router) *SpecOpenAPIHandler {
func (h *SpecOpenAPIHandler) getReflector() (*openapi3.Reflector, error) {
h.reflectorOnce.Do(func() {
h._reflector = new(openapi3.Reflector)
h._reflector.SpecEns().Info.WithTitle(Config.GetDocPageTitle())
h.computeDocError = PopulateReflector(h._reflector, h.router, Config.ignoredPaths())
})

return h._reflector, h.computeDocError
}

// Serve implements the handler.Handler interface
func (h *SpecOpenAPIHandler) Serve(w http.ResponseWriter, r *http.Request) (interface{}, error) {
// if h.auth != nil {
// authorized, err := h.auth.Authorize(w, r)
// if err != nil {
// return nil, err
// }
// if !authorized {
// return h.auth.Login(w, r)
// }
// }

func (h *SpecOpenAPIHandler) Serve(_ http.ResponseWriter, _ *http.Request) (interface{}, error) {
reflector, err := h.getReflector()
if err != nil {
return nil, err
Expand Down
47 changes: 39 additions & 8 deletions openapi/rapidoc.go
Original file line number Diff line number Diff line change
@@ -1,48 +1,79 @@
package openapi

import (
"fmt"
"net/http"
"strings"

"github.com/mwm-io/gapi/handler"
)

// RapiDocHandler is a server.SpecOpenAPIHandler that will serve a html page with rapidoc loading the given openAPIJsonURL
type RapiDocHandler struct {
handler.WithMiddlewares
openAPIJsonURL string
}

// NewRapiDocHandler build a new RapiDocHandler.
func NewRapiDocHandler() RapiDocHandler {
return RapiDocHandler{
func NewRapiDocHandler(middlewares ...handler.Middleware) handler.Handler {
return &RapiDocHandler{
openAPIJsonURL: Config.GetSpecOpenAPIURI(),
WithMiddlewares: handler.WithMiddlewares{
MiddlewareList: middlewares,
},
}
}

// Serve implements the server.SpecOpenAPIHandler interface
func (h RapiDocHandler) Serve(w http.ResponseWriter, r *http.Request) (interface{}, error) {
// Serve implements the handler.Handler interface
func (h RapiDocHandler) Serve(_ http.ResponseWriter, _ *http.Request) (interface{}, error) {
// TODO add customisation options : like icon, theme etc...
// TODO add auth middleware as option

var favicon string
if faviconURL := Config.GetFaviconURL(); faviconURL != "" {
favicon = fmt.Sprintf(`<link rel="icon" type="image/x-icon" href="%s">`, faviconURL)
}

return strings.NewReader(`
<!doctype html>
<html>
<head>
<title>` + Config.GetDocPageTitle() + `</title>
` + favicon + `
<meta charset="utf-8"> <!-- Important: rapi-doc uses utf8 characters -->
<script type="module" src="https://unpkg.com/rapidoc/dist/rapidoc-min.js"></script>
</head>
<body>
<rapi-doc
spec-url="` + h.openAPIJsonURL + `"
bg-color="#14191f"
text-color="#aec2e0"
theme="dark"
primary-color="#f54c47"
bg-color="#222c3d"
text-color="#fff"
show-header="false"
render-style="read"
schema-style="table"
show-header="false"
> </rapi-doc>
</body>
</html>`), nil
}

// RapiDocReceiverHandler is a server.SpecOpenAPIHandler that will serve the rapidoc oauth receiver
func RapiDocReceiverHandler(w http.ResponseWriter, r *http.Request) (interface{}, error) {
type RapiDocReceiverHandler struct {
handler.WithMiddlewares
}

// NewRapiDocReceiverHandler build a new RapiDocReceiverHandler.
func NewRapiDocReceiverHandler(middlewares ...handler.Middleware) handler.Handler {
return &RapiDocReceiverHandler{
WithMiddlewares: handler.WithMiddlewares{
MiddlewareList: middlewares,
},
}
}

// Serve implements the handler.Handler interface
func (h RapiDocReceiverHandler) Serve(_ http.ResponseWriter, _ *http.Request) (interface{}, error) {
return strings.NewReader(`
<!doctype html>
<head>
Expand Down
9 changes: 5 additions & 4 deletions server/openapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@ import (
"net/http"

"github.com/gorilla/mux"

"github.com/mwm-io/gapi/handler"
"github.com/mwm-io/gapi/openapi"
)

// AddDocHandlers will add the necessary handlers to serve a rapidoc endpoint:
// 2 endpoints to serve rapidoc.html and oauth-receiver.html from rapidoc
// and one endpoint to serve the json openapi definition of your API.
func AddDocHandlers(r *mux.Router) error {
func AddDocHandlers(r *mux.Router, middlewares ...handler.Middleware) error {

AddHandler(r, http.MethodGet, openapi.Config.GetDocURI(), openapi.NewRapiDocHandler())
AddHandler(r, http.MethodGet, openapi.Config.GetAuthReceiverURI(), handler.Func(openapi.RapiDocReceiverHandler))
AddHandler(r, http.MethodGet, openapi.Config.GetSpecOpenAPIURI(), openapi.NewSpecOpenAPIHandler(r))
AddHandler(r, http.MethodGet, openapi.Config.GetDocURI(), openapi.NewRapiDocHandler(middlewares...))
AddHandler(r, http.MethodGet, openapi.Config.GetAuthReceiverURI(), openapi.NewRapiDocReceiverHandler(middlewares...))
AddHandler(r, http.MethodGet, openapi.Config.GetSpecOpenAPIURI(), openapi.NewSpecOpenAPIHandler(r, middlewares...))

return nil
}

0 comments on commit d7e2f9e

Please sign in to comment.