Fix config handling with empty config entries after merge

Fixes #8701
This commit is contained in:
Bjørn Erik Pedersen 2021-06-27 13:24:49 +02:00
parent 923dd9d1c1
commit 19aa95fc7f
4 changed files with 58 additions and 5 deletions

View file

@ -52,6 +52,24 @@ func (p Params) Set(pp Params) {
} }
} }
// IsZero returns true if p is considered empty.
func (p Params) IsZero() bool {
if p == nil || len(p) == 0 {
return true
}
if len(p) > 1 {
return false
}
for k, _ := range p {
return k == mergeStrategyKey
}
return false
}
// Merge transfers values from pp to p for new keys. // Merge transfers values from pp to p for new keys.
// This is done recursively. // This is done recursively.
func (p Params) Merge(pp Params) { func (p Params) Merge(pp Params) {
@ -82,12 +100,9 @@ func (p Params) merge(ps ParamsMergeStrategy, pp Params) {
if pv, ok := v.(Params); ok { if pv, ok := v.(Params); ok {
vvv.merge(ms, pv) vvv.merge(ms, pv)
} }
} }
} else if !noUpdate { } else if !noUpdate {
p[k] = v p[k] = v
} }
} }

View file

@ -156,3 +156,15 @@ func TestParamsSetAndMerge(t *testing.T) {
}) })
} }
func TestParamsIsZero(t *testing.T) {
c := qt.New(t)
var nilParams Params
c.Assert(Params{}.IsZero(), qt.IsTrue)
c.Assert(nilParams.IsZero(), qt.IsTrue)
c.Assert(Params{"foo": "bar"}.IsZero(), qt.IsFalse)
c.Assert(Params{"_merge": "foo", "foo": "bar"}.IsZero(), qt.IsFalse)
c.Assert(Params{"_merge": "foo"}.IsZero(), qt.IsTrue)
}

View file

@ -214,6 +214,7 @@ func (c *defaultConfigProvider) Merge(k string, v interface{}) {
if p, ok := maps.ToParamsAndPrepare(v); ok { if p, ok := maps.ToParamsAndPrepare(v); ok {
// As there may be keys in p not in root, we need to handle // As there may be keys in p not in root, we need to handle
// those as a special case. // those as a special case.
var keysToDelete []string
for kk, vv := range p { for kk, vv := range p {
if pp, ok := vv.(maps.Params); ok { if pp, ok := vv.(maps.Params); ok {
if pppi, ok := c.root[kk]; ok { if pppi, ok := c.root[kk]; ok {
@ -261,14 +262,19 @@ func (c *defaultConfigProvider) Merge(k string, v interface{}) {
strategy := c.determineMergeStrategy(KeyParams{Key: "", Params: c.root}, KeyParams{Key: kk, Params: np}) strategy := c.determineMergeStrategy(KeyParams{Key: "", Params: c.root}, KeyParams{Key: kk, Params: np})
np.SetDefaultMergeStrategy(strategy) np.SetDefaultMergeStrategy(strategy)
np.Merge(pp) np.Merge(pp)
if len(np) > 0 { c.root[kk] = np
c.root[kk] = np if np.IsZero() {
// Just keep it until merge is done.
keysToDelete = append(keysToDelete, kk)
} }
} }
} }
} }
// Merge the rest. // Merge the rest.
c.root.Merge(p) c.root.Merge(p)
for _, k := range keysToDelete {
delete(c.root, k)
}
} else { } else {
panic(fmt.Sprintf("unsupported type %T received in Merge", v)) panic(fmt.Sprintf("unsupported type %T received in Merge", v))
} }

View file

@ -283,6 +283,26 @@ func TestDefaultConfigProvider(t *testing.T) {
}) })
// Issue #8701
c.Run("Prevent _merge only maps", func(c *qt.C) {
cfg := New()
cfg.Set("", map[string]interface{}{
"B": "bv",
})
cfg.Merge("", map[string]interface{}{
"c": map[string]interface{}{
"_merge": "shallow",
"d": "dv2",
},
})
c.Assert(cfg.Get(""), qt.DeepEquals, maps.Params{
"b": "bv",
})
})
c.Run("IsSet", func(c *qt.C) { c.Run("IsSet", func(c *qt.C) {
cfg := New() cfg := New()