correct GetStringMap inconsistency, fixes #708

This commit is contained in:
Darrian 2022-04-27 14:46:38 +01:00
parent 81089eeca9
commit a60b38b333
2 changed files with 53 additions and 3 deletions

View file

@ -1041,21 +1041,21 @@ func (v *Viper) GetStringSlice(key string) []string {
func GetStringMap(key string) map[string]interface{} { return v.GetStringMap(key) }
func (v *Viper) GetStringMap(key string) map[string]interface{} {
return cast.ToStringMap(v.Get(key))
return cast.ToStringMap(v.allSettingsUnderParent(key))
}
// GetStringMapString returns the value associated with the key as a map of strings.
func GetStringMapString(key string) map[string]string { return v.GetStringMapString(key) }
func (v *Viper) GetStringMapString(key string) map[string]string {
return cast.ToStringMapString(v.Get(key))
return cast.ToStringMapString(v.allSettingsUnderParent(key))
}
// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.
func GetStringMapStringSlice(key string) map[string][]string { return v.GetStringMapStringSlice(key) }
func (v *Viper) GetStringMapStringSlice(key string) map[string][]string {
return cast.ToStringMapStringSlice(v.Get(key))
return cast.ToStringMapStringSlice(v.allSettingsUnderParent(key))
}
// GetSizeInBytes returns the size of the value associated with the given key
@ -2016,6 +2016,29 @@ func (v *Viper) AllSettings() map[string]interface{} {
return m
}
func (v *Viper) allSettingsUnderParent(parent string) map[string]interface{} {
m := map[string]interface{}{}
// start from the list of keys, and construct the map one value at a time
for _, k := range v.AllKeys() {
if !strings.HasPrefix(k, parent) {
continue
}
value := v.Get(k)
if value == nil {
// should not happen, since AllKeys() returns only keys holding a value,
// check just in case anything changes
continue
}
path := strings.Split(strings.TrimPrefix(k, parent+v.keyDelim), v.keyDelim)
lastKey := strings.ToLower(path[len(path)-1])
deepestMap := deepSearch(m, path[0:len(path)-1])
// set innermost value
deepestMap[lastKey] = value
}
return m
}
// SetFs sets the filesystem to use to read configuration.
func SetFs(fs afero.Fs) { v.SetFs(fs) }

View file

@ -878,6 +878,33 @@ func TestRecursiveAliases(t *testing.T) {
RegisterAlias("Roo", "baz")
}
func TestStringMapOverrides(t *testing.T) {
v := New()
v.SetDefault("foo.bar", "default-value")
v.SetDefault("foo.baz", 1)
v.SetDefault("deeper.nest.a", "default-value")
v.SetDefault("deeper.nest.b", "default-value")
v.SetEnvPrefix("test")
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
v.AutomaticEnv()
testutil.Setenv(t, "TEST_FOO_BAR", "overidden")
testutil.Setenv(t, "TEST_DEEPER_NEST_A", "overidden")
assert.Equal(t, "overidden", v.GetString("foo.bar"))
assert.Equal(t, map[string]interface{}{
"bar": "overidden",
"baz": 1,
}, v.GetStringMap("foo"))
assert.Equal(t, map[string]string{
"a": "overidden",
"b": "default-value",
}, v.GetStringMapString("deeper.nest"))
}
func TestUnmarshal(t *testing.T) {
SetDefault("port", 1313)
Set("name", "Steve")