Hi,
Can anyone help, I think I am approaching this the wrong way :-)
I am sure this has been addressed but couldn't find anything in the issues.
I have a Build function that goes ahead and constructs all my constructures (New..) - most constructors receive a "config".
My issue is that the config is populated by Viper which is built via my Cobra implementation. (called from Cobra's OnInitialize function)
So in the constructors the config is actually empty so I can't use it, I have used an approach which I don't think is the best, I have put a "Initialize()" function in places where I need to ensure that things are constructed correctly - but of course, I have to call this manually.
Is there a way or some kind of order that can construct just the Cobra / Viper stuff first and then continue once I have this loaded? So the rest of my constructors receive the "config" populated.
It is a little difficult to explain in words - so I have placed some snippets below that I think help to illustrate it better.
Here is my build function.
func (d *DI) Build(funcs ...interface{}) *fx.App {
app := fx.New(
provider.ProvideConfiguration(),
provider.ProvideLogger(),
provider.ProvideHandler(),
provider.ProvideController(),
provider.ProvideRepository(),
provider.ProvideGateway(),
provider.ProvideCollector(),
provider.ProvidePackage(),
// d.provideContext(),
d.provideVersion(),
fx.Invoke(funcs...),
fx.WithLogger(
func() fxevent.Logger {
return fxevent.NopLogger
},
),
)
return app
}
This is constructed like so and actually calls into Cobra (execute)
app := d.Build(func(lifecycle fx.Lifecycle, command *command.Command) {
lifecycle.Append(
fx.Hook{
OnStart: func(ctx context.Context) error {
// Execute command
command.Execute()
return nil
},
})
})
And the thing that provides the configuration (There are 2 provides here, 1 is the COnfiguration which takes care of building "config")
import (
// more imports..
"go.uber.org/fx"
)
func ProvideConfiguration() fx.Option {
return fx.Options(
fx.Provide(
configuration.NewConfiguration,
),
fx.Provide(func(configuration *configuration.Configuration) *config.Config {
return &configuration.Config
}),
)
}
I do have access to a method, that I do call manually anyway to bootstrap the config, this is in my "configuration" file. I will just paste some of it here to illustrate it.
func (c *Configuration) InitConfig(cfgFile string, dataDir string) error {
if cfgFile != "" {
// Return an error if the config file being passed could not be found
if _, err := os.Stat(cfgFile); errors.Is(err, os.ErrNotExist) {
return ErrConfigFileNotFound
}
// Return an error if the wrong config extension is being passed
if ext := filepath.Ext(cfgFile); ext != ".yaml" && ext != ".yml" {
return ErrConfigTypeUnsupported
}
viper.SetConfigFile(cfgFile)
} else {
var (
configPath = c.DefaultConfigDirectory()
configName = "config"
configExtension = "yaml"
)
configFullPath := path.Join(configPath, fmt.Sprintf("%s.%s", configName, configExtension)
I basically call this an event I get from Cobra
cobra.OnInitialize(func() {
err := c.configuration.InitConfig(cfgFile, dataDir)
if err != nil {
log.Fatalf("could not get current directory, err: %v", err)
}
c.logger.Initialize()
configMask, _ := masker.Struct(c.config)
log.Debugf("configuration loaded, config: %+v", configMask)
})