-
Notifications
You must be signed in to change notification settings - Fork 291
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
[Feature Request] OverrideSupply and OverrideProvide #825
Comments
Hey there! Apologies for the delay in responding. This has indeed been a long-standing feature request in this or other forms. There's also some discussion #653. We've been unable to reach a satisfactory design for a while, especially because there's risk of random modules stomping on your injected entities. For example, consider a random package redisfx
var Module = fx.Options(
fx.Provide(New),
)
func New(log *zap.Logger) *redis.Client {
log.Info("hello from redis")
// ...
} You include it in your application like so: func main() {
fx.New(
zapfx.Module,
redisfx.Module,
// ...
)
} Now if at some point, redisfx decides to do this: var Module = fx.Options(
fx.Provide(New),
fx.OverrideSupply(zap.NewNop()),
) Your logger is suddenly overridden without your knowledge or permission. var Module = fx.Options(
fx.Provide(New),
fx.SomeFunction(func(original *zap.Logger) *zap.Logger {
return original.With(zap.String("component", "redis"))
}),
) The proposal above would not cover these cases. However, all's not lost! We've reached a design for this that we're happy with The approach does go deeper into |
Thank you for your response! I am really in favor of augmenting the feature and can think of multiple cases where I would use it.
Yay 🎉. Like your approach and looking forward to it! |
This adds `fx.Decorate`, which lets you specify decorators to an fx app. A decorator can take in one or more dependencies that have already been `Provide`d to the app, and produce one or more values that will be used as replacements in the object graph. For example, suppose there is a simple app like this: ```go fx.New( fx.Provide(func() *Logger { return &Logger{Name: "logger"} }), fx.Invoke(func(l *Logger) { fmt.Println(l.Name) }), ) ``` Running this app will print "logger" on the console. Now let us suppose a decorator was provided: ```go fx.New( fx.Provide(...), // Provide same function as above fx.Decorate(func(l *Logger) *Logger { return &Logger{Name: "decorated " + l.Name} }), fx.Invoke(...), // Invoke same function as above ) ``` The decorator here will take in the provided Logger and replace it with another logger whose `Name` is `decorated logger`. The `Invoke`d function is then executed with this replacement value, so running this app will print "decorated logger" on the console. In terms of implementation, a decorator is represented by the target decorator function and the call stack it was provided from, similar to a provider. `module` contains a list of decorators that were specified within its scope. The dig dependency had to be updated to the latest master branch of Dig to ensure the fix for uber-go/dig#316 is in. Following this PR, there are two additional pieces I will be adding: 1. An eventing system for fx.Decorate. 2. fx.Replace, which takes in a value instead of a function to replace a value in the object graph. This is similar to what fx.Supply is to fx.Provide. This PR along with the two PRs above should make the long-awaited feature of graph modifications in fx finally possible. --- Refs #653, #649, #825, uber-go/dig#230, GO-1203, GO-736
@Wondertan @yiminc Fx version 1.17.0 has been released with Closing this issue for now, but let me know if you have any questions regarding these. I know you had some questions/feedback regarding the way this works @yiminc but I'll respond in the corresponding thread. |
What
Currently, the FX errors if you Provide/Supply for an unnamed/ungrouped type multiple times. However, there are use cases where overriding an existing Provide or Supply becomes useful, like in testing(#649). Also, this becomes useful for big applications with a liberal API allowing their users to override the default setup of modules. That is, the API devs prepare the default set of modules, while a user can swap out a module of their choice through overriding.
Why
Essentially, the feature would allow APIs like:
Currently, users have to decouple Modules, understand what needs to drop out, and pass their custom one. With this feature, users would add desired things on top.
How
API
Implementation ideas
A naive idea would be to collect all the overrides and exclude Provides and Supplies with conflicting types. However, the problem arises when provided ctor returns multiple types, and overriding happens for only one of them, as the whole ctor is excluded. The issue might be ok for some cases and easily detected, thus errored. The good thing is that this can be implemented beneath FX only, without digging into
dig
. Also, this should be fine enough for testing needs in possible to use fx to inject mocks? #649.A more thorough approach without the issue of the approach above would go dipper into
dig
and somehow be part of graph construction. Unfortunately, I am not that familiar withdig
implementation to understand the complexity and feasibility, so kindly ask you to help. Ideally, the implementation should still execute a conflicting ctor but exclude an overridden type from the dependency graph.Happy to submit a patch once there is a decision on how to proceed.
The text was updated successfully, but these errors were encountered: