Skip to content

Commit 5097ab2

Browse files
author
dmitriy kalinin
committed
provide context in errors
1 parent 77b858b commit 5097ab2

File tree

2 files changed

+86
-20
lines changed

2 files changed

+86
-20
lines changed

patch/op_definition.go

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,45 @@
11
package patch
22

33
import (
4+
"encoding/json"
45
"fmt"
6+
"strings"
57
)
68

79
// OpDefinition struct is useful for JSON and YAML unmarshaling
810
type OpDefinition struct {
9-
Type string
10-
Path *string
11-
Value *interface{}
11+
Type string `json:",omitempty"`
12+
Path *string `json:",omitempty"`
13+
Value *interface{} `json:",omitempty"`
1214
}
1315

16+
type parser struct{}
17+
1418
func NewOpsFromDefinitions(opDefs []OpDefinition) (Ops, error) {
1519
var ops []Op
16-
var op Op
17-
var err error
20+
var p parser
1821

1922
for i, opDef := range opDefs {
23+
var op Op
24+
var err error
25+
26+
opFmt := p.fmtOpDef(opDef)
27+
2028
switch opDef.Type {
2129
case "replace":
22-
op, err = newReplaceOp(opDef)
30+
op, err = p.newReplaceOp(opDef)
2331
if err != nil {
24-
return nil, fmt.Errorf("Replace operation [%d]: %s", i, err)
32+
return nil, fmt.Errorf("Replace operation [%d]: %s within\n%s", i, err, opFmt)
2533
}
2634

2735
case "remove":
28-
op, err = newRemoveOp(opDef)
36+
op, err = p.newRemoveOp(opDef)
2937
if err != nil {
30-
return nil, fmt.Errorf("Remove operation [%d]: %s", i, err)
38+
return nil, fmt.Errorf("Remove operation [%d]: %s within\n%s", i, err, opFmt)
3139
}
3240

3341
default:
34-
return nil, fmt.Errorf("Unknown operation [%d] with type '%s'", i, opDef.Type)
42+
return nil, fmt.Errorf("Unknown operation [%d] with type '%s' within\n%s", i, opDef.Type, opFmt)
3543
}
3644

3745
ops = append(ops, op)
@@ -40,7 +48,7 @@ func NewOpsFromDefinitions(opDefs []OpDefinition) (Ops, error) {
4048
return Ops(ops), nil
4149
}
4250

43-
func newReplaceOp(opDef OpDefinition) (ReplaceOp, error) {
51+
func (parser) newReplaceOp(opDef OpDefinition) (ReplaceOp, error) {
4452
if opDef.Path == nil {
4553
return ReplaceOp{}, fmt.Errorf("Missing path")
4654
}
@@ -57,7 +65,7 @@ func newReplaceOp(opDef OpDefinition) (ReplaceOp, error) {
5765
return ReplaceOp{Path: ptr, Value: *opDef.Value}, nil
5866
}
5967

60-
func newRemoveOp(opDef OpDefinition) (RemoveOp, error) {
68+
func (parser) newRemoveOp(opDef OpDefinition) (RemoveOp, error) {
6169
if opDef.Path == nil {
6270
return RemoveOp{}, fmt.Errorf("Missing path")
6371
}
@@ -73,3 +81,22 @@ func newRemoveOp(opDef OpDefinition) (RemoveOp, error) {
7381

7482
return RemoveOp{Path: ptr}, nil
7583
}
84+
85+
func (parser) fmtOpDef(opDef OpDefinition) string {
86+
var (
87+
redactedVal interface{} = "<redacted>"
88+
htmlDecoder = strings.NewReplacer("\\u003c", "<", "\\u003e", ">")
89+
)
90+
91+
if opDef.Value != nil {
92+
// can't JSON serialize generic interface{} anyway
93+
opDef.Value = &redactedVal
94+
}
95+
96+
bytes, err := json.MarshalIndent(opDef, "", " ")
97+
if err != nil {
98+
return "<unknown>"
99+
}
100+
101+
return htmlDecoder.Replace(string(bytes))
102+
}

patch/op_definition_test.go

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ var _ = Describe("NewOpsFromDefinitions", func() {
1212
path = "/abc"
1313
invalidPath = "abc"
1414
val interface{} = 123
15+
complexVal interface{} = map[interface{}]interface{}{123: 123}
1516
)
1617

1718
It("supports 'replace' and 'remove' operations", func() {
@@ -32,52 +33,90 @@ var _ = Describe("NewOpsFromDefinitions", func() {
3233
It("returns error if operation type is unknown", func() {
3334
_, err := NewOpsFromDefinitions([]OpDefinition{{Type: "test"}})
3435
Expect(err).To(HaveOccurred())
35-
Expect(err.Error()).To(Equal("Unknown operation [0] with type 'test'"))
36+
Expect(err.Error()).To(Equal(`Unknown operation [0] with type 'test' within
37+
{
38+
"Type": "test"
39+
}`))
3640
})
3741

3842
It("returns error if operation type is find since it's not useful in list of operations", func() {
3943
_, err := NewOpsFromDefinitions([]OpDefinition{{Type: "find"}})
4044
Expect(err).To(HaveOccurred())
41-
Expect(err.Error()).To(Equal("Unknown operation [0] with type 'find'"))
45+
Expect(err.Error()).To(ContainSubstring("Unknown operation [0] with type 'find'"))
46+
})
47+
48+
It("allows values to be complex in error messages", func() {
49+
_, err := NewOpsFromDefinitions([]OpDefinition{{Type: "test", Path: &invalidPath, Value: &complexVal}})
50+
Expect(err).To(HaveOccurred())
51+
Expect(err.Error()).To(Equal(`Unknown operation [0] with type 'test' within
52+
{
53+
"Type": "test",
54+
"Path": "abc",
55+
"Value": "<redacted>"
56+
}`))
4257
})
4358

4459
Describe("replace", func() {
4560
It("requires path", func() {
4661
_, err := NewOpsFromDefinitions([]OpDefinition{{Type: "replace"}})
4762
Expect(err).To(HaveOccurred())
48-
Expect(err.Error()).To(Equal("Replace operation [0]: Missing path"))
63+
Expect(err.Error()).To(Equal(`Replace operation [0]: Missing path within
64+
{
65+
"Type": "replace"
66+
}`))
4967
})
5068

5169
It("requires value", func() {
5270
_, err := NewOpsFromDefinitions([]OpDefinition{{Type: "replace", Path: &path}})
5371
Expect(err).To(HaveOccurred())
54-
Expect(err.Error()).To(Equal("Replace operation [0]: Missing value"))
72+
Expect(err.Error()).To(Equal(`Replace operation [0]: Missing value within
73+
{
74+
"Type": "replace",
75+
"Path": "/abc"
76+
}`))
5577
})
5678

5779
It("requires valid path", func() {
5880
_, err := NewOpsFromDefinitions([]OpDefinition{{Type: "replace", Path: &invalidPath, Value: &val}})
5981
Expect(err).To(HaveOccurred())
60-
Expect(err.Error()).To(ContainSubstring("Replace operation [0]: Invalid path: Expected to start with '/'"))
82+
Expect(err.Error()).To(Equal(`Replace operation [0]: Invalid path: Expected to start with '/' within
83+
{
84+
"Type": "replace",
85+
"Path": "abc",
86+
"Value": "<redacted>"
87+
}`))
6188
})
6289
})
6390

6491
Describe("remove", func() {
6592
It("requires path", func() {
6693
_, err := NewOpsFromDefinitions([]OpDefinition{{Type: "remove"}})
6794
Expect(err).To(HaveOccurred())
68-
Expect(err.Error()).To(Equal("Remove operation [0]: Missing path"))
95+
Expect(err.Error()).To(Equal(`Remove operation [0]: Missing path within
96+
{
97+
"Type": "remove"
98+
}`))
6999
})
70100

71101
It("does not allow value", func() {
72102
_, err := NewOpsFromDefinitions([]OpDefinition{{Type: "remove", Path: &path, Value: &val}})
73103
Expect(err).To(HaveOccurred())
74-
Expect(err.Error()).To(Equal("Remove operation [0]: Cannot specify value"))
104+
Expect(err.Error()).To(Equal(`Remove operation [0]: Cannot specify value within
105+
{
106+
"Type": "remove",
107+
"Path": "/abc",
108+
"Value": "<redacted>"
109+
}`))
75110
})
76111

77112
It("requires valid path", func() {
78113
_, err := NewOpsFromDefinitions([]OpDefinition{{Type: "remove", Path: &invalidPath}})
79114
Expect(err).To(HaveOccurred())
80-
Expect(err.Error()).To(ContainSubstring("Remove operation [0]: Invalid path: Expected to start with '/'"))
115+
Expect(err.Error()).To(Equal(`Remove operation [0]: Invalid path: Expected to start with '/' within
116+
{
117+
"Type": "remove",
118+
"Path": "abc"
119+
}`))
81120
})
82121
})
83122
})

0 commit comments

Comments
 (0)