From a29393fbf3697c4dd3341fcf44a1c1acc9fe837c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Szafra=C5=84ski?= Date: Tue, 11 Jan 2022 12:44:49 +0100 Subject: [PATCH] Port support for deleting by value to v5 --- v5/patch.go | 33 ++++++++++++++++++++++++++++++++- v5/patch_test.go | 25 +++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/v5/patch.go b/v5/patch.go index 73ff2c5..c1e4433 100644 --- a/v5/patch.go +++ b/v5/patch.go @@ -21,6 +21,9 @@ var ( // allowing negative indices to mean indices starting at the end of an array. // Default to true. SupportNegativeIndices bool = true + // SupportDeleteByValue decides whether to support non-standard practice of + // allowing deleting from array by value. + SupportDeleteByValue bool = true // AccumulatedCopySizeLimit limits the total size increase in bytes caused by // "copy" operations in a patch. AccumulatedCopySizeLimit int64 = 0 @@ -76,6 +79,9 @@ type ApplyOptions struct { // allowing negative indices to mean indices starting at the end of an array. // Default to true. SupportNegativeIndices bool + // SupportDeleteByValue decides whether to support non-standard practice of + // allowing deleting from array by value. + SupportDeleteByValue bool // AccumulatedCopySizeLimit limits the total size increase in bytes caused by // "copy" operations in a patch. AccumulatedCopySizeLimit int64 @@ -91,6 +97,7 @@ type ApplyOptions struct { func NewApplyOptions() *ApplyOptions { return &ApplyOptions{ SupportNegativeIndices: SupportNegativeIndices, + SupportDeleteByValue: SupportDeleteByValue, AccumulatedCopySizeLimit: AccumulatedCopySizeLimit, AllowMissingPathOnRemove: false, EnsurePathExistsOnAdd: false, @@ -701,6 +708,19 @@ func (d *partialArray) remove(key string, options *ApplyOptions) error { return nil } +func (d *partialArray) removeByValue(key *lazyNode, options *ApplyOptions) error { + for i, v := range *d { + if key.equal(v) { + return d.remove(strconv.Itoa(i), options) + } + } + + if options.AllowMissingPathOnRemove { + return nil + } + return errors.Wrapf(ErrMissing, "value not found") +} + func (p Patch) add(doc *container, op Operation, options *ApplyOptions) error { path, err := op.Path() if err != nil { @@ -872,7 +892,18 @@ func (p Patch) remove(doc *container, op Operation, options *ApplyOptions) error return errors.Wrapf(ErrMissing, "remove operation does not apply: doc is missing path: \"%s\"", path) } - err = con.remove(key, options) + if key == "-" && options.SupportDeleteByValue { + value := op.value() + switch v := con.(type) { + case *partialArray: + err = v.removeByValue(value, options) + default: + return errors.Wrapf(ErrMissing, "remove operation does not apply: not an array: \"%s\"", path) + } + } else { + err = con.remove(key, options) + } + if err != nil { return errors.Wrapf(err, "error in remove for path: '%s'", path) } diff --git a/v5/patch_test.go b/v5/patch_test.go index 58bb7f1..727eb28 100644 --- a/v5/patch_test.go +++ b/v5/patch_test.go @@ -157,6 +157,27 @@ var Cases = []Case{ false, false, }, + { + `{ "foo": [ "bar", "qux", "baz" ] }`, + `[ { "op": "remove", "path": "/foo/-" , "value": "qux" }]`, + `{ "foo": [ "bar", "baz" ] }`, + false, + false, + }, + { + `{ "foo": [ "3", "2", "1" ] }`, + `[ { "op": "remove", "path": "/foo/-" , "value": "1" }]`, + `{ "foo": [ "3", "2" ] }`, + false, + false, + }, + { + `{ "foo": [ [ "bar", "qux", "baz" ], [ "bar", "baz" ], "1" ] }`, + `[ { "op": "remove", "path": "/foo/-" , "value": [ "bar", "baz" ] }]`, + `{ "foo": [ [ "bar", "qux", "baz" ], "1" ] }`, + false, + false, + }, { `{ "baz": "qux", "foo": "bar" }`, `[ { "op": "replace", "path": "/baz", "value": "boo" } ]`, @@ -665,6 +686,10 @@ var BadCases = []BadCase{ `[ {"op": "remove", "path": "/foo/-"}]`, false, }, + { + `{ "foo": []}`, + `[ {"op": "remove", "path": "/foo/-", "value": "asd"}]`, + }, { `{ "foo": []}`, `[ {"op": "remove", "path": "/foo/-1"}]`,