From a73303ee8947b4498218d22e361db6f448ce6654 Mon Sep 17 00:00:00 2001 From: Mark Sagi-Kazar Date: Wed, 6 Nov 2019 12:40:41 +0100 Subject: [PATCH] Add key delimiter setter --- viper.go | 19 ++++++++++---- viper_test.go | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 5 deletions(-) diff --git a/viper.go b/viper.go index a944cde..0e3eeaf 100644 --- a/viper.go +++ b/viper.go @@ -34,14 +34,14 @@ import ( "sync" "time" - yaml "gopkg.in/yaml.v2" + "gopkg.in/yaml.v2" "github.com/fsnotify/fsnotify" "github.com/hashicorp/hcl" "github.com/hashicorp/hcl/hcl/printer" "github.com/magiconair/properties" "github.com/mitchellh/mapstructure" - toml "github.com/pelletier/go-toml" + "github.com/pelletier/go-toml" "github.com/spf13/afero" "github.com/spf13/cast" jww "github.com/spf13/jwalterweatherman" @@ -245,6 +245,15 @@ func Reset() { SupportedRemoteProviders = []string{"etcd", "consul"} } +// SetKeyDelimiter sets the delimiter used for determining key parts. +// By default it's value is ".". +func SetKeyDelimiter(keyDelim string) { v.SetKeyDelimiter(keyDelim) } +func (v *Viper) SetKeyDelimiter(keyDelim string) { + if keyDelim != "" { + v.keyDelim = keyDelim + } +} + type defaultRemoteProvider struct { provider string endpoint string @@ -1720,7 +1729,7 @@ func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]interface{} func (v *Viper) watchKeyValueConfigOnChannel() error { for _, rp := range v.remoteProviders { respc, _ := RemoteConfig.WatchChannel(rp) - //Todo: Add quit channel + // Todo: Add quit channel go func(rc <-chan *RemoteResponse) { for { b := <-rc @@ -1756,7 +1765,7 @@ func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]interface } // AllKeys returns all keys holding a value, regardless of where they are set. -// Nested keys are returned with a v.keyDelim (= ".") separator +// Nested keys are returned with a v.keyDelim separator func AllKeys() []string { return v.AllKeys() } func (v *Viper) AllKeys() []string { m := map[string]bool{} @@ -1779,7 +1788,7 @@ func (v *Viper) AllKeys() []string { // flattenAndMergeMap recursively flattens the given map into a map[string]bool // of key paths (used as a set, easier to manipulate than a []string): -// - each path is merged into a single key string, delimited with v.keyDelim (= ".") +// - each path is merged into a single key string, delimited with v.keyDelim // - if a path is shadowed by an earlier value in the initial shadow map, // it is skipped. // The resulting set of paths is merged to the given shadow set at the same time. diff --git a/viper_test.go b/viper_test.go index c1cf557..baf0998 100644 --- a/viper_test.go +++ b/viper_test.go @@ -2009,6 +2009,74 @@ func TestUnmarshal_DotSeparatorBackwardCompatibility(t *testing.T) { assert.Equal(t, "cobra_flag", config.Foo.Bar) } +var yamlExampleWithDot = []byte(`Hacker: true +name: steve +hobbies: + - skateboarding + - snowboarding + - go +clothing: + jacket: leather + trousers: denim + pants: + size: large +age: 35 +eyes : brown +beard: true +emails: + steve@hacker.com: + created: 01/02/03 + active: true +`) + +func TestSetKeyDelimiter(t *testing.T) { + v := New() + v.SetKeyDelimiter("::") + v.SetConfigType("yaml") + r := strings.NewReader(string(yamlExampleWithDot)) + + err := v.unmarshalReader(r, v.config) + require.NoError(t, err) + + values := map[string]interface{}{ + "image": map[string]interface{}{ + "repository": "someImage", + "tag": "1.0.0", + }, + "ingress": map[string]interface{}{ + "annotations": map[string]interface{}{ + "traefik.frontend.rule.type": "PathPrefix", + "traefik.ingress.kubernetes.io/ssl-redirect": "true", + }, + }, + } + + v.SetDefault("charts::values", values) + + assert.Equal(t, "leather", v.GetString("clothing::jacket")) + assert.Equal(t, "01/02/03", v.GetString("emails::steve@hacker.com::created")) + + type config struct { + Charts struct { + Values map[string]interface{} + } + } + + expected := config{ + Charts: struct { + Values map[string]interface{} + }{ + Values: values, + }, + } + + var actual config + + assert.NoError(t, v.Unmarshal(&actual)) + + assert.Equal(t, expected, actual) +} + func BenchmarkGetBool(b *testing.B) { key := "BenchmarkGetBool" v = New()