From 2c4e0fd8d3993c9b444da5854951d5f700c53a2f Mon Sep 17 00:00:00 2001 From: Alexis Viscogliosi Date: Tue, 5 Sep 2023 11:33:46 +0200 Subject: [PATCH] dig: use our dig fork --- _tools/go_mod_tidy_all.sh | 9 +++ exemple/go.mod | 2 + exemple/go.sum | 2 + exemple/internal/app/app.go | 2 +- exemple/internal/app/handler/handle_home.go | 5 ++ makefile | 2 + xfoundation/app.go | 67 ++++++++------------- xfoundation/go.mod | 18 +++++- xfoundation/go.sum | 20 +++++- xweb/go.mod | 1 + xweb/go.sum | 2 + xweb/provider.go | 10 ++- xweb/router.go | 18 +++--- 13 files changed, 101 insertions(+), 57 deletions(-) create mode 100644 _tools/go_mod_tidy_all.sh create mode 100644 makefile diff --git a/_tools/go_mod_tidy_all.sh b/_tools/go_mod_tidy_all.sh new file mode 100644 index 0000000..55b8cf2 --- /dev/null +++ b/_tools/go_mod_tidy_all.sh @@ -0,0 +1,9 @@ +# This script walk through all subdirectories and run go mod tidy if go.mod exists. +# It prints out the directory name if go mod tidy is run. + +set -e + +find . -name "go.mod" | xargs -n1 dirname | while read dir; do + echo "go mod tidy in $dir" + (cd "$dir" && go mod tidy) +done diff --git a/exemple/go.mod b/exemple/go.mod index b4bd725..34c1b1b 100644 --- a/exemple/go.mod +++ b/exemple/go.mod @@ -3,6 +3,7 @@ module github.com/caumette-co/x/example go 1.21.0 replace github.com/caumette-co/x/xfoundation => ../xfoundation + replace github.com/caumette-co/x/xweb => ../xweb require ( @@ -11,6 +12,7 @@ require ( ) require ( + github.com/expectedsh/dig v0.0.1-expected // indirect github.com/go-chi/chi v1.5.4 // indirect github.com/samber/lo v1.38.1 // indirect go.uber.org/multierr v1.11.0 // indirect diff --git a/exemple/go.sum b/exemple/go.sum index a80914d..b27556b 100644 --- a/exemple/go.sum +++ b/exemple/go.sum @@ -6,6 +6,8 @@ github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/expectedsh/dig v0.0.1-expected h1:9E1CxIFWjjmkPwslEJgIiMBGW805JNrBTBq1VqiDrMA= +github.com/expectedsh/dig v0.0.1-expected/go.mod h1:TlTW0Xtcx4YY6J6vp9sFGQPDDxCUC7UJIwUOfieEM8g= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= diff --git a/exemple/internal/app/app.go b/exemple/internal/app/app.go index 4de676f..c3569b6 100644 --- a/exemple/internal/app/app.go +++ b/exemple/internal/app/app.go @@ -8,7 +8,7 @@ import ( ) var Default = xfoundation.App{ - Env: os.Getenv("APP_ENV"), + Env: xfoundation.AppEnv(os.Getenv("APP_ENV")), Providers: []xfoundation.Provider{ &xweb.Provider{ Addr: os.Getenv("ADDR"), diff --git a/exemple/internal/app/handler/handle_home.go b/exemple/internal/app/handler/handle_home.go index 827445e..d933df2 100644 --- a/exemple/internal/app/handler/handle_home.go +++ b/exemple/internal/app/handler/handle_home.go @@ -1,6 +1,7 @@ package handler import ( + "errors" "fmt" "github.com/caumette-co/x/xweb" "net/http" @@ -41,6 +42,10 @@ type Contact struct { Email string `query:"email"` } +func (c Contact) Validate() error { + return errors.New("not implemented") +} + func HandleContact(r *xweb.Request[Contact]) (xweb.Response, error) { fmt.Println(r.Params().Email) diff --git a/makefile b/makefile new file mode 100644 index 0000000..3aeb421 --- /dev/null +++ b/makefile @@ -0,0 +1,2 @@ +go-mod-tidy-all: + sh ./_tools/go_mod_tidy_all.sh diff --git a/xfoundation/app.go b/xfoundation/app.go index 512a912..3a57a9a 100644 --- a/xfoundation/app.go +++ b/xfoundation/app.go @@ -2,7 +2,8 @@ package xfoundation import ( "context" - "fmt" + "github.com/expectedsh/dig" + "github.com/samber/lo" "go.uber.org/zap" "os" "os/signal" @@ -16,38 +17,39 @@ type ( } App struct { - Container - Env string + Container *dig.Container + + Env AppEnv Logger *zap.Logger Providers []Provider startHooks []func(ctx context.Context) error stopHooks []func(ctx context.Context) error } - Container struct { - values map[reflect.Type]reflect.Value - } + AppEnv string ) const ( - AppEnvProduction = "production" - AppEnvDevelopment = "development" + AppEnvProduction AppEnv = "production" + AppEnvDevelopment AppEnv = "development" ) func (app *App) Run() { - app.values = make(map[reflect.Type]reflect.Value) + app.Container = dig.New() + if app.Env == "" { app.Env = AppEnvDevelopment } if app.Logger == nil { if app.Env == AppEnvProduction { - app.Logger = panicOnError(zap.NewProduction()) + app.Logger = lo.Must(zap.NewProduction()) } else { - app.Logger = panicOnError(zap.NewDevelopment()) + app.Logger = lo.Must(zap.NewDevelopment()) } } - app.Provide(app.Logger) + + lo.Must0(app.Provide(ProvideSingleValueFunc(app.Logger))) for _, provider := range app.Providers { log := app.Logger.With(zap.Any("provider", reflect.TypeOf(provider))) @@ -83,40 +85,21 @@ func (app *App) OnStop(hook func(ctx context.Context) error) { app.stopHooks = append(app.stopHooks, hook) } -func (c *Container) Provide(v any) { - c.values[reflect.TypeOf(v)] = reflect.ValueOf(v) +func (app *App) Provide(v any) error { + return app.Container.Provide(v) } -func (c *Container) Invoke(f any) ([]reflect.Value, error) { - fType := reflect.TypeOf(f) - if fType.Kind() != reflect.Func { - return nil, fmt.Errorf("app.Invoke: invalid func type %v", fType) - } - var dependencies []reflect.Value - for i := 0; i < fType.NumIn(); i++ { - depType := fType.In(i) - value, ok := c.values[depType] - if !ok { - return nil, fmt.Errorf("app.Invoke: cannot find dependency %v", depType) - } - dependencies = append(dependencies, value) - } - - returnValues := reflect.ValueOf(f).Call(dependencies) - if returnValuesLen := len(returnValues); returnValuesLen > 0 { - if err, ok := returnValues[returnValuesLen-1].Interface().(error); ok { - if err != nil { - return nil, err - } - returnValues = returnValues[:returnValuesLen-1] - } - } - return returnValues, nil +func ProvideSingleValueFunc[T any](v T) func() T { + return func() T { return v } } -func panicOnError[T any](value T, err error) T { +func (a App) Invoke(f any) ([]reflect.Value, error) { + invokeInfo := dig.InvokeInfo{} + + err := a.Container.Invoke(f, dig.FillInvokeInfo(&invokeInfo)) if err != nil { - panic(err) + return nil, err } - return value + + return invokeInfo.Outputs, nil } diff --git a/xfoundation/go.mod b/xfoundation/go.mod index 14f626f..ccba11d 100644 --- a/xfoundation/go.mod +++ b/xfoundation/go.mod @@ -3,7 +3,21 @@ module github.com/caumette-co/x/xfoundation go 1.21.0 require ( - go.uber.org/dig v1.17.0 // indirect + github.com/expectedsh/dig v0.0.1-expected + github.com/samber/lo v1.38.1 + go.uber.org/zap v1.25.0 +) + +require ( go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.25.0 // indirect + golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect ) + +// Note on github.com/expectedsh/dig, this is a fork of go.uber.org/dig +// Our fork is needed to add outputs informations for an invoke +// A merge request is opened, waiting for the merge +// When the merge will occur we could remove this fork and use the official dig package +// +// The fork contains two branches : +// - the master branch is the same as the official dig package +// - the expected branch contains the changes we made to add outputs informations for an invoke and replaced go.mod module name diff --git a/xfoundation/go.sum b/xfoundation/go.sum index baa5893..a45ee31 100644 --- a/xfoundation/go.sum +++ b/xfoundation/go.sum @@ -1,6 +1,22 @@ -go.uber.org/dig v1.17.0 h1:5Chju+tUvcC+N7N6EV08BJz41UZuO3BmHcN4A287ZLI= -go.uber.org/dig v1.17.0/go.mod h1:rTxpf7l5I0eBTlE6/9RL+lDybC7WFwY2QH55ZSjy1mU= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/expectedsh/dig v0.0.1-expected h1:9E1CxIFWjjmkPwslEJgIiMBGW805JNrBTBq1VqiDrMA= +github.com/expectedsh/dig v0.0.1-expected/go.mod h1:TlTW0Xtcx4YY6J6vp9sFGQPDDxCUC7UJIwUOfieEM8g= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= +github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= +golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= +golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/xweb/go.mod b/xweb/go.mod index 077a68a..c51eba6 100644 --- a/xweb/go.mod +++ b/xweb/go.mod @@ -17,6 +17,7 @@ require ( github.com/ajg/form v1.5.1 // indirect github.com/andybalholm/brotli v1.0.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/expectedsh/dig v0.0.1-expected // indirect github.com/fatih/color v1.13.0 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/gobwas/glob v0.2.3 // indirect diff --git a/xweb/go.sum b/xweb/go.sum index fb89fe3..6c06c0d 100644 --- a/xweb/go.sum +++ b/xweb/go.sum @@ -8,6 +8,8 @@ github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/expectedsh/dig v0.0.1-expected h1:9E1CxIFWjjmkPwslEJgIiMBGW805JNrBTBq1VqiDrMA= +github.com/expectedsh/dig v0.0.1-expected/go.mod h1:TlTW0Xtcx4YY6J6vp9sFGQPDDxCUC7UJIwUOfieEM8g= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= diff --git a/xweb/provider.go b/xweb/provider.go index 1ff29e1..a477143 100644 --- a/xweb/provider.go +++ b/xweb/provider.go @@ -2,6 +2,7 @@ package xweb import ( "context" + "fmt" "github.com/caumette-co/x/xfoundation" "go.uber.org/zap" "net" @@ -16,8 +17,13 @@ type Provider struct { func (p *Provider) Register(app *xfoundation.App) error { router := newRouter(app) - app.Provide(router) - app.Provide(p) + if err := app.Provide(xfoundation.ProvideSingleValueFunc(router)); err != nil { + return fmt.Errorf("failed to provide *webx.Router: %w", err) + } + if err := app.Provide(xfoundation.ProvideSingleValueFunc(p)); err != nil { + return fmt.Errorf("failed to provide *webx.Provider: %w", err) + } + httpServer := &http.Server{Handler: router.handler} app.OnStart(func(ctx context.Context) error { diff --git a/xweb/router.go b/xweb/router.go index 670c3cb..425b421 100644 --- a/xweb/router.go +++ b/xweb/router.go @@ -1,6 +1,7 @@ package xweb import ( + "fmt" "github.com/caumette-co/x/xfoundation" "github.com/go-chi/chi" "go.uber.org/zap" @@ -21,9 +22,10 @@ func newRouter(app *xfoundation.App) *Router { } func (r *Router) Route(method, path string, handler any) { - httpHandler := r.getHttpHandler(handler) - if httpHandler == nil { + httpHandler, err := r.getHttpHandler(handler) + if err != nil { r.app.Logger.Error("invalid handler", + zap.Error(err), zap.String("method", method), zap.String("path", path), zap.String("handler", reflect.TypeOf(handler).String()), @@ -57,18 +59,18 @@ func (r *Router) Delete(path string, handler any) { r.Route(http.MethodDelete, path, handler) } -func (r *Router) getHttpHandler(handler any) http.Handler { +func (r *Router) getHttpHandler(handler any) (http.Handler, error) { handlerType := reflect.TypeOf(handler) if handlerType.Kind() != reflect.Func { - return nil + return nil, fmt.Errorf("handler is not a function") } if handlerType.NumOut() == 1 && handlerType.Out(0).Kind() == reflect.Func { values, err := r.app.Invoke(handler) if err != nil { - return nil + return nil, fmt.Errorf("invoke: %w", err) } else if len(values) != 1 { - return nil + return nil, fmt.Errorf("invoke: expected at least 1 value, got %d", len(values)) } handler = values[0].Interface() handlerType = reflect.TypeOf(handler) @@ -80,7 +82,7 @@ func (r *Router) getHttpHandler(handler any) http.Handler { value, ok := handler.(http.Handler) if !ok { - return nil + return nil, fmt.Errorf("handler is not a http.Handler") } - return value + return value, nil }