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

Handling optional fields in fx.Decorate #1093

Open
mehdihadeli opened this issue Jun 11, 2023 · 3 comments
Open

Handling optional fields in fx.Decorate #1093

mehdihadeli opened this issue Jun 11, 2023 · 3 comments

Comments

@mehdihadeli
Copy link

mehdihadeli commented Jun 11, 2023

Hi,
Is there any way for handling optional input parameter in fx.decorate method?

Actually, I'm looking for a way if, in one of my sub module s, one dependency like *Test not provided, I can instantiate it; otherwise, I want to return an existing register object.

fx.New(
fx.Decorate(func(l TestParam) *Test {
	if l.TestData == nil {
		return &Test{Name: "Test " + l.TestData.Name}
	}
	return l.TestData
},
),
fx.Invoke(func(t *Test) {
	fmt.Print(t.Name)
}),
).Run()

type TestParam struct {
	fx.In

	TestData *Test `optional:"true"`
}

Error:

missing type:
        - *main.Test (did you mean to Provide it?)
@sywhang
Copy link
Contributor

sywhang commented Jun 11, 2023

Hi @mehdihadeli , thanks for submitting an issue.

Decorators actually allow optional dependencies with optional tag.

The issue in your code is because your Invoke needs a Test type that isn't provided any provider yet. A decorator cannot be a provider at the same time. You need to specify a provider that knows how to instantiate a Test type into the object graph.

@mehdihadeli
Copy link
Author

mehdihadeli commented Jun 11, 2023

Hi @sywhang, thanks for your quick response.

I'm looking for a way that if one struct like Test is not registered in the outer modules, my inner module can register this dependency itself. Maybe this is not possible with decorator. Is there any solution here?
For example, our inner module needed a logger, and I want the inner module to check if it is not provided and then register it.

@JacobOaks
Copy link
Contributor

@mehdihadeli Might be a better way to do this, but off the top of my head, you can accomplish this by using a name tag for the final logger you want to use. Something like:

type LoggerParams struct {
	fx.In

	Logger *zap.Logger `optional:"true"`
}

type TestParams struct {
	fx.In

	Logger *zap.Logger `name:"finallogger"`
}

func main() {
	fx.New(
		fx.Provide(
			fx.Annotate(
				func(p LoggerParams) (*zap.Logger, error) {
					if p.Logger != nil {
						return p.Logger, nil
					}
					return zap.NewDevelopment()
				},
				fx.ResultTags(`name:"finallogger"`),
			),
		),
		fx.Invoke(func(p TestParams) {
			p.Logger.Info("log message")
		}),
	)
}

Then if a logger is already provided it will be passed along as finallogger, otherwise a new one will be created. In this simple case you can also just remove the XParams structs and just use fx.Annotate and fx.ParamTags/fx.ResultTags, but this is assuming you have other params for the functions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

3 participants