From ec4fddfada2ff6a67a0338a0898b96e2c16c3ede Mon Sep 17 00:00:00 2001 From: illarion Kovalchuk Date: Wed, 23 Dec 2020 15:28:47 +0200 Subject: [PATCH 1/2] Allow merging configs with different types of leaf values --- viper.go | 29 ++++++++++++++++------------- viper_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/viper.go b/viper.go index e5accf7ab..343253382 100644 --- a/viper.go +++ b/viper.go @@ -1780,17 +1780,6 @@ func mergeMaps( svType := reflect.TypeOf(sv) tvType := reflect.TypeOf(tv) - if tvType != nil && svType != tvType { // Allow for the target to be nil - v.logger.Error( - "svType != tvType", - "key", sk, - "st", svType, - "tt", tvType, - "sv", sv, - "tv", tv, - ) - continue - } v.logger.Trace( "processing", @@ -1804,13 +1793,27 @@ func mergeMaps( switch ttv := tv.(type) { case map[interface{}]interface{}: v.logger.Trace("merging maps (must convert)") - tsv := sv.(map[interface{}]interface{}) + tsv, ok := sv.(map[interface{}]interface{}) + if !ok { + v.logger.Error( + "Could not cast sv to map[interface{}]interface{}; key=%s, st=%v, tt=%v, sv=%v, tv=%v", + sk, svType, tvType, sv, tv) + continue + } + ssv := castToMapStringInterface(tsv) stv := castToMapStringInterface(ttv) mergeMaps(ssv, stv, ttv) case map[string]interface{}: v.logger.Trace("merging maps") - mergeMaps(sv.(map[string]interface{}), ttv, nil) + tsv, ok := sv.(map[string]interface{}) + if !ok { + jww.ERROR.Printf( + "Could not cast sv to map[string]interface{}; key=%s, st=%v, tt=%v, sv=%v, tv=%v", + sk, svType, tvType, sv, tv) + continue + } + mergeMaps(tsv, ttv, nil) default: v.logger.Trace("setting value") tgt[tk] = sv diff --git a/viper_test.go b/viper_test.go index 8c864f117..f882fdc46 100644 --- a/viper_test.go +++ b/viper_test.go @@ -1912,6 +1912,22 @@ hello: fu: bar `) +var jsonMergeExampleTgt = []byte(` +{ + "hello": { + "pop": 123456 + } +} +`) + +var jsonMergeExampleSrc = []byte(` +{ + "hello": { + "pop": "pop str" + } +} +`) + func TestMergeConfig(t *testing.T) { v := New() v.SetConfigType("yml") @@ -1984,6 +2000,22 @@ func TestMergeConfig(t *testing.T) { } } +func TestMergeConfigOverrideType(t *testing.T) { + v := New() + v.SetConfigType("json") + if err := v.ReadConfig(bytes.NewBuffer(jsonMergeExampleTgt)); err != nil { + t.Fatal(err) + } + + if err := v.MergeConfig(bytes.NewBuffer(jsonMergeExampleSrc)); err != nil { + t.Fatal(err) + } + + if pop := v.GetString("hello.pop"); pop != "pop str" { + t.Fatalf("pop != \"pop str\", = %s", pop) + } +} + func TestMergeConfigNoMerge(t *testing.T) { v := New() v.SetConfigType("yml") From 02cf2b2406312c14d45061d2cb7e7c5d0ebc57e7 Mon Sep 17 00:00:00 2001 From: Illarion Kovalchuk Date: Thu, 14 Oct 2021 16:29:39 +0300 Subject: [PATCH 2/2] Added test case for merging configs, where target is null, and source is not null --- viper.go | 2 +- viper_test.go | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/viper.go b/viper.go index 343253382..3481d1da1 100644 --- a/viper.go +++ b/viper.go @@ -1808,7 +1808,7 @@ func mergeMaps( v.logger.Trace("merging maps") tsv, ok := sv.(map[string]interface{}) if !ok { - jww.ERROR.Printf( + v.logger.Error( "Could not cast sv to map[string]interface{}; key=%s, st=%v, tt=%v, sv=%v, tv=%v", sk, svType, tvType, sv, tv) continue diff --git a/viper_test.go b/viper_test.go index f882fdc46..8a5dec688 100644 --- a/viper_test.go +++ b/viper_test.go @@ -1915,6 +1915,7 @@ fu: bar var jsonMergeExampleTgt = []byte(` { "hello": { + "foo": null, "pop": 123456 } } @@ -1923,6 +1924,7 @@ var jsonMergeExampleTgt = []byte(` var jsonMergeExampleSrc = []byte(` { "hello": { + "foo": "foo str", "pop": "pop str" } } @@ -2014,6 +2016,10 @@ func TestMergeConfigOverrideType(t *testing.T) { if pop := v.GetString("hello.pop"); pop != "pop str" { t.Fatalf("pop != \"pop str\", = %s", pop) } + + if foo := v.GetString("hello.foo"); foo != "foo str" { + t.Fatalf("foo != \"foo str\", = %s", foo) + } } func TestMergeConfigNoMerge(t *testing.T) {