diff --git a/decorate.go b/decorate.go index 1e863d391..a241a3cbb 100644 --- a/decorate.go +++ b/decorate.go @@ -29,41 +29,82 @@ import ( ) // Decorate specifies one or more decorator functions to an Fx application. -// Decorator functions let users augment objects in the graph. They can take in -// zero or more dependencies that must be provided to the application with fx.Provide, -// and produce one or more values that can be used by other invoked values. // -// An example decorator is the following function which accepts a value, augments that value, -// and returns the replacement value. +// Decorator functions +// +// Decorator functions let users augment objects in the graph. +// They can take in zero or more dependencies that must be provided to the +// application with fx.Provide, and produce one or more values that can be used +// by other fx.Provide and fx.Invoke calls. // // fx.Decorate(func(log *zap.Logger) *zap.Logger { // return log.Named("myapp") // }) +// fx.Invoke(func(log *zap.Logger) { +// log.Info("hello") +// // Output: +// // {"level": "info","logger":"myapp","msg":"hello"} +// }) // -// The following decorator accepts multiple dependencies from the graph, augments and returns -// one of them. +// The following decorator accepts multiple dependencies from the graph, +// augments and returns one of them. // // fx.Decorate(func(log *zap.Logger, cfg *Config) *zap.Logger { // return log.Named(cfg.Name) // }) // -// Similar to fx.Provide, functions passed to fx.Decorate may optionally return an error -// as their last result. If a decorator returns a non-nil error, it will halt application startup. +// Similar to fx.Provide, functions passed to fx.Decorate may optionally return +// an error as their last result. +// If a decorator returns a non-nil error, it will halt application startup. +// +// fx.Decorate(func(conn *sql.DB, cfg *Config) (*sql.DB, error) { +// if err := conn.Ping(); err != nil { +// return sql.Open("driver-name", cfg.FallbackDB) +// } +// return conn, nil +// }) +// +// Decorators support both, fx.In and fx.Out structs, similar to fx.Provide and +// fx.Invoke. +// +// type Params struct { +// fx.In +// +// Client usersvc.Client `name:"readOnly"` +// } // -// All modifications in the object graph due to a decorator are scoped to the fx.Module it was -// specified from. Decorations specified in the top-level fx.New call apply across the application. +// type Result struct { +// fx.Out +// +// Client usersvc.Client `name:"readOnly"` +// } // -// Decorators can be annotated using fx.Annotate, but not with fx.Annotated. Refer to documentation -// on fx.Annotate() to learn how to use it for annotating functions. +// fx.Decorate(func(p Params) Result { +// ... +// }) // -// Decorators support fx.In and fx.Out structs, similar to fx.Provide and fx.Invoke. +// Decorators can be annotated with the fx.Annotate function, but not with the +// fx.Annotated type. Refer to documentation on fx.Annotate() to learn how to +// use it for annotating functions. +// +// fx.Decorate( +// fx.Annotate( +// func(client usersvc.Client) usersvc.Client { +// // ... +// }, +// fx.ParamTags(`name:"readOnly"`), +// fx.ResultTags(`name:"readOnly"`), +// ), +// ) // -// Decorators support value groups as well. For example, the following code shows a decorator -// which takes in a value group using fx.In struct, and returns another value group. +// Decorators support augmenting, filtering, or replacing value groups. +// To decorate a value group, expect the entire value group slice and produce +// the new slice. // // type HandlerParam struct { // fx.In // +// Log *zap.Logger // Handlers []Handler `group:"server" // } // @@ -73,11 +114,60 @@ import ( // Handlers []Handler `group:"server" // } // +// fx.Decorate(func(p HandlerParam) HandlerResult { +// var r HandlerResult +// for _, handler := range p.Handlers { +// r.Handlers = append(r.Handlers, wrapWithLogger(p.Log, handler)) +// } +// return r +// }), +// +// Decorator scope +// +// Modifications made to the Fx graph with fx.Decorate are scoped to the +// deepest fx.Module inside which the decorator was specified. +// +// fx.Module("mymodule", +// fx.Decorate(func(log *zap.Logger) *zap.Logger { +// return log.Named("myapp") +// }), +// fx.Invoke(func(log *zap.Logger) { +// log.Info("decorated logger") +// // Output: +// // {"level": "info","logger":"myapp","msg":"decorated logger"} +// }), +// ), +// fx.Invoke(func(log *zap.Logger) { +// log.Info("plain logger") +// // Output: +// // {"level": "info","msg":"plain logger"} +// }), +// +// Decorations specified in the top-level fx.New call apply across the +// application and chain with module-specific decorators. +// // fx.New( // // ... -// fx.Decorate(func(p HandlerParam) HandlerResult { -// // ... +// fx.Decorate(func(log *zap.Logger) *zap.Logger { +// return log.With(zap.Field("service", "myservice")) // }), +// // ... +// fx.Invoke(func(log *zap.Logger) { +// log.Info("outer decorator") +// // Output: +// // {"level": "info","service":"myservice","msg":"outer decorator"} +// }), +// // ... +// fx.Module("mymodule", +// fx.Decorate(func(log *zap.Logger) *zap.Logger { +// return log.Named("myapp") +// }), +// fx.Invoke(func(log *zap.Logger) { +// log.Info("inner decorator") +// // Output: +// // {"level": "info","logger":"myapp","service":"myservice","msg":"inner decorator"} +// }), +// ), // ) func Decorate(decorators ...interface{}) Option { return decorateOption{