mirror of
https://github.com/spf13/viper
synced 2025-05-06 04:07:17 +00:00
Merge pull request #21 from DataDog/albertvaka/viper-fixes-subset
Make AllKeys and AllSettings return set-but-empty keys Add Unset method
This commit is contained in:
commit
86d951dc13
2 changed files with 69 additions and 11 deletions
44
viper.go
44
viper.go
|
@ -339,6 +339,16 @@ func (v *Viper) getEnv(key string) (string, bool) {
|
||||||
return val, ok && (v.allowEmptyEnv || val != "")
|
return val, ok && (v.allowEmptyEnv || val != "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Viper) getAllEnvBoundAndSet() map[string]interface{} {
|
||||||
|
tgt := map[string]interface{}{}
|
||||||
|
for key, value := range v.env {
|
||||||
|
if _, ok := v.getEnv(v.mergeWithEnvPrefix(key)); ok {
|
||||||
|
tgt[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tgt
|
||||||
|
}
|
||||||
|
|
||||||
// ConfigFileUsed returns the file used to populate the config registry.
|
// ConfigFileUsed returns the file used to populate the config registry.
|
||||||
func ConfigFileUsed() string { return v.ConfigFileUsed() }
|
func ConfigFileUsed() string { return v.ConfigFileUsed() }
|
||||||
func (v *Viper) ConfigFileUsed() string { return v.configFile }
|
func (v *Viper) ConfigFileUsed() string { return v.configFile }
|
||||||
|
@ -1259,6 +1269,22 @@ func (v *Viper) Set(key string, value interface{}) {
|
||||||
deepestMap[lastKey] = value
|
deepestMap[lastKey] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unset removes a value set with Set
|
||||||
|
// Unset is case-insensitive for a key.
|
||||||
|
// Values which come from flags, config file, ENV or default can't be unset.
|
||||||
|
func Unset(key string) { v.Unset(key) }
|
||||||
|
func (v *Viper) Unset(key string) {
|
||||||
|
// If alias passed in, then set the proper override
|
||||||
|
key = v.realKey(strings.ToLower(key))
|
||||||
|
|
||||||
|
path := strings.Split(key, v.keyDelim)
|
||||||
|
lastKey := strings.ToLower(path[len(path)-1])
|
||||||
|
deepestMap := deepSearch(v.override, path[0:len(path)-1])
|
||||||
|
|
||||||
|
// unset innermost value
|
||||||
|
delete(deepestMap, lastKey)
|
||||||
|
}
|
||||||
|
|
||||||
// ReadInConfig will discover and load the configuration file from disk
|
// ReadInConfig will discover and load the configuration file from disk
|
||||||
// and key/value stores, searching in one of the defined paths.
|
// and key/value stores, searching in one of the defined paths.
|
||||||
func ReadInConfig() error { return v.ReadInConfig() }
|
func ReadInConfig() error { return v.ReadInConfig() }
|
||||||
|
@ -1571,14 +1597,6 @@ func castToMapStringInterface(
|
||||||
return tgt
|
return tgt
|
||||||
}
|
}
|
||||||
|
|
||||||
func castMapStringSliceToMapInterface(src map[string][]string) map[string]interface{} {
|
|
||||||
tgt := map[string]interface{}{}
|
|
||||||
for k, v := range src {
|
|
||||||
tgt[k] = v
|
|
||||||
}
|
|
||||||
return tgt
|
|
||||||
}
|
|
||||||
|
|
||||||
func castMapStringToMapInterface(src map[string]string) map[string]interface{} {
|
func castMapStringToMapInterface(src map[string]string) map[string]interface{} {
|
||||||
tgt := map[string]interface{}{}
|
tgt := map[string]interface{}{}
|
||||||
for k, v := range src {
|
for k, v := range src {
|
||||||
|
@ -1745,7 +1763,7 @@ func (v *Viper) AllKeys() []string {
|
||||||
m = v.flattenAndMergeMap(m, castMapStringToMapInterface(v.aliases), "")
|
m = v.flattenAndMergeMap(m, castMapStringToMapInterface(v.aliases), "")
|
||||||
m = v.flattenAndMergeMap(m, v.override, "")
|
m = v.flattenAndMergeMap(m, v.override, "")
|
||||||
m = v.mergeFlatMap(m, castMapFlagToMapInterface(v.pflags))
|
m = v.mergeFlatMap(m, castMapFlagToMapInterface(v.pflags))
|
||||||
m = v.mergeFlatMap(m, castMapStringSliceToMapInterface(v.env))
|
m = v.mergeFlatMap(m, v.getAllEnvBoundAndSet())
|
||||||
m = v.flattenAndMergeMap(m, v.config, "")
|
m = v.flattenAndMergeMap(m, v.config, "")
|
||||||
m = v.flattenAndMergeMap(m, v.kvstore, "")
|
m = v.flattenAndMergeMap(m, v.kvstore, "")
|
||||||
m = v.flattenAndMergeMap(m, v.defaults, "")
|
m = v.flattenAndMergeMap(m, v.defaults, "")
|
||||||
|
@ -1789,6 +1807,10 @@ func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interfac
|
||||||
shadow[strings.ToLower(fullKey)] = true
|
shadow[strings.ToLower(fullKey)] = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if len(m2) == 0 {
|
||||||
|
// empty dict explicitly present in the map
|
||||||
|
shadow[strings.ToLower(fullKey)] = true
|
||||||
|
}
|
||||||
// recursively merge to shadow map
|
// recursively merge to shadow map
|
||||||
shadow = v.flattenAndMergeMap(shadow, m2, fullKey)
|
shadow = v.flattenAndMergeMap(shadow, m2, fullKey)
|
||||||
}
|
}
|
||||||
|
@ -1825,8 +1847,8 @@ func (v *Viper) AllSettings() map[string]interface{} {
|
||||||
for _, k := range v.AllKeys() {
|
for _, k := range v.AllKeys() {
|
||||||
value := v.Get(k)
|
value := v.Get(k)
|
||||||
if value == nil {
|
if value == nil {
|
||||||
// should not happen, since AllKeys() returns only keys holding a value,
|
// Key set but empty, include it in the output as a null value
|
||||||
// check just in case anything changes
|
m[k] = nil
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1818,3 +1818,39 @@ func TestKnownKeys(t *testing.T) {
|
||||||
t.Error("SetKnown didn't mark key as known")
|
t.Error("SetKnown didn't mark key as known")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEmptySection(t *testing.T) {
|
||||||
|
|
||||||
|
var yamlWithEnvVars = `
|
||||||
|
key:
|
||||||
|
subkey:
|
||||||
|
another_key:
|
||||||
|
empty_dict: {}
|
||||||
|
`
|
||||||
|
|
||||||
|
v := New()
|
||||||
|
initConfig(v, "yaml", yamlWithEnvVars)
|
||||||
|
v.SetKnown("is_known")
|
||||||
|
v.SetDefault("has_default", true)
|
||||||
|
v.BindEnv("is_bound")
|
||||||
|
|
||||||
|
// AllKeys includes empty keys
|
||||||
|
keys := v.AllKeys()
|
||||||
|
assert.NotContains(t, keys, "key") // Only empty leaf nodes are returned
|
||||||
|
assert.Contains(t, keys, "key.subkey")
|
||||||
|
assert.Contains(t, keys, "another_key")
|
||||||
|
assert.Contains(t, keys, "empty_dict")
|
||||||
|
assert.NotContains(t, keys, "is_known")
|
||||||
|
assert.Contains(t, keys, "has_default")
|
||||||
|
assert.NotContains(t, keys, "is_bound")
|
||||||
|
|
||||||
|
// AllSettings includes empty keys
|
||||||
|
vars := v.AllSettings()
|
||||||
|
assert.NotContains(t, vars, "key") // Only empty leaf nodes are returned
|
||||||
|
assert.Contains(t, vars, "key.subkey")
|
||||||
|
assert.Contains(t, vars, "another_key")
|
||||||
|
assert.Contains(t, vars, "empty_dict")
|
||||||
|
assert.NotContains(t, vars, "is_known")
|
||||||
|
assert.Contains(t, vars, "has_default")
|
||||||
|
assert.NotContains(t, vars, "is_bound")
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue