mirror of
https://github.com/spf13/viper
synced 2025-05-07 20:57:18 +00:00
feat: auto bind environment variables in viper.Unmarshal
This commit is contained in:
parent
c4dcd31f68
commit
049cf065a8
2 changed files with 52 additions and 0 deletions
21
viper.go
21
viper.go
|
@ -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 {
|
||||||
|
|
|
@ -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\"}")
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue