Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions viper.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ func (fn optionFunc) apply(v *Viper) {
// By default it's value is ".".
func KeyDelimiter(d string) Option {
return optionFunc(func(v *Viper) {
v.keyDelim = d
v.keyDelim = strings.ToLower(d)
})
}

Expand Down Expand Up @@ -486,7 +486,6 @@ func (v *Viper) searchIndexableWithPathPrefixes(source any, path []string) any {
// search for path prefixes, starting from the longest one
for i := len(path); i > 0; i-- {
prefixKey := strings.ToLower(strings.Join(path[0:i], v.keyDelim))

var val any
switch sourceIndexable := source.(type) {
case []any:
Expand Down Expand Up @@ -1464,10 +1463,12 @@ func Set(key string, value any) { v.Set(key, value) }

func (v *Viper) Set(key string, value any) {
// If alias passed in, then set the proper override

key = v.realKey(strings.ToLower(key))
value = toCaseInsensitiveValue(value)

path := strings.Split(key, v.keyDelim)

lastKey := strings.ToLower(path[len(path)-1])
deepestMap := deepSearch(v.override, path[0:len(path)-1])

Expand Down
148 changes: 115 additions & 33 deletions viper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2533,50 +2533,132 @@ func TestUnmarshal_DotSeparatorBackwardCompatibility(t *testing.T) {
// `)

func TestKeyDelimiter(t *testing.T) {
v := NewWithOptions(KeyDelimiter("::"))
v.SetConfigType("yaml")
r := strings.NewReader(string(yamlExampleWithDot))
t.Run("KeyDelimiterYAMLAndUnmarshal", func(t *testing.T) {
v := NewWithOptions(KeyDelimiter("::"))
v.SetConfigType("yaml")
r := strings.NewReader(string(yamlExampleWithDot))

err := v.unmarshalReader(r, v.config)
require.NoError(t, err)
err := v.unmarshalReader(r, v.config)
require.NoError(t, err)

values := map[string]any{
"image": map[string]any{
"repository": "someImage",
"tag": "1.0.0",
},
"ingress": map[string]any{
"annotations": map[string]any{
"traefik.frontend.rule.type": "PathPrefix",
"traefik.ingress.kubernetes.io/ssl-redirect": "true",
values := map[string]any{
"image": map[string]any{
"repository": "someImage",
"tag": "1.0.0",
},
},
}
"ingress": map[string]any{
"annotations": map[string]any{
"traefik.frontend.rule.type": "PathPrefix",
"traefik.ingress.kubernetes.io/ssl-redirect": "true",
},
},
}

v.SetDefault("charts::values", values)
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"))
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]any
type config struct {
Charts struct {
Values map[string]any
}
}
}

expected := config{
Charts: struct {
Values map[string]any
}{
Values: values,
},
}
expected := config{
Charts: struct {
Values map[string]any
}{
Values: values,
},
}

var actual config
var actual config

require.NoError(t, v.Unmarshal(&actual))
require.NoError(t, v.Unmarshal(&actual))

assert.Equal(t, expected, actual)
assert.Equal(t, expected, actual)
})

// Test the Set method with key delimiter for case insenitivty
t.Run("CaseInsensitiveDelimiter", func(t *testing.T) {
v := NewWithOptions(KeyDelimiter("Z"))
v.Set("fooZbar", "Foo Bar Baz")

got := v.Get("foo")
want := map[string]any{"bar": "Foo Bar Baz"}
assert.Equal(t, want, got)

v.Set("foozbar", "Foo Bar Baz")
got = v.Get("foo")
assert.Equal(t, want, got)

v.Set("baz", "Bazzz")
got = v.Get("baz")
want2 := "Bazzz"
assert.Equal(t, want2, got)
})

// Test the InConfig method with key delimiter for case insenitivty
t.Run("UpperCasedDelimInConfig", func(t *testing.T) {
v := NewWithOptions(KeyDelimiter("Z"))

v.config = map[string]any{
"foo": map[string]any{
"bar": "nestedValue",
},
}
assert.True(t, v.InConfig("fooZbar"))
assert.True(t, v.InConfig("foozbar"))
})

// Test the SetDefault method with key delimiter for case insenitivty
t.Run("UpperCasedDelimSetDefault", func(t *testing.T) {
v := NewWithOptions(KeyDelimiter("Z"))
v.SetDefault("fooZbar", "Foo Bar Baz")

got := v.Get("foo")
want := map[string]any{"bar": "Foo Bar Baz"}
assert.Equal(t, want, got)

v.SetDefault("foozbar", "Foo Bar Baz")
got = v.Get("foo")
assert.Equal(t, want, got)
})

// Test the flattenAndMergeMap private method with key delimiter for case insenitivty
t.Run("UpperCasedDelimFlattenAndMerge", func(t *testing.T) {
config := map[string]any{
"foo": map[string]any{
"bar": 123,
"baz": 456,
},
}

v := NewWithOptions(KeyDelimiter("Z"))
shadow := make(map[string]bool)
shadow = v.flattenAndMergeMap(shadow, config, "")

assert.True(t, shadow["foozbar"])
assert.True(t, shadow["foozbaz"])
})

// Test the AllSettings method with key delimiter for case insenitivty
t.Run("UpperCasedDelimAllSettings", func(t *testing.T) {
v := NewWithOptions(KeyDelimiter("Z"))

v.Set("foozbar", 123)

got := v.AllSettings()

want := map[string]any{
"foo": map[string]any{
"bar": 123,
},
}

assert.Equal(t, want, got)
})
}

var yamlDeepNestedSlices = []byte(`TV:
Expand Down