Skip to content

Commit fe25593

Browse files
committed
crud: support operation_data field in errors
This patch adds `operation_data` decoding for the `crud.Error`. The `operation_data` type is determined as `rowType` in `crud.Result`. Also, according to [1], an error can contain one of the following: - an error - an array of errors - nil So the error decoding logic has been modified to consider each case, in order to avoid comparing an error to nil. 1. https://github.com/tarantool/crud/tree/master#api Closes #330
1 parent b17735b commit fe25593

File tree

4 files changed

+77
-13
lines changed

4 files changed

+77
-13
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
1616
- More linters on CI (#310)
1717
- Meaningful description for read/write socket errors (#129)
1818
- Support password and password file to decrypt private SSL key file (#319)
19+
- Support `operation_data` in `crud.Error` (#330)
1920

2021
### Changed
2122

crud/error.go

+34-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package crud
22

33
import (
4+
"reflect"
45
"strings"
56

67
"github.com/vmihailenco/msgpack/v5"
@@ -21,6 +22,15 @@ type Error struct {
2122
Stack string
2223
// Str is the text of reason with error class.
2324
Str string
25+
// OperationData is the object/tuple with which an error occurred.
26+
OperationData interface{}
27+
// operationDataType contains the type of OperationData.
28+
operationDataType reflect.Type
29+
}
30+
31+
// newError creates an Error object with a custom operation data type to decoding.
32+
func newError(operationDataType reflect.Type) *Error {
33+
return &Error{operationDataType: operationDataType}
2434
}
2535

2636
// DecodeMsgpack provides custom msgpack decoder.
@@ -59,6 +69,20 @@ func (e *Error) DecodeMsgpack(d *msgpack.Decoder) error {
5969
if e.Str, err = d.DecodeString(); err != nil {
6070
return err
6171
}
72+
case "operation_data":
73+
if e.operationDataType != nil {
74+
tuple := reflect.New(e.operationDataType)
75+
if err = d.DecodeValue(tuple); err != nil {
76+
return err
77+
}
78+
e.OperationData = tuple.Elem().Interface()
79+
} else {
80+
var decoded interface{}
81+
if err = d.Decode(&decoded); err != nil {
82+
return err
83+
}
84+
e.OperationData = decoded
85+
}
6286
default:
6387
if err := d.Skip(); err != nil {
6488
return err
@@ -77,6 +101,13 @@ func (e Error) Error() string {
77101
// ErrorMany describes CRUD error object for `_many` methods.
78102
type ErrorMany struct {
79103
Errors []Error
104+
// operationDataType contains the type of OperationData for each Error.
105+
operationDataType reflect.Type
106+
}
107+
108+
// newErrorMany creates an ErrorMany object with a custom operation data type to decoding.
109+
func newErrorMany(operationDataType reflect.Type) *ErrorMany {
110+
return &ErrorMany{operationDataType: operationDataType}
80111
}
81112

82113
// DecodeMsgpack provides custom msgpack decoder.
@@ -88,16 +119,15 @@ func (e *ErrorMany) DecodeMsgpack(d *msgpack.Decoder) error {
88119

89120
var errs []Error
90121
for i := 0; i < l; i++ {
91-
var crudErr *Error = nil
122+
crudErr := newError(e.operationDataType)
92123
if err := d.Decode(&crudErr); err != nil {
93124
return err
94-
} else if crudErr != nil {
95-
errs = append(errs, *crudErr)
96125
}
126+
errs = append(errs, *crudErr)
97127
}
98128

99129
if len(errs) > 0 {
100-
*e = ErrorMany{Errors: errs}
130+
e.Errors = errs
101131
}
102132

103133
return nil

crud/example_test.go

+37
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,43 @@ func ExampleResult_rowsCustomType() {
7878
// [{{} 2010 45 bla}]
7979
}
8080

81+
func ExampleResult_operationDataCustomType() {
82+
conn := exampleConnect()
83+
req := crud.MakeInsertManyRequest(exampleSpace).Tuples([]interface{}{
84+
[]interface{}{uint(1), 1, "bla"},
85+
[]interface{}{uint(1), 2, "blabla"},
86+
[]interface{}{uint(2011), 3, nil},
87+
})
88+
89+
type Tuple struct {
90+
_msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
91+
Id uint64
92+
BucketId uint64
93+
Name string
94+
}
95+
96+
ret := crud.MakeResult(reflect.TypeOf(Tuple{}))
97+
if err := conn.Do(req).GetTyped(&ret); err != nil {
98+
crudErrs := err.(crud.ErrorMany)
99+
// We need to trim the error message to make the example repeatable.
100+
errmsg := crudErrs.Error()[:10]
101+
fmt.Printf("Failed to execute request: %s\n", errmsg)
102+
fmt.Println("Erroneous data:")
103+
for _, crudErr := range crudErrs.Errors {
104+
operationData := crudErr.OperationData.(Tuple)
105+
fmt.Println(operationData)
106+
}
107+
} else {
108+
fmt.Println(ret.Metadata)
109+
fmt.Println(ret.Rows)
110+
}
111+
// Output:
112+
// Failed to execute request: CallError:
113+
// Erroneous data:
114+
// {{} 1 2 blabla}
115+
// {{} 2011 3 }
116+
}
117+
81118
// ExampleResult_many demonstrates that there is no difference in a
82119
// response from *ManyRequest.
83120
func ExampleResult_many() {

crud/result.go

+5-9
Original file line numberDiff line numberDiff line change
@@ -137,21 +137,17 @@ func (r *Result) DecodeMsgpack(d *msgpack.Decoder) error {
137137

138138
var retErr error
139139
if msgpackIsArray(code) {
140-
var crudErr *ErrorMany
140+
crudErr := newErrorMany(r.rowType)
141141
if err := d.Decode(&crudErr); err != nil {
142142
return err
143143
}
144-
if crudErr != nil {
145-
retErr = *crudErr
146-
}
147-
} else {
148-
var crudErr *Error
144+
retErr = *crudErr
145+
} else if code != msgpcode.Nil {
146+
crudErr := newError(r.rowType)
149147
if err := d.Decode(&crudErr); err != nil {
150148
return err
151149
}
152-
if crudErr != nil {
153-
retErr = *crudErr
154-
}
150+
retErr = *crudErr
155151
}
156152

157153
for i := 2; i < arrLen; i++ {

0 commit comments

Comments
 (0)