Skip to content

Commit d0b8b4a

Browse files
committed
Change API return type to JSONPatchList
1 parent 9b5c3af commit d0b8b4a

File tree

3 files changed

+80
-51
lines changed

3 files changed

+80
-51
lines changed

patch.go

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,45 @@ type JSONPatch struct {
1212
Value interface{} `json:"value,omitempty"`
1313
}
1414

15-
// Patch is a byte encoded JSON patch
16-
type Patch []byte
15+
// JSONPatchList is a list of JSONPatch
16+
type JSONPatchList struct {
17+
list []JSONPatch
18+
raw []byte
19+
}
20+
21+
// Empty returns true if the JSONPatchList is empty
22+
func (l JSONPatchList) Empty() bool {
23+
return l.Len() == 0
24+
}
25+
26+
// Len returns the length of the JSONPatchList
27+
func (l JSONPatchList) Len() int {
28+
return len(l.list)
29+
}
1730

18-
// Empty returns true if the Patch is empty
19-
func (p Patch) Empty() bool {
20-
return len(p) == 0
31+
// String returns the encoded JSON string of the JSONPatchList
32+
func (l JSONPatchList) String() string {
33+
return string(l.raw)
2134
}
2235

23-
// String converts the Patch to a string
24-
func (p Patch) String() string {
25-
return string(p)
36+
// Raw returns the raw encoded JSON of the JSONPatchList
37+
func (l JSONPatchList) Raw() []byte {
38+
return l.raw
39+
}
40+
41+
// List returns a copy of the underlying JSONPatch slice
42+
func (l JSONPatchList) List() []JSONPatch {
43+
ret := make([]JSONPatch, l.Len())
44+
45+
for i, patch := range l.list {
46+
ret[i] = patch
47+
}
48+
49+
return ret
2650
}
2751

2852
// CreateJSONPatch compares two JSON data structures and creates a JSONPatch according to RFC 6902
29-
func CreateJSONPatch(modified, current interface{}, options ...Option) (Patch, int, error) {
53+
func CreateJSONPatch(modified, current interface{}, options ...Option) (JSONPatchList, error) {
3054
// create a new walker
3155
w := &walker{
3256
handler: &DefaultHandler{},
@@ -40,14 +64,14 @@ func CreateJSONPatch(modified, current interface{}, options ...Option) (Patch, i
4064
}
4165

4266
if err := w.walk(reflect.ValueOf(modified), reflect.ValueOf(current), w.prefix); err != nil {
43-
return []byte{}, 0, err
67+
return JSONPatchList{}, err
4468
}
4569

46-
patches := w.patchList
47-
if len(patches) == 0 {
48-
return []byte{}, 0, nil
70+
list := w.patchList
71+
if len(list) == 0 {
72+
return JSONPatchList{}, nil
4973
}
50-
p, err := json.Marshal(patches)
74+
raw, err := json.Marshal(list)
5175

52-
return p, len(patches), err
76+
return JSONPatchList{list: list, raw: raw}, err
5377
}

patch_test.go

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package jsonpatch_test
33
import (
44
"encoding/json"
55
"strconv"
6+
"strings"
67

78
. "github.com/onsi/ginkgo"
89
. "github.com/onsi/gomega"
@@ -325,6 +326,7 @@ var _ = Describe("JSONPatch", func() {
325326
testPatchWithExpected(F{B: &B{Bool: true, Str: "str"}}, F{}, F{B: &B{Bool: true, Str: "str"}}, jsonpatch.WithPrefix([]string{""}))
326327
})
327328
It("pointer prefix", func() {
329+
prefix := "/a/ptr"
328330
modified := F{A: &A{B: &B{Bool: true, Str: "str"}}}
329331
current := F{A: &A{}}
330332
expected := F{A: &A{B: &B{Bool: true, Str: "str"}}}
@@ -336,16 +338,18 @@ var _ = Describe("JSONPatch", func() {
336338
expectedJSON, err := json.Marshal(expected)
337339
Ω(err).ShouldNot(HaveOccurred())
338340

339-
bytes, _, err := jsonpatch.CreateJSONPatch(modified.A.B, current.A.B, jsonpatch.WithPrefix(jsonpatch.ParseJSONPointer("/a/ptr")))
341+
list, err := jsonpatch.CreateJSONPatch(modified.A.B, current.A.B, jsonpatch.WithPrefix(jsonpatch.ParseJSONPointer(prefix)))
340342
Ω(err).ShouldNot(HaveOccurred())
341-
Ω(bytes.String()).ShouldNot(Equal(""))
342-
jsonPatch, err := jsonpatch2.DecodePatch(bytes)
343+
Ω(list.String()).ShouldNot(Equal(""))
344+
Ω(list.List()).Should(ContainElement(WithTransform(func(p jsonpatch.JSONPatch) string { return p.Path }, HavePrefix(prefix))))
345+
jsonPatch, err := jsonpatch2.DecodePatch(list.Raw())
343346
Ω(err).ShouldNot(HaveOccurred())
344347
patchedJSON, err := jsonPatch.Apply(currentJSON)
345348
Ω(err).ShouldNot(HaveOccurred())
346349
Ω(patchedJSON).Should(MatchJSON(expectedJSON))
347350
})
348351
It("string prefix", func() {
352+
prefix := []string{"b"}
349353
modified := F{B: &B{Bool: true, Str: "str"}}
350354
current := F{}
351355
expected := F{B: &B{Bool: true, Str: "str"}}
@@ -357,10 +361,11 @@ var _ = Describe("JSONPatch", func() {
357361
expectedJSON, err := json.Marshal(expected)
358362
Ω(err).ShouldNot(HaveOccurred())
359363

360-
bytes, _, err := jsonpatch.CreateJSONPatch(modified.B, current.B, jsonpatch.WithPrefix([]string{"b"}))
364+
list, err := jsonpatch.CreateJSONPatch(modified.B, current.B, jsonpatch.WithPrefix(prefix))
361365
Ω(err).ShouldNot(HaveOccurred())
362-
Ω(bytes.String()).ShouldNot(Equal(""))
363-
jsonPatch, err := jsonpatch2.DecodePatch(bytes)
366+
Ω(list.String()).ShouldNot(Equal(""))
367+
Ω(list.List()).Should(ContainElement(WithTransform(func(p jsonpatch.JSONPatch) string { return p.Path }, HavePrefix("/"+strings.Join(prefix, "/")))))
368+
jsonPatch, err := jsonpatch2.DecodePatch(list.Raw())
364369
Ω(err).ShouldNot(HaveOccurred())
365370
patchedJSON, err := jsonPatch.Apply(currentJSON)
366371
Ω(err).ShouldNot(HaveOccurred())
@@ -369,25 +374,25 @@ var _ = Describe("JSONPatch", func() {
369374
})
370375
Context("CreateJsonPatch_errors", func() {
371376
It("not matching types", func() {
372-
_, _, err := jsonpatch.CreateJSONPatch(A{}, B{})
377+
_, err := jsonpatch.CreateJSONPatch(A{}, B{})
373378
Ω(err).Should(HaveOccurred())
374379
})
375380
It("not matching interface types", func() {
376-
_, _, err := jsonpatch.CreateJSONPatch(G{1}, G{"str"})
381+
_, err := jsonpatch.CreateJSONPatch(G{1}, G{"str"})
377382
Ω(err).Should(HaveOccurred())
378383
})
379384
It("invalid map (map[string]int)", func() {
380-
_, _, err := jsonpatch.CreateJSONPatch(G{map[string]int{"key": 2}}, G{map[string]int{"key": 3}})
385+
_, err := jsonpatch.CreateJSONPatch(G{map[string]int{"key": 2}}, G{map[string]int{"key": 3}})
381386
Ω(err).Should(HaveOccurred())
382387
})
383388
It("invalid map (map[int]string)", func() {
384-
_, _, err := jsonpatch.CreateJSONPatch(G{map[int]string{1: "value"}}, G{map[int]string{2: "value"}})
389+
_, err := jsonpatch.CreateJSONPatch(G{map[int]string{1: "value"}}, G{map[int]string{2: "value"}})
385390
Ω(err).Should(HaveOccurred())
386391
})
387392
It("ignore slice order failed (duplicated key)", func() {
388-
_, _, err := jsonpatch.CreateJSONPatch([]int{1, 1, 1, 1}, []int{1, 2, 3}, jsonpatch.IgnoreSliceOrder())
393+
_, err := jsonpatch.CreateJSONPatch([]int{1, 1, 1, 1}, []int{1, 2, 3}, jsonpatch.IgnoreSliceOrder())
389394
Ω(err).Should(HaveOccurred())
390-
_, _, err = jsonpatch.CreateJSONPatch([]string{"1", "2", "3"}, []string{"1", "1"}, jsonpatch.IgnoreSliceOrder())
395+
_, err = jsonpatch.CreateJSONPatch([]string{"1", "2", "3"}, []string{"1", "1"}, jsonpatch.IgnoreSliceOrder())
391396
Ω(err).Should(HaveOccurred())
392397
})
393398
})
@@ -418,19 +423,19 @@ var _ = Describe("JSONPatch", func() {
418423
modifiedJSON, err := json.Marshal(modified)
419424
Ω(err).ShouldNot(HaveOccurred())
420425

421-
var bytes jsonpatch.Patch
422-
var changes int
426+
var list jsonpatch.JSONPatchList
423427
_ = b.Time("runtime", func() {
424-
bytes, changes, err = jsonpatch.CreateJSONPatch(modified, current)
428+
list, err = jsonpatch.CreateJSONPatch(modified, current)
425429
})
426430
Ω(err).ShouldNot(HaveOccurred())
427-
if bytes.Empty() {
431+
if list.Empty() {
428432
Ω(currentJSON).Should(MatchJSON(modifiedJSON))
429-
Ω(changes).Should(Equal(0))
433+
Ω(list.Len()).Should(Equal(0))
434+
430435
return
431436
}
432437

433-
jsonPatch, err := jsonpatch2.DecodePatch(bytes)
438+
jsonPatch, err := jsonpatch2.DecodePatch(list.Raw())
434439
Ω(err).ShouldNot(HaveOccurred())
435440
patchedJSON, err := jsonPatch.Apply(currentJSON)
436441
Ω(err).ShouldNot(HaveOccurred())
@@ -445,18 +450,18 @@ func testPatch(modified, current interface{}) {
445450
modifiedJSON, err := json.Marshal(modified)
446451
Ω(err).ShouldNot(HaveOccurred())
447452

448-
bytes, changes, err := jsonpatch.CreateJSONPatch(modified, current)
453+
list, err := jsonpatch.CreateJSONPatch(modified, current)
449454
Ω(err).ShouldNot(HaveOccurred())
450-
if bytes.Empty() {
455+
if list.Empty() {
451456
Ω(currentJSON).Should(MatchJSON(modifiedJSON))
452-
Ω(changes).Should(Equal(0))
453-
Ω(bytes.String()).Should(Equal(""))
457+
Ω(list.Len()).Should(Equal(0))
458+
Ω(list.String()).Should(Equal(""))
454459

455460
return
456461
}
457462

458-
Ω(bytes.String()).ShouldNot(Equal(""))
459-
jsonPatch, err := jsonpatch2.DecodePatch(bytes)
463+
Ω(list.String()).ShouldNot(Equal(""))
464+
jsonPatch, err := jsonpatch2.DecodePatch(list.Raw())
460465
Ω(err).ShouldNot(HaveOccurred())
461466
patchedJSON, err := jsonPatch.Apply(currentJSON)
462467
Ω(err).ShouldNot(HaveOccurred())
@@ -471,18 +476,18 @@ func testPatchWithExpected(modified, current, expected interface{}, options ...j
471476
expectedJSON, err := json.Marshal(expected)
472477
Ω(err).ShouldNot(HaveOccurred())
473478

474-
bytes, changes, err := jsonpatch.CreateJSONPatch(modified, current, options...)
479+
list, err := jsonpatch.CreateJSONPatch(modified, current, options...)
475480
Ω(err).ShouldNot(HaveOccurred())
476-
if bytes.Empty() {
481+
if list.Empty() {
477482
Ω(currentJSON).Should(MatchJSON(expectedJSON))
478-
Ω(changes).Should(Equal(0))
479-
Ω(bytes.String()).Should(Equal(""))
483+
Ω(list.Len()).Should(Equal(0))
484+
Ω(list.String()).Should(Equal(""))
480485

481486
return
482487
}
483488

484-
Ω(bytes.String()).ShouldNot(Equal(""))
485-
jsonPatch, err := jsonpatch2.DecodePatch(bytes)
489+
Ω(list.String()).ShouldNot(Equal(""))
490+
jsonPatch, err := jsonpatch2.DecodePatch(list.Raw())
486491
Ω(err).ShouldNot(HaveOccurred())
487492
patchedJSON, err := jsonPatch.Apply(currentJSON)
488493
Ω(err).ShouldNot(HaveOccurred())

walker.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ type walker struct {
2020
ignoredSlices []IgnorePattern
2121
}
2222

23-
// walk recursively process the modified and current JSON data structures simultaneously and in every step it compares
23+
// walk recursively processes the modified and current JSON data structures simultaneously and in every step it compares
2424
// the value of them with each other
2525
func (w *walker) walk(modified, current reflect.Value, pointer JSONPointer) error {
2626
// the data structures of both JSON objects must be identical
@@ -70,7 +70,7 @@ func (w *walker) walk(modified, current reflect.Value, pointer JSONPointer) erro
7070
return nil
7171
}
7272

73-
// processInterface processes to reflect.Interface values
73+
// processInterface processes reflect.Interface values
7474
func (w *walker) processInterface(modified reflect.Value, current reflect.Value, pointer JSONPointer) error {
7575
// extract the value form the interface and try to process it further
7676
if err := w.walk(reflect.ValueOf(modified.Interface()), reflect.ValueOf(current.Interface()), pointer); err != nil {
@@ -80,7 +80,7 @@ func (w *walker) processInterface(modified reflect.Value, current reflect.Value,
8080
return nil
8181
}
8282

83-
// processMap processes to reflect.Map values
83+
// processMap processes reflect.Map values
8484
func (w *walker) processMap(modified reflect.Value, current reflect.Value, pointer JSONPointer) error {
8585
// NOTE: currently only map[string]string are supported
8686
if len(modified.MapKeys()) > 0 && len(current.MapKeys()) == 0 {
@@ -130,7 +130,7 @@ func (w *walker) processMap(modified reflect.Value, current reflect.Value, point
130130
return nil
131131
}
132132

133-
// processSlice processes to reflect.Slice values
133+
// processSlice processes reflect.Slice values
134134
func (w *walker) processSlice(modified reflect.Value, current reflect.Value, pointer JSONPointer) error {
135135
if !w.predicate.Replace(pointer, modified.Interface(), current.Interface()) {
136136
return nil
@@ -235,7 +235,7 @@ func (w *walker) processSlice(modified reflect.Value, current reflect.Value, poi
235235
return nil
236236
}
237237

238-
// processPtr processes to reflect.Ptr values
238+
// processPtr processes reflect.Ptr values
239239
func (w *walker) processPtr(modified reflect.Value, current reflect.Value, pointer JSONPointer) error {
240240
if !modified.IsNil() && !current.IsNil() {
241241
// the values of the pointers will be processed in a next step
@@ -251,7 +251,7 @@ func (w *walker) processPtr(modified reflect.Value, current reflect.Value, point
251251
return nil
252252
}
253253

254-
// processStruct processes to reflect.Struct values
254+
// processStruct processes reflect.Struct values
255255
func (w *walker) processStruct(modified, current reflect.Value, pointer JSONPointer) error {
256256
if !w.predicate.Replace(pointer, modified.Interface(), current.Interface()) {
257257
return nil

0 commit comments

Comments
 (0)