Skip to content
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

dig: use our dig fork #2

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
9 changes: 9 additions & 0 deletions _tools/go_mod_tidy_all.sh
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions exemple/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions exemple/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
2 changes: 1 addition & 1 deletion exemple/internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down
5 changes: 5 additions & 0 deletions exemple/internal/app/handler/handle_home.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package handler

import (
"errors"
"fmt"
"github.com/caumette-co/x/xweb"
"net/http"
Expand Down Expand Up @@ -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)

Expand Down
2 changes: 2 additions & 0 deletions makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go-mod-tidy-all:
sh ./_tools/go_mod_tidy_all.sh
67 changes: 25 additions & 42 deletions xfoundation/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ package xfoundation

import (
"context"
"fmt"
"github.com/expectedsh/dig"
"github.com/samber/lo"
"go.uber.org/zap"
"os"
"os/signal"
Expand All @@ -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
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add type alias string for AppEnv (for convenience)

)

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())
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use of lo library to just not create code not tested

} 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)))
Expand Down Expand Up @@ -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 {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add utility func to pass single value as function for Provide

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
}
18 changes: 16 additions & 2 deletions xfoundation/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment on lines +16 to +23
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our dig is temporary but if the merge request is declined, we can handle that.

20 changes: 18 additions & 2 deletions xfoundation/go.sum
Original file line number Diff line number Diff line change
@@ -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=
1 change: 1 addition & 0 deletions xweb/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions xweb/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
10 changes: 8 additions & 2 deletions xweb/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package xweb

import (
"context"
"fmt"
"github.com/caumette-co/x/xfoundation"
"go.uber.org/zap"
"net"
Expand All @@ -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 {
Expand Down
18 changes: 10 additions & 8 deletions xweb/router.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package xweb

import (
"fmt"
"github.com/caumette-co/x/xfoundation"
"github.com/go-chi/chi"
"go.uber.org/zap"
Expand All @@ -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()),
Expand Down Expand Up @@ -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) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add error return type here

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)
Expand All @@ -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
}