mirror of https://github.com/spf13/viper.git
fix slice usage with env variables
This commit is contained in:
parent
568aa46942
commit
e01b6d7d0e
56
viper.go
56
viper.go
|
@ -28,6 +28,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -692,52 +693,85 @@ func (v *Viper) searchMap(source map[string]any, path []string) any {
|
||||||
// searchMapWithAliases recursively searches for slice field in source map and
|
// searchMapWithAliases recursively searches for slice field in source map and
|
||||||
// replace them with the environment variable value if it exists.
|
// replace them with the environment variable value if it exists.
|
||||||
//
|
//
|
||||||
// Returns replaced values.
|
// Returns replaced values, and a boolean if the value was found in
|
||||||
func (v *Viper) searchAndReplaceSliceValueWithEnv(source any, envKey string) any {
|
// environment varaible.
|
||||||
|
func (v *Viper) searchAndReplaceSliceValueWithEnv(source any, envKey string) (any, bool) {
|
||||||
|
|
||||||
switch sourceValue := source.(type) {
|
switch sourceValue := source.(type) {
|
||||||
case []any:
|
case []any:
|
||||||
var newSliceValues []any
|
var newSliceValues []any
|
||||||
for i, sliceValue := range sourceValue {
|
if len(sourceValue) <= 0 {
|
||||||
|
return newSliceValues, false
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var exists []bool
|
||||||
|
for i := 0; ; i++ {
|
||||||
envKey := envKey + v.keyDelim + strconv.Itoa(i)
|
envKey := envKey + v.keyDelim + strconv.Itoa(i)
|
||||||
switch existingValue := sliceValue.(type) {
|
var value any
|
||||||
|
var existDefault = true
|
||||||
|
if len(sourceValue) < i+1 {
|
||||||
|
value = sourceValue[0]
|
||||||
|
existDefault = false
|
||||||
|
} else {
|
||||||
|
value = sourceValue[i]
|
||||||
|
}
|
||||||
|
switch existingValue := value.(type) {
|
||||||
case map[string]any:
|
case map[string]any:
|
||||||
newVal := v.searchAndReplaceSliceValueWithEnv(existingValue, envKey)
|
newVal, found := v.searchAndReplaceSliceValueWithEnv(existingValue, envKey)
|
||||||
|
if !found && !existDefault {
|
||||||
|
return newSliceValues, slices.Contains(exists, true)
|
||||||
|
}
|
||||||
newSliceValues = append(newSliceValues, newVal)
|
newSliceValues = append(newSliceValues, newVal)
|
||||||
|
exists = append(exists, found || existDefault)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if newVal, ok := v.getEnv(v.mergeWithEnvPrefix(envKey)); ok {
|
if newVal, ok := v.getEnv(v.mergeWithEnvPrefix(envKey)); ok {
|
||||||
newSliceValues = append(newSliceValues, newVal)
|
newSliceValues = append(newSliceValues, newVal)
|
||||||
|
exists = append(exists, true)
|
||||||
} else {
|
} else {
|
||||||
|
exists = append(exists, false || existDefault)
|
||||||
|
if existDefault {
|
||||||
newSliceValues = append(newSliceValues, existingValue)
|
newSliceValues = append(newSliceValues, existingValue)
|
||||||
|
} else {
|
||||||
|
return newSliceValues, slices.Contains(exists, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return newSliceValues
|
}
|
||||||
|
return newSliceValues, slices.Contains(exists, true)
|
||||||
|
|
||||||
case map[string]any:
|
case map[string]any:
|
||||||
var newMapValues map[string]any = make(map[string]any)
|
var newMapValues map[string]any = make(map[string]any)
|
||||||
|
var exists []bool
|
||||||
for key, mapValue := range sourceValue {
|
for key, mapValue := range sourceValue {
|
||||||
envKey := envKey + v.keyDelim + key
|
envKey := envKey + v.keyDelim + key
|
||||||
switch existingValue := mapValue.(type) {
|
switch existingValue := mapValue.(type) {
|
||||||
case map[string]any:
|
case map[string]any:
|
||||||
newVal := v.searchAndReplaceSliceValueWithEnv(existingValue, envKey)
|
newVal, found := v.searchAndReplaceSliceValueWithEnv(existingValue, envKey)
|
||||||
|
if !found {
|
||||||
|
return newMapValues, false
|
||||||
|
}
|
||||||
newMapValues[key] = newVal
|
newMapValues[key] = newVal
|
||||||
|
exists = append(exists, found)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if newVal, ok := v.getEnv(v.mergeWithEnvPrefix(envKey)); ok {
|
if newVal, ok := v.getEnv(v.mergeWithEnvPrefix(envKey)); ok {
|
||||||
newMapValues[key] = newVal
|
newMapValues[key] = newVal
|
||||||
|
exists = append(exists, true)
|
||||||
} else {
|
} else {
|
||||||
|
exists = append(exists, false)
|
||||||
newMapValues[key] = existingValue
|
newMapValues[key] = existingValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return newMapValues
|
return newMapValues, slices.Contains(exists, true)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if newVal, ok := v.getEnv(v.mergeWithEnvPrefix(envKey)); ok {
|
if newVal, ok := v.getEnv(v.mergeWithEnvPrefix(envKey)); ok {
|
||||||
return newVal
|
return newVal, true
|
||||||
} else {
|
} else {
|
||||||
return source
|
return source, false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -961,7 +995,7 @@ func (v *Viper) Get(key string) any {
|
||||||
|
|
||||||
// Check for Env override again, to handle slices
|
// Check for Env override again, to handle slices
|
||||||
if v.automaticEnvApplied {
|
if v.automaticEnvApplied {
|
||||||
val = v.searchAndReplaceSliceValueWithEnv(val, lcaseKey)
|
val, _ = v.searchAndReplaceSliceValueWithEnv(val, lcaseKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.typeByDefValue {
|
if v.typeByDefValue {
|
||||||
|
|
|
@ -2668,6 +2668,7 @@ func TestSliceIndexAutomaticEnv(t *testing.T) {
|
||||||
t.Setenv("MODES_2", "300")
|
t.Setenv("MODES_2", "300")
|
||||||
t.Setenv("CLIENTS_1_NAME", "baz")
|
t.Setenv("CLIENTS_1_NAME", "baz")
|
||||||
t.Setenv("PROXY_CLIENTS_0_NAME", "ProxyFoo")
|
t.Setenv("PROXY_CLIENTS_0_NAME", "ProxyFoo")
|
||||||
|
t.Setenv("PROXY_CLIENTS_3_NAME", "ProxyNew")
|
||||||
|
|
||||||
SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||||
AutomaticEnv()
|
AutomaticEnv()
|
||||||
|
@ -2683,6 +2684,7 @@ func TestSliceIndexAutomaticEnv(t *testing.T) {
|
||||||
assert.Equal(t, "foo", config.Clients[0].Name)
|
assert.Equal(t, "foo", config.Clients[0].Name)
|
||||||
assert.Equal(t, "baz", config.Clients[1].Name)
|
assert.Equal(t, "baz", config.Clients[1].Name)
|
||||||
assert.Equal(t, "ProxyFoo", config.Proxy.Clients[0].Name)
|
assert.Equal(t, "ProxyFoo", config.Proxy.Clients[0].Name)
|
||||||
|
assert.Equal(t, "ProxyNew", config.Proxy.Clients[3].Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsPathShadowedInFlatMap(t *testing.T) {
|
func TestIsPathShadowedInFlatMap(t *testing.T) {
|
||||||
|
|
Loading…
Reference in New Issue