From 9141a0a8b3d2d0c4f1563ab3fb6aac8143698635 Mon Sep 17 00:00:00 2001 From: Benoit Masson Date: Thu, 25 Aug 2016 10:12:07 +0200 Subject: [PATCH] Fix: AllSettings() includes nested values overridden by env variables This commit replaces and enhances 6457afe Fix: AllSettings() includes nested values overridden by flags The solution is now more general and AllSettings() takes into account values overridden by env variables, as well as flags, and probably others. --- viper.go | 36 ++++++++++++++++++------------------ viper_test.go | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/viper.go b/viper.go index 14088102a8..eb101569a1 100644 --- a/viper.go +++ b/viper.go @@ -1225,31 +1225,31 @@ func (v *Viper) AllKeys() []string { // Return all settings as a map[string]interface{} func AllSettings() map[string]interface{} { return v.AllSettings() } func (v *Viper) AllSettings() map[string]interface{} { - nestedFlags := make([]string, 0) m := map[string]interface{}{} for _, x := range v.AllKeys() { - if strings.Contains(x, v.keyDelim) { - nestedFlags = append(nestedFlags, x) - continue - } - m[x] = v.Get(x) + m[x] = v.deepGet(v.Get(x), x) } + return m +} - // build (or replace) inner maps, for nested flag values - for _, k := range nestedFlags { - val := v.Get(k) - path := strings.Split(k, v.keyDelim) - lastKey := path[len(path)-1] - deepestMap := deepSearch(m, path[0:len(path)-1]) - - // set innermost value - deepestMap[lastKey] = val +// deepGet recursively performs a deep scan of a map[string]interface{}, +// and updates any "final" value with the value returned by v.Get(). +// Useful to retrieve real values overridden by flags or environment variables, +// for example. +func (v *Viper) deepGet(val interface{}, fullPath string) interface{} { + m, ok := val.(map[string]interface{}) + if !ok { + // retrieve and return the real, potentially overridden, value + return v.Get(fullPath) } - - return m + for k, innerVal := range m { + // recursive scan and update + m[k] = v.deepGet(innerVal, fullPath+v.keyDelim+k) + } + return val // "final" values were changed in the process } -// Se the filesystem to use to read configuration. +// Set the filesystem to use to read configuration. func SetFs(fs afero.Fs) { v.SetFs(fs) } func (v *Viper) SetFs(fs afero.Fs) { v.fs = fs diff --git a/viper_test.go b/viper_test.go index d5d4b1b6c3..0c0c7e59bd 100644 --- a/viper_test.go +++ b/viper_test.go @@ -422,7 +422,7 @@ func TestAllKeys(t *testing.T) { ks := sort.StringSlice{"title", "newkey", "owner", "name", "beard", "ppu", "batters", "hobbies", "clothing", "age", "hacker", "id", "type", "eyes", "p_id", "p_ppu", "p_batters.batter.type", "p_type", "p_name", "foos"} dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z") - all := map[string]interface{}{"owner": map[string]interface{}{"organization": "MongoDB", "Bio": "MongoDB Chief Developer Advocate & Hacker at Large", "dob": dob}, "title": "TOML Example", "ppu": 0.55, "eyes": "brown", "clothing": map[interface{}]interface{}{"trousers": "denim", "jacket": "leather", "pants": map[interface{}]interface{}{"size": "large"}}, "id": "0001", "batters": map[string]interface{}{"batter": []interface{}{map[string]interface{}{"type": "Regular"}, map[string]interface{}{"type": "Chocolate"}, map[string]interface{}{"type": "Blueberry"}, map[string]interface{}{"type": "Devil's Food"}}}, "hacker": true, "beard": true, "hobbies": []interface{}{"skateboarding", "snowboarding", "go"}, "age": 35, "type": "donut", "newkey": "remote", "name": "Cake", "p_id": "0001", "p_ppu": "0.55", "p_name": "Cake", "p_batters": map[string]interface{}{"batter": map[string]interface{}{"type": "Regular"}}, "p_type": "donut", "foos": []map[string]interface{}{map[string]interface{}{"foo": []map[string]interface{}{map[string]interface{}{"key": 1}, map[string]interface{}{"key": 2}, map[string]interface{}{"key": 3}, map[string]interface{}{"key": 4}}}}} + all := map[string]interface{}{"owner": map[string]interface{}{"organization": "MongoDB", "Bio": "MongoDB Chief Developer Advocate & Hacker at Large", "dob": dob}, "title": "TOML Example", "ppu": 0.55, "eyes": "brown", "clothing": map[interface{}]interface{}{"trousers": "denim", "jacket": "leather", "pants": map[interface{}]interface{}{"size": "large"}}, "id": "0001", "batters": map[string]interface{}{"batter": []interface{}{map[string]interface{}{"type": "Regular"}, map[string]interface{}{"type": "Chocolate"}, map[string]interface{}{"type": "Blueberry"}, map[string]interface{}{"type": "Devil's Food"}}}, "hacker": true, "beard": true, "hobbies": []interface{}{"skateboarding", "snowboarding", "go"}, "age": 35, "type": "donut", "newkey": "remote", "name": "Cake", "p_id": "0001", "p_ppu": "0.55", "p_name": "Cake", "p_batters.batter.type": "Regular", "p_type": "donut", "foos": []map[string]interface{}{map[string]interface{}{"foo": []map[string]interface{}{map[string]interface{}{"key": 1}, map[string]interface{}{"key": 2}, map[string]interface{}{"key": 3}, map[string]interface{}{"key": 4}}}}} var allkeys sort.StringSlice allkeys = AllKeys()