Skip to content

Commit

Permalink
Gin compat
Browse files Browse the repository at this point in the history
  • Loading branch information
EwenQuim committed Dec 14, 2024
1 parent a8319c7 commit b9fb28f
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 51 deletions.
3 changes: 0 additions & 3 deletions ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,6 @@ type ctx[B any] interface {
QueryParamBoolErr(name string) (bool, error)
QueryParams() url.Values

MainLang() string // ex: fr. MainLang returns the main language of the request. It is the first language of the Accept-Language header. To get the main locale (ex: fr-CA), use [Ctx.MainLocale].
MainLocale() string // ex: en-US. MainLocale returns the main locale of the request. It is the first locale of the Accept-Language header. To get the main language (ex: en), use [Ctx.MainLang].

// Render renders the given templates with the given data.
// Example:
// fuego.Get(s, "/recipes", func(c fuego.ContextNoBody) (any, error) {
Expand Down
44 changes: 35 additions & 9 deletions extra/fuegogin/adaptor.go
Original file line number Diff line number Diff line change
@@ -1,50 +1,76 @@
package fuegogin

import (
"github.com/getkin/kin-openapi/openapi3"
"github.com/gin-gonic/gin"

"github.com/go-fuego/fuego"
)

func Get[T, B any](
s *fuego.Server,
s *fuego.OpenAPI,
e *gin.Engine,
path string,
handler func(c *ContextWithBody[T]) (B, error),
options ...func(*fuego.BaseRoute),
) *fuego.Route[B, T] {
return Handle(s, e, "GET", path, handler)
return Handle(s, e, "GET", path, handler, options...)
}

func Post[T, B any](
s *fuego.Server,
s *fuego.OpenAPI,
e *gin.Engine,
path string,
handler func(c *ContextWithBody[T]) (B, error),
options ...func(*fuego.BaseRoute),
) *fuego.Route[B, T] {
return Handle(s, e, "GET", path, handler)
return Handle(s, e, "POST", path, handler, options...)
}

func Handle[T, B any](
s *fuego.Server,
openapi *fuego.OpenAPI,
e *gin.Engine,
method,
path string,
handler func(c *ContextWithBody[T]) (B, error),
options ...func(*fuego.BaseRoute),
) *fuego.Route[B, T] {
route := &fuego.Route[B, T]{
BaseRoute: fuego.BaseRoute{
Method: method,
Path: path,
Params: make(map[string]fuego.OpenAPIParam),
FullName: fuego.FuncName(handler),
Operation: openapi3.NewOperation(),
OpenAPI: openapi,
},
}

for _, o := range options {
o(&route.BaseRoute)
}

e.Handle(method, path, func(c *gin.Context) {
ans, err := handler(&ContextWithBody[T]{})
context := &ContextWithBody[T]{
ContextNoBody: ContextNoBody{
ginCtx: c,
},
}
ans, err := handler(context)
if err != nil {
c.Error(err)
return
}

if c.Request.Header.Get("Accept") == "application/xml" {
c.XML(200, ans)
return
}

c.JSON(200, ans)
})

// Also register the route with fuego!!!
// Useful for the OpenAPI spec but also allows for to run Fuego in parallel.
return fuego.Get(s, path, handler, options...)
route.RegisterOpenAPIOperation(openapi)

return route
}
2 changes: 1 addition & 1 deletion extra/fuegogin/adaptor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/go-fuego/fuego/extra/fuegogin/lib"
)

func TestXxx(t *testing.T) {
func TestFuegoGin(t *testing.T) {
e, _ := lib.SetupGin()

t.Run("simply test gin", func(t *testing.T) {
Expand Down
55 changes: 31 additions & 24 deletions extra/fuegogin/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,35 @@ package fuegogin

import (
"context"
"fmt"
"net/http"
"net/url"

"github.com/gin-gonic/gin"

"github.com/go-fuego/fuego"
)

type ContextWithBody[B any] struct{}
type ContextWithBody[B any] struct {
ContextNoBody
}

type ContextNoBody struct {
fuego.ContextNoBody
ginCtx *gin.Context
}

// Body implements fuego.Ctx.
func (c *ContextWithBody[B]) Body() (B, error) {
panic("unimplemented")
fmt.Println("c.ginCtx", c.ginCtx)
var body B
err := c.ginCtx.Bind(&body)
return body, err
}

// Context implements fuego.Ctx.
func (c *ContextWithBody[B]) Context() context.Context {
panic("unimplemented")
return c.ginCtx
}

// Cookie implements fuego.Ctx.
Expand All @@ -27,32 +40,26 @@ func (c *ContextWithBody[B]) Cookie(name string) (*http.Cookie, error) {

// Header implements fuego.Ctx.
func (c *ContextWithBody[B]) Header(key string) string {
panic("unimplemented")
}

// MainLang implements fuego.Ctx.
func (c *ContextWithBody[B]) MainLang() string {
panic("unimplemented")
}

// MainLocale implements fuego.Ctx.
func (c *ContextWithBody[B]) MainLocale() string {
panic("unimplemented")
return c.ginCtx.GetHeader(key)
}

// MustBody implements fuego.Ctx.
func (c *ContextWithBody[B]) MustBody() B {
panic("unimplemented")
body, err := c.Body()
if err != nil {
panic(err)
}
return body
}

// PathParam implements fuego.Ctx.
func (c *ContextWithBody[B]) PathParam(name string) string {
panic("unimplemented")
return c.ginCtx.Param(name)
}

// QueryParam implements fuego.Ctx.
func (c *ContextWithBody[B]) QueryParam(name string) string {
panic("unimplemented")
return c.ginCtx.Query(name)
}

// QueryParamArr implements fuego.Ctx.
Expand Down Expand Up @@ -82,12 +89,13 @@ func (c *ContextWithBody[B]) QueryParamIntErr(name string) (int, error) {

// QueryParams implements fuego.Ctx.
func (c *ContextWithBody[B]) QueryParams() url.Values {
panic("unimplemented")
return c.ginCtx.Request.URL.Query()
}

// Redirect implements fuego.Ctx.
func (c *ContextWithBody[B]) Redirect(code int, url string) (any, error) {
panic("unimplemented")
c.ginCtx.Redirect(code, url)
return nil, nil
}

// Render implements fuego.Ctx.
Expand All @@ -97,25 +105,24 @@ func (c *ContextWithBody[B]) Render(templateToExecute string, data any, template

// Request implements fuego.Ctx.
func (c *ContextWithBody[B]) Request() *http.Request {
panic("unimplemented")
return c.ginCtx.Request
}

// Response implements fuego.Ctx.
func (c *ContextWithBody[B]) Response() http.ResponseWriter {
panic("unimplemented")
return c.ginCtx.Writer
}

// SetCookie implements fuego.Ctx.
func (c *ContextWithBody[B]) SetCookie(cookie http.Cookie) {
panic("unimplemented")
}

// SetHeader implements fuego.Ctx.
func (c *ContextWithBody[B]) SetHeader(key, value string) {
panic("unimplemented")
c.ginCtx.Header(key, value)
}

// SetStatus implements fuego.Ctx.
func (c *ContextWithBody[B]) SetStatus(code int) {
panic("unimplemented")
c.ginCtx.Status(code)
}
4 changes: 2 additions & 2 deletions extra/fuegogin/go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
module github.com/go-fuego/fuego/extra/fuegogin

go 1.22.5
go 1.23.0

require (
github.com/getkin/kin-openapi v0.128.0
github.com/gin-gonic/gin v1.10.0
github.com/go-fuego/fuego v0.16.1
github.com/stretchr/testify v1.10.0
Expand All @@ -15,7 +16,6 @@ require (
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.7 // indirect
github.com/getkin/kin-openapi v0.128.0 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
Expand Down
53 changes: 48 additions & 5 deletions extra/fuegogin/lib/lib.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package lib

import (
"fmt"
"net/http"

"github.com/gin-gonic/gin"

"github.com/go-fuego/fuego"
"github.com/go-fuego/fuego/extra/fuegogin"
"github.com/go-fuego/fuego/option"
"github.com/go-fuego/fuego/param"
)

type HelloRequest struct {
Expand All @@ -15,22 +20,60 @@ type HelloResponse struct {
Message string `json:"message"`
}

func SetupGin() (*gin.Engine, *fuego.Server) {
func SetupGin() (*gin.Engine, *fuego.OpenAPI) {
e := gin.Default()
s := fuego.NewServer()
openapi := fuego.NewOpenAPI()

// Register Gin controller
e.GET("/gin", ginController)
fuegogin.Get(s, e, "/fuego", fuegoController)

return e, s
// Register to Gin router with Fuego wrapper for same OpenAPI spec
fuegogin.Get(openapi, e, "/fuego", fuegoController)
fuegogin.Get(openapi, e, "/fuego-with-options", fuegoController,
option.Description("Some description"),
option.OperationID("SomeOperationID"),
option.AddError(409, "Name Already Exists"),
option.DefaultStatusCode(201),
option.Query("name", "Your name", param.Example("name example", "John Carmack")),
option.Header("X-Request-ID", "Request ID", param.Default("123456")),
option.Header("Content-Type", "Content Type", param.Default("application/json")),
)

// Serve the OpenAPI spec
e.GET("/openapi.json", serveController(openapi))
e.GET("/swagger", DefaultOpenAPIHandler("/openapi.json"))

return e, openapi
}

func ginController(c *gin.Context) {
c.String(200, "pong")
}

func fuegoController(c *fuegogin.ContextWithBody[HelloRequest]) (HelloResponse, error) {
body, err := c.Body()
if err != nil {
return HelloResponse{}, err
}
fmt.Println("body", body)

name := c.QueryParam("name")
fmt.Println("name", name)

return HelloResponse{
Message: "Hello ",
Message: "Hello " + body.Name,
}, nil
}

func serveController(s *fuego.OpenAPI) func(ctx *gin.Context) {
return func(ctx *gin.Context) {
ctx.JSON(http.StatusOK, s.Description())
}
}

func DefaultOpenAPIHandler(specURL string) gin.HandlerFunc {
return func(ctx *gin.Context) {
ctx.Header("Content-Type", "text/html; charset=utf-8")
ctx.String(200, fuego.DefaultOpenAPIHTML(specURL))
}
}
12 changes: 5 additions & 7 deletions extra/fuegogin/serve/main.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package main

import "github.com/go-fuego/fuego/extra/fuegogin/lib"
import (
"github.com/go-fuego/fuego/extra/fuegogin/lib"
)

func main() {
e, s := lib.SetupGin()
e, _ := lib.SetupGin()

go func() {
s.Run()
}()

err := e.Run(":8080")
err := e.Run(":8980")
if err != nil {
panic(err)
}
Expand Down
6 changes: 6 additions & 0 deletions openapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,12 @@ func RegisterOpenAPIOperation[T, B any](openapi *OpenAPI, route Route[T, B]) (*o
}
}

// Names & default description

if route.Operation.OperationID == "" {
route.Operation.OperationID = route.Method + "_" + strings.ReplaceAll(strings.ReplaceAll(route.Path, "{", ":"), "}", "")
}

openapi.Description().AddOperation(route.Path, route.Method, route.Operation)

return route.Operation, nil
Expand Down

0 comments on commit b9fb28f

Please sign in to comment.