Codebase list golang-github-evanphx-json-patch / 01e247a
Update upstream source from tag 'upstream/5.2.0' Update to upstream version '5.2.0' with Debian dir 4dc356085998886c940dd7cff3ef6de3b36c6a94 Arthur Diniz 3 years ago
19 changed file(s) with 3644 addition(s) and 83 deletion(s). Raw diff Collapse all Expand all
0 name: Go
1
2 on:
3 push:
4 branches: [ master ]
5 pull_request:
6 branches: [ master ]
7
8 jobs:
9
10 build:
11 runs-on: ubuntu-latest
12 steps:
13 - uses: actions/checkout@v2
14
15 - name: Set up Go
16 uses: actions/setup-go@v2
17 with:
18 go-version: 1.15
19
20 - name: Setup
21 run: go get
22
23 - name: Test
24 run: go test -v ./...
+0
-16
.travis.yml less more
0 language: go
1
2 go:
3 - 1.8
4 - 1.7
5
6 install:
7 - if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
8 - go get github.com/jessevdk/go-flags
9
10 script:
11 - go get
12 - go test -cover ./...
13
14 notifications:
15 email: false
55
66 * Redistributions of source code must retain the above copyright notice, this
77 list of conditions and the following disclaimer.
8 * Redistributions in binary form must reproduce the above copyright notice
8 * Redistributions in binary form must reproduce the above copyright notice,
99 this list of conditions and the following disclaimer in the documentation
1010 and/or other materials provided with the distribution.
1111 * Neither the name of the Evan Phoenix nor the names of its contributors
1010
1111 **Latest and greatest**:
1212 ```bash
13 go get -u github.com/evanphx/json-patch
13 go get -u github.com/evanphx/json-patch/v5
1414 ```
1515
1616 **Stable Versions**:
17 * Version 5: `go get -u gopkg.in/evanphx/json-patch.v5`
1718 * Version 4: `go get -u gopkg.in/evanphx/json-patch.v4`
1819
1920 (previous versions below `v3` are unavailable)
3738 which limits the total size increase in bytes caused by "copy" operations in a
3839 patch. It defaults to 0, which means there is no limit.
3940
41 These global variables control the behavior of `jsonpatch.Apply`.
42
43 An alternative to `jsonpatch.Apply` is `jsonpatch.ApplyWithOptions` whose behavior
44 is controlled by an `options` parameter of type `*jsonpatch.ApplyOptions`.
45
46 Structure `jsonpatch.ApplyOptions` includes the configuration options above
47 and adds two new options: `AllowMissingPathOnRemove` and `EnsurePathExistsOnAdd`.
48
49 When `AllowMissingPathOnRemove` is set to `true`, `jsonpatch.ApplyWithOptions` will ignore
50 `remove` operations whose `path` points to a non-existent location in the JSON document.
51 `AllowMissingPathOnRemove` defaults to `false` which will lead to `jsonpatch.ApplyWithOptions`
52 returning an error when hitting a missing `path` on `remove`.
53
54 When `EnsurePathExistsOnAdd` is set to `true`, `jsonpatch.ApplyWithOptions` will make sure
55 that `add` operations produce all the `path` elements that are missing from the target object.
56
57 Use `jsonpatch.NewApplyOptions` to create an instance of `jsonpatch.ApplyOptions`
58 whose values are populated from the global configuration variables.
59
4060 ## Create and apply a merge patch
4161 Given both an original JSON document and a modified JSON document, you can create
4262 a [Merge Patch](https://tools.ietf.org/html/rfc7396) document.
81101 ```bash
82102 $ go run main.go
83103 patch document: {"height":null,"name":"Jane"}
84 updated tina doc: {"age":28,"name":"Jane"}
104 updated alternative doc: {"age":28,"name":"Jane"}
85105 ```
86106
87107 ## Create and apply a JSON Patch
00 module github.com/evanphx/json-patch
11
2 go 1.12
2 go 1.14
33
4 require github.com/pkg/errors v0.8.1
4 require (
5 github.com/jessevdk/go-flags v1.4.0
6 github.com/pkg/errors v0.9.1
7 )
0 github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
1 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
0 github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
1 github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
2 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
3 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
8787 return ary
8888 }
8989
90 var errBadJSONDoc = fmt.Errorf("Invalid JSON Document")
91 var errBadJSONPatch = fmt.Errorf("Invalid JSON Patch")
90 var ErrBadJSONDoc = fmt.Errorf("Invalid JSON Document")
91 var ErrBadJSONPatch = fmt.Errorf("Invalid JSON Patch")
9292 var errBadMergeTypes = fmt.Errorf("Mismatched JSON Documents")
9393
9494 // MergeMergePatches merges two merge patches together, such that
113113 patchErr := json.Unmarshal(patchData, patch)
114114
115115 if _, ok := docErr.(*json.SyntaxError); ok {
116 return nil, errBadJSONDoc
116 return nil, ErrBadJSONDoc
117117 }
118118
119119 if _, ok := patchErr.(*json.SyntaxError); ok {
120 return nil, errBadJSONPatch
120 return nil, ErrBadJSONPatch
121121 }
122122
123123 if docErr == nil && *doc == nil {
124 return nil, errBadJSONDoc
124 return nil, ErrBadJSONDoc
125125 }
126126
127127 if patchErr == nil && *patch == nil {
128 return nil, errBadJSONPatch
128 return nil, ErrBadJSONPatch
129129 }
130130
131131 if docErr != nil || patchErr != nil {
141141 patchErr = json.Unmarshal(patchData, patchAry)
142142
143143 if patchErr != nil {
144 return nil, errBadJSONPatch
144 return nil, ErrBadJSONPatch
145145 }
146146
147147 pruneAryNulls(patchAry)
149149 out, patchErr := json.Marshal(patchAry)
150150
151151 if patchErr != nil {
152 return nil, errBadJSONPatch
152 return nil, ErrBadJSONPatch
153153 }
154154
155155 return out, nil
206206
207207 err := json.Unmarshal(originalJSON, &originalDoc)
208208 if err != nil {
209 return nil, errBadJSONDoc
209 return nil, ErrBadJSONDoc
210210 }
211211
212212 err = json.Unmarshal(modifiedJSON, &modifiedDoc)
213213 if err != nil {
214 return nil, errBadJSONDoc
214 return nil, ErrBadJSONDoc
215215 }
216216
217217 dest, err := getDiff(originalDoc, modifiedDoc)
232232
233233 err := json.Unmarshal(originalJSON, &originalDocs)
234234 if err != nil {
235 return nil, errBadJSONDoc
235 return nil, ErrBadJSONDoc
236236 }
237237
238238 err = json.Unmarshal(modifiedJSON, &modifiedDocs)
239239 if err != nil {
240 return nil, errBadJSONDoc
240 return nil, ErrBadJSONDoc
241241 }
242242
243243 total := len(originalDocs)
244244 if len(modifiedDocs) != total {
245 return nil, errBadJSONDoc
245 return nil, ErrBadJSONDoc
246246 }
247247
248248 result := []json.RawMessage{}
310310 return false
311311 }
312312 for key := range bt {
313 if !matchesValue(at[key], bt[key]) {
313 av, aOK := at[key]
314 bv, bOK := bt[key]
315 if aOK != bOK {
316 return false
317 }
318 if !matchesValue(av, bv) {
314319 return false
315320 }
316321 }
177177
178178 out, err := MergePatch([]byte(doc), []byte(pat))
179179
180 if err != errBadJSONPatch {
180 if err != ErrBadJSONPatch {
181181 t.Errorf("error not returned properly: %s, %s", err, string(out))
182182 }
183183 }
461461 }
462462 }
463463
464 func TestMatchesValue(t *testing.T) {
465 testcases := []struct {
466 name string
467 a interface{}
468 b interface{}
469 want bool
470 }{
471 {
472 name: "map empty",
473 a: map[string]interface{}{},
474 b: map[string]interface{}{},
475 want: true,
476 },
477 {
478 name: "map equal keys, equal non-nil value",
479 a: map[string]interface{}{"1": true},
480 b: map[string]interface{}{"1": true},
481 want: true,
482 },
483 {
484 name: "map equal keys, equal nil value",
485 a: map[string]interface{}{"1": nil},
486 b: map[string]interface{}{"1": nil},
487 want: true,
488 },
489
490 {
491 name: "map different value",
492 a: map[string]interface{}{"1": true},
493 b: map[string]interface{}{"1": false},
494 want: false,
495 },
496 {
497 name: "map different key, matching non-nil value",
498 a: map[string]interface{}{"1": true},
499 b: map[string]interface{}{"2": true},
500 want: false,
501 },
502 {
503 name: "map different key, matching nil value",
504 a: map[string]interface{}{"1": nil},
505 b: map[string]interface{}{"2": nil},
506 want: false,
507 },
508 {
509 name: "map different key, first nil value",
510 a: map[string]interface{}{"1": true},
511 b: map[string]interface{}{"2": nil},
512 want: false,
513 },
514 {
515 name: "map different key, second nil value",
516 a: map[string]interface{}{"1": nil},
517 b: map[string]interface{}{"2": true},
518 want: false,
519 },
520 }
521 for _, tc := range testcases {
522 t.Run(tc.name, func(t *testing.T) {
523 got := matchesValue(tc.a, tc.b)
524 if got != tc.want {
525 t.Fatalf("want %v, got %v", tc.want, got)
526 }
527 })
528 }
529 }
530
464531 func benchmarkMatchesValueWithDeeplyNestedFields(depth int, b *testing.B) {
465532 a := map[string]interface{}{}
466533 objCount := 1
209209 ov, ok := o.doc[k]
210210
211211 if !ok {
212 return false
213 }
214
215 if (v == nil) != (ov == nil) {
212216 return false
213217 }
214218
387391 }
388392
389393 func (d *partialDoc) get(key string) (*lazyNode, error) {
390 v, ok := (*d)[key]
391 if !ok {
392 return v, errors.Wrapf(ErrMissing, "unable to get nonexistent key: %s", key)
393 }
394 return v, nil
394 return (*d)[key], nil
395395 }
396396
397397 func (d *partialDoc) remove(key string) error {
398398 _, ok := (*d)[key]
399399 if !ok {
400 return errors.Wrapf(ErrMissing, "unable to remove nonexistent key: %s", key)
400 return errors.Wrapf(ErrMissing, "Unable to remove nonexistent key: %s", key)
401401 }
402402
403403 delete(*d, key)
619619 }
620620
621621 val, err := con.get(key)
622 if err != nil && errors.Cause(err) != ErrMissing {
622 if err != nil {
623623 return errors.Wrapf(err, "error in test for path: '%s'", path)
624624 }
625625
186186 `{ "foo": [["bar"], "bar"]}`,
187187 },
188188 {
189 `{ "foo": null}`,
190 `[{"op": "copy", "path": "/bar", "from": "/foo"}]`,
191 `{ "foo": null, "bar": null}`,
192 },
193 {
194189 `{ "foo": ["bar","qux","baz"]}`,
195190 `[ { "op": "remove", "path": "/foo/-2"}]`,
196191 `{ "foo": ["bar", "baz"]}`,
335330 {
336331 `{ "foo": [ "all", "grass", "cows", "eat" ] }`,
337332 `[ { "op": "move", "from": "/foo/1", "path": "/foo/4" } ]`,
338 },
339 {
340 `{ "baz": "qux" }`,
341 `[ { "op": "replace", "path": "/foo", "value": "bar" } ]`,
342 },
343 // Can't copy from non-existent "from" key.
344 {
345 `{ "foo": "bar"}`,
346 `[{"op": "copy", "path": "/qux", "from": "/baz"}]`,
347333 },
348334 }
349335
471457 `[ { "op": "test", "path": "/foo"} ]`,
472458 false,
473459 "/foo",
474 },
475 {
476 `{ "foo": "bar" }`,
477 `[ { "op": "test", "path": "/baz", "value": "bar" } ]`,
478 false,
479 "/baz",
480 },
481 {
482 `{ "foo": "bar" }`,
483 `[ { "op": "test", "path": "/baz", "value": null } ]`,
484 true,
485 "/baz",
486460 },
487461 }
488462
566540 }
567541
568542 type EqualityCase struct {
543 name string
569544 a, b string
570545 equal bool
571546 }
572547
573548 var EqualityCases = []EqualityCase{
574 EqualityCase{
549 {
550 "ExtraKeyFalse",
575551 `{"foo": "bar"}`,
576552 `{"foo": "bar", "baz": "qux"}`,
577553 false,
578554 },
555 {
556 "StripWhitespaceTrue",
557 `{
558 "foo": "bar",
559 "baz": "qux"
560 }`,
561 `{"foo": "bar", "baz": "qux"}`,
562 true,
563 },
564 {
565 "KeysOutOfOrderTrue",
566 `{
567 "baz": "qux",
568 "foo": "bar"
569 }`,
570 `{"foo": "bar", "baz": "qux"}`,
571 true,
572 },
573 {
574 "ComparingNullFalse",
575 `{"foo": null}`,
576 `{"foo": "bar"}`,
577 false,
578 },
579 {
580 "ComparingNullTrue",
581 `{"foo": null}`,
582 `{"foo": null}`,
583 true,
584 },
585 {
586 "ArrayOutOfOrderFalse",
587 `["foo", "bar", "baz"]`,
588 `["bar", "baz", "foo"]`,
589 false,
590 },
591 {
592 "ArrayTrue",
593 `["foo", "bar", "baz"]`,
594 `["foo", "bar", "baz"]`,
595 true,
596 },
597 {
598 "NonStringTypesTrue",
599 `{"int": 6, "bool": true, "float": 7.0, "string": "the_string", "null": null}`,
600 `{"int": 6, "bool": true, "float": 7.0, "string": "the_string", "null": null}`,
601 true,
602 },
603 {
604 "NestedNullFalse",
605 `{"foo": ["an", "array"], "bar": {"an": "object"}}`,
606 `{"foo": null, "bar": null}`,
607 false,
608 },
609 {
610 "NullCompareStringFalse",
611 `"foo"`,
612 `null`,
613 false,
614 },
615 {
616 "NullCompareIntFalse",
617 `6`,
618 `null`,
619 false,
620 },
621 {
622 "NullCompareFloatFalse",
623 `6.01`,
624 `null`,
625 false,
626 },
627 {
628 "NullCompareBoolFalse",
629 `false`,
630 `null`,
631 false,
632 },
579633 }
580634
581635 func TestEquality(t *testing.T) {
582636 for _, tc := range EqualityCases {
583 got := Equal([]byte(tc.a), []byte(tc.b))
584 if got != tc.equal {
585 t.Errorf("Expected Equal(%s, %s) to return %t, but got %t", tc.a, tc.b, tc.equal, got)
586 }
587
588 got = Equal([]byte(tc.b), []byte(tc.a))
589 if got != tc.equal {
590 t.Errorf("Expected Equal(%s, %s) to return %t, but got %t", tc.b, tc.a, tc.equal, got)
591 }
592 }
593 }
637 t.Run(tc.name, func(t *testing.T) {
638 got := Equal([]byte(tc.a), []byte(tc.b))
639 if got != tc.equal {
640 t.Errorf("Expected Equal(%s, %s) to return %t, but got %t", tc.a, tc.b, tc.equal, got)
641 }
642
643 got = Equal([]byte(tc.b), []byte(tc.a))
644 if got != tc.equal {
645 t.Errorf("Expected Equal(%s, %s) to return %t, but got %t", tc.b, tc.a, tc.equal, got)
646 }
647 })
648 }
649 }
0 package main
1
2 // Borrowed from Concourse: https://github.com/concourse/atc/blob/master/atccmd/file_flag.go
3
4 import (
5 "fmt"
6 "os"
7 "path/filepath"
8 )
9
10 // FileFlag is a flag for passing a path to a file on disk. The file is
11 // expected to be a file, not a directory, that actually exists.
12 type FileFlag string
13
14 // UnmarshalFlag implements go-flag's Unmarshaler interface
15 func (f *FileFlag) UnmarshalFlag(value string) error {
16 stat, err := os.Stat(value)
17 if err != nil {
18 return err
19 }
20
21 if stat.IsDir() {
22 return fmt.Errorf("path '%s' is a directory, not a file", value)
23 }
24
25 abs, err := filepath.Abs(value)
26 if err != nil {
27 return err
28 }
29
30 *f = FileFlag(abs)
31
32 return nil
33 }
34
35 // Path is the path to the file
36 func (f FileFlag) Path() string {
37 return string(f)
38 }
0 package main
1
2 import (
3 "fmt"
4 "io/ioutil"
5 "log"
6 "os"
7
8 jsonpatch "github.com/evanphx/json-patch/v5"
9 flags "github.com/jessevdk/go-flags"
10 )
11
12 type opts struct {
13 PatchFilePaths []FileFlag `long:"patch-file" short:"p" value-name:"PATH" description:"Path to file with one or more operations"`
14 }
15
16 func main() {
17 var o opts
18 _, err := flags.Parse(&o)
19 if err != nil {
20 log.Fatalf("error: %s\n", err)
21 }
22
23 patches := make([]jsonpatch.Patch, len(o.PatchFilePaths))
24
25 for i, patchFilePath := range o.PatchFilePaths {
26 var bs []byte
27 bs, err = ioutil.ReadFile(patchFilePath.Path())
28 if err != nil {
29 log.Fatalf("error reading patch file: %s", err)
30 }
31
32 var patch jsonpatch.Patch
33 patch, err = jsonpatch.DecodePatch(bs)
34 if err != nil {
35 log.Fatalf("error decoding patch file: %s", err)
36 }
37
38 patches[i] = patch
39 }
40
41 doc, err := ioutil.ReadAll(os.Stdin)
42 if err != nil {
43 log.Fatalf("error reading from stdin: %s", err)
44 }
45
46 mdoc := doc
47 for _, patch := range patches {
48 mdoc, err = patch.Apply(mdoc)
49 if err != nil {
50 log.Fatalf("error applying patch: %s", err)
51 }
52 }
53
54 fmt.Printf("%s", mdoc)
55 }
0 package jsonpatch
1
2 import "fmt"
3
4 // AccumulatedCopySizeError is an error type returned when the accumulated size
5 // increase caused by copy operations in a patch operation has exceeded the
6 // limit.
7 type AccumulatedCopySizeError struct {
8 limit int64
9 accumulated int64
10 }
11
12 // NewAccumulatedCopySizeError returns an AccumulatedCopySizeError.
13 func NewAccumulatedCopySizeError(l, a int64) *AccumulatedCopySizeError {
14 return &AccumulatedCopySizeError{limit: l, accumulated: a}
15 }
16
17 // Error implements the error interface.
18 func (a *AccumulatedCopySizeError) Error() string {
19 return fmt.Sprintf("Unable to complete the copy, the accumulated size increase of copy is %d, exceeding the limit %d", a.accumulated, a.limit)
20 }
21
22 // ArraySizeError is an error type returned when the array size has exceeded
23 // the limit.
24 type ArraySizeError struct {
25 limit int
26 size int
27 }
28
29 // NewArraySizeError returns an ArraySizeError.
30 func NewArraySizeError(l, s int) *ArraySizeError {
31 return &ArraySizeError{limit: l, size: s}
32 }
33
34 // Error implements the error interface.
35 func (a *ArraySizeError) Error() string {
36 return fmt.Sprintf("Unable to create array of size %d, limit is %d", a.size, a.limit)
37 }
0 module github.com/evanphx/json-patch/v5
1
2 go 1.12
3
4 require (
5 github.com/jessevdk/go-flags v1.4.0
6 github.com/pkg/errors v0.8.1
7 )
0 github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
1 github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
2 github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
3 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
0 package jsonpatch
1
2 import (
3 "bytes"
4 "encoding/json"
5 "fmt"
6 "reflect"
7 )
8
9 func merge(cur, patch *lazyNode, mergeMerge bool) *lazyNode {
10 curDoc, err := cur.intoDoc()
11
12 if err != nil {
13 pruneNulls(patch)
14 return patch
15 }
16
17 patchDoc, err := patch.intoDoc()
18
19 if err != nil {
20 return patch
21 }
22
23 mergeDocs(curDoc, patchDoc, mergeMerge)
24
25 return cur
26 }
27
28 func mergeDocs(doc, patch *partialDoc, mergeMerge bool) {
29 for k, v := range patch.obj {
30 if v == nil {
31 if mergeMerge {
32 idx := -1
33 for i, key := range doc.keys {
34 if key == k {
35 idx = i
36 break
37 }
38 }
39 if idx == -1 {
40 doc.keys = append(doc.keys, k)
41 }
42 doc.obj[k] = nil
43 } else {
44 _ = doc.remove(k, &ApplyOptions{})
45 }
46 } else {
47 cur, ok := doc.obj[k]
48
49 if !ok || cur == nil {
50 pruneNulls(v)
51 _ = doc.set(k, v, &ApplyOptions{})
52 } else {
53 _ = doc.set(k, merge(cur, v, mergeMerge), &ApplyOptions{})
54 }
55 }
56 }
57 }
58
59 func pruneNulls(n *lazyNode) {
60 sub, err := n.intoDoc()
61
62 if err == nil {
63 pruneDocNulls(sub)
64 } else {
65 ary, err := n.intoAry()
66
67 if err == nil {
68 pruneAryNulls(ary)
69 }
70 }
71 }
72
73 func pruneDocNulls(doc *partialDoc) *partialDoc {
74 for k, v := range doc.obj {
75 if v == nil {
76 _ = doc.remove(k, &ApplyOptions{})
77 } else {
78 pruneNulls(v)
79 }
80 }
81
82 return doc
83 }
84
85 func pruneAryNulls(ary *partialArray) *partialArray {
86 newAry := []*lazyNode{}
87
88 for _, v := range *ary {
89 if v != nil {
90 pruneNulls(v)
91 newAry = append(newAry, v)
92 }
93 }
94
95 *ary = newAry
96
97 return ary
98 }
99
100 var errBadJSONDoc = fmt.Errorf("Invalid JSON Document")
101 var errBadJSONPatch = fmt.Errorf("Invalid JSON Patch")
102 var errBadMergeTypes = fmt.Errorf("Mismatched JSON Documents")
103
104 // MergeMergePatches merges two merge patches together, such that
105 // applying this resulting merged merge patch to a document yields the same
106 // as merging each merge patch to the document in succession.
107 func MergeMergePatches(patch1Data, patch2Data []byte) ([]byte, error) {
108 return doMergePatch(patch1Data, patch2Data, true)
109 }
110
111 // MergePatch merges the patchData into the docData.
112 func MergePatch(docData, patchData []byte) ([]byte, error) {
113 return doMergePatch(docData, patchData, false)
114 }
115
116 func doMergePatch(docData, patchData []byte, mergeMerge bool) ([]byte, error) {
117 doc := &partialDoc{}
118
119 docErr := json.Unmarshal(docData, doc)
120
121 patch := &partialDoc{}
122
123 patchErr := json.Unmarshal(patchData, patch)
124
125 if isSyntaxError(docErr) {
126 return nil, errBadJSONDoc
127 }
128
129 if isSyntaxError(patchErr) {
130 return nil, errBadJSONPatch
131 }
132
133 if docErr == nil && doc.obj == nil {
134 return nil, errBadJSONDoc
135 }
136
137 if patchErr == nil && patch.obj == nil {
138 return nil, errBadJSONPatch
139 }
140
141 if docErr != nil || patchErr != nil {
142 // Not an error, just not a doc, so we turn straight into the patch
143 if patchErr == nil {
144 if mergeMerge {
145 doc = patch
146 } else {
147 doc = pruneDocNulls(patch)
148 }
149 } else {
150 patchAry := &partialArray{}
151 patchErr = json.Unmarshal(patchData, patchAry)
152
153 if patchErr != nil {
154 return nil, errBadJSONPatch
155 }
156
157 pruneAryNulls(patchAry)
158
159 out, patchErr := json.Marshal(patchAry)
160
161 if patchErr != nil {
162 return nil, errBadJSONPatch
163 }
164
165 return out, nil
166 }
167 } else {
168 mergeDocs(doc, patch, mergeMerge)
169 }
170
171 return json.Marshal(doc)
172 }
173
174 func isSyntaxError(err error) bool {
175 if _, ok := err.(*json.SyntaxError); ok {
176 return true
177 }
178 if _, ok := err.(*syntaxError); ok {
179 return true
180 }
181 return false
182 }
183
184 // resemblesJSONArray indicates whether the byte-slice "appears" to be
185 // a JSON array or not.
186 // False-positives are possible, as this function does not check the internal
187 // structure of the array. It only checks that the outer syntax is present and
188 // correct.
189 func resemblesJSONArray(input []byte) bool {
190 input = bytes.TrimSpace(input)
191
192 hasPrefix := bytes.HasPrefix(input, []byte("["))
193 hasSuffix := bytes.HasSuffix(input, []byte("]"))
194
195 return hasPrefix && hasSuffix
196 }
197
198 // CreateMergePatch will return a merge patch document capable of converting
199 // the original document(s) to the modified document(s).
200 // The parameters can be bytes of either two JSON Documents, or two arrays of
201 // JSON documents.
202 // The merge patch returned follows the specification defined at http://tools.ietf.org/html/draft-ietf-appsawg-json-merge-patch-07
203 func CreateMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) {
204 originalResemblesArray := resemblesJSONArray(originalJSON)
205 modifiedResemblesArray := resemblesJSONArray(modifiedJSON)
206
207 // Do both byte-slices seem like JSON arrays?
208 if originalResemblesArray && modifiedResemblesArray {
209 return createArrayMergePatch(originalJSON, modifiedJSON)
210 }
211
212 // Are both byte-slices are not arrays? Then they are likely JSON objects...
213 if !originalResemblesArray && !modifiedResemblesArray {
214 return createObjectMergePatch(originalJSON, modifiedJSON)
215 }
216
217 // None of the above? Then return an error because of mismatched types.
218 return nil, errBadMergeTypes
219 }
220
221 // createObjectMergePatch will return a merge-patch document capable of
222 // converting the original document to the modified document.
223 func createObjectMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) {
224 originalDoc := map[string]interface{}{}
225 modifiedDoc := map[string]interface{}{}
226
227 err := json.Unmarshal(originalJSON, &originalDoc)
228 if err != nil {
229 return nil, errBadJSONDoc
230 }
231
232 err = json.Unmarshal(modifiedJSON, &modifiedDoc)
233 if err != nil {
234 return nil, errBadJSONDoc
235 }
236
237 dest, err := getDiff(originalDoc, modifiedDoc)
238 if err != nil {
239 return nil, err
240 }
241
242 return json.Marshal(dest)
243 }
244
245 // createArrayMergePatch will return an array of merge-patch documents capable
246 // of converting the original document to the modified document for each
247 // pair of JSON documents provided in the arrays.
248 // Arrays of mismatched sizes will result in an error.
249 func createArrayMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) {
250 originalDocs := []json.RawMessage{}
251 modifiedDocs := []json.RawMessage{}
252
253 err := json.Unmarshal(originalJSON, &originalDocs)
254 if err != nil {
255 return nil, errBadJSONDoc
256 }
257
258 err = json.Unmarshal(modifiedJSON, &modifiedDocs)
259 if err != nil {
260 return nil, errBadJSONDoc
261 }
262
263 total := len(originalDocs)
264 if len(modifiedDocs) != total {
265 return nil, errBadJSONDoc
266 }
267
268 result := []json.RawMessage{}
269 for i := 0; i < len(originalDocs); i++ {
270 original := originalDocs[i]
271 modified := modifiedDocs[i]
272
273 patch, err := createObjectMergePatch(original, modified)
274 if err != nil {
275 return nil, err
276 }
277
278 result = append(result, json.RawMessage(patch))
279 }
280
281 return json.Marshal(result)
282 }
283
284 // Returns true if the array matches (must be json types).
285 // As is idiomatic for go, an empty array is not the same as a nil array.
286 func matchesArray(a, b []interface{}) bool {
287 if len(a) != len(b) {
288 return false
289 }
290 if (a == nil && b != nil) || (a != nil && b == nil) {
291 return false
292 }
293 for i := range a {
294 if !matchesValue(a[i], b[i]) {
295 return false
296 }
297 }
298 return true
299 }
300
301 // Returns true if the values matches (must be json types)
302 // The types of the values must match, otherwise it will always return false
303 // If two map[string]interface{} are given, all elements must match.
304 func matchesValue(av, bv interface{}) bool {
305 if reflect.TypeOf(av) != reflect.TypeOf(bv) {
306 return false
307 }
308 switch at := av.(type) {
309 case string:
310 bt := bv.(string)
311 if bt == at {
312 return true
313 }
314 case float64:
315 bt := bv.(float64)
316 if bt == at {
317 return true
318 }
319 case bool:
320 bt := bv.(bool)
321 if bt == at {
322 return true
323 }
324 case nil:
325 // Both nil, fine.
326 return true
327 case map[string]interface{}:
328 bt := bv.(map[string]interface{})
329 if len(bt) != len(at) {
330 return false
331 }
332 for key := range bt {
333 av, aOK := at[key]
334 bv, bOK := bt[key]
335 if aOK != bOK {
336 return false
337 }
338 if !matchesValue(av, bv) {
339 return false
340 }
341 }
342 return true
343 case []interface{}:
344 bt := bv.([]interface{})
345 return matchesArray(at, bt)
346 }
347 return false
348 }
349
350 // getDiff returns the (recursive) difference between a and b as a map[string]interface{}.
351 func getDiff(a, b map[string]interface{}) (map[string]interface{}, error) {
352 into := map[string]interface{}{}
353 for key, bv := range b {
354 av, ok := a[key]
355 // value was added
356 if !ok {
357 into[key] = bv
358 continue
359 }
360 // If types have changed, replace completely
361 if reflect.TypeOf(av) != reflect.TypeOf(bv) {
362 into[key] = bv
363 continue
364 }
365 // Types are the same, compare values
366 switch at := av.(type) {
367 case map[string]interface{}:
368 bt := bv.(map[string]interface{})
369 dst := make(map[string]interface{}, len(bt))
370 dst, err := getDiff(at, bt)
371 if err != nil {
372 return nil, err
373 }
374 if len(dst) > 0 {
375 into[key] = dst
376 }
377 case string, float64, bool:
378 if !matchesValue(av, bv) {
379 into[key] = bv
380 }
381 case []interface{}:
382 bt := bv.([]interface{})
383 if !matchesArray(at, bt) {
384 into[key] = bv
385 }
386 case nil:
387 switch bv.(type) {
388 case nil:
389 // Both nil, fine.
390 default:
391 into[key] = bv
392 }
393 default:
394 panic(fmt.Sprintf("Unknown type:%T in key %s", av, key))
395 }
396 }
397 // Now add all deleted values as nil
398 for key := range a {
399 _, found := b[key]
400 if !found {
401 into[key] = nil
402 }
403 }
404 return into, nil
405 }
0 package jsonpatch
1
2 import (
3 "fmt"
4 "strings"
5 "testing"
6 )
7
8 func mergePatch(doc, patch string) string {
9 out, err := MergePatch([]byte(doc), []byte(patch))
10
11 if err != nil {
12 panic(err)
13 }
14
15 return string(out)
16 }
17
18 func TestMergePatchReplaceKey(t *testing.T) {
19 doc := `{ "title": "hello" }`
20 pat := `{ "title": "goodbye" }`
21
22 res := mergePatch(doc, pat)
23
24 if !compareJSON(pat, res) {
25 t.Fatalf("Key was not replaced")
26 }
27 }
28
29 func TestMergePatchIgnoresOtherValues(t *testing.T) {
30 doc := `{ "title": "hello", "age": 18 }`
31 pat := `{ "title": "goodbye" }`
32
33 res := mergePatch(doc, pat)
34
35 exp := `{ "title": "goodbye", "age": 18 }`
36
37 if !compareJSON(exp, res) {
38 t.Fatalf("Key was not replaced")
39 }
40 }
41
42 func TestMergePatchNilDoc(t *testing.T) {
43 doc := `{ "title": null }`
44 pat := `{ "title": {"foo": "bar"} }`
45
46 res := mergePatch(doc, pat)
47
48 exp := `{ "title": {"foo": "bar"} }`
49
50 if !compareJSON(exp, res) {
51 t.Fatalf("Key was not replaced")
52 }
53 }
54
55 func TestMergePatchRecursesIntoObjects(t *testing.T) {
56 doc := `{ "person": { "title": "hello", "age": 18 } }`
57 pat := `{ "person": { "title": "goodbye" } }`
58
59 res := mergePatch(doc, pat)
60
61 exp := `{ "person": { "title": "goodbye", "age": 18 } }`
62
63 if !compareJSON(exp, res) {
64 t.Fatalf("Key was not replaced: %s", res)
65 }
66 }
67
68 type nonObjectCases struct {
69 doc, pat, res string
70 }
71
72 func TestMergePatchReplacesNonObjectsWholesale(t *testing.T) {
73 a1 := `[1]`
74 a2 := `[2]`
75 o1 := `{ "a": 1 }`
76 o2 := `{ "a": 2 }`
77 o3 := `{ "a": 1, "b": 1 }`
78 o4 := `{ "a": 2, "b": 1 }`
79
80 cases := []nonObjectCases{
81 {a1, a2, a2},
82 {o1, a2, a2},
83 {a1, o1, o1},
84 {o3, o2, o4},
85 }
86
87 for _, c := range cases {
88 act := mergePatch(c.doc, c.pat)
89
90 if !compareJSON(c.res, act) {
91 t.Errorf("whole object replacement failed")
92 }
93 }
94 }
95
96 func TestMergePatchReturnsErrorOnBadJSON(t *testing.T) {
97 _, err := MergePatch([]byte(`[[[[`), []byte(`1`))
98
99 if err == nil {
100 t.Errorf("Did not return an error for bad json: %s", err)
101 }
102
103 _, err = MergePatch([]byte(`1`), []byte(`[[[[`))
104
105 if err == nil {
106 t.Errorf("Did not return an error for bad json: %s", err)
107 }
108 }
109
110 func TestMergePatchReturnsEmptyArrayOnEmptyArray(t *testing.T) {
111 doc := `{ "array": ["one", "two"] }`
112 pat := `{ "array": [] }`
113
114 exp := `{ "array": [] }`
115
116 res, err := MergePatch([]byte(doc), []byte(pat))
117
118 if err != nil {
119 t.Errorf("Unexpected error: %s, %s", err, string(res))
120 }
121
122 if !compareJSON(exp, string(res)) {
123 t.Fatalf("Emtpy array did not return not return as empty array")
124 }
125 }
126
127 var rfcTests = []struct {
128 target string
129 patch string
130 expected string
131 }{
132 // test cases from https://tools.ietf.org/html/rfc7386#appendix-A
133 {target: `{"a":"b"}`, patch: `{"a":"c"}`, expected: `{"a":"c"}`},
134 {target: `{"a":"b"}`, patch: `{"b":"c"}`, expected: `{"a":"b","b":"c"}`},
135 {target: `{"a":"b"}`, patch: `{"a":null}`, expected: `{}`},
136 {target: `{"a":"b","b":"c"}`, patch: `{"a":null}`, expected: `{"b":"c"}`},
137 {target: `{"a":["b"]}`, patch: `{"a":"c"}`, expected: `{"a":"c"}`},
138 {target: `{"a":"c"}`, patch: `{"a":["b"]}`, expected: `{"a":["b"]}`},
139 {target: `{"a":{"b": "c"}}`, patch: `{"a": {"b": "d","c": null}}`, expected: `{"a":{"b":"d"}}`},
140 {target: `{"a":[{"b":"c"}]}`, patch: `{"a":[1]}`, expected: `{"a":[1]}`},
141 {target: `["a","b"]`, patch: `["c","d"]`, expected: `["c","d"]`},
142 {target: `{"a":"b"}`, patch: `["c"]`, expected: `["c"]`},
143 // {target: `{"a":"foo"}`, patch: `null`, expected: `null`},
144 // {target: `{"a":"foo"}`, patch: `"bar"`, expected: `"bar"`},
145 {target: `{"e":null}`, patch: `{"a":1}`, expected: `{"a":1,"e":null}`},
146 {target: `[1,2]`, patch: `{"a":"b","c":null}`, expected: `{"a":"b"}`},
147 {target: `{}`, patch: `{"a":{"bb":{"ccc":null}}}`, expected: `{"a":{"bb":{}}}`},
148 }
149
150 func TestMergePatchRFCCases(t *testing.T) {
151 for i, c := range rfcTests {
152 out := mergePatch(c.target, c.patch)
153
154 if !compareJSON(out, c.expected) {
155 t.Errorf("case[%d], patch '%s' did not apply properly to '%s'. expected:\n'%s'\ngot:\n'%s'", i, c.patch, c.target, c.expected, out)
156 }
157 }
158 }
159
160 var rfcFailTests = `
161 {"a":"foo"} | null
162 {"a":"foo"} | "bar"
163 `
164
165 func TestMergePatchFailRFCCases(t *testing.T) {
166 tests := strings.Split(rfcFailTests, "\n")
167
168 for _, c := range tests {
169 if strings.TrimSpace(c) == "" {
170 continue
171 }
172
173 parts := strings.SplitN(c, "|", 2)
174
175 doc := strings.TrimSpace(parts[0])
176 pat := strings.TrimSpace(parts[1])
177
178 out, err := MergePatch([]byte(doc), []byte(pat))
179
180 if err != errBadJSONPatch {
181 t.Errorf("error not returned properly: %s, %s", err, string(out))
182 }
183 }
184
185 }
186
187 func TestResembleJSONArray(t *testing.T) {
188 testCases := []struct {
189 input []byte
190 expected bool
191 }{
192 // Failure cases
193 {input: []byte(``), expected: false},
194 {input: []byte(`not an array`), expected: false},
195 {input: []byte(`{"foo": "bar"}`), expected: false},
196 {input: []byte(`{"fizz": ["buzz"]}`), expected: false},
197 {input: []byte(`[bad suffix`), expected: false},
198 {input: []byte(`bad prefix]`), expected: false},
199 {input: []byte(`][`), expected: false},
200
201 // Valid cases
202 {input: []byte(`[]`), expected: true},
203 {input: []byte(`["foo", "bar"]`), expected: true},
204 {input: []byte(`[["foo", "bar"]]`), expected: true},
205 {input: []byte(`[not valid syntax]`), expected: true},
206
207 // Valid cases with whitespace
208 {input: []byte(` []`), expected: true},
209 {input: []byte(`[] `), expected: true},
210 {input: []byte(` [] `), expected: true},
211 {input: []byte(` [ ] `), expected: true},
212 {input: []byte("\t[]"), expected: true},
213 {input: []byte("[]\n"), expected: true},
214 {input: []byte("\n\t\r[]"), expected: true},
215 }
216
217 for _, test := range testCases {
218 result := resemblesJSONArray(test.input)
219 if result != test.expected {
220 t.Errorf(
221 `expected "%t" but received "%t" for case: "%s"`,
222 test.expected,
223 result,
224 string(test.input),
225 )
226 }
227 }
228 }
229
230 func TestCreateMergePatchReplaceKey(t *testing.T) {
231 doc := `{ "title": "hello", "nested": {"one": 1, "two": 2} }`
232 pat := `{ "title": "goodbye", "nested": {"one": 2, "two": 2} }`
233
234 exp := `{ "title": "goodbye", "nested": {"one": 2} }`
235
236 res, err := CreateMergePatch([]byte(doc), []byte(pat))
237
238 if err != nil {
239 t.Errorf("Unexpected error: %s, %s", err, string(res))
240 }
241
242 if !compareJSON(exp, string(res)) {
243 t.Fatalf("Key was not replaced")
244 }
245 }
246
247 func TestCreateMergePatchGetArray(t *testing.T) {
248 doc := `{ "title": "hello", "array": ["one", "two"], "notmatch": [1, 2, 3] }`
249 pat := `{ "title": "hello", "array": ["one", "two", "three"], "notmatch": [1, 2, 3] }`
250
251 exp := `{ "array": ["one", "two", "three"] }`
252
253 res, err := CreateMergePatch([]byte(doc), []byte(pat))
254
255 if err != nil {
256 t.Errorf("Unexpected error: %s, %s", err, string(res))
257 }
258
259 if !compareJSON(exp, string(res)) {
260 t.Fatalf("Array was not added")
261 }
262 }
263
264 func TestCreateMergePatchGetObjArray(t *testing.T) {
265 doc := `{ "title": "hello", "array": [{"banana": true}, {"evil": false}], "notmatch": [{"one":1}, {"two":2}, {"three":3}] }`
266 pat := `{ "title": "hello", "array": [{"banana": false}, {"evil": true}], "notmatch": [{"one":1}, {"two":2}, {"three":3}] }`
267
268 exp := `{ "array": [{"banana": false}, {"evil": true}] }`
269
270 res, err := CreateMergePatch([]byte(doc), []byte(pat))
271
272 if err != nil {
273 t.Errorf("Unexpected error: %s, %s", err, string(res))
274 }
275
276 if !compareJSON(exp, string(res)) {
277 t.Fatalf("Object array was not added")
278 }
279 }
280
281 func TestCreateMergePatchDeleteKey(t *testing.T) {
282 doc := `{ "title": "hello", "nested": {"one": 1, "two": 2} }`
283 pat := `{ "title": "hello", "nested": {"one": 1} }`
284
285 exp := `{"nested":{"two":null}}`
286
287 res, err := CreateMergePatch([]byte(doc), []byte(pat))
288
289 if err != nil {
290 t.Errorf("Unexpected error: %s, %s", err, string(res))
291 }
292
293 // We cannot use "compareJSON", since Equals does not report a difference if the value is null
294 if exp != string(res) {
295 t.Fatalf("Key was not removed")
296 }
297 }
298
299 func TestCreateMergePatchEmptyArray(t *testing.T) {
300 doc := `{ "array": null }`
301 pat := `{ "array": [] }`
302
303 exp := `{"array":[]}`
304
305 res, err := CreateMergePatch([]byte(doc), []byte(pat))
306
307 if err != nil {
308 t.Errorf("Unexpected error: %s, %s", err, string(res))
309 }
310
311 // We cannot use "compareJSON", since Equals does not report a difference if the value is null
312 if exp != string(res) {
313 t.Fatalf("Key was not removed")
314 }
315 }
316
317 func TestCreateMergePatchNil(t *testing.T) {
318 doc := `{ "title": "hello", "nested": {"one": 1, "two": [{"one":null}, {"two":null}, {"three":null}]} }`
319 pat := doc
320
321 exp := `{}`
322
323 res, err := CreateMergePatch([]byte(doc), []byte(pat))
324
325 if err != nil {
326 t.Errorf("Unexpected error: %s, %s", err, string(res))
327 }
328
329 if !compareJSON(exp, string(res)) {
330 t.Fatalf("Object array was not added")
331 }
332 }
333
334 func TestCreateMergePatchObjArray(t *testing.T) {
335 doc := `{ "array": [ {"a": {"b": 2}}, {"a": {"b": 3}} ]}`
336 exp := `{}`
337
338 res, err := CreateMergePatch([]byte(doc), []byte(doc))
339
340 if err != nil {
341 t.Errorf("Unexpected error: %s, %s", err, string(res))
342 }
343
344 // We cannot use "compareJSON", since Equals does not report a difference if the value is null
345 if exp != string(res) {
346 t.Fatalf("Array was not empty, was " + string(res))
347 }
348 }
349
350 func TestCreateMergePatchSameOuterArray(t *testing.T) {
351 doc := `[{"foo": "bar"}]`
352 pat := doc
353 exp := `[{}]`
354
355 res, err := CreateMergePatch([]byte(doc), []byte(pat))
356
357 if err != nil {
358 t.Errorf("Unexpected error: %s, %s", err, string(res))
359 }
360
361 if !compareJSON(exp, string(res)) {
362 t.Fatalf("Outer array was not unmodified")
363 }
364 }
365
366 func TestCreateMergePatchModifiedOuterArray(t *testing.T) {
367 doc := `[{"name": "John"}, {"name": "Will"}]`
368 pat := `[{"name": "Jane"}, {"name": "Will"}]`
369 exp := `[{"name": "Jane"}, {}]`
370
371 res, err := CreateMergePatch([]byte(doc), []byte(pat))
372
373 if err != nil {
374 t.Errorf("Unexpected error: %s, %s", err, string(res))
375 }
376
377 if !compareJSON(exp, string(res)) {
378 t.Fatalf("Expected %s but received %s", exp, res)
379 }
380 }
381
382 func TestCreateMergePatchMismatchedOuterArray(t *testing.T) {
383 doc := `[{"name": "John"}, {"name": "Will"}]`
384 pat := `[{"name": "Jane"}]`
385
386 _, err := CreateMergePatch([]byte(doc), []byte(pat))
387
388 if err == nil {
389 t.Errorf("Expected error due to array length differences but received none")
390 }
391 }
392
393 func TestCreateMergePatchMismatchedOuterTypes(t *testing.T) {
394 doc := `[{"name": "John"}]`
395 pat := `{"name": "Jane"}`
396
397 _, err := CreateMergePatch([]byte(doc), []byte(pat))
398
399 if err == nil {
400 t.Errorf("Expected error due to mismatched types but received none")
401 }
402 }
403
404 func TestCreateMergePatchNoDifferences(t *testing.T) {
405 doc := `{ "title": "hello", "nested": {"one": 1, "two": 2} }`
406 pat := doc
407
408 exp := `{}`
409
410 res, err := CreateMergePatch([]byte(doc), []byte(pat))
411
412 if err != nil {
413 t.Errorf("Unexpected error: %s, %s", err, string(res))
414 }
415
416 if !compareJSON(exp, string(res)) {
417 t.Fatalf("Key was not replaced")
418 }
419 }
420
421 func TestCreateMergePatchComplexMatch(t *testing.T) {
422 doc := `{"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4], "nested": {"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4]} }`
423 empty := `{}`
424 res, err := CreateMergePatch([]byte(doc), []byte(doc))
425
426 if err != nil {
427 t.Errorf("Unexpected error: %s, %s", err, string(res))
428 }
429
430 // We cannot use "compareJSON", since Equals does not report a difference if the value is null
431 if empty != string(res) {
432 t.Fatalf("Did not get empty result, was:%s", string(res))
433 }
434 }
435
436 func TestCreateMergePatchComplexAddAll(t *testing.T) {
437 doc := `{"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4], "nested": {"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4]} }`
438 empty := `{}`
439 res, err := CreateMergePatch([]byte(empty), []byte(doc))
440
441 if err != nil {
442 t.Errorf("Unexpected error: %s, %s", err, string(res))
443 }
444
445 if !compareJSON(doc, string(res)) {
446 t.Fatalf("Did not get everything as, it was:\n%s", string(res))
447 }
448 }
449
450 // createNestedMap created a series of nested map objects such that the number of
451 // objects is roughly 2^n (precisely, 2^(n+1)-1).
452 func createNestedMap(m map[string]interface{}, depth int, objectCount *int) {
453 if depth == 0 {
454 return
455 }
456 for i := 0; i< 2;i++ {
457 nested := map[string]interface{}{}
458 *objectCount += 1
459 createNestedMap(nested, depth-1, objectCount)
460 m[fmt.Sprintf("key-%v", i)] = nested
461 }
462 }
463
464 func TestMatchesValue(t *testing.T) {
465 testcases := []struct {
466 name string
467 a interface{}
468 b interface{}
469 want bool
470 }{
471 {
472 name: "map empty",
473 a: map[string]interface{}{},
474 b: map[string]interface{}{},
475 want: true,
476 },
477 {
478 name: "map equal keys, equal non-nil value",
479 a: map[string]interface{}{"1": true},
480 b: map[string]interface{}{"1": true},
481 want: true,
482 },
483 {
484 name: "map equal keys, equal nil value",
485 a: map[string]interface{}{"1": nil},
486 b: map[string]interface{}{"1": nil},
487 want: true,
488 },
489
490 {
491 name: "map different value",
492 a: map[string]interface{}{"1": true},
493 b: map[string]interface{}{"1": false},
494 want: false,
495 },
496 {
497 name: "map different key, matching non-nil value",
498 a: map[string]interface{}{"1": true},
499 b: map[string]interface{}{"2": true},
500 want: false,
501 },
502 {
503 name: "map different key, matching nil value",
504 a: map[string]interface{}{"1": nil},
505 b: map[string]interface{}{"2": nil},
506 want: false,
507 },
508 {
509 name: "map different key, first nil value",
510 a: map[string]interface{}{"1": true},
511 b: map[string]interface{}{"2": nil},
512 want: false,
513 },
514 {
515 name: "map different key, second nil value",
516 a: map[string]interface{}{"1": nil},
517 b: map[string]interface{}{"2": true},
518 want: false,
519 },
520 }
521 for _, tc := range testcases {
522 t.Run(tc.name, func(t *testing.T) {
523 got := matchesValue(tc.a, tc.b)
524 if got != tc.want {
525 t.Fatalf("want %v, got %v", tc.want, got)
526 }
527 })
528 }
529 }
530
531 func benchmarkMatchesValueWithDeeplyNestedFields(depth int, b *testing.B) {
532 a := map[string]interface{}{}
533 objCount := 1
534 createNestedMap(a, depth, &objCount)
535 b.ResetTimer()
536 b.Run(fmt.Sprintf("objectCount=%v", objCount), func(b *testing.B) {
537 for i := 0; i < b.N; i++ {
538 if !matchesValue(a, a) {
539 b.Errorf("Should be equal")
540 }
541 }
542 })
543 }
544
545 func BenchmarkMatchesValue1(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(1, b) }
546 func BenchmarkMatchesValue2(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(2, b) }
547 func BenchmarkMatchesValue3(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(3, b) }
548 func BenchmarkMatchesValue4(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(4, b) }
549 func BenchmarkMatchesValue5(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(5, b) }
550 func BenchmarkMatchesValue6(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(6, b) }
551 func BenchmarkMatchesValue7(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(7, b) }
552 func BenchmarkMatchesValue8(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(8, b) }
553 func BenchmarkMatchesValue9(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(9, b) }
554 func BenchmarkMatchesValue10(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(10, b) }
555
556 func TestCreateMergePatchComplexRemoveAll(t *testing.T) {
557 doc := `{"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4], "nested": {"hello": "world","t": true ,"f": false, "n": null,"i": 123,"pi": 3.1416,"a": [1, 2, 3, 4]} }`
558 exp := `{"a":null,"f":null,"hello":null,"i":null,"n":null,"nested":null,"pi":null,"t":null}`
559 empty := `{}`
560 res, err := CreateMergePatch([]byte(doc), []byte(empty))
561
562 if err != nil {
563 t.Errorf("Unexpected error: %s, %s", err, string(res))
564 }
565
566 if exp != string(res) {
567 t.Fatalf("Did not get result, was:%s", string(res))
568 }
569
570 // FIXME: Crashes if using compareJSON like this:
571 /*
572 if !compareJSON(doc, string(res)) {
573 t.Fatalf("Did not get everything as, it was:\n%s", string(res))
574 }
575 */
576 }
577
578 func TestCreateMergePatchObjectWithInnerArray(t *testing.T) {
579 stateString := `{
580 "OuterArray": [
581 {
582 "InnerArray": [
583 {
584 "StringAttr": "abc123"
585 }
586 ],
587 "StringAttr": "def456"
588 }
589 ]
590 }`
591
592 patch, err := CreateMergePatch([]byte(stateString), []byte(stateString))
593 if err != nil {
594 t.Fatal(err)
595 }
596
597 if string(patch) != "{}" {
598 t.Fatalf("Patch should have been {} but was: %v", string(patch))
599 }
600 }
601
602 func TestCreateMergePatchReplaceKeyNotEscape(t *testing.T) {
603 doc := `{ "title": "hello", "nested": {"title/escaped": 1, "two": 2} }`
604 pat := `{ "title": "goodbye", "nested": {"title/escaped": 2, "two": 2} }`
605
606 exp := `{ "title": "goodbye", "nested": {"title/escaped": 2} }`
607
608 res, err := CreateMergePatch([]byte(doc), []byte(pat))
609
610 if err != nil {
611 t.Errorf("Unexpected error: %s, %s", err, string(res))
612 }
613
614 if !compareJSON(exp, string(res)) {
615 t.Log(string(res))
616 t.Fatalf("Key was not replaced")
617 }
618 }
619
620 func TestMergePatchReplaceKeyNotEscaping(t *testing.T) {
621 doc := `{ "obj": { "title/escaped": "hello" } }`
622 pat := `{ "obj": { "title/escaped": "goodbye" } }`
623 exp := `{ "obj": { "title/escaped": "goodbye" } }`
624
625 res := mergePatch(doc, pat)
626
627 if !compareJSON(exp, res) {
628 t.Fatalf("Key was not replaced")
629 }
630 }
631
632 func TestMergeMergePatches(t *testing.T) {
633 cases := []struct {
634 demonstrates string
635 p1 string
636 p2 string
637 exp string
638 }{
639 {
640 demonstrates: "simple patches are merged normally",
641 p1: `{"add1": 1}`,
642 p2: `{"add2": 2}`,
643 exp: `{"add1": 1, "add2": 2}`,
644 },
645 {
646 demonstrates: "nulls are kept",
647 p1: `{"del1": null}`,
648 p2: `{"del2": null}`,
649 exp: `{"del1": null, "del2": null}`,
650 },
651 {
652 demonstrates: "a key added then deleted is kept deleted",
653 p1: `{"add_then_delete": "atd"}`,
654 p2: `{"add_then_delete": null}`,
655 exp: `{"add_then_delete": null}`,
656 },
657 {
658 demonstrates: "a key deleted then added is kept added",
659 p1: `{"delete_then_add": null}`,
660 p2: `{"delete_then_add": "dta"}`,
661 exp: `{"delete_then_add": "dta"}`,
662 },
663 {
664 demonstrates: "object overrides array",
665 p1: `[]`,
666 p2: `{"del": null, "add": "a"}`,
667 exp: `{"del": null, "add": "a"}`,
668 },
669 {
670 demonstrates: "array overrides object",
671 p1: `{"del": null, "add": "a"}`,
672 p2: `[]`,
673 exp: `[]`,
674 },
675 }
676
677 for _, c := range cases {
678 out, err := MergeMergePatches([]byte(c.p1), []byte(c.p2))
679
680 if err != nil {
681 panic(err)
682 }
683
684 if !compareJSON(c.exp, string(out)) {
685 t.Logf("Error while trying to demonstrate: %v", c.demonstrates)
686 t.Logf("Got %v", string(out))
687 t.Logf("Expected %v", c.exp)
688 t.Fatalf("Merged merge patch is incorrect")
689 }
690 }
691 }
0 package jsonpatch
1
2 import (
3 "bytes"
4 "encoding/json"
5 "fmt"
6 "strconv"
7 "strings"
8
9 "github.com/pkg/errors"
10 )
11
12 const (
13 eRaw = iota
14 eDoc
15 eAry
16 )
17
18 var (
19 // SupportNegativeIndices decides whether to support non-standard practice of
20 // allowing negative indices to mean indices starting at the end of an array.
21 // Default to true.
22 SupportNegativeIndices bool = true
23 // AccumulatedCopySizeLimit limits the total size increase in bytes caused by
24 // "copy" operations in a patch.
25 AccumulatedCopySizeLimit int64 = 0
26 startObject = json.Delim('{')
27 endObject = json.Delim('}')
28 startArray = json.Delim('[')
29 endArray = json.Delim(']')
30 )
31
32 var (
33 ErrTestFailed = errors.New("test failed")
34 ErrMissing = errors.New("missing value")
35 ErrUnknownType = errors.New("unknown object type")
36 ErrInvalid = errors.New("invalid state detected")
37 ErrInvalidIndex = errors.New("invalid index referenced")
38
39 rawJSONArray = []byte("[]")
40 rawJSONObject = []byte("{}")
41 rawJSONNull = []byte("null")
42 )
43
44 type lazyNode struct {
45 raw *json.RawMessage
46 doc *partialDoc
47 ary partialArray
48 which int
49 }
50
51 // Operation is a single JSON-Patch step, such as a single 'add' operation.
52 type Operation map[string]*json.RawMessage
53
54 // Patch is an ordered collection of Operations.
55 type Patch []Operation
56
57 type partialDoc struct {
58 keys []string
59 obj map[string]*lazyNode
60 }
61
62 type partialArray []*lazyNode
63
64 type container interface {
65 get(key string, options *ApplyOptions) (*lazyNode, error)
66 set(key string, val *lazyNode, options *ApplyOptions) error
67 add(key string, val *lazyNode, options *ApplyOptions) error
68 remove(key string, options *ApplyOptions) error
69 }
70
71 // ApplyOptions specifies options for calls to ApplyWithOptions.
72 // Use NewApplyOptions to obtain default values for ApplyOptions.
73 type ApplyOptions struct {
74 // SupportNegativeIndices decides whether to support non-standard practice of
75 // allowing negative indices to mean indices starting at the end of an array.
76 // Default to true.
77 SupportNegativeIndices bool
78 // AccumulatedCopySizeLimit limits the total size increase in bytes caused by
79 // "copy" operations in a patch.
80 AccumulatedCopySizeLimit int64
81 // AllowMissingPathOnRemove indicates whether to fail "remove" operations when the target path is missing.
82 // Default to false.
83 AllowMissingPathOnRemove bool
84 // EnsurePathExistsOnAdd instructs json-patch to recursively create the missing parts of path on "add" operation.
85 // Default to false.
86 EnsurePathExistsOnAdd bool
87 }
88
89 // NewApplyOptions creates a default set of options for calls to ApplyWithOptions.
90 func NewApplyOptions() *ApplyOptions {
91 return &ApplyOptions{
92 SupportNegativeIndices: SupportNegativeIndices,
93 AccumulatedCopySizeLimit: AccumulatedCopySizeLimit,
94 AllowMissingPathOnRemove: false,
95 EnsurePathExistsOnAdd: false,
96 }
97 }
98
99 func newLazyNode(raw *json.RawMessage) *lazyNode {
100 return &lazyNode{raw: raw, doc: nil, ary: nil, which: eRaw}
101 }
102
103 func newRawMessage(buf []byte) *json.RawMessage {
104 ra := make(json.RawMessage, len(buf))
105 copy(ra, buf)
106 return &ra
107 }
108
109 func (n *lazyNode) MarshalJSON() ([]byte, error) {
110 switch n.which {
111 case eRaw:
112 return json.Marshal(n.raw)
113 case eDoc:
114 return json.Marshal(n.doc)
115 case eAry:
116 return json.Marshal(n.ary)
117 default:
118 return nil, ErrUnknownType
119 }
120 }
121
122 func (n *lazyNode) UnmarshalJSON(data []byte) error {
123 dest := make(json.RawMessage, len(data))
124 copy(dest, data)
125 n.raw = &dest
126 n.which = eRaw
127 return nil
128 }
129
130 func (n *partialDoc) MarshalJSON() ([]byte, error) {
131 var buf bytes.Buffer
132 if _, err := buf.WriteString("{"); err != nil {
133 return nil, err
134 }
135 for i, k := range n.keys {
136 if i > 0 {
137 if _, err := buf.WriteString(", "); err != nil {
138 return nil, err
139 }
140 }
141 key, err := json.Marshal(k)
142 if err != nil {
143 return nil, err
144 }
145 if _, err := buf.Write(key); err != nil {
146 return nil, err
147 }
148 if _, err := buf.WriteString(": "); err != nil {
149 return nil, err
150 }
151 value, err := json.Marshal(n.obj[k])
152 if err != nil {
153 return nil, err
154 }
155 if _, err := buf.Write(value); err != nil {
156 return nil, err
157 }
158 }
159 if _, err := buf.WriteString("}"); err != nil {
160 return nil, err
161 }
162 return buf.Bytes(), nil
163 }
164
165 type syntaxError struct {
166 msg string
167 }
168
169 func (err *syntaxError) Error() string {
170 return err.msg
171 }
172
173 func (n *partialDoc) UnmarshalJSON(data []byte) error {
174 if err := json.Unmarshal(data, &n.obj); err != nil {
175 return err
176 }
177 buffer := bytes.NewBuffer(data)
178 d := json.NewDecoder(buffer)
179 if t, err := d.Token(); err != nil {
180 return err
181 } else if t != startObject {
182 return &syntaxError{fmt.Sprintf("unexpected JSON token in document node: %s", t)}
183 }
184 for d.More() {
185 k, err := d.Token()
186 if err != nil {
187 return err
188 }
189 key, ok := k.(string)
190 if !ok {
191 return &syntaxError{fmt.Sprintf("unexpected JSON token as document node key: %s", k)}
192 }
193 if err := skipValue(d); err != nil {
194 return err
195 }
196 n.keys = append(n.keys, key)
197 }
198 return nil
199 }
200
201 func skipValue(d *json.Decoder) error {
202 t, err := d.Token()
203 if err != nil {
204 return err
205 }
206 if t != startObject && t != startArray {
207 return nil
208 }
209 for d.More() {
210 if t == startObject {
211 // consume key token
212 if _, err := d.Token(); err != nil {
213 return err
214 }
215 }
216 if err := skipValue(d); err != nil {
217 return err
218 }
219 }
220 end, err := d.Token()
221 if err != nil {
222 return err
223 }
224 if t == startObject && end != endObject {
225 return &syntaxError{msg: "expected close object token"}
226 }
227 if t == startArray && end != endArray {
228 return &syntaxError{msg: "expected close object token"}
229 }
230 return nil
231 }
232
233 func deepCopy(src *lazyNode) (*lazyNode, int, error) {
234 if src == nil {
235 return nil, 0, nil
236 }
237 a, err := src.MarshalJSON()
238 if err != nil {
239 return nil, 0, err
240 }
241 sz := len(a)
242 return newLazyNode(newRawMessage(a)), sz, nil
243 }
244
245 func (n *lazyNode) intoDoc() (*partialDoc, error) {
246 if n.which == eDoc {
247 return n.doc, nil
248 }
249
250 if n.raw == nil {
251 return nil, ErrInvalid
252 }
253
254 err := json.Unmarshal(*n.raw, &n.doc)
255
256 if err != nil {
257 return nil, err
258 }
259
260 n.which = eDoc
261 return n.doc, nil
262 }
263
264 func (n *lazyNode) intoAry() (*partialArray, error) {
265 if n.which == eAry {
266 return &n.ary, nil
267 }
268
269 if n.raw == nil {
270 return nil, ErrInvalid
271 }
272
273 err := json.Unmarshal(*n.raw, &n.ary)
274
275 if err != nil {
276 return nil, err
277 }
278
279 n.which = eAry
280 return &n.ary, nil
281 }
282
283 func (n *lazyNode) compact() []byte {
284 buf := &bytes.Buffer{}
285
286 if n.raw == nil {
287 return nil
288 }
289
290 err := json.Compact(buf, *n.raw)
291
292 if err != nil {
293 return *n.raw
294 }
295
296 return buf.Bytes()
297 }
298
299 func (n *lazyNode) tryDoc() bool {
300 if n.raw == nil {
301 return false
302 }
303
304 err := json.Unmarshal(*n.raw, &n.doc)
305
306 if err != nil {
307 return false
308 }
309
310 n.which = eDoc
311 return true
312 }
313
314 func (n *lazyNode) tryAry() bool {
315 if n.raw == nil {
316 return false
317 }
318
319 err := json.Unmarshal(*n.raw, &n.ary)
320
321 if err != nil {
322 return false
323 }
324
325 n.which = eAry
326 return true
327 }
328
329 func (n *lazyNode) equal(o *lazyNode) bool {
330 if n.which == eRaw {
331 if !n.tryDoc() && !n.tryAry() {
332 if o.which != eRaw {
333 return false
334 }
335
336 return bytes.Equal(n.compact(), o.compact())
337 }
338 }
339
340 if n.which == eDoc {
341 if o.which == eRaw {
342 if !o.tryDoc() {
343 return false
344 }
345 }
346
347 if o.which != eDoc {
348 return false
349 }
350
351 if len(n.doc.obj) != len(o.doc.obj) {
352 return false
353 }
354
355 for k, v := range n.doc.obj {
356 ov, ok := o.doc.obj[k]
357
358 if !ok {
359 return false
360 }
361
362 if (v == nil) != (ov == nil) {
363 return false
364 }
365
366 if v == nil && ov == nil {
367 continue
368 }
369
370 if !v.equal(ov) {
371 return false
372 }
373 }
374
375 return true
376 }
377
378 if o.which != eAry && !o.tryAry() {
379 return false
380 }
381
382 if len(n.ary) != len(o.ary) {
383 return false
384 }
385
386 for idx, val := range n.ary {
387 if !val.equal(o.ary[idx]) {
388 return false
389 }
390 }
391
392 return true
393 }
394
395 // Kind reads the "op" field of the Operation.
396 func (o Operation) Kind() string {
397 if obj, ok := o["op"]; ok && obj != nil {
398 var op string
399
400 err := json.Unmarshal(*obj, &op)
401
402 if err != nil {
403 return "unknown"
404 }
405
406 return op
407 }
408
409 return "unknown"
410 }
411
412 // Path reads the "path" field of the Operation.
413 func (o Operation) Path() (string, error) {
414 if obj, ok := o["path"]; ok && obj != nil {
415 var op string
416
417 err := json.Unmarshal(*obj, &op)
418
419 if err != nil {
420 return "unknown", err
421 }
422
423 return op, nil
424 }
425
426 return "unknown", errors.Wrapf(ErrMissing, "operation missing path field")
427 }
428
429 // From reads the "from" field of the Operation.
430 func (o Operation) From() (string, error) {
431 if obj, ok := o["from"]; ok && obj != nil {
432 var op string
433
434 err := json.Unmarshal(*obj, &op)
435
436 if err != nil {
437 return "unknown", err
438 }
439
440 return op, nil
441 }
442
443 return "unknown", errors.Wrapf(ErrMissing, "operation, missing from field")
444 }
445
446 func (o Operation) value() *lazyNode {
447 if obj, ok := o["value"]; ok {
448 return newLazyNode(obj)
449 }
450
451 return nil
452 }
453
454 // ValueInterface decodes the operation value into an interface.
455 func (o Operation) ValueInterface() (interface{}, error) {
456 if obj, ok := o["value"]; ok && obj != nil {
457 var v interface{}
458
459 err := json.Unmarshal(*obj, &v)
460
461 if err != nil {
462 return nil, err
463 }
464
465 return v, nil
466 }
467
468 return nil, errors.Wrapf(ErrMissing, "operation, missing value field")
469 }
470
471 func isArray(buf []byte) bool {
472 Loop:
473 for _, c := range buf {
474 switch c {
475 case ' ':
476 case '\n':
477 case '\t':
478 continue
479 case '[':
480 return true
481 default:
482 break Loop
483 }
484 }
485
486 return false
487 }
488
489 func findObject(pd *container, path string, options *ApplyOptions) (container, string) {
490 doc := *pd
491
492 split := strings.Split(path, "/")
493
494 if len(split) < 2 {
495 return nil, ""
496 }
497
498 parts := split[1 : len(split)-1]
499
500 key := split[len(split)-1]
501
502 var err error
503
504 for _, part := range parts {
505
506 next, ok := doc.get(decodePatchKey(part), options)
507
508 if next == nil || ok != nil {
509 return nil, ""
510 }
511
512 if isArray(*next.raw) {
513 doc, err = next.intoAry()
514
515 if err != nil {
516 return nil, ""
517 }
518 } else {
519 doc, err = next.intoDoc()
520
521 if err != nil {
522 return nil, ""
523 }
524 }
525 }
526
527 return doc, decodePatchKey(key)
528 }
529
530 func (d *partialDoc) set(key string, val *lazyNode, options *ApplyOptions) error {
531 found := false
532 for _, k := range d.keys {
533 if k == key {
534 found = true
535 break
536 }
537 }
538 if !found {
539 d.keys = append(d.keys, key)
540 }
541 d.obj[key] = val
542 return nil
543 }
544
545 func (d *partialDoc) add(key string, val *lazyNode, options *ApplyOptions) error {
546 return d.set(key, val, options)
547 }
548
549 func (d *partialDoc) get(key string, options *ApplyOptions) (*lazyNode, error) {
550 v, ok := d.obj[key]
551 if !ok {
552 return v, errors.Wrapf(ErrMissing, "unable to get nonexistent key: %s", key)
553 }
554 return v, nil
555 }
556
557 func (d *partialDoc) remove(key string, options *ApplyOptions) error {
558 _, ok := d.obj[key]
559 if !ok {
560 if options.AllowMissingPathOnRemove {
561 return nil
562 }
563 return errors.Wrapf(ErrMissing, "unable to remove nonexistent key: %s", key)
564 }
565 idx := -1
566 for i, k := range d.keys {
567 if k == key {
568 idx = i
569 break
570 }
571 }
572 d.keys = append(d.keys[0:idx], d.keys[idx+1:]...)
573 delete(d.obj, key)
574 return nil
575 }
576
577 // set should only be used to implement the "replace" operation, so "key" must
578 // be an already existing index in "d".
579 func (d *partialArray) set(key string, val *lazyNode, options *ApplyOptions) error {
580 idx, err := strconv.Atoi(key)
581 if err != nil {
582 return err
583 }
584
585 if idx < 0 {
586 if !options.SupportNegativeIndices {
587 return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
588 }
589 if idx < -len(*d) {
590 return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
591 }
592 idx += len(*d)
593 }
594
595 (*d)[idx] = val
596 return nil
597 }
598
599 func (d *partialArray) add(key string, val *lazyNode, options *ApplyOptions) error {
600 if key == "-" {
601 *d = append(*d, val)
602 return nil
603 }
604
605 idx, err := strconv.Atoi(key)
606 if err != nil {
607 return errors.Wrapf(err, "value was not a proper array index: '%s'", key)
608 }
609
610 sz := len(*d) + 1
611
612 ary := make([]*lazyNode, sz)
613
614 cur := *d
615
616 if idx >= len(ary) {
617 return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
618 }
619
620 if idx < 0 {
621 if !options.SupportNegativeIndices {
622 return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
623 }
624 if idx < -len(ary) {
625 return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
626 }
627 idx += len(ary)
628 }
629
630 copy(ary[0:idx], cur[0:idx])
631 ary[idx] = val
632 copy(ary[idx+1:], cur[idx:])
633
634 *d = ary
635 return nil
636 }
637
638 func (d *partialArray) get(key string, options *ApplyOptions) (*lazyNode, error) {
639 idx, err := strconv.Atoi(key)
640
641 if err != nil {
642 return nil, err
643 }
644
645 if idx < 0 {
646 if !options.SupportNegativeIndices {
647 return nil, errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
648 }
649 if idx < -len(*d) {
650 return nil, errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
651 }
652 idx += len(*d)
653 }
654
655 if idx >= len(*d) {
656 return nil, errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
657 }
658
659 return (*d)[idx], nil
660 }
661
662 func (d *partialArray) remove(key string, options *ApplyOptions) error {
663 idx, err := strconv.Atoi(key)
664 if err != nil {
665 return err
666 }
667
668 cur := *d
669
670 if idx >= len(cur) {
671 if options.AllowMissingPathOnRemove {
672 return nil
673 }
674 return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
675 }
676
677 if idx < 0 {
678 if !options.SupportNegativeIndices {
679 return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
680 }
681 if idx < -len(cur) {
682 if options.AllowMissingPathOnRemove {
683 return nil
684 }
685 return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx)
686 }
687 idx += len(cur)
688 }
689
690 ary := make([]*lazyNode, len(cur)-1)
691
692 copy(ary[0:idx], cur[0:idx])
693 copy(ary[idx:], cur[idx+1:])
694
695 *d = ary
696 return nil
697 }
698
699 func (p Patch) add(doc *container, op Operation, options *ApplyOptions) error {
700 path, err := op.Path()
701 if err != nil {
702 return errors.Wrapf(ErrMissing, "add operation failed to decode path")
703 }
704
705 if options.EnsurePathExistsOnAdd {
706 err = ensurePathExists(doc, path, options)
707
708 if err != nil {
709 return err
710 }
711 }
712
713 con, key := findObject(doc, path, options)
714
715 if con == nil {
716 return errors.Wrapf(ErrMissing, "add operation does not apply: doc is missing path: \"%s\"", path)
717 }
718
719 err = con.add(key, op.value(), options)
720 if err != nil {
721 return errors.Wrapf(err, "error in add for path: '%s'", path)
722 }
723
724 return nil
725 }
726
727 // Given a document and a path to a key, walk the path and create all missing elements
728 // creating objects and arrays as needed.
729 func ensurePathExists(pd *container, path string, options *ApplyOptions) error {
730 doc := *pd
731
732 var err error
733 var arrIndex int
734
735 split := strings.Split(path, "/")
736
737 if len(split) < 2 {
738 return nil
739 }
740
741 parts := split[1:]
742
743 for pi, part := range parts {
744
745 // Have we reached the key part of the path?
746 // If yes, we're done.
747 if pi == len(parts)-1 {
748 return nil
749 }
750
751 target, ok := doc.get(decodePatchKey(part), options)
752
753 if target == nil || ok != nil {
754
755 // If the current container is an array which has fewer elements than our target index,
756 // pad the current container with nulls.
757 if arrIndex, err = strconv.Atoi(part); err == nil {
758 pa, ok := doc.(*partialArray)
759
760 if ok && arrIndex >= len(*pa)+1 {
761 // Pad the array with null values up to the required index.
762 for i := len(*pa); i <= arrIndex-1; i++ {
763 doc.add(strconv.Itoa(i), newLazyNode(newRawMessage(rawJSONNull)), options)
764 }
765 }
766 }
767
768 // Check if the next part is a numeric index.
769 // If yes, then create an array, otherwise, create an object.
770 if arrIndex, err = strconv.Atoi(parts[pi+1]); err == nil {
771 if arrIndex < 0 {
772
773 if !options.SupportNegativeIndices {
774 return errors.Wrapf(ErrInvalidIndex, "Unable to ensure path for invalid index: %d", arrIndex)
775 }
776
777 if arrIndex < -1 {
778 return errors.Wrapf(ErrInvalidIndex, "Unable to ensure path for negative index other than -1: %d", arrIndex)
779 }
780
781 arrIndex = 0
782 }
783
784 newNode := newLazyNode(newRawMessage(rawJSONArray))
785 doc.add(part, newNode, options)
786 doc, _ = newNode.intoAry()
787
788 // Pad the new array with null values up to the required index.
789 for i := 0; i < arrIndex; i++ {
790 doc.add(strconv.Itoa(i), newLazyNode(newRawMessage(rawJSONNull)), options)
791 }
792 } else {
793 newNode := newLazyNode(newRawMessage(rawJSONObject))
794
795 doc.add(part, newNode, options)
796 doc, _ = newNode.intoDoc()
797 }
798 } else {
799 if isArray(*target.raw) {
800 doc, err = target.intoAry()
801
802 if err != nil {
803 return err
804 }
805 } else {
806 doc, err = target.intoDoc()
807
808 if err != nil {
809 return err
810 }
811 }
812 }
813 }
814
815 return nil
816 }
817
818 func (p Patch) remove(doc *container, op Operation, options *ApplyOptions) error {
819 path, err := op.Path()
820 if err != nil {
821 return errors.Wrapf(ErrMissing, "remove operation failed to decode path")
822 }
823
824 con, key := findObject(doc, path, options)
825
826 if con == nil {
827 if options.AllowMissingPathOnRemove {
828 return nil
829 }
830 return errors.Wrapf(ErrMissing, "remove operation does not apply: doc is missing path: \"%s\"", path)
831 }
832
833 err = con.remove(key, options)
834 if err != nil {
835 return errors.Wrapf(err, "error in remove for path: '%s'", path)
836 }
837
838 return nil
839 }
840
841 func (p Patch) replace(doc *container, op Operation, options *ApplyOptions) error {
842 path, err := op.Path()
843 if err != nil {
844 return errors.Wrapf(err, "replace operation failed to decode path")
845 }
846
847 con, key := findObject(doc, path, options)
848
849 if con == nil {
850 return errors.Wrapf(ErrMissing, "replace operation does not apply: doc is missing path: %s", path)
851 }
852
853 _, ok := con.get(key, options)
854 if ok != nil {
855 return errors.Wrapf(ErrMissing, "replace operation does not apply: doc is missing key: %s", path)
856 }
857
858 err = con.set(key, op.value(), options)
859 if err != nil {
860 return errors.Wrapf(err, "error in remove for path: '%s'", path)
861 }
862
863 return nil
864 }
865
866 func (p Patch) move(doc *container, op Operation, options *ApplyOptions) error {
867 from, err := op.From()
868 if err != nil {
869 return errors.Wrapf(err, "move operation failed to decode from")
870 }
871
872 con, key := findObject(doc, from, options)
873
874 if con == nil {
875 return errors.Wrapf(ErrMissing, "move operation does not apply: doc is missing from path: %s", from)
876 }
877
878 val, err := con.get(key, options)
879 if err != nil {
880 return errors.Wrapf(err, "error in move for path: '%s'", key)
881 }
882
883 err = con.remove(key, options)
884 if err != nil {
885 return errors.Wrapf(err, "error in move for path: '%s'", key)
886 }
887
888 path, err := op.Path()
889 if err != nil {
890 return errors.Wrapf(err, "move operation failed to decode path")
891 }
892
893 con, key = findObject(doc, path, options)
894
895 if con == nil {
896 return errors.Wrapf(ErrMissing, "move operation does not apply: doc is missing destination path: %s", path)
897 }
898
899 err = con.add(key, val, options)
900 if err != nil {
901 return errors.Wrapf(err, "error in move for path: '%s'", path)
902 }
903
904 return nil
905 }
906
907 func (p Patch) test(doc *container, op Operation, options *ApplyOptions) error {
908 path, err := op.Path()
909 if err != nil {
910 return errors.Wrapf(err, "test operation failed to decode path")
911 }
912
913 con, key := findObject(doc, path, options)
914
915 if con == nil {
916 return errors.Wrapf(ErrMissing, "test operation does not apply: is missing path: %s", path)
917 }
918
919 val, err := con.get(key, options)
920 if err != nil && errors.Cause(err) != ErrMissing {
921 return errors.Wrapf(err, "error in test for path: '%s'", path)
922 }
923
924 if val == nil {
925 if op.value().raw == nil {
926 return nil
927 }
928 return errors.Wrapf(ErrTestFailed, "testing value %s failed", path)
929 } else if op.value() == nil {
930 return errors.Wrapf(ErrTestFailed, "testing value %s failed", path)
931 }
932
933 if val.equal(op.value()) {
934 return nil
935 }
936
937 return errors.Wrapf(ErrTestFailed, "testing value %s failed", path)
938 }
939
940 func (p Patch) copy(doc *container, op Operation, accumulatedCopySize *int64, options *ApplyOptions) error {
941 from, err := op.From()
942 if err != nil {
943 return errors.Wrapf(err, "copy operation failed to decode from")
944 }
945
946 con, key := findObject(doc, from, options)
947
948 if con == nil {
949 return errors.Wrapf(ErrMissing, "copy operation does not apply: doc is missing from path: %s", from)
950 }
951
952 val, err := con.get(key, options)
953 if err != nil {
954 return errors.Wrapf(err, "error in copy for from: '%s'", from)
955 }
956
957 path, err := op.Path()
958 if err != nil {
959 return errors.Wrapf(ErrMissing, "copy operation failed to decode path")
960 }
961
962 con, key = findObject(doc, path, options)
963
964 if con == nil {
965 return errors.Wrapf(ErrMissing, "copy operation does not apply: doc is missing destination path: %s", path)
966 }
967
968 valCopy, sz, err := deepCopy(val)
969 if err != nil {
970 return errors.Wrapf(err, "error while performing deep copy")
971 }
972
973 (*accumulatedCopySize) += int64(sz)
974 if options.AccumulatedCopySizeLimit > 0 && *accumulatedCopySize > options.AccumulatedCopySizeLimit {
975 return NewAccumulatedCopySizeError(options.AccumulatedCopySizeLimit, *accumulatedCopySize)
976 }
977
978 err = con.add(key, valCopy, options)
979 if err != nil {
980 return errors.Wrapf(err, "error while adding value during copy")
981 }
982
983 return nil
984 }
985
986 // Equal indicates if 2 JSON documents have the same structural equality.
987 func Equal(a, b []byte) bool {
988 la := newLazyNode(newRawMessage(a))
989 lb := newLazyNode(newRawMessage(b))
990
991 return la.equal(lb)
992 }
993
994 // DecodePatch decodes the passed JSON document as an RFC 6902 patch.
995 func DecodePatch(buf []byte) (Patch, error) {
996 var p Patch
997
998 err := json.Unmarshal(buf, &p)
999
1000 if err != nil {
1001 return nil, err
1002 }
1003
1004 return p, nil
1005 }
1006
1007 // Apply mutates a JSON document according to the patch, and returns the new
1008 // document.
1009 func (p Patch) Apply(doc []byte) ([]byte, error) {
1010 return p.ApplyWithOptions(doc, NewApplyOptions())
1011 }
1012
1013 // ApplyWithOptions mutates a JSON document according to the patch and the passed in ApplyOptions.
1014 // It returns the new document.
1015 func (p Patch) ApplyWithOptions(doc []byte, options *ApplyOptions) ([]byte, error) {
1016 return p.ApplyIndentWithOptions(doc, "", options)
1017 }
1018
1019 // ApplyIndent mutates a JSON document according to the patch, and returns the new
1020 // document indented.
1021 func (p Patch) ApplyIndent(doc []byte, indent string) ([]byte, error) {
1022 return p.ApplyIndentWithOptions(doc, indent, NewApplyOptions())
1023 }
1024
1025 // ApplyIndentWithOptions mutates a JSON document according to the patch and the passed in ApplyOptions.
1026 // It returns the new document indented.
1027 func (p Patch) ApplyIndentWithOptions(doc []byte, indent string, options *ApplyOptions) ([]byte, error) {
1028 var pd container
1029 if doc[0] == '[' {
1030 pd = &partialArray{}
1031 } else {
1032 pd = &partialDoc{}
1033 }
1034
1035 err := json.Unmarshal(doc, pd)
1036
1037 if err != nil {
1038 return nil, err
1039 }
1040
1041 err = nil
1042
1043 var accumulatedCopySize int64
1044
1045 for _, op := range p {
1046 switch op.Kind() {
1047 case "add":
1048 err = p.add(&pd, op, options)
1049 case "remove":
1050 err = p.remove(&pd, op, options)
1051 case "replace":
1052 err = p.replace(&pd, op, options)
1053 case "move":
1054 err = p.move(&pd, op, options)
1055 case "test":
1056 err = p.test(&pd, op, options)
1057 case "copy":
1058 err = p.copy(&pd, op, &accumulatedCopySize, options)
1059 default:
1060 err = fmt.Errorf("Unexpected kind: %s", op.Kind())
1061 }
1062
1063 if err != nil {
1064 return nil, err
1065 }
1066 }
1067
1068 if indent != "" {
1069 return json.MarshalIndent(pd, "", indent)
1070 }
1071
1072 return json.Marshal(pd)
1073 }
1074
1075 // From http://tools.ietf.org/html/rfc6901#section-4 :
1076 //
1077 // Evaluation of each reference token begins by decoding any escaped
1078 // character sequence. This is performed by first transforming any
1079 // occurrence of the sequence '~1' to '/', and then transforming any
1080 // occurrence of the sequence '~0' to '~'.
1081
1082 var (
1083 rfc6901Decoder = strings.NewReplacer("~1", "/", "~0", "~")
1084 )
1085
1086 func decodePatchKey(k string) string {
1087 return rfc6901Decoder.Replace(k)
1088 }
0 package jsonpatch
1
2 import (
3 "bytes"
4 "encoding/json"
5 "fmt"
6 "reflect"
7 "testing"
8 )
9
10 func reformatJSON(j string) string {
11 buf := new(bytes.Buffer)
12
13 json.Indent(buf, []byte(j), "", " ")
14
15 return buf.String()
16 }
17
18 func compareJSON(a, b string) bool {
19 // return Equal([]byte(a), []byte(b))
20
21 var objA, objB interface{}
22 json.Unmarshal([]byte(a), &objA)
23 json.Unmarshal([]byte(b), &objB)
24
25 // fmt.Printf("Comparing %#v\nagainst %#v\n", objA, objB)
26 return reflect.DeepEqual(objA, objB)
27 }
28
29 func applyPatch(doc, patch string) (string, error) {
30 obj, err := DecodePatch([]byte(patch))
31
32 if err != nil {
33 panic(err)
34 }
35
36 out, err := obj.Apply([]byte(doc))
37
38 if err != nil {
39 return "", err
40 }
41
42 return string(out), nil
43 }
44
45 func applyPatchIndented(doc, patch string) (string, error) {
46 obj, err := DecodePatch([]byte(patch))
47
48 if err != nil {
49 panic(err)
50 }
51
52 out, err := obj.ApplyIndent([]byte(doc), " ")
53
54 if err != nil {
55 return "", err
56 }
57
58 return string(out), nil
59 }
60
61 func applyPatchWithOptions(doc, patch string, options *ApplyOptions) (string, error) {
62 obj, err := DecodePatch([]byte(patch))
63
64 if err != nil {
65 panic(err)
66 }
67
68 out, err := obj.ApplyWithOptions([]byte(doc), options)
69
70 if err != nil {
71 return "", err
72 }
73
74 return string(out), nil
75 }
76
77 type Case struct {
78 doc, patch, result string
79 allowMissingPathOnRemove bool
80 ensurePathExistsOnAdd bool
81 }
82
83 func repeatedA(r int) string {
84 var s string
85 for i := 0; i < r; i++ {
86 s += "A"
87 }
88 return s
89 }
90
91 var Cases = []Case{
92 {
93 `{ "foo": "bar"}`,
94 `[
95 { "op": "add", "path": "/baz", "value": "qux" }
96 ]`,
97 `{
98 "baz": "qux",
99 "foo": "bar"
100 }`,
101 false,
102 false,
103 },
104 {
105 `{ "foo": [ "bar", "baz" ] }`,
106 `[
107 { "op": "add", "path": "/foo/1", "value": "qux" }
108 ]`,
109 `{ "foo": [ "bar", "qux", "baz" ] }`,
110 false,
111 false,
112 },
113 {
114 `{ "foo": [ "bar", "baz" ] }`,
115 `[
116 { "op": "add", "path": "/foo/-1", "value": "qux" }
117 ]`,
118 `{ "foo": [ "bar", "baz", "qux" ] }`,
119 false,
120 false,
121 },
122 {
123 `{ "baz": "qux", "foo": "bar" }`,
124 `[ { "op": "remove", "path": "/baz" } ]`,
125 `{ "foo": "bar" }`,
126 false,
127 false,
128 },
129 {
130 `{ "foo": [ "bar", "qux", "baz" ] }`,
131 `[ { "op": "remove", "path": "/foo/1" } ]`,
132 `{ "foo": [ "bar", "baz" ] }`,
133 false,
134 false,
135 },
136 {
137 `{ "foo": [ "bar", "qux", "baz" ] }`,
138 `[ { "op": "remove", "path": "/foo/-1" } ]`,
139 `{ "foo": [ "bar", "qux" ] }`,
140 false,
141 false,
142 },
143 {
144 `{ "foo": [ "bar", "qux", {"a": "abc", "b": "xyz" } ] }`,
145 `[ { "op": "remove", "path": "/foo/-1/a" } ]`,
146 `{ "foo": [ "bar", "qux", {"b": "xyz" } ] }`,
147 false,
148 false,
149 },
150 {
151 `{ "baz": "qux", "foo": "bar" }`,
152 `[ { "op": "replace", "path": "/baz", "value": "boo" } ]`,
153 `{ "baz": "boo", "foo": "bar" }`,
154 false,
155 false,
156 },
157 {
158 `{
159 "foo": {
160 "bar": "baz",
161 "waldo": "fred"
162 },
163 "qux": {
164 "corge": "grault"
165 }
166 }`,
167 `[ { "op": "move", "from": "/foo/waldo", "path": "/qux/thud" } ]`,
168 `{
169 "foo": {
170 "bar": "baz"
171 },
172 "qux": {
173 "corge": "grault",
174 "thud": "fred"
175 }
176 }`,
177 false,
178 false,
179 },
180 {
181 `{ "foo": [ "all", "grass", "cows", "eat" ] }`,
182 `[ { "op": "move", "from": "/foo/1", "path": "/foo/3" } ]`,
183 `{ "foo": [ "all", "cows", "eat", "grass" ] }`,
184 false,
185 false,
186 },
187 {
188 `{ "foo": [ "all", "grass", "cows", "eat" ] }`,
189 `[ { "op": "move", "from": "/foo/1", "path": "/foo/2" } ]`,
190 `{ "foo": [ "all", "cows", "grass", "eat" ] }`,
191 false,
192 false,
193 },
194 {
195 `{ "foo": "bar" }`,
196 `[ { "op": "add", "path": "/child", "value": { "grandchild": { } } } ]`,
197 `{ "foo": "bar", "child": { "grandchild": { } } }`,
198 false,
199 false,
200 },
201 {
202 `{ "foo": ["bar"] }`,
203 `[ { "op": "add", "path": "/foo/-", "value": ["abc", "def"] } ]`,
204 `{ "foo": ["bar", ["abc", "def"]] }`,
205 false,
206 false,
207 },
208 {
209 `{ "foo": "bar", "qux": { "baz": 1, "bar": null } }`,
210 `[ { "op": "remove", "path": "/qux/bar" } ]`,
211 `{ "foo": "bar", "qux": { "baz": 1 } }`,
212 false,
213 false,
214 },
215 {
216 `{ "foo": "bar" }`,
217 `[ { "op": "add", "path": "/baz", "value": null } ]`,
218 `{ "baz": null, "foo": "bar" }`,
219 false,
220 false,
221 },
222 {
223 `{ "foo": ["bar"]}`,
224 `[ { "op": "replace", "path": "/foo/0", "value": "baz"}]`,
225 `{ "foo": ["baz"]}`,
226 false,
227 false,
228 },
229 {
230 `{ "foo": ["bar"]}`,
231 `[ { "op": "replace", "path": "/foo/-1", "value": "baz"}]`,
232 `{ "foo": ["baz"]}`,
233 false,
234 false,
235 },
236 {
237 `{ "foo": [{"bar": "x"}]}`,
238 `[ { "op": "replace", "path": "/foo/-1/bar", "value": "baz"}]`,
239 `{ "foo": [{"bar": "baz"}]}`,
240 false,
241 false,
242 },
243 {
244 `{ "foo": ["bar","baz"]}`,
245 `[ { "op": "replace", "path": "/foo/0", "value": "bum"}]`,
246 `{ "foo": ["bum","baz"]}`,
247 false,
248 false,
249 },
250 {
251 `{ "foo": ["bar","qux","baz"]}`,
252 `[ { "op": "replace", "path": "/foo/1", "value": "bum"}]`,
253 `{ "foo": ["bar", "bum","baz"]}`,
254 false,
255 false,
256 },
257 {
258 `[ {"foo": ["bar","qux","baz"]}]`,
259 `[ { "op": "replace", "path": "/0/foo/0", "value": "bum"}]`,
260 `[ {"foo": ["bum","qux","baz"]}]`,
261 false,
262 false,
263 },
264 {
265 `[ {"foo": ["bar","qux","baz"], "bar": ["qux","baz"]}]`,
266 `[ { "op": "copy", "from": "/0/foo/0", "path": "/0/bar/0"}]`,
267 `[ {"foo": ["bar","qux","baz"], "bar": ["bar", "qux", "baz"]}]`,
268 false,
269 false,
270 },
271 {
272 `[ {"foo": ["bar","qux","baz"], "bar": ["qux","baz"]}]`,
273 `[ { "op": "copy", "from": "/0/foo/0", "path": "/0/bar"}]`,
274 `[ {"foo": ["bar","qux","baz"], "bar": "bar"}]`,
275 false,
276 false,
277 },
278 {
279 `[ { "foo": {"bar": ["qux","baz"]}, "baz": {"qux": "bum"}}]`,
280 `[ { "op": "copy", "from": "/0/foo/bar", "path": "/0/baz/bar"}]`,
281 `[ { "baz": {"bar": ["qux","baz"], "qux":"bum"}, "foo": {"bar": ["qux","baz"]}}]`,
282 false,
283 false,
284 },
285 {
286 `{ "foo": ["bar"]}`,
287 `[{"op": "copy", "path": "/foo/0", "from": "/foo"}]`,
288 `{ "foo": [["bar"], "bar"]}`,
289 false,
290 false,
291 },
292 {
293 `{ "foo": null}`,
294 `[{"op": "copy", "path": "/bar", "from": "/foo"}]`,
295 `{ "foo": null, "bar": null}`,
296 false,
297 false,
298 },
299 {
300 `{ "foo": ["bar","qux","baz"]}`,
301 `[ { "op": "remove", "path": "/foo/-2"}]`,
302 `{ "foo": ["bar", "baz"]}`,
303 false,
304 false,
305 },
306 {
307 `{ "foo": []}`,
308 `[ { "op": "add", "path": "/foo/-1", "value": "qux"}]`,
309 `{ "foo": ["qux"]}`,
310 false,
311 false,
312 },
313 {
314 `{ "bar": [{"baz": null}]}`,
315 `[ { "op": "replace", "path": "/bar/0/baz", "value": 1 } ]`,
316 `{ "bar": [{"baz": 1}]}`,
317 false,
318 false,
319 },
320 {
321 `{ "bar": [{"baz": 1}]}`,
322 `[ { "op": "replace", "path": "/bar/0/baz", "value": null } ]`,
323 `{ "bar": [{"baz": null}]}`,
324 false,
325 false,
326 },
327 {
328 `{ "bar": [null]}`,
329 `[ { "op": "replace", "path": "/bar/0", "value": 1 } ]`,
330 `{ "bar": [1]}`,
331 false,
332 false,
333 },
334 {
335 `{ "bar": [1]}`,
336 `[ { "op": "replace", "path": "/bar/0", "value": null } ]`,
337 `{ "bar": [null]}`,
338 false,
339 false,
340 },
341 {
342 fmt.Sprintf(`{ "foo": ["A", %q] }`, repeatedA(48)),
343 // The wrapping quotes around 'A's are included in the copy
344 // size, so each copy operation increases the size by 50 bytes.
345 `[ { "op": "copy", "path": "/foo/-", "from": "/foo/1" },
346 { "op": "copy", "path": "/foo/-", "from": "/foo/1" }]`,
347 fmt.Sprintf(`{ "foo": ["A", %q, %q, %q] }`, repeatedA(48), repeatedA(48), repeatedA(48)),
348 false,
349 false,
350 },
351 {
352 `[1, 2, 3]`,
353 `[ { "op": "remove", "path": "/0" } ]`,
354 `[2, 3]`,
355 false,
356 false,
357 },
358 {
359 `{ "a": { "b": { "d": 1 } } }`,
360 `[ { "op": "remove", "path": "/a/b/c" } ]`,
361 `{ "a": { "b": { "d": 1 } } }`,
362 true,
363 false,
364 },
365 {
366 `{ "a": { "b": { "d": 1 } } }`,
367 `[ { "op": "remove", "path": "/x/y/z" } ]`,
368 `{ "a": { "b": { "d": 1 } } }`,
369 true,
370 false,
371 },
372 {
373 `[1, 2, 3]`,
374 `[ { "op": "remove", "path": "/10" } ]`,
375 `[1, 2, 3]`,
376 true,
377 false,
378 },
379 {
380 `[1, 2, 3]`,
381 `[ { "op": "remove", "path": "/10/x/y/z" } ]`,
382 `[1, 2, 3]`,
383 true,
384 false,
385 },
386 {
387 `[1, 2, 3]`,
388 `[ { "op": "remove", "path": "/-10" } ]`,
389 `[1, 2, 3]`,
390 true,
391 false,
392 },
393 {
394 `{}`,
395 `[ { "op": "add", "path": "/a", "value": "hello" } ]`,
396 `{"a": "hello" }`,
397 false,
398 true,
399 },
400 {
401 `{}`,
402 `[ { "op": "add", "path": "/a/b", "value": "hello" } ]`,
403 `{"a": {"b": "hello" } }`,
404 false,
405 true,
406 },
407 {
408 `{}`,
409 `[ { "op": "add", "path": "/a/b/c", "value": "hello" } ]`,
410 `{"a": {"b": {"c": "hello" } } }`,
411 false,
412 true,
413 },
414 {
415 `{"a": {} }`,
416 `[ { "op": "add", "path": "/a/b/c", "value": "hello" } ]`,
417 `{"a": {"b": {"c": "hello" } } }`,
418 false,
419 true,
420 },
421 {
422 `{"a": {} }`,
423 `[ { "op": "add", "path": "/x/y/z", "value": "hello" } ]`,
424 `{"a": {}, "x" : {"y": {"z": "hello" } } }`,
425 false,
426 true,
427 },
428 {
429 `{}`,
430 `[ { "op": "add", "path": "/a/0/b", "value": "hello" } ]`,
431 `{"a": [{"b": "hello"}] }`,
432 false,
433 true,
434 },
435 {
436 `{}`,
437 `[ { "op": "add", "path": "/a/b/0", "value": "hello" } ]`,
438 `{"a": {"b": ["hello"] } }`,
439 false,
440 true,
441 },
442 {
443 `{}`,
444 `[ { "op": "add", "path": "/a/b/-1", "value": "hello" } ]`,
445 `{"a": {"b": ["hello"] } }`,
446 false,
447 true,
448 },
449 {
450 `{}`,
451 `[ { "op": "add", "path": "/a/b/-1/c", "value": "hello" } ]`,
452 `{"a": {"b": [ { "c": "hello" } ] } }`,
453 false,
454 true,
455 },
456 {
457 `{"a": {"b": [ { "c": "whatever" } ] } }`,
458 `[ { "op": "add", "path": "/a/b/-1/c", "value": "hello" } ]`,
459 `{"a": {"b": [ { "c": "hello" } ] } }`,
460 false,
461 true,
462 },
463 {
464 `{}`,
465 `[ { "op": "add", "path": "/a/b/3", "value": "hello" } ]`,
466 `{"a": {"b": [null, null, null, "hello"] } }`,
467 false,
468 true,
469 },
470 {
471 `{"a": []}`,
472 `[ { "op": "add", "path": "/a/-1", "value": "hello" } ]`,
473 `{"a": ["hello"]}`,
474 false,
475 true,
476 },
477 {
478 `{}`,
479 `[ { "op": "add", "path": "/a/0/0", "value": "hello" } ]`,
480 `{"a": [["hello"]]}`,
481 false,
482 true,
483 },
484 {
485 `{"a": [{}]}`,
486 `[ { "op": "add", "path": "/a/-1/b/c", "value": "hello" } ]`,
487 `{"a": [{"b": {"c": "hello"}}]}`,
488 false,
489 true,
490 },
491 {
492 `{"a": [{"b": "whatever"}]}`,
493 `[ { "op": "add", "path": "/a/2/b/c", "value": "hello" } ]`,
494 `{"a": [{"b": "whatever"}, null, {"b": {"c": "hello"}}]}`,
495 false,
496 true,
497 },
498 {
499 `{"a": [{"b": "whatever"}]}`,
500 `[ { "op": "add", "path": "/a/1/b/c", "value": "hello" } ]`,
501 `{"a": [{"b": "whatever"}, {"b": {"c": "hello"}}]}`,
502 false,
503 true,
504 },
505 }
506
507 type BadCase struct {
508 doc, patch string
509 }
510
511 var MutationTestCases = []BadCase{
512 {
513 `{ "foo": "bar", "qux": { "baz": 1, "bar": null } }`,
514 `[ { "op": "remove", "path": "/qux/bar" } ]`,
515 },
516 {
517 `{ "foo": "bar", "qux": { "baz": 1, "bar": null } }`,
518 `[ { "op": "replace", "path": "/qux/baz", "value": null } ]`,
519 },
520 }
521
522 var BadCases = []BadCase{
523 {
524 `{ "foo": "bar" }`,
525 `[ { "op": "add", "path": "/baz/bat", "value": "qux" } ]`,
526 },
527 {
528 `{ "a": { "b": { "d": 1 } } }`,
529 `[ { "op": "remove", "path": "/a/b/c" } ]`,
530 },
531 {
532 `{ "a": { "b": { "d": 1 } } }`,
533 `[ { "op": "move", "from": "/a/b/c", "path": "/a/b/e" } ]`,
534 },
535 {
536 `{ "a": { "b": [1] } }`,
537 `[ { "op": "remove", "path": "/a/b/1" } ]`,
538 },
539 {
540 `{ "a": { "b": [1] } }`,
541 `[ { "op": "move", "from": "/a/b/1", "path": "/a/b/2" } ]`,
542 },
543 {
544 `{ "foo": "bar" }`,
545 `[ { "op": "add", "pathz": "/baz", "value": "qux" } ]`,
546 },
547 {
548 `{ "foo": "bar" }`,
549 `[ { "op": "add", "path": "", "value": "qux" } ]`,
550 },
551 {
552 `{ "foo": ["bar","baz"]}`,
553 `[ { "op": "replace", "path": "/foo/2", "value": "bum"}]`,
554 },
555 {
556 `{ "foo": ["bar","baz"]}`,
557 `[ { "op": "add", "path": "/foo/-4", "value": "bum"}]`,
558 },
559 {
560 `{ "name":{ "foo": "bat", "qux": "bum"}}`,
561 `[ { "op": "replace", "path": "/foo/bar", "value":"baz"}]`,
562 },
563 {
564 `{ "foo": ["bar"]}`,
565 `[ {"op": "add", "path": "/foo/2", "value": "bum"}]`,
566 },
567 {
568 `{ "foo": []}`,
569 `[ {"op": "remove", "path": "/foo/-"}]`,
570 },
571 {
572 `{ "foo": []}`,
573 `[ {"op": "remove", "path": "/foo/-1"}]`,
574 },
575 {
576 `{ "foo": ["bar"]}`,
577 `[ {"op": "remove", "path": "/foo/-2"}]`,
578 },
579 {
580 `{}`,
581 `[ {"op":null,"path":""} ]`,
582 },
583 {
584 `{}`,
585 `[ {"op":"add","path":null} ]`,
586 },
587 {
588 `{}`,
589 `[ { "op": "copy", "from": null }]`,
590 },
591 {
592 `{ "foo": ["bar"]}`,
593 `[{"op": "copy", "path": "/foo/6666666666", "from": "/"}]`,
594 },
595 // Can't copy into an index greater than the size of the array
596 {
597 `{ "foo": ["bar"]}`,
598 `[{"op": "copy", "path": "/foo/2", "from": "/foo/0"}]`,
599 },
600 // Accumulated copy size cannot exceed AccumulatedCopySizeLimit.
601 {
602 fmt.Sprintf(`{ "foo": ["A", %q] }`, repeatedA(49)),
603 // The wrapping quotes around 'A's are included in the copy
604 // size, so each copy operation increases the size by 51 bytes.
605 `[ { "op": "copy", "path": "/foo/-", "from": "/foo/1" },
606 { "op": "copy", "path": "/foo/-", "from": "/foo/1" }]`,
607 },
608 // Can't move into an index greater than or equal to the size of the array
609 {
610 `{ "foo": [ "all", "grass", "cows", "eat" ] }`,
611 `[ { "op": "move", "from": "/foo/1", "path": "/foo/4" } ]`,
612 },
613 {
614 `{ "baz": "qux" }`,
615 `[ { "op": "replace", "path": "/foo", "value": "bar" } ]`,
616 },
617 // Can't copy from non-existent "from" key.
618 {
619 `{ "foo": "bar"}`,
620 `[{"op": "copy", "path": "/qux", "from": "/baz"}]`,
621 },
622 }
623
624 // This is not thread safe, so we cannot run patch tests in parallel.
625 func configureGlobals(accumulatedCopySizeLimit int64) func() {
626 oldAccumulatedCopySizeLimit := AccumulatedCopySizeLimit
627 AccumulatedCopySizeLimit = accumulatedCopySizeLimit
628 return func() {
629 AccumulatedCopySizeLimit = oldAccumulatedCopySizeLimit
630 }
631 }
632
633 func TestAllCases(t *testing.T) {
634 defer configureGlobals(int64(100))()
635
636 // Test patch.Apply happy-path cases.
637 for i, c := range Cases {
638 t.Run(fmt.Sprintf("Case %d", i), func(t *testing.T) {
639 if !c.allowMissingPathOnRemove && !c.ensurePathExistsOnAdd {
640 out, err := applyPatch(c.doc, c.patch)
641
642 if err != nil {
643 t.Errorf("Unable to apply patch: %s", err)
644 }
645
646 if !compareJSON(out, c.result) {
647 t.Errorf("Patch did not apply. Expected:\n%s\n\nActual:\n%s",
648 reformatJSON(c.result), reformatJSON(out))
649 }
650 }
651 })
652 }
653
654 // Test patch.ApplyWithOptions happy-path cases.
655 options := NewApplyOptions()
656
657 for _, c := range Cases {
658 options.AllowMissingPathOnRemove = c.allowMissingPathOnRemove
659 options.EnsurePathExistsOnAdd = c.ensurePathExistsOnAdd
660
661 out, err := applyPatchWithOptions(c.doc, c.patch, options)
662
663 if err != nil {
664 t.Errorf("Unable to apply patch: %s", err)
665 }
666
667 if !compareJSON(out, c.result) {
668 t.Errorf("Patch did not apply. Expected:\n%s\n\nActual:\n%s",
669 reformatJSON(c.result), reformatJSON(out))
670 }
671 }
672
673 for _, c := range MutationTestCases {
674 out, err := applyPatch(c.doc, c.patch)
675
676 if err != nil {
677 t.Errorf("Unable to apply patch: %s", err)
678 }
679
680 if compareJSON(out, c.doc) {
681 t.Errorf("Patch did not apply. Original:\n%s\n\nPatched:\n%s",
682 reformatJSON(c.doc), reformatJSON(out))
683 }
684 }
685
686 for _, c := range BadCases {
687 _, err := applyPatch(c.doc, c.patch)
688
689 if err == nil {
690 t.Errorf("Patch %q should have failed to apply but it did not", c.patch)
691 }
692 }
693 }
694
695 type TestCase struct {
696 doc, patch string
697 result bool
698 failedPath string
699 }
700
701 var TestCases = []TestCase{
702 {
703 `{
704 "baz": "qux",
705 "foo": [ "a", 2, "c" ]
706 }`,
707 `[
708 { "op": "test", "path": "/baz", "value": "qux" },
709 { "op": "test", "path": "/foo/1", "value": 2 }
710 ]`,
711 true,
712 "",
713 },
714 {
715 `{ "baz": "qux" }`,
716 `[ { "op": "test", "path": "/baz", "value": "bar" } ]`,
717 false,
718 "/baz",
719 },
720 {
721 `{
722 "baz": "qux",
723 "foo": ["a", 2, "c"]
724 }`,
725 `[
726 { "op": "test", "path": "/baz", "value": "qux" },
727 { "op": "test", "path": "/foo/1", "value": "c" }
728 ]`,
729 false,
730 "/foo/1",
731 },
732 {
733 `{ "baz": "qux" }`,
734 `[ { "op": "test", "path": "/foo", "value": 42 } ]`,
735 false,
736 "/foo",
737 },
738 {
739 `{ "baz": "qux" }`,
740 `[ { "op": "test", "path": "/foo", "value": null } ]`,
741 true,
742 "",
743 },
744 {
745 `{ "foo": null }`,
746 `[ { "op": "test", "path": "/foo", "value": null } ]`,
747 true,
748 "",
749 },
750 {
751 `{ "foo": {} }`,
752 `[ { "op": "test", "path": "/foo", "value": null } ]`,
753 false,
754 "/foo",
755 },
756 {
757 `{ "foo": [] }`,
758 `[ { "op": "test", "path": "/foo", "value": null } ]`,
759 false,
760 "/foo",
761 },
762 {
763 `{ "baz/foo": "qux" }`,
764 `[ { "op": "test", "path": "/baz~1foo", "value": "qux"} ]`,
765 true,
766 "",
767 },
768 {
769 `{ "foo": [] }`,
770 `[ { "op": "test", "path": "/foo"} ]`,
771 false,
772 "/foo",
773 },
774 {
775 `{ "foo": "bar" }`,
776 `[ { "op": "test", "path": "/baz", "value": "bar" } ]`,
777 false,
778 "/baz",
779 },
780 {
781 `{ "foo": "bar" }`,
782 `[ { "op": "test", "path": "/baz", "value": null } ]`,
783 true,
784 "/baz",
785 },
786 }
787
788 func TestAllTest(t *testing.T) {
789 for _, c := range TestCases {
790 _, err := applyPatch(c.doc, c.patch)
791
792 if c.result && err != nil {
793 t.Errorf("Testing failed when it should have passed: %s", err)
794 } else if !c.result && err == nil {
795 t.Errorf("Testing passed when it should have failed: %s", err)
796 } else if !c.result {
797 expected := fmt.Sprintf("testing value %s failed: test failed", c.failedPath)
798 if err.Error() != expected {
799 t.Errorf("Testing failed as expected but invalid message: expected [%s], got [%s]", expected, err)
800 }
801 }
802 }
803 }
804
805 func TestAdd(t *testing.T) {
806 testCases := []struct {
807 name string
808 key string
809 val lazyNode
810 arr partialArray
811 rejectNegativeIndicies bool
812 err string
813 }{
814 {
815 name: "should work",
816 key: "0",
817 val: lazyNode{},
818 arr: partialArray{},
819 },
820 {
821 name: "index too large",
822 key: "1",
823 val: lazyNode{},
824 arr: partialArray{},
825 err: "Unable to access invalid index: 1: invalid index referenced",
826 },
827 {
828 name: "negative should work",
829 key: "-1",
830 val: lazyNode{},
831 arr: partialArray{},
832 },
833 {
834 name: "negative too small",
835 key: "-2",
836 val: lazyNode{},
837 arr: partialArray{},
838 err: "Unable to access invalid index: -2: invalid index referenced",
839 },
840 {
841 name: "negative but negative disabled",
842 key: "-1",
843 val: lazyNode{},
844 arr: partialArray{},
845 rejectNegativeIndicies: true,
846 err: "Unable to access invalid index: -1: invalid index referenced",
847 },
848 }
849
850 options := NewApplyOptions()
851
852 for _, tc := range testCases {
853 t.Run(tc.name, func(t *testing.T) {
854 key := tc.key
855 arr := &tc.arr
856 val := &tc.val
857 options.SupportNegativeIndices = !tc.rejectNegativeIndicies
858 err := arr.add(key, val, options)
859 if err == nil && tc.err != "" {
860 t.Errorf("Expected error but got none! %v", tc.err)
861 } else if err != nil && tc.err == "" {
862 t.Errorf("Did not expect error but go: %v", err)
863 } else if err != nil && err.Error() != tc.err {
864 t.Errorf("Expected error %v but got error %v", tc.err, err)
865 }
866 })
867 }
868 }
869
870 type EqualityCase struct {
871 name string
872 a, b string
873 equal bool
874 }
875
876 var EqualityCases = []EqualityCase{
877 {
878 "ExtraKeyFalse",
879 `{"foo": "bar"}`,
880 `{"foo": "bar", "baz": "qux"}`,
881 false,
882 },
883 {
884 "StripWhitespaceTrue",
885 `{
886 "foo": "bar",
887 "baz": "qux"
888 }`,
889 `{"foo": "bar", "baz": "qux"}`,
890 true,
891 },
892 {
893 "KeysOutOfOrderTrue",
894 `{
895 "baz": "qux",
896 "foo": "bar"
897 }`,
898 `{"foo": "bar", "baz": "qux"}`,
899 true,
900 },
901 {
902 "ComparingNullFalse",
903 `{"foo": null}`,
904 `{"foo": "bar"}`,
905 false,
906 },
907 {
908 "ComparingNullTrue",
909 `{"foo": null}`,
910 `{"foo": null}`,
911 true,
912 },
913 {
914 "ArrayOutOfOrderFalse",
915 `["foo", "bar", "baz"]`,
916 `["bar", "baz", "foo"]`,
917 false,
918 },
919 {
920 "ArrayTrue",
921 `["foo", "bar", "baz"]`,
922 `["foo", "bar", "baz"]`,
923 true,
924 },
925 {
926 "NonStringTypesTrue",
927 `{"int": 6, "bool": true, "float": 7.0, "string": "the_string", "null": null}`,
928 `{"int": 6, "bool": true, "float": 7.0, "string": "the_string", "null": null}`,
929 true,
930 },
931 {
932 "NestedNullFalse",
933 `{"foo": ["an", "array"], "bar": {"an": "object"}}`,
934 `{"foo": null, "bar": null}`,
935 false,
936 },
937 {
938 "NullCompareStringFalse",
939 `"foo"`,
940 `null`,
941 false,
942 },
943 {
944 "NullCompareIntFalse",
945 `6`,
946 `null`,
947 false,
948 },
949 {
950 "NullCompareFloatFalse",
951 `6.01`,
952 `null`,
953 false,
954 },
955 {
956 "NullCompareBoolFalse",
957 `false`,
958 `null`,
959 false,
960 },
961 }
962
963 func TestEquality(t *testing.T) {
964 for _, tc := range EqualityCases {
965 t.Run(tc.name, func(t *testing.T) {
966 got := Equal([]byte(tc.a), []byte(tc.b))
967 if got != tc.equal {
968 t.Errorf("Expected Equal(%s, %s) to return %t, but got %t", tc.a, tc.b, tc.equal, got)
969 }
970
971 got = Equal([]byte(tc.b), []byte(tc.a))
972 if got != tc.equal {
973 t.Errorf("Expected Equal(%s, %s) to return %t, but got %t", tc.b, tc.a, tc.equal, got)
974 }
975 })
976 }
977 }
978
979 func TestMaintainOrdering(t *testing.T) {
980 cases := []struct {
981 doc string
982 patch string
983 expected string
984 }{
985 {
986 `{"z":"1","a":["baz"],"y":3,"b":true,"x":null}`,
987 `[{"op": "add", "path": "/foo", "value": "bar"}]`,
988 `{"z":"1","a":["baz"],"y":3,"b":true,"x":null,"foo":"bar"}`,
989 },
990 {
991 `{"z":"1","a":["baz"],"y":3,"b":true,"x":null}`,
992 `[{"op": "remove", "path": "/y"}]`,
993 `{"z":"1","a":["baz"],"b":true,"x":null}`,
994 },
995 {
996 `{"z":"1","a":["baz"],"y":3,"b":true,"x":null}`,
997 `[{"op": "move", "from": "/z", "path": "/a/-"},{"op": "remove", "path": "/y"}]`,
998 `{"a":["baz","1"],"b":true,"x":null}`,
999 },
1000 {
1001 `{"z":"1","a":["baz"],"y":3,"b":true,"x":null}`,
1002 `[
1003 {"op": "add", "path": "/foo", "value": "bar"},
1004 {"op": "replace", "path": "/b", "value": {"zz":1,"aa":"foo","yy":true,"bb":null}},
1005 {"op": "copy", "from": "/foo", "path": "/b/cc"},
1006 {"op": "move", "from": "/z", "path": "/a/0"},
1007 {"op": "remove", "path": "/y"}
1008 ]`,
1009 `{"a":["1","baz"],"b":{"zz":1,"aa":"foo","yy":true,"bb":null,"cc":"bar"},"x":null,"foo":"bar"}`,
1010 },
1011 }
1012 for i, c := range cases {
1013 t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
1014 res, err := applyPatch(c.doc, c.patch)
1015 if err != nil {
1016 t.Errorf("unexpected error: %+v", err)
1017 } else if res != c.expected {
1018 t.Errorf("expected:\n%s\ngot:\n%s", c.expected, res)
1019 }
1020 })
1021 }
1022 }
1023
1024 func TestMaintainOrderingIndented(t *testing.T) {
1025 cases := []struct {
1026 doc string
1027 patch string
1028 expected string
1029 }{
1030 {
1031 `{"z":"1","a":["baz"],"y":3,"b":true,"x":null}`,
1032 `[
1033 {"op": "add", "path": "/foo", "value": "bar"},
1034 {"op": "replace", "path": "/b", "value": {"zz":1,"aa":"foo","yy":true,"bb":null}},
1035 {"op": "copy", "from": "/foo", "path": "/b/cc"},
1036 {"op": "move", "from": "/z", "path": "/a/0"},
1037 {"op": "remove", "path": "/y"}
1038 ]`,
1039 `{
1040 "a": [
1041 "1",
1042 "baz"
1043 ],
1044 "b": {
1045 "zz": 1,
1046 "aa": "foo",
1047 "yy": true,
1048 "bb": null,
1049 "cc": "bar"
1050 },
1051 "x": null,
1052 "foo": "bar"
1053 }`,
1054 },
1055 }
1056 for i, c := range cases {
1057 t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
1058 res, err := applyPatchIndented(c.doc, c.patch)
1059 if err != nil {
1060 t.Errorf("unexpected error: %+v", err)
1061 } else if res != c.expected {
1062 t.Errorf("expected:\n%s\ngot:\n%s", c.expected, res)
1063 }
1064 })
1065 }
1066 }