mirror of
https://github.com/davidebianchi/gswagger.git
synced 2025-12-23 23:38:43 -05:00
feat: add strong types on Route
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
package apirouter
|
||||
|
||||
type Router[HandlerFunc any] interface {
|
||||
type Router[HandlerFunc any, Route any] interface {
|
||||
AddRoute(method string, path string, handler HandlerFunc) Route
|
||||
SwaggerHandler(contentType string, blob []byte) HandlerFunc
|
||||
}
|
||||
|
||||
type Route any
|
||||
|
||||
@@ -21,8 +21,8 @@ const (
|
||||
swaggerOpenapiVersion = "test swagger version"
|
||||
)
|
||||
|
||||
type GorillaSwaggerRouter = swagger.Router[gorilla.HandlerFunc]
|
||||
type echoSwaggerRouter = swagger.Router[http.HandlerFunc]
|
||||
type GorillaSwaggerRouter = swagger.Router[gorilla.HandlerFunc, gorilla.Route]
|
||||
type echoSwaggerRouter = swagger.Router[http.HandlerFunc, *echo.Route]
|
||||
|
||||
func TestIntegration(t *testing.T) {
|
||||
t.Run("router works correctly", func(t *testing.T) {
|
||||
@@ -248,7 +248,7 @@ func echoOkHandler(c echo.Context) error {
|
||||
return c.String(http.StatusOK, "OK")
|
||||
}
|
||||
|
||||
func newEchoRouter(e *echo.Echo) apirouter.Router[http.HandlerFunc] {
|
||||
func newEchoRouter(e *echo.Echo) apirouter.Router[http.HandlerFunc, *echo.Route] {
|
||||
return echoRouter{router: e}
|
||||
}
|
||||
|
||||
@@ -256,7 +256,7 @@ type echoRouter struct {
|
||||
router *echo.Echo
|
||||
}
|
||||
|
||||
func (r echoRouter) AddRoute(method, path string, handler http.HandlerFunc) apirouter.Route {
|
||||
func (r echoRouter) AddRoute(method, path string, handler http.HandlerFunc) *echo.Route {
|
||||
return r.router.Add(method, path, echo.WrapHandler(http.HandlerFunc(handler)))
|
||||
}
|
||||
|
||||
|
||||
14
main.go
14
main.go
@@ -30,8 +30,8 @@ const (
|
||||
// Router handle the api router and the swagger schema.
|
||||
// api router supported out of the box are:
|
||||
// - gorilla mux
|
||||
type Router[T any] struct {
|
||||
router apirouter.Router[T]
|
||||
type Router[HandlerFunc, Route any] struct {
|
||||
router apirouter.Router[HandlerFunc, Route]
|
||||
swaggerSchema *openapi3.T
|
||||
context context.Context
|
||||
jsonDocumentationPath string
|
||||
@@ -52,7 +52,7 @@ type Options struct {
|
||||
}
|
||||
|
||||
// NewRouter generate new router with swagger. Default to OpenAPI 3.0.0
|
||||
func NewRouter[T any](router apirouter.Router[T], options Options) (*Router[T], error) {
|
||||
func NewRouter[HandlerFunc, Route any](router apirouter.Router[HandlerFunc, Route], options Options) (*Router[HandlerFunc, Route], error) {
|
||||
swagger, err := generateNewValidSwagger(options.Openapi)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %s", ErrValidatingSwagger, err)
|
||||
@@ -79,7 +79,7 @@ func NewRouter[T any](router apirouter.Router[T], options Options) (*Router[T],
|
||||
jsonDocumentationPath = options.JSONDocumentationPath
|
||||
}
|
||||
|
||||
return &Router[T]{
|
||||
return &Router[HandlerFunc, Route]{
|
||||
router: router,
|
||||
swaggerSchema: swagger,
|
||||
context: ctx,
|
||||
@@ -93,8 +93,8 @@ type SubRouterOptions struct {
|
||||
PathPrefix string
|
||||
}
|
||||
|
||||
func (r Router[T]) SubRouter(router apirouter.Router[T], opts SubRouterOptions) (*Router[T], error) {
|
||||
return &Router[T]{
|
||||
func (r Router[HandlerFunc, Route]) SubRouter(router apirouter.Router[HandlerFunc, Route], opts SubRouterOptions) (*Router[HandlerFunc, Route], error) {
|
||||
return &Router[HandlerFunc, Route]{
|
||||
router: router,
|
||||
swaggerSchema: r.swaggerSchema,
|
||||
context: r.context,
|
||||
@@ -130,7 +130,7 @@ func generateNewValidSwagger(swagger *openapi3.T) (*openapi3.T, error) {
|
||||
|
||||
// GenerateAndExposeSwagger creates a /documentation/json route on router and
|
||||
// expose the generated swagger
|
||||
func (r Router[T]) GenerateAndExposeSwagger() error {
|
||||
func (r Router[_, _]) GenerateAndExposeSwagger() error {
|
||||
if err := r.swaggerSchema.Validate(r.context); err != nil {
|
||||
return fmt.Errorf("%w: %s", ErrValidatingSwagger, err)
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ func TestNewRouter(t *testing.T) {
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, &Router[gorilla.HandlerFunc]{
|
||||
require.Equal(t, &Router[gorilla.HandlerFunc, gorilla.Route]{
|
||||
context: context.Background(),
|
||||
router: mAPIRouter,
|
||||
swaggerSchema: openapi,
|
||||
@@ -60,7 +60,7 @@ func TestNewRouter(t *testing.T) {
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, &Router[gorilla.HandlerFunc]{
|
||||
require.Equal(t, &Router[gorilla.HandlerFunc, gorilla.Route]{
|
||||
context: ctx,
|
||||
router: mAPIRouter,
|
||||
swaggerSchema: openapi,
|
||||
@@ -80,7 +80,7 @@ func TestNewRouter(t *testing.T) {
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, &Router[gorilla.HandlerFunc]{
|
||||
require.Equal(t, &Router[gorilla.HandlerFunc, gorilla.Route]{
|
||||
context: ctx,
|
||||
router: mAPIRouter,
|
||||
swaggerSchema: openapi,
|
||||
|
||||
35
route.go
35
route.go
@@ -24,12 +24,12 @@ var (
|
||||
|
||||
// AddRawRoute add route to router with specific method, path and handler. Add the
|
||||
// router also to the swagger schema, after validating it
|
||||
func (r Router[HandlerFunc]) AddRawRoute(method string, routePath string, handler HandlerFunc, operation Operation) (interface{}, error) {
|
||||
func (r Router[HandlerFunc, Route]) AddRawRoute(method string, routePath string, handler HandlerFunc, operation Operation) (Route, error) {
|
||||
op := operation.Operation
|
||||
if op != nil {
|
||||
err := operation.Validate(r.context)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return getZero[Route](), err
|
||||
}
|
||||
} else {
|
||||
op = openapi3.NewOperation()
|
||||
@@ -88,46 +88,46 @@ const (
|
||||
cookieParamType = "cookie"
|
||||
)
|
||||
|
||||
// AddRoute add a route with json schema inferted by passed schema.
|
||||
func (r Router[HandlerFunc]) AddRoute(method string, path string, handler HandlerFunc, schema Definitions) (interface{}, error) {
|
||||
// AddRoute add a route with json schema inferred by passed schema.
|
||||
func (r Router[HandlerFunc, Route]) AddRoute(method string, path string, handler HandlerFunc, schema Definitions) (Route, error) {
|
||||
operation := NewOperation()
|
||||
operation.Responses = make(openapi3.Responses)
|
||||
operation.Tags = schema.Tags
|
||||
|
||||
err := r.resolveRequestBodySchema(schema.RequestBody, operation)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %s", ErrRequestBody, err)
|
||||
return getZero[Route](), fmt.Errorf("%w: %s", ErrRequestBody, err)
|
||||
}
|
||||
|
||||
err = r.resolveResponsesSchema(schema.Responses, operation)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %s", ErrResponses, err)
|
||||
return getZero[Route](), fmt.Errorf("%w: %s", ErrResponses, err)
|
||||
}
|
||||
|
||||
err = r.resolveParameterSchema(pathParamsType, getPathParamsAutofilled(schema, path), operation)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %s", ErrPathParams, err)
|
||||
return getZero[Route](), fmt.Errorf("%w: %s", ErrPathParams, err)
|
||||
}
|
||||
|
||||
err = r.resolveParameterSchema(queryParamType, schema.Querystring, operation)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %s", ErrPathParams, err)
|
||||
return getZero[Route](), fmt.Errorf("%w: %s", ErrPathParams, err)
|
||||
}
|
||||
|
||||
err = r.resolveParameterSchema(headerParamType, schema.Headers, operation)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %s", ErrPathParams, err)
|
||||
return getZero[Route](), fmt.Errorf("%w: %s", ErrPathParams, err)
|
||||
}
|
||||
|
||||
err = r.resolveParameterSchema(cookieParamType, schema.Cookies, operation)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %s", ErrPathParams, err)
|
||||
return getZero[Route](), fmt.Errorf("%w: %s", ErrPathParams, err)
|
||||
}
|
||||
|
||||
return r.AddRawRoute(method, path, handler, operation)
|
||||
}
|
||||
|
||||
func (r Router[_]) getSchemaFromInterface(v interface{}, allowAdditionalProperties bool) (*openapi3.Schema, error) {
|
||||
func (r Router[_, _]) getSchemaFromInterface(v interface{}, allowAdditionalProperties bool) (*openapi3.Schema, error) {
|
||||
if v == nil {
|
||||
return &openapi3.Schema{}, nil
|
||||
}
|
||||
@@ -158,7 +158,7 @@ func (r Router[_]) getSchemaFromInterface(v interface{}, allowAdditionalProperti
|
||||
return schema, nil
|
||||
}
|
||||
|
||||
func (r Router[_]) resolveRequestBodySchema(bodySchema *ContentValue, operation Operation) error {
|
||||
func (r Router[_, _]) resolveRequestBodySchema(bodySchema *ContentValue, operation Operation) error {
|
||||
if bodySchema == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -177,7 +177,7 @@ func (r Router[_]) resolveRequestBodySchema(bodySchema *ContentValue, operation
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r Router[_]) resolveResponsesSchema(responses map[int]ContentValue, operation Operation) error {
|
||||
func (r Router[_, _]) resolveResponsesSchema(responses map[int]ContentValue, operation Operation) error {
|
||||
if responses == nil {
|
||||
operation.Responses = openapi3.NewResponses()
|
||||
}
|
||||
@@ -196,7 +196,7 @@ func (r Router[_]) resolveResponsesSchema(responses map[int]ContentValue, operat
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r Router[_]) resolveParameterSchema(paramType string, paramConfig ParameterValue, operation Operation) error {
|
||||
func (r Router[_, _]) resolveParameterSchema(paramType string, paramConfig ParameterValue, operation Operation) error {
|
||||
var keys = make([]string, 0, len(paramConfig))
|
||||
for k := range paramConfig {
|
||||
keys = append(keys, k)
|
||||
@@ -247,7 +247,7 @@ func (r Router[_]) resolveParameterSchema(paramType string, paramConfig Paramete
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r Router[_]) addContentToOASSchema(content Content) (openapi3.Content, error) {
|
||||
func (r Router[_, _]) addContentToOASSchema(content Content) (openapi3.Content, error) {
|
||||
oasContent := openapi3.NewContent()
|
||||
for k, v := range content {
|
||||
var err error
|
||||
@@ -278,3 +278,8 @@ func getPathParamsAutofilled(schema Definitions, path string) ParameterValue {
|
||||
}
|
||||
return schema.PathParams
|
||||
}
|
||||
|
||||
func getZero[T any]() T {
|
||||
var result T
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
const jsonType = "application/json"
|
||||
const formDataType = "multipart/form-data"
|
||||
|
||||
type TestRouter = Router[gorilla.HandlerFunc]
|
||||
type TestRouter = Router[gorilla.HandlerFunc, gorilla.Route]
|
||||
|
||||
func TestAddRoutes(t *testing.T) {
|
||||
|
||||
|
||||
@@ -6,18 +6,19 @@ import (
|
||||
)
|
||||
|
||||
type HandlerFunc = fiber.Handler
|
||||
type Route = fiber.Router
|
||||
|
||||
type fiberRouter struct {
|
||||
router fiber.Router
|
||||
}
|
||||
|
||||
func NewRouter(router fiber.Router) apirouter.Router[HandlerFunc] {
|
||||
func NewRouter(router fiber.Router) apirouter.Router[HandlerFunc, Route] {
|
||||
return fiberRouter{
|
||||
router: router,
|
||||
}
|
||||
}
|
||||
|
||||
func (r fiberRouter) AddRoute(method string, path string, handler HandlerFunc) apirouter.Route {
|
||||
func (r fiberRouter) AddRoute(method string, path string, handler HandlerFunc) Route {
|
||||
return r.router.Add(method, path, handler)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,16 +16,14 @@ func TestFiberRouterSupport(t *testing.T) {
|
||||
ar := NewRouter(fiberRouter)
|
||||
|
||||
t.Run("create a new api router", func(t *testing.T) {
|
||||
require.Implements(t, (*apirouter.Router[HandlerFunc])(nil), ar)
|
||||
require.Implements(t, (*apirouter.Router[HandlerFunc, Route])(nil), ar)
|
||||
})
|
||||
|
||||
t.Run("add new route", func(t *testing.T) {
|
||||
route := ar.AddRoute(http.MethodGet, "/foo", func(c *fiber.Ctx) error {
|
||||
return c.SendStatus(http.StatusOK)
|
||||
})
|
||||
|
||||
_, ok := route.(fiber.Router)
|
||||
require.True(t, ok)
|
||||
require.IsType(t, route, fiber.New())
|
||||
|
||||
t.Run("router exposes correctly api", func(t *testing.T) {
|
||||
r := httptest.NewRequest(http.MethodGet, "/foo", nil)
|
||||
|
||||
58
support/fiber/integration_test.go
Normal file
58
support/fiber/integration_test.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package fiber_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
swagger "github.com/davidebianchi/gswagger"
|
||||
oasFiber "github.com/davidebianchi/gswagger/support/fiber"
|
||||
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type SwaggerRouter = swagger.Router[oasFiber.HandlerFunc, oasFiber.Route]
|
||||
|
||||
const (
|
||||
swaggerOpenapiTitle = "test swagger title"
|
||||
swaggerOpenapiVersion = "test swagger version"
|
||||
)
|
||||
|
||||
func TestWithFiber(t *testing.T) {
|
||||
require.True(t, true)
|
||||
}
|
||||
|
||||
func setupSwagger(t *testing.T) (*fiber.App, *SwaggerRouter) {
|
||||
t.Helper()
|
||||
|
||||
context := context.Background()
|
||||
fiberRouter := fiber.New()
|
||||
|
||||
router, err := swagger.NewRouter(oasFiber.NewRouter(fiberRouter), swagger.Options{
|
||||
Context: context,
|
||||
Openapi: &openapi3.T{
|
||||
Info: &openapi3.Info{
|
||||
Title: swaggerOpenapiTitle,
|
||||
Version: swaggerOpenapiVersion,
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
operation := swagger.Operation{}
|
||||
|
||||
_, err = router.AddRawRoute(http.MethodGet, "/hello", okHandler, operation)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = router.GenerateAndExposeSwagger()
|
||||
require.NoError(t, err)
|
||||
|
||||
return fiberRouter, router
|
||||
}
|
||||
|
||||
func okHandler(c *fiber.Ctx) error {
|
||||
c.Status(http.StatusOK)
|
||||
return c.SendString("OK")
|
||||
}
|
||||
@@ -10,12 +10,13 @@ import (
|
||||
|
||||
// HandlerFunc is the http type handler used by gorilla/mux
|
||||
type HandlerFunc func(w http.ResponseWriter, req *http.Request)
|
||||
type Route = *mux.Route
|
||||
|
||||
type gorillaRouter struct {
|
||||
router *mux.Router
|
||||
}
|
||||
|
||||
func (r gorillaRouter) AddRoute(method string, path string, handler HandlerFunc) apirouter.Route {
|
||||
func (r gorillaRouter) AddRoute(method string, path string, handler HandlerFunc) Route {
|
||||
return r.router.HandleFunc(path, handler).Methods(method)
|
||||
}
|
||||
|
||||
@@ -27,7 +28,7 @@ func (r gorillaRouter) SwaggerHandler(contentType string, blob []byte) HandlerFu
|
||||
}
|
||||
}
|
||||
|
||||
func NewRouter(router *mux.Router) apirouter.Router[HandlerFunc] {
|
||||
func NewRouter(router *mux.Router) apirouter.Router[HandlerFunc, Route] {
|
||||
return gorillaRouter{
|
||||
router: router,
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ func TestGorillaMuxRouter(t *testing.T) {
|
||||
ar := NewRouter(muxRouter)
|
||||
|
||||
t.Run("create a new api router", func(t *testing.T) {
|
||||
require.Implements(t, (*apirouter.Router[HandlerFunc])(nil), ar)
|
||||
require.Implements(t, (*apirouter.Router[HandlerFunc, Route])(nil), ar)
|
||||
})
|
||||
|
||||
t.Run("add new route", func(t *testing.T) {
|
||||
@@ -24,9 +24,7 @@ func TestGorillaMuxRouter(t *testing.T) {
|
||||
w.WriteHeader(200)
|
||||
w.Write(nil)
|
||||
})
|
||||
|
||||
_, ok := route.(*mux.Route)
|
||||
require.True(t, ok)
|
||||
require.IsType(t, route, &mux.Route{})
|
||||
|
||||
t.Run("router exposes correctly api", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
Reference in New Issue
Block a user