Merge pull request #11 from davidebianchi/custom-docs-path

feat: add custom documentation path
This commit is contained in:
Davide Bianchi
2021-01-27 00:33:27 +01:00
committed by GitHub
4 changed files with 161 additions and 23 deletions

View File

@@ -34,7 +34,7 @@ func TestIntegration(t *testing.T) {
t.Run("and generate swagger", func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, JSONDocumentationPath, nil)
r := httptest.NewRequest(http.MethodGet, DefaultJSONDocumentationPath, nil)
muxRouter.ServeHTTP(w, r)

58
main.go
View File

@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"net/http"
"strings"
"github.com/getkin/kin-openapi/openapi3"
"github.com/ghodss/yaml"
@@ -19,24 +20,30 @@ var (
)
const (
// JSONDocumentationPath is the path of the swagger documentation in json format.
JSONDocumentationPath = "/documentation/json"
// YAMLDocumentationPath is the path of the swagger documentation in yaml format.
YAMLDocumentationPath = "/documentation/yaml"
defaultOpenapiVersion = "3.0.0"
// DefaultJSONDocumentationPath is the path of the swagger documentation in json format.
DefaultJSONDocumentationPath = "/documentation/json"
// DefaultYAMLDocumentationPath is the path of the swagger documentation in yaml format.
DefaultYAMLDocumentationPath = "/documentation/yaml"
defaultOpenapiVersion = "3.0.0"
)
// Router handle the gorilla mux router and the swagger schema
type Router struct {
router *mux.Router
swaggerSchema *openapi3.Swagger
context context.Context
router *mux.Router
swaggerSchema *openapi3.Swagger
context context.Context
jsonDocumentationPath string
yamlDocumentationPath string
}
// Options to be passed to create the new router and swagger
type Options struct {
Context context.Context
Openapi *openapi3.Swagger
// JSONDocumentationPath is the path exposed by json endpoint. Default to /documentation/json.
JSONDocumentationPath string
// YAMLDocumentationPath is the path exposed by yaml endpoint. Default to /documentation/yaml.
YAMLDocumentationPath string
}
// NewRouter generate new router with swagger. Default to OpenAPI 3.0.0
@@ -51,10 +58,28 @@ func NewRouter(router *mux.Router, options Options) (*Router, error) {
ctx = context.Background()
}
yamlDocumentationPath := DefaultYAMLDocumentationPath
if options.YAMLDocumentationPath != "" {
if err := isValidDocumentationPath(options.YAMLDocumentationPath); err != nil {
return nil, err
}
yamlDocumentationPath = options.YAMLDocumentationPath
}
jsonDocumentationPath := DefaultJSONDocumentationPath
if options.JSONDocumentationPath != "" {
if err := isValidDocumentationPath(options.JSONDocumentationPath); err != nil {
return nil, err
}
jsonDocumentationPath = options.JSONDocumentationPath
}
return &Router{
router: router,
swaggerSchema: swagger,
context: ctx,
router: router,
swaggerSchema: swagger,
context: ctx,
yamlDocumentationPath: yamlDocumentationPath,
jsonDocumentationPath: jsonDocumentationPath,
}, nil
}
@@ -93,7 +118,7 @@ func (r Router) GenerateAndExposeSwagger() error {
if err != nil {
return fmt.Errorf("%w json marshal: %s", ErrGenerateSwagger, err)
}
r.router.HandleFunc(JSONDocumentationPath, func(w http.ResponseWriter, req *http.Request) {
r.router.HandleFunc(r.jsonDocumentationPath, func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(jsonSwagger)
@@ -103,7 +128,7 @@ func (r Router) GenerateAndExposeSwagger() error {
if err != nil {
return fmt.Errorf("%w yaml marshal: %s", ErrGenerateSwagger, err)
}
r.router.HandleFunc(YAMLDocumentationPath, func(w http.ResponseWriter, req *http.Request) {
r.router.HandleFunc(r.yamlDocumentationPath, func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
w.Write(yamlSwagger)
@@ -111,3 +136,10 @@ func (r Router) GenerateAndExposeSwagger() error {
return nil
}
func isValidDocumentationPath(path string) error {
if !strings.HasPrefix(path, "/") {
return fmt.Errorf("invalid path %s. Path should start with '/'", path)
}
return nil
}

View File

@@ -40,9 +40,11 @@ func TestNewRouter(t *testing.T) {
require.NoError(t, err)
require.Equal(t, &Router{
context: context.Background(),
router: mRouter,
swaggerSchema: openapi,
context: context.Background(),
router: mRouter,
swaggerSchema: openapi,
jsonDocumentationPath: DefaultJSONDocumentationPath,
yamlDocumentationPath: DefaultYAMLDocumentationPath,
}, r)
})
@@ -56,11 +58,61 @@ func TestNewRouter(t *testing.T) {
require.NoError(t, err)
require.Equal(t, &Router{
context: ctx,
router: mRouter,
swaggerSchema: openapi,
context: ctx,
router: mRouter,
swaggerSchema: openapi,
jsonDocumentationPath: DefaultJSONDocumentationPath,
yamlDocumentationPath: DefaultYAMLDocumentationPath,
}, r)
})
t.Run("ok - with custom docs paths", func(t *testing.T) {
type key struct{}
ctx := context.WithValue(context.Background(), key{}, "value")
r, err := NewRouter(mRouter, Options{
Openapi: openapi,
Context: ctx,
JSONDocumentationPath: "/json/path",
YAMLDocumentationPath: "/yaml/path",
})
require.NoError(t, err)
require.Equal(t, &Router{
context: ctx,
router: mRouter,
swaggerSchema: openapi,
jsonDocumentationPath: "/json/path",
yamlDocumentationPath: "/yaml/path",
}, r)
})
t.Run("ko - json documentation path does not start with /", func(t *testing.T) {
type key struct{}
ctx := context.WithValue(context.Background(), key{}, "value")
r, err := NewRouter(mRouter, Options{
Openapi: openapi,
Context: ctx,
JSONDocumentationPath: "json/path",
YAMLDocumentationPath: "/yaml/path",
})
require.EqualError(t, err, "invalid path json/path. Path should start with '/'")
require.Nil(t, r)
})
t.Run("ko - yaml documentation path does not start with /", func(t *testing.T) {
type key struct{}
ctx := context.WithValue(context.Background(), key{}, "value")
r, err := NewRouter(mRouter, Options{
Openapi: openapi,
Context: ctx,
JSONDocumentationPath: "/json/path",
YAMLDocumentationPath: "yaml/path",
})
require.EqualError(t, err, "invalid path yaml/path. Path should start with '/'")
require.Nil(t, r)
})
}
func TestGenerateValidSwagger(t *testing.T) {
@@ -166,7 +218,34 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
require.NoError(t, err)
w := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, JSONDocumentationPath, nil)
req := httptest.NewRequest(http.MethodGet, DefaultJSONDocumentationPath, nil)
mRouter.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
require.True(t, strings.Contains(w.Result().Header.Get("content-type"), "application/json"))
body := readBody(t, w.Result().Body)
actual, err := ioutil.ReadFile("testdata/users_employees.json")
require.NoError(t, err)
require.JSONEq(t, string(actual), body)
})
t.Run("correctly expose json documentation from loaded swagger file - custom path", func(t *testing.T) {
mRouter := mux.NewRouter()
swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromFile("testdata/users_employees.json")
require.NoError(t, err)
router, err := NewRouter(mRouter, Options{
Openapi: swagger,
JSONDocumentationPath: "/custom/path",
})
err = router.GenerateAndExposeSwagger()
require.NoError(t, err)
w := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/custom/path", nil)
mRouter.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
@@ -192,7 +271,34 @@ func TestGenerateAndExposeSwagger(t *testing.T) {
require.NoError(t, err)
w := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, YAMLDocumentationPath, nil)
req := httptest.NewRequest(http.MethodGet, DefaultYAMLDocumentationPath, nil)
mRouter.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
require.True(t, strings.Contains(w.Result().Header.Get("content-type"), "text/plain"))
body := readBody(t, w.Result().Body)
expected, err := ioutil.ReadFile("testdata/users_employees.yaml")
require.NoError(t, err)
require.YAMLEq(t, string(expected), body, string(body))
})
t.Run("correctly expose yaml documentation from loaded swagger file - custom path", func(t *testing.T) {
mRouter := mux.NewRouter()
swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromFile("testdata/users_employees.json")
require.NoError(t, err)
router, err := NewRouter(mRouter, Options{
Openapi: swagger,
YAMLDocumentationPath: "/custom/path",
})
err = router.GenerateAndExposeSwagger()
require.NoError(t, err)
w := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/custom/path", nil)
mRouter.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Result().StatusCode)

View File

@@ -386,7 +386,7 @@ func TestAddRoutes(t *testing.T) {
t.Run("and generate swagger documentation in json", func(t *testing.T) {
w := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, JSONDocumentationPath, nil)
req := httptest.NewRequest(http.MethodGet, DefaultJSONDocumentationPath, nil)
r.ServeHTTP(w, req)