feat: auto bind environment variables in viper.Unmarshal

This commit is contained in:
Matheus Nogueira 2023-12-04 12:40:21 -03:00
parent c4dcd31f68
commit 049cf065a8
2 changed files with 52 additions and 0 deletions

View file

@ -1111,9 +1111,30 @@ func Unmarshal(rawVal any, opts ...DecoderConfigOption) error {
} }
func (v *Viper) Unmarshal(rawVal any, opts ...DecoderConfigOption) error { func (v *Viper) Unmarshal(rawVal any, opts ...DecoderConfigOption) error {
err := v.autoBindEnvs(rawVal)
if err != nil {
return fmt.Errorf("could not auto bind environment variables: %w", err)
}
return decode(v.AllSettings(), defaultDecoderConfig(rawVal, opts...)) return decode(v.AllSettings(), defaultDecoderConfig(rawVal, opts...))
} }
func (v *Viper) autoBindEnvs(rawVal any) error {
envKeys := map[string]any{}
if err := mapstructure.Decode(rawVal, &envKeys); err != nil {
return fmt.Errorf("could not decode mapstructure: %w", err)
}
structKeys := v.flattenAndMergeMap(map[string]bool{}, envKeys, "")
for key, _ := range structKeys {
if err := v.BindEnv(key); err != nil {
return fmt.Errorf(`could not bind env "%s": %w`, key, err)
}
}
return nil
}
// defaultDecoderConfig returns default mapstructure.DecoderConfig with support // defaultDecoderConfig returns default mapstructure.DecoderConfig with support
// of time.Duration values & string slices. // of time.Duration values & string slices.
func defaultDecoderConfig(output any, opts ...DecoderConfigOption) *mapstructure.DecoderConfig { func defaultDecoderConfig(output any, opts ...DecoderConfigOption) *mapstructure.DecoderConfig {

View file

@ -914,6 +914,37 @@ func TestUnmarshal(t *testing.T) {
) )
} }
func TestUnmarshalWithEnvs(t *testing.T) {
type config struct {
Port int
Host string
Nested struct {
Value string
AnotherValue string
RenamedValue string `mapstructure:"another"`
}
}
t.Setenv("CONFIG_PORT", "8080")
t.Setenv("CONFIG_HOST", "http://localhost")
t.Setenv("CONFIG_NESTED_VALUE", "baz")
t.Setenv("CONFIG_NESTED_ANOTHER", "value")
v := New()
v.SetEnvPrefix("CONFIG")
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
configObject := config{}
err := v.Unmarshal(&configObject)
require.NoError(t, err)
assert.Equal(t, 8080, configObject.Port)
assert.Equal(t, "http://localhost", configObject.Host)
assert.Equal(t, "baz", configObject.Nested.Value)
assert.Empty(t, configObject.Nested.AnotherValue)
assert.Equal(t, "value", configObject.Nested.RenamedValue)
}
func TestUnmarshalWithDecoderOptions(t *testing.T) { func TestUnmarshalWithDecoderOptions(t *testing.T) {
Set("credentials", "{\"foo\":\"bar\"}") Set("credentials", "{\"foo\":\"bar\"}")