Skip to content

fix: NewRoute/NewBaseRoute to take engine not just OpeAPI for accepted content types #334

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ type Engine struct {
ErrorHandler func(error) error
OpenAPIConfig OpenAPIConfig

acceptedContentTypes []string
requestContentTypes []string
}

type OpenAPIConfig struct {
Expand All @@ -59,6 +59,12 @@ var defaultOpenAPIConfig = OpenAPIConfig{
JSONFilePath: "doc/openapi.json",
}

// WithRequestContentType sets the accepted content types for the engine.
// By default, the accepted content types is */*.
func WithRequestContentType(consumes ...string) func(*Engine) {
return func(e *Engine) { e.requestContentTypes = consumes }
}

func WithOpenAPIConfig(config OpenAPIConfig) func(*Engine) {
return func(e *Engine) {
if config.JSONFilePath != "" {
Expand Down
33 changes: 33 additions & 0 deletions engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -38,3 +39,35 @@ func TestWithErrorHandler(t *testing.T) {
})
})
}

func TestWithRequestContentType(t *testing.T) {
t.Run("base", func(t *testing.T) {
e := NewEngine()
require.Nil(t, e.requestContentTypes)
})

t.Run("input", func(t *testing.T) {
arr := []string{"application/json", "application/xml"}
e := NewEngine(WithRequestContentType("application/json", "application/xml"))
require.ElementsMatch(t, arr, e.requestContentTypes)
})

t.Run("ensure applied to route", func(t *testing.T) {
s := NewServer(WithEngineOptions(
WithRequestContentType("application/json", "application/xml")),
)
route := Post(s, "/test", dummyController)

content := route.Operation.RequestBody.Value.Content
require.NotNil(t, content["application/json"])
assert.Equal(t, "#/components/schemas/ReqBody", content["application/json"].Schema.Ref)

require.NotNil(t, content["application/xml"])
assert.Equal(t, "#/components/schemas/ReqBody", content["application/xml"].Schema.Ref)

require.Nil(t, content["application/x-yaml"])

_, ok := s.OpenAPI.Description().Components.RequestBodies["ReqBody"]
require.False(t, ok)
})
}
4 changes: 2 additions & 2 deletions extra/fuegogin/adaptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func Post[T, B any](engine *fuego.Engine, ginRouter gin.IRouter, path string, ha
}

func handleFuego[T, B any](engine *fuego.Engine, ginRouter gin.IRouter, method, path string, fuegoHandler func(c fuego.ContextWithBody[B]) (T, error), options ...func(*fuego.BaseRoute)) *fuego.Route[T, B] {
baseRoute := fuego.NewBaseRoute(method, path, fuegoHandler, engine.OpenAPI, options...)
baseRoute := fuego.NewBaseRoute(method, path, fuegoHandler, engine, options...)
return fuego.Registers(engine, ginRouteRegisterer[T, B]{
ginRouter: ginRouter,
route: fuego.Route[T, B]{BaseRoute: baseRoute},
Expand All @@ -35,7 +35,7 @@ func handleFuego[T, B any](engine *fuego.Engine, ginRouter gin.IRouter, method,
}

func handleGin(engine *fuego.Engine, ginRouter gin.IRouter, method, path string, ginHandler gin.HandlerFunc, options ...func(*fuego.BaseRoute)) *fuego.Route[any, any] {
baseRoute := fuego.NewBaseRoute(method, path, ginHandler, engine.OpenAPI, options...)
baseRoute := fuego.NewBaseRoute(method, path, ginHandler, engine, options...)
return fuego.Registers(engine, ginRouteRegisterer[any, any]{
ginRouter: ginRouter,
route: fuego.Route[any, any]{BaseRoute: baseRoute},
Expand Down
4 changes: 2 additions & 2 deletions net_http_mux.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ func PatchStd(s *Server, path string, controller func(http.ResponseWriter, *http
}

func registerFuegoController[T, B any](s *Server, method, path string, controller func(ContextWithBody[B]) (T, error), options ...func(*BaseRoute)) *Route[T, B] {
route := NewRoute[T, B](method, path, controller, s.OpenAPI, append(s.routeOptions, options...)...)
route := NewRoute[T, B](method, path, controller, s.Engine, append(s.routeOptions, options...)...)

acceptHeaderParameter := openapi3.NewHeaderParameter("Accept")
acceptHeaderParameter.Schema = openapi3.NewStringSchema().NewRef()
Expand All @@ -149,7 +149,7 @@ func registerFuegoController[T, B any](s *Server, method, path string, controlle
}

func registerStdController(s *Server, method, path string, controller func(http.ResponseWriter, *http.Request), options ...func(*BaseRoute)) *Route[any, any] {
route := NewRoute[any, any](method, path, controller, s.OpenAPI, append(s.routeOptions, options...)...)
route := NewRoute[any, any](method, path, controller, s.Engine, append(s.routeOptions, options...)...)

return Registers(s.Engine, netHttpRouteRegisterer[any, any]{
s: s,
Expand Down
2 changes: 1 addition & 1 deletion openapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ func RegisterOpenAPIOperation[T, B any](openapi *OpenAPI, route Route[T, B]) (*o
bodyTag := SchemaTagFromType(openapi, *new(B))

if bodyTag.Name != "unknown-interface" {
requestBody := newRequestBody[B](bodyTag, route.AcceptedContentTypes)
requestBody := newRequestBody[B](bodyTag, route.RequestContentTypes)

// add request body to operation
route.Operation.RequestBody = &openapi3.RequestBodyRef{
Expand Down
2 changes: 1 addition & 1 deletion option.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ func OptionAddResponse(code int, description string, response Response) func(*Ba
// This will override any options set at the server level.
func OptionRequestContentType(consumes ...string) func(*BaseRoute) {
return func(r *BaseRoute) {
r.AcceptedContentTypes = consumes
r.RequestContentTypes = consumes
}
}

Expand Down
8 changes: 5 additions & 3 deletions option_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,8 +340,8 @@ func TestRequestContentType(t *testing.T) {
s.Mux.ServeHTTP(w, r)

require.Equal(t, "{\"message\":\"hello world\"}\n", w.Body.String())
require.Len(t, route.AcceptedContentTypes, 1)
require.Equal(t, "application/json", route.AcceptedContentTypes[0])
require.Len(t, route.RequestContentTypes, 1)
require.Equal(t, "application/json", route.RequestContentTypes[0])
})

t.Run("base", func(t *testing.T) {
Expand Down Expand Up @@ -376,7 +376,9 @@ func TestRequestContentType(t *testing.T) {
})

t.Run("override server", func(t *testing.T) {
s := fuego.NewServer(fuego.WithRequestContentType("application/json", "application/xml"))
s := fuego.NewServer(fuego.WithEngineOptions(
fuego.WithRequestContentType("application/json", "application/xml"),
))
route := fuego.Post(
s, "/test", dummyController,
fuego.OptionRequestContentType("my/content-type"),
Expand Down
21 changes: 11 additions & 10 deletions route.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import (
"github.com/getkin/kin-openapi/openapi3"
)

func NewRoute[T, B any](method, path string, handler any, openapi *OpenAPI, options ...func(*BaseRoute)) Route[T, B] {
func NewRoute[T, B any](method, path string, handler any, e *Engine, options ...func(*BaseRoute)) Route[T, B] {
return Route[T, B]{
BaseRoute: NewBaseRoute(method, path, handler, openapi, options...),
BaseRoute: NewBaseRoute(method, path, handler, e, options...),
}
}

Expand All @@ -20,14 +20,15 @@ type Route[ResponseBody any, RequestBody any] struct {
BaseRoute
}

func NewBaseRoute(method, path string, handler any, openapi *OpenAPI, options ...func(*BaseRoute)) BaseRoute {
func NewBaseRoute(method, path string, handler any, e *Engine, options ...func(*BaseRoute)) BaseRoute {
baseRoute := BaseRoute{
Method: method,
Path: path,
Params: make(map[string]OpenAPIParam),
FullName: FuncName(handler),
Operation: openapi3.NewOperation(),
OpenAPI: openapi,
Method: method,
Path: path,
Params: make(map[string]OpenAPIParam),
FullName: FuncName(handler),
Operation: openapi3.NewOperation(),
OpenAPI: e.OpenAPI,
RequestContentTypes: e.requestContentTypes,
}

for _, o := range options {
Expand Down Expand Up @@ -61,7 +62,7 @@ type BaseRoute struct {
FullName string

// Content types accepted for the request body. If nil, all content types (*/*) are accepted.
AcceptedContentTypes []string
RequestContentTypes []string

Middlewares []func(http.Handler) http.Handler

Expand Down
6 changes: 0 additions & 6 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,12 +343,6 @@ func WithLogHandler(handler slog.Handler) func(*Server) {
}
}

// WithRequestContentType sets the accepted content types for the server.
// By default, the accepted content types is */*.
func WithRequestContentType(consumes ...string) func(*Server) {
return func(s *Server) { s.acceptedContentTypes = consumes }
}

// WithSerializer sets a custom serializer of type Sender that overrides the default one.
// Please send a PR if you think the default serializer should be improved, instead of jumping to this option.
func WithSerializer(serializer Sender) func(*Server) {
Expand Down
43 changes: 0 additions & 43 deletions server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"html/template"
"io"
"log/slog"
"net"
"net/http"
"net/http/httptest"
"testing"
Expand Down Expand Up @@ -321,48 +320,6 @@ func dummyController(_ ContextWithBody[ReqBody]) (Resp, error) {
return Resp{Message: "hello world"}, nil
}

func TestWithRequestContentType(t *testing.T) {
t.Run("base", func(t *testing.T) {
s := NewServer()
require.Nil(t, s.acceptedContentTypes)
})

t.Run("input", func(t *testing.T) {
arr := []string{"application/json", "application/xml"}
s := NewServer(WithRequestContentType("application/json", "application/xml"))
require.ElementsMatch(t, arr, s.acceptedContentTypes)
})

t.Run("ensure applied to route", func(t *testing.T) {
s := NewServer(WithRequestContentType("application/json", "application/xml"))
route := Post(s, "/test", dummyController)

content := route.Operation.RequestBody.Value.Content
require.NotNil(t, content.Get("application/json"))
require.NotNil(t, content.Get("application/xml"))
require.Equal(t, "#/components/schemas/ReqBody", content.Get("application/json").Schema.Ref)
require.Equal(t, "#/components/schemas/ReqBody", content.Get("application/xml").Schema.Ref)
_, ok := s.OpenAPI.Description().Components.RequestBodies["ReqBody"]
require.False(t, ok)
})
}

func TestWithListener(t *testing.T) {
t.Run("with custom listener", func(t *testing.T) {
listener, err := net.Listen("tcp", ":8080")
require.NoError(t, err)
s := NewServer(
WithListener(listener),
)
require.NotNil(t, s.listener)
})

t.Run("no custom listener", func(t *testing.T) {
s := NewServer()
require.Nil(t, s.listener)
})
}

func TestCustomSerialization(t *testing.T) {
s := NewServer(
WithSerializer(func(w http.ResponseWriter, r *http.Request, a any) error {
Expand Down
Loading