Merge pull request #287 from go-kit/addsvc-errors
examples/addsvc: more sophisticated error encoding
Peter Bourgon
6 years ago
41 | 41 | if err != nil { |
42 | 42 | return 0, err |
43 | 43 | } |
44 | return response.(sumResponse).V, nil | |
44 | return response.(sumResponse).V, response.(sumResponse).Err | |
45 | 45 | } |
46 | 46 | |
47 | 47 | // Concat implements Service. Primarily useful in a client. |
51 | 51 | if err != nil { |
52 | 52 | return "", err |
53 | 53 | } |
54 | return response.(concatResponse).V, err | |
54 | return response.(concatResponse).V, response.(concatResponse).Err | |
55 | 55 | } |
56 | 56 | |
57 | 57 | // MakeSumEndpoint returns an endpoint that invokes Sum on the service. |
60 | 60 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { |
61 | 61 | sumReq := request.(sumRequest) |
62 | 62 | v, err := s.Sum(ctx, sumReq.A, sumReq.B) |
63 | if err != nil { | |
64 | return nil, err | |
63 | if err == ErrIntOverflow { | |
64 | return nil, err // special case; see comment on ErrIntOverflow | |
65 | 65 | } |
66 | 66 | return sumResponse{ |
67 | V: v, | |
67 | V: v, | |
68 | Err: err, | |
68 | 69 | }, nil |
69 | 70 | } |
70 | 71 | } |
75 | 76 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { |
76 | 77 | concatReq := request.(concatRequest) |
77 | 78 | v, err := s.Concat(ctx, concatReq.A, concatReq.B) |
78 | if err != nil { | |
79 | return nil, err | |
80 | } | |
81 | 79 | return concatResponse{ |
82 | V: v, | |
80 | V: v, | |
81 | Err: err, | |
83 | 82 | }, nil |
84 | 83 | } |
85 | 84 | } |
123 | 122 | |
124 | 123 | type sumRequest struct{ A, B int } |
125 | 124 | |
126 | type sumResponse struct{ V int } | |
125 | type sumResponse struct { | |
126 | V int | |
127 | Err error | |
128 | } | |
127 | 129 | |
128 | 130 | type concatRequest struct{ A, B string } |
129 | 131 | |
130 | type concatResponse struct{ V string } | |
132 | type concatResponse struct { | |
133 | V string | |
134 | Err error | |
135 | } |
46 | 46 | |
47 | 47 | // The sum response contains the result of the calculation. |
48 | 48 | type SumReply struct { |
49 | V int64 `protobuf:"varint,1,opt,name=v" json:"v,omitempty"` | |
49 | V int64 `protobuf:"varint,1,opt,name=v" json:"v,omitempty"` | |
50 | Err string `protobuf:"bytes,2,opt,name=err" json:"err,omitempty"` | |
50 | 51 | } |
51 | 52 | |
52 | 53 | func (m *SumReply) Reset() { *m = SumReply{} } |
67 | 68 | |
68 | 69 | // The Concat response contains the result of the concatenation. |
69 | 70 | type ConcatReply struct { |
70 | V string `protobuf:"bytes,1,opt,name=v" json:"v,omitempty"` | |
71 | V string `protobuf:"bytes,1,opt,name=v" json:"v,omitempty"` | |
72 | Err string `protobuf:"bytes,2,opt,name=err" json:"err,omitempty"` | |
71 | 73 | } |
72 | 74 | |
73 | 75 | func (m *ConcatReply) Reset() { *m = ConcatReply{} } |
191 | 193 | } |
192 | 194 | |
193 | 195 | var fileDescriptor0 = []byte{ |
194 | // 174 bytes of a gzipped FileDescriptorProto | |
196 | // 188 bytes of a gzipped FileDescriptorProto | |
195 | 197 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x49, 0x4c, 0x49, 0x29, |
196 | 198 | 0x2e, 0x4b, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2a, 0x48, 0x52, 0xd2, 0xe0, 0xe2, |
197 | 199 | 0x0a, 0x2e, 0xcd, 0x0d, 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, 0x2e, 0x11, 0xe2, 0xe1, 0x62, 0x4c, 0x94, |
198 | 200 | 0x60, 0x54, 0x60, 0xd4, 0x60, 0x0e, 0x62, 0x4c, 0x04, 0xf1, 0x92, 0x24, 0x98, 0x20, 0xbc, 0x24, |
199 | 0x25, 0x09, 0x2e, 0x0e, 0xb0, 0xca, 0x82, 0x9c, 0x4a, 0x90, 0x4c, 0x19, 0x4c, 0x5d, 0x99, 0x92, | |
200 | 0x36, 0x17, 0xaf, 0x73, 0x7e, 0x5e, 0x72, 0x62, 0x09, 0x86, 0x31, 0x9c, 0x28, 0xc6, 0x70, 0x82, | |
201 | 0x8c, 0x91, 0xe6, 0xe2, 0x86, 0x29, 0x46, 0x31, 0x09, 0x28, 0x59, 0x66, 0x14, 0xc3, 0xc5, 0xec, | |
202 | 0x98, 0x92, 0x22, 0xa4, 0xca, 0xc5, 0x0c, 0xb4, 0x4a, 0x88, 0x4f, 0xaf, 0x20, 0x49, 0x0f, 0xe1, | |
203 | 0x3a, 0x29, 0x1e, 0x38, 0x1f, 0xa8, 0x53, 0x89, 0x41, 0x48, 0x8f, 0x8b, 0x0d, 0x62, 0x94, 0x90, | |
204 | 0x20, 0x48, 0x06, 0xc5, 0x0d, 0x52, 0xfc, 0xc8, 0x42, 0x60, 0xf5, 0x49, 0x6c, 0x60, 0x6f, 0x1b, | |
205 | 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x8b, 0x2c, 0x12, 0xb4, 0x06, 0x01, 0x00, 0x00, | |
206 | } | |
201 | 0x25, 0x2d, 0x2e, 0x0e, 0xb0, 0xca, 0x82, 0x9c, 0x4a, 0x90, 0x4c, 0x19, 0x4c, 0x5d, 0x99, 0x90, | |
202 | 0x00, 0x17, 0x73, 0x6a, 0x51, 0x11, 0x58, 0x25, 0x67, 0x10, 0x88, 0xa9, 0xa4, 0xcd, 0xc5, 0xeb, | |
203 | 0x9c, 0x9f, 0x97, 0x9c, 0x58, 0x82, 0x61, 0x30, 0x27, 0x8a, 0xc1, 0x9c, 0x20, 0x83, 0x75, 0xb9, | |
204 | 0xb8, 0x61, 0x8a, 0x51, 0xcc, 0xe6, 0xc4, 0x6a, 0xb6, 0x51, 0x0c, 0x17, 0xb3, 0x63, 0x4a, 0x8a, | |
205 | 0x90, 0x2a, 0x17, 0x33, 0xd0, 0x39, 0x42, 0x7c, 0x7a, 0x05, 0x49, 0x7a, 0x08, 0x1f, 0x48, 0xf1, | |
206 | 0xc0, 0xf9, 0x40, 0xb3, 0x94, 0x18, 0x84, 0xf4, 0xb8, 0xd8, 0x20, 0x86, 0x0b, 0x09, 0x82, 0x64, | |
207 | 0x50, 0x5c, 0x25, 0xc5, 0x8f, 0x2c, 0x04, 0x56, 0x9f, 0xc4, 0x06, 0x0e, 0x1a, 0x63, 0x40, 0x00, | |
208 | 0x00, 0x00, 0xff, 0xff, 0xdc, 0x37, 0x81, 0x99, 0x2a, 0x01, 0x00, 0x00, | |
209 | } |
19 | 19 | // The sum response contains the result of the calculation. |
20 | 20 | message SumReply { |
21 | 21 | int64 v = 1; |
22 | string err = 2; | |
22 | 23 | } |
23 | 24 | |
24 | 25 | // The Concat request contains two parameters. |
30 | 31 | // The Concat response contains the result of the concatenation. |
31 | 32 | message ConcatReply { |
32 | 33 | string v = 1; |
34 | string err = 2; | |
33 | 35 | } |
18 | 18 | Concat(ctx context.Context, a, b string) (string, error) |
19 | 19 | } |
20 | 20 | |
21 | // Business-domain errors like these may be served in two ways: returned | |
22 | // directly by endpoints, or bundled into the response struct. Both methods can | |
23 | // be made to work, but errors returned directly by endpoints are counted by | |
24 | // middlewares that check errors, like circuit breakers. | |
25 | // | |
26 | // If you don't want that behavior -- and you probably don't -- then it's better | |
27 | // to bundle errors into the response struct. | |
28 | ||
21 | 29 | var ( |
22 | 30 | // ErrTwoZeroes is an arbitrary business rule for the Add method. |
23 | 31 | ErrTwoZeroes = errors.New("can't sum two zeroes") |
24 | 32 | |
25 | // ErrIntOverflow protects the Add method. | |
33 | // ErrIntOverflow protects the Add method. We've decided that this error | |
34 | // indicates a misbehaving service and should count against e.g. circuit | |
35 | // breakers. So, we return it directly in endpoints, to illustrate the | |
36 | // difference. In a real service, this probably wouldn't be the case. | |
26 | 37 | ErrIntOverflow = errors.New("integer overflow") |
27 | 38 | |
28 | 39 | // ErrMaxSizeExceeded protects the Concat method. |
29 | 40 | ErrMaxSizeExceeded = errors.New("result exceeds maximum size") |
30 | 41 | ) |
42 | ||
43 | // These annoying helper functions are required to translate Go error types to | |
44 | // and from strings, which is the type we use in our IDLs to represent errors. | |
45 | // There is special casing to treat empty strings as nil errors. | |
46 | ||
47 | func str2err(s string) error { | |
48 | if s == "" { | |
49 | return nil | |
50 | } | |
51 | return errors.New(s) | |
52 | } | |
53 | ||
54 | func err2str(err error) string { | |
55 | if err == nil { | |
56 | return "" | |
57 | } | |
58 | return err.Error() | |
59 | } | |
31 | 60 | |
32 | 61 | // NewBasicService returns a naïve, stateless implementation of Service. |
33 | 62 | func NewBasicService() Service { |
0 | 0 | struct SumReply { |
1 | 1 | 1: i64 value |
2 | 2: string err | |
2 | 3 | } |
3 | 4 | |
4 | 5 | struct ConcatReply { |
5 | 6 | 1: string value |
7 | 2: string err | |
6 | 8 | } |
7 | 9 | |
8 | 10 | service AddService { |
17 | 17 | |
18 | 18 | // Attributes: |
19 | 19 | // - Value |
20 | // - Err | |
20 | 21 | type SumReply struct { |
21 | Value int64 `thrift:"value,1" json:"value"` | |
22 | Value int64 `thrift:"value,1" json:"value"` | |
23 | Err string `thrift:"err,2" json:"err"` | |
22 | 24 | } |
23 | 25 | |
24 | 26 | func NewSumReply() *SumReply { |
27 | 29 | |
28 | 30 | func (p *SumReply) GetValue() int64 { |
29 | 31 | return p.Value |
32 | } | |
33 | ||
34 | func (p *SumReply) GetErr() string { | |
35 | return p.Err | |
30 | 36 | } |
31 | 37 | func (p *SumReply) Read(iprot thrift.TProtocol) error { |
32 | 38 | if _, err := iprot.ReadStructBegin(); err != nil { |
46 | 52 | if err := p.readField1(iprot); err != nil { |
47 | 53 | return err |
48 | 54 | } |
55 | case 2: | |
56 | if err := p.readField2(iprot); err != nil { | |
57 | return err | |
58 | } | |
49 | 59 | default: |
50 | 60 | if err := iprot.Skip(fieldTypeId); err != nil { |
51 | 61 | return err |
70 | 80 | return nil |
71 | 81 | } |
72 | 82 | |
83 | func (p *SumReply) readField2(iprot thrift.TProtocol) error { | |
84 | if v, err := iprot.ReadString(); err != nil { | |
85 | return thrift.PrependError("error reading field 2: ", err) | |
86 | } else { | |
87 | p.Err = v | |
88 | } | |
89 | return nil | |
90 | } | |
91 | ||
73 | 92 | func (p *SumReply) Write(oprot thrift.TProtocol) error { |
74 | 93 | if err := oprot.WriteStructBegin("SumReply"); err != nil { |
75 | 94 | return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) |
77 | 96 | if err := p.writeField1(oprot); err != nil { |
78 | 97 | return err |
79 | 98 | } |
99 | if err := p.writeField2(oprot); err != nil { | |
100 | return err | |
101 | } | |
80 | 102 | if err := oprot.WriteFieldStop(); err != nil { |
81 | 103 | return thrift.PrependError("write field stop error: ", err) |
82 | 104 | } |
99 | 121 | return err |
100 | 122 | } |
101 | 123 | |
124 | func (p *SumReply) writeField2(oprot thrift.TProtocol) (err error) { | |
125 | if err := oprot.WriteFieldBegin("err", thrift.STRING, 2); err != nil { | |
126 | return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:err: ", p), err) | |
127 | } | |
128 | if err := oprot.WriteString(string(p.Err)); err != nil { | |
129 | return thrift.PrependError(fmt.Sprintf("%T.err (2) field write error: ", p), err) | |
130 | } | |
131 | if err := oprot.WriteFieldEnd(); err != nil { | |
132 | return thrift.PrependError(fmt.Sprintf("%T write field end error 2:err: ", p), err) | |
133 | } | |
134 | return err | |
135 | } | |
136 | ||
102 | 137 | func (p *SumReply) String() string { |
103 | 138 | if p == nil { |
104 | 139 | return "<nil>" |
108 | 143 | |
109 | 144 | // Attributes: |
110 | 145 | // - Value |
146 | // - Err | |
111 | 147 | type ConcatReply struct { |
112 | 148 | Value string `thrift:"value,1" json:"value"` |
149 | Err string `thrift:"err,2" json:"err"` | |
113 | 150 | } |
114 | 151 | |
115 | 152 | func NewConcatReply() *ConcatReply { |
118 | 155 | |
119 | 156 | func (p *ConcatReply) GetValue() string { |
120 | 157 | return p.Value |
158 | } | |
159 | ||
160 | func (p *ConcatReply) GetErr() string { | |
161 | return p.Err | |
121 | 162 | } |
122 | 163 | func (p *ConcatReply) Read(iprot thrift.TProtocol) error { |
123 | 164 | if _, err := iprot.ReadStructBegin(); err != nil { |
137 | 178 | if err := p.readField1(iprot); err != nil { |
138 | 179 | return err |
139 | 180 | } |
181 | case 2: | |
182 | if err := p.readField2(iprot); err != nil { | |
183 | return err | |
184 | } | |
140 | 185 | default: |
141 | 186 | if err := iprot.Skip(fieldTypeId); err != nil { |
142 | 187 | return err |
161 | 206 | return nil |
162 | 207 | } |
163 | 208 | |
209 | func (p *ConcatReply) readField2(iprot thrift.TProtocol) error { | |
210 | if v, err := iprot.ReadString(); err != nil { | |
211 | return thrift.PrependError("error reading field 2: ", err) | |
212 | } else { | |
213 | p.Err = v | |
214 | } | |
215 | return nil | |
216 | } | |
217 | ||
164 | 218 | func (p *ConcatReply) Write(oprot thrift.TProtocol) error { |
165 | 219 | if err := oprot.WriteStructBegin("ConcatReply"); err != nil { |
166 | 220 | return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) |
168 | 222 | if err := p.writeField1(oprot); err != nil { |
169 | 223 | return err |
170 | 224 | } |
225 | if err := p.writeField2(oprot); err != nil { | |
226 | return err | |
227 | } | |
171 | 228 | if err := oprot.WriteFieldStop(); err != nil { |
172 | 229 | return thrift.PrependError("write field stop error: ", err) |
173 | 230 | } |
190 | 247 | return err |
191 | 248 | } |
192 | 249 | |
250 | func (p *ConcatReply) writeField2(oprot thrift.TProtocol) (err error) { | |
251 | if err := oprot.WriteFieldBegin("err", thrift.STRING, 2); err != nil { | |
252 | return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:err: ", p), err) | |
253 | } | |
254 | if err := oprot.WriteString(string(p.Err)); err != nil { | |
255 | return thrift.PrependError(fmt.Sprintf("%T.err (2) field write error: ", p), err) | |
256 | } | |
257 | if err := oprot.WriteFieldEnd(); err != nil { | |
258 | return thrift.PrependError(fmt.Sprintf("%T write field end error 2:err: ", p), err) | |
259 | } | |
260 | return err | |
261 | } | |
262 | ||
193 | 263 | func (p *ConcatReply) String() string { |
194 | 264 | if p == nil { |
195 | 265 | return "<nil>" |
75 | 75 | // gRPC sum reply to a user-domain sum response. Primarily useful in a client. |
76 | 76 | func DecodeGRPCSumResponse(_ context.Context, grpcReply interface{}) (interface{}, error) { |
77 | 77 | reply := grpcReply.(*pb.SumReply) |
78 | return sumResponse{V: int(reply.V)}, nil | |
78 | return sumResponse{V: int(reply.V), Err: str2err(reply.Err)}, nil | |
79 | 79 | } |
80 | 80 | |
81 | 81 | // DecodeGRPCConcatResponse is a transport/grpc.DecodeResponseFunc that converts |
83 | 83 | // client. |
84 | 84 | func DecodeGRPCConcatResponse(_ context.Context, grpcReply interface{}) (interface{}, error) { |
85 | 85 | reply := grpcReply.(*pb.ConcatReply) |
86 | return concatResponse{V: reply.V}, nil | |
86 | return concatResponse{V: reply.V, Err: str2err(reply.Err)}, nil | |
87 | 87 | } |
88 | 88 | |
89 | 89 | // EncodeGRPCSumResponse is a transport/grpc.EncodeResponseFunc that converts a |
90 | 90 | // user-domain sum response to a gRPC sum reply. Primarily useful in a server. |
91 | 91 | func EncodeGRPCSumResponse(_ context.Context, response interface{}) (interface{}, error) { |
92 | 92 | resp := response.(sumResponse) |
93 | return &pb.SumReply{V: int64(resp.V)}, nil | |
93 | return &pb.SumReply{V: int64(resp.V), Err: err2str(resp.Err)}, nil | |
94 | 94 | } |
95 | 95 | |
96 | 96 | // EncodeGRPCConcatResponse is a transport/grpc.EncodeResponseFunc that converts |
98 | 98 | // server. |
99 | 99 | func EncodeGRPCConcatResponse(_ context.Context, response interface{}) (interface{}, error) { |
100 | 100 | resp := response.(concatResponse) |
101 | return &pb.ConcatReply{V: resp.V}, nil | |
101 | return &pb.ConcatReply{V: resp.V, Err: err2str(resp.Err)}, nil | |
102 | 102 | } |
103 | 103 | |
104 | 104 | // EncodeGRPCSumRequest is a transport/grpc.EncodeRequestFunc that converts a |
34 | 34 | return nil, err |
35 | 35 | } |
36 | 36 | resp := response.(sumResponse) |
37 | return &thriftadd.SumReply{Value: int64(resp.V)}, nil | |
37 | return &thriftadd.SumReply{Value: int64(resp.V), Err: err2str(resp.Err)}, nil | |
38 | 38 | } |
39 | 39 | |
40 | 40 | func (s *thriftServer) Concat(a string, b string) (*thriftadd.ConcatReply, error) { |
44 | 44 | return nil, err |
45 | 45 | } |
46 | 46 | resp := response.(concatResponse) |
47 | return &thriftadd.ConcatReply{Value: resp.V}, nil | |
47 | return &thriftadd.ConcatReply{Value: resp.V, Err: err2str(resp.Err)}, nil | |
48 | 48 | } |
49 | 49 | |
50 | 50 | // MakeThriftSumEndpoint returns an endpoint that invokes the passed Thrift client. |
53 | 53 | return func(ctx context.Context, request interface{}) (interface{}, error) { |
54 | 54 | req := request.(sumRequest) |
55 | 55 | reply, err := client.Sum(int64(req.A), int64(req.B)) |
56 | if err != nil { | |
57 | return nil, err | |
56 | if err == ErrIntOverflow { | |
57 | return nil, err // special case; see comment on ErrIntOverflow | |
58 | 58 | } |
59 | return sumResponse{V: int(reply.Value)}, nil | |
59 | return sumResponse{V: int(reply.Value), Err: err}, nil | |
60 | 60 | } |
61 | 61 | } |
62 | 62 | |
67 | 67 | return func(ctx context.Context, request interface{}) (interface{}, error) { |
68 | 68 | req := request.(concatRequest) |
69 | 69 | reply, err := client.Concat(req.A, req.B) |
70 | if err != nil { | |
71 | return nil, err | |
72 | } | |
73 | return concatResponse{V: reply.Value}, nil | |
70 | return concatResponse{V: reply.Value, Err: err}, nil | |
74 | 71 | } |
75 | 72 | } |