Ensure that fields are not removed when set to NULL

This commit is contained in:
Tobie Morgan Hitchcock 2018-04-27 00:40:36 +01:00
parent 9b03178dfd
commit eb62515a05
5 changed files with 50 additions and 33 deletions

View file

@ -108,7 +108,7 @@ func (d *document) delFld(ctx context.Context, met method) (err error) {
// Loop over the allowed keys
for _, fd := range fds {
d.current.Walk(func(key string, val interface{}) (err error) {
d.current.Walk(func(key string, val interface{}, ok bool) (err error) {
keys[key] = struct{}{}
return
}, fd.Name.ID)
@ -291,9 +291,7 @@ func (d *document) mrgFld(ctx context.Context, met method) (err error) {
for _, fd := range fds {
err = d.current.Walk(func(key string, val interface{}) error {
vars := data.New()
err = d.current.Walk(func(key string, val interface{}, exi bool) error {
var old = d.initial.Get(key).Data()
@ -313,6 +311,7 @@ func (d *document) mrgFld(ctx context.Context, met method) (err error) {
// Reset the variables
vars := data.New()
vars.Set(val, varKeyValue)
vars.Set(val, varKeyAfter)
vars.Set(old, varKeyBefore)
@ -332,6 +331,7 @@ func (d *document) mrgFld(ctx context.Context, met method) (err error) {
// Reset the variables
vars := data.New()
vars.Set(val, varKeyValue)
vars.Set(val, varKeyAfter)
vars.Set(old, varKeyBefore)
@ -354,6 +354,7 @@ func (d *document) mrgFld(ctx context.Context, met method) (err error) {
// Reset the variables
vars := data.New()
vars.Set(val, varKeyValue)
vars.Set(val, varKeyAfter)
vars.Set(old, varKeyBefore)
@ -398,7 +399,11 @@ func (d *document) mrgFld(ctx context.Context, met method) (err error) {
switch val.(type) {
default:
d.current.Iff(val, key)
if exi {
d.current.Set(val, key)
} else {
d.current.Iff(val, key)
}
case *sql.Void:
d.current.Del(key)
}

View file

@ -51,7 +51,7 @@ func (d *document) perms(ctx context.Context, doc *data.Doc) (err error) {
if fd.Perms != nil {
err = doc.Walk(func(key string, val interface{}) error {
err = doc.Walk(func(key string, val interface{}, exi bool) error {
// We are checking the permissions of the field

View file

@ -242,7 +242,7 @@ func (d *document) yield(ctx context.Context, stm sql.Statement, output sql.Toke
break
case *sql.Ident:
out.Walk(func(key string, val interface{}) error {
out.Walk(func(key string, val interface{}, exi bool) error {
switch res := val.(type) {
case []interface{}:

View file

@ -47,6 +47,9 @@ type Fetcher func(key string, val interface{}, path []string) interface{}
// Iterator is used when iterating over items.
type Iterator func(key string, val interface{}) error
// Walker is used when walking over items.
type Walker func(key string, val interface{}, exi bool) error
// New creates a new data object.
func New() *Doc {
return &Doc{data: map[string]interface{}{}}
@ -995,7 +998,7 @@ func (d *Doc) each(exec Iterator, prev []string) error {
// --------------------------------------------------------------------------------
// Walk walks the value or values at a specified path.
func (d *Doc) Walk(exec Iterator, path ...string) error {
func (d *Doc) Walk(exec Walker, path ...string) error {
path = d.path(path...)
@ -1003,7 +1006,7 @@ func (d *Doc) Walk(exec Iterator, path ...string) error {
}
func (d *Doc) walk(exec Iterator, prev []string, path ...string) error {
func (d *Doc) walk(exec Walker, prev []string, path ...string) error {
if len(path) == 0 {
return nil
@ -1037,7 +1040,7 @@ func (d *Doc) walk(exec Iterator, prev []string, path ...string) error {
if m, ok := object.(map[string]interface{}); ok {
if object, ok = m[p]; !ok {
return exec(d.join(prev, path), nil)
return exec(d.join(prev, path), nil, false)
}
continue
}
@ -1051,7 +1054,7 @@ func (d *Doc) walk(exec Iterator, prev []string, path ...string) error {
c, i, r := d.what(p, a, choose)
if r == one && len(c) == 0 {
return fmt.Errorf("No item with index %s in array, using path %s", p, path)
return nil
}
if r == one {
@ -1060,7 +1063,7 @@ func (d *Doc) walk(exec Iterator, prev []string, path ...string) error {
keep = append(keep, prev...)
keep = append(keep, path[:k]...)
keep = append(keep, fmt.Sprintf("[%d]", i[0]))
return exec(d.join(keep), c[0])
return exec(d.join(keep), c[0], true)
} else {
var keep []string
keep = append(keep, prev...)
@ -1077,7 +1080,7 @@ func (d *Doc) walk(exec Iterator, prev []string, path ...string) error {
keep = append(keep, prev...)
keep = append(keep, path[:k]...)
keep = append(keep, fmt.Sprintf("[%d]", i[j]))
if err := exec(d.join(keep), v); err != nil {
if err := exec(d.join(keep), v, true); err != nil {
return err
}
} else {
@ -1098,10 +1101,10 @@ func (d *Doc) walk(exec Iterator, prev []string, path ...string) error {
// The current path item is not an object or an array
// but there are still other items in the search path.
return fmt.Errorf("Can not get path %s from %v", path, object)
return nil
}
return exec(d.join(prev, path), object)
return exec(d.join(prev, path), object, true)
}

View file

@ -953,16 +953,18 @@ func TestOperations(t *testing.T) {
})
Convey("Can walk nil", t, func() {
doc.Walk(func(key string, val interface{}) error {
doc.Walk(func(key string, val interface{}, exi bool) error {
doc.Set(tmp, "none")
So(exi, ShouldBeTrue)
return nil
})
So(doc.Exists("none"), ShouldBeFalse)
})
Convey("Can walk array", t, func() {
doc.Walk(func(key string, val interface{}) error {
doc.Walk(func(key string, val interface{}, exi bool) error {
So(key, ShouldResemble, "the.item.arrays")
So(exi, ShouldBeFalse)
doc.Set(tmp, key)
return nil
}, "the.item.arrays")
@ -970,98 +972,105 @@ func TestOperations(t *testing.T) {
})
Convey("Can walk array → *", t, func() {
doc.Walk(func(key string, val interface{}) error {
doc.Walk(func(key string, val interface{}, exi bool) error {
So(key, ShouldBeIn, "the.item.arrays.[0]", "the.item.arrays.[1]", "the.item.arrays.[2]")
So(val, ShouldBeIn, tmp[0], tmp[1], tmp[2])
So(exi, ShouldBeTrue)
return nil
}, "the.item.arrays.*")
})
Convey("Can walk array → * → object", t, func() {
doc.Walk(func(key string, val interface{}) error {
doc.Walk(func(key string, val interface{}, exi bool) error {
So(key, ShouldBeIn, "the.item.arrays.[0].test", "the.item.arrays.[1].test", "the.item.arrays.[2].test")
So(val, ShouldBeIn, "one", "two", "tre")
So(exi, ShouldBeTrue)
return nil
}, "the.item.arrays.*.test")
})
Convey("Can walk array → first → object", t, func() {
doc.Walk(func(key string, val interface{}) error {
doc.Walk(func(key string, val interface{}, exi bool) error {
So(key, ShouldResemble, "the.item.arrays.[0].test")
So(val, ShouldResemble, "one")
So(exi, ShouldBeTrue)
return nil
}, "the.item.arrays.first.test")
})
Convey("Can walk array → last → object", t, func() {
doc.Walk(func(key string, val interface{}) error {
doc.Walk(func(key string, val interface{}, exi bool) error {
So(key, ShouldResemble, "the.item.arrays.[2].test")
So(val, ShouldResemble, "tre")
So(exi, ShouldBeTrue)
return nil
}, "the.item.arrays.last.test")
})
Convey("Can walk array → 0 → value", t, func() {
doc.Walk(func(key string, val interface{}) error {
doc.Walk(func(key string, val interface{}, exi bool) error {
So(key, ShouldResemble, "the.item.arrays.[0]")
So(val, ShouldResemble, map[string]interface{}{"test": "one"})
So(exi, ShouldBeTrue)
return nil
}, "the.item.arrays.0")
})
Convey("Can walk array → 1 → value", t, func() {
doc.Walk(func(key string, val interface{}) error {
doc.Walk(func(key string, val interface{}, exi bool) error {
So(key, ShouldResemble, "the.item.arrays.[1]")
So(val, ShouldResemble, map[string]interface{}{"test": "two"})
So(exi, ShouldBeTrue)
return nil
}, "the.item.arrays.1")
})
Convey("Can walk array → 2 → value", t, func() {
doc.Walk(func(key string, val interface{}) error {
doc.Walk(func(key string, val interface{}, exi bool) error {
So(key, ShouldResemble, "the.item.arrays.[2]")
So(val, ShouldResemble, map[string]interface{}{"test": "tre"})
So(exi, ShouldBeTrue)
return nil
}, "the.item.arrays.2")
})
Convey("Can walk array → 3 → value", t, func() {
err := doc.Walk(func(key string, val interface{}) error {
err := doc.Walk(func(key string, val interface{}, exi bool) error {
return nil
}, "the.item.arrays.3")
So(err, ShouldNotBeNil)
So(err, ShouldBeNil)
})
Convey("Can walk array → 0 → value → value", t, func() {
err := doc.Walk(func(key string, val interface{}) error {
err := doc.Walk(func(key string, val interface{}, exi bool) error {
return nil
}, "the.item.arrays.0.test.value")
So(err, ShouldNotBeNil)
So(err, ShouldBeNil)
})
Convey("Can walk array → 0 → value → value → value", t, func() {
err := doc.Walk(func(key string, val interface{}) error {
err := doc.Walk(func(key string, val interface{}, exi bool) error {
return nil
}, "the.item.arrays.0.test.value.value")
So(err, ShouldNotBeNil)
So(err, ShouldBeNil)
})
Convey("Can force error from walk", t, func() {
err := doc.Walk(func(key string, val interface{}) error {
err := doc.Walk(func(key string, val interface{}, exi bool) error {
return errors.New("Testing")
}, "the.item.something")
So(err, ShouldNotBeNil)
})
Convey("Can force error from walk array → *", t, func() {
err := doc.Walk(func(key string, val interface{}) error {
err := doc.Walk(func(key string, val interface{}, exi bool) error {
return errors.New("Testing")
}, "the.item.arrays.*")
So(err, ShouldNotBeNil)
})
Convey("Can force error from walk array → * → value", t, func() {
err := doc.Walk(func(key string, val interface{}) error {
err := doc.Walk(func(key string, val interface{}, exi bool) error {
return errors.New("Testing")
}, "the.item.arrays.*.test")
So(err, ShouldNotBeNil)