-
-
Notifications
You must be signed in to change notification settings - Fork 564
/
Copy patherror.go
136 lines (126 loc) Β· 4.13 KB
/
error.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package grpc
import (
"fmt"
"github.com/golang/protobuf/proto"
"goa.design/goa"
goapb "goa.design/goa/grpc/pb"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type (
// ClientError is an error returned by a gRPC service client.
ClientError struct {
// Name is a name for this class of errors.
Name string
// Message contains the specific error details.
Message string
// Service is the name of the service.
Service string
// Method is the name of the service method.
Method string
// Is the error temporary?
Temporary bool
// Is the error a timeout?
Timeout bool
// Is the error a server-side fault?
Fault bool
}
)
// NewErrorResponse creates a new ErrorResponse protocol buffer message from
// the given error. If the given error is a goa ServiceError, the ErrorResponse
// message will be set with the corresponding Timeout, Temporary, and Fault
// characteristics. If the error is not a goa ServiceError, it creates an
// ErrorResponse message with the Fault field set to true.
func NewErrorResponse(err error) *goapb.ErrorResponse {
if gerr, ok := err.(*goa.ServiceError); ok {
return &goapb.ErrorResponse{
Name: gerr.Name,
Id: gerr.ID,
Msg: gerr.Message,
Timeout: gerr.Timeout,
Temporary: gerr.Temporary,
Fault: gerr.Fault,
}
}
return NewErrorResponse(goa.Fault(err.Error()))
}
// NewServiceError returns a goa ServiceError type for the given ErrorResponse
// message.
func NewServiceError(resp *goapb.ErrorResponse) *goa.ServiceError {
return &goa.ServiceError{
Name: resp.Name,
ID: resp.Id,
Message: resp.Msg,
Timeout: resp.Timeout,
Temporary: resp.Temporary,
Fault: resp.Fault,
}
}
// NewStatusError creates a gRPC status error with the error response
// messages added to its details.
func NewStatusError(code codes.Code, err error, details ...proto.Message) error {
st := status.New(code, err.Error())
if s, err := st.WithDetails(details...); err == nil {
return s.Err()
}
return st.Err()
}
// EncodeError returns a gRPC status error from the given error with the error
// response encoded in the status details. If error is a goa ServiceError type
// it implements a heuristic to compute the status code from the Timeout,
// Fault, and Temporary characteristics of the ServiceError. If error is not a
// ServiceError or a gRPC status error it returns a gRPC status error with
// Unknown code and Fault characteristic set.
func EncodeError(err error) error {
if st, ok := status.FromError(err); ok {
if s, err := st.WithDetails(NewErrorResponse(err)); err == nil {
return s.Err()
}
return st.Err()
}
if gerr, ok := err.(*goa.ServiceError); ok {
// goa service error type. Compute the status code from the service error
// characteristics and create a new detailed gRPC status error.
var code codes.Code
{
code = codes.Unknown
if gerr.Fault {
code = codes.Internal
}
if gerr.Timeout {
code = codes.DeadlineExceeded
}
if gerr.Temporary {
code = codes.Unavailable
}
}
return NewStatusError(code, err, NewErrorResponse(err))
}
// Return an unknown gRPC status error with fault characteristic set.
return NewStatusError(codes.Unknown, err, NewErrorResponse(err))
}
// DecodeError returns the error message encoded in the status details if error
// is a gRPC status error. It assumes that the error message is encoded as the
// first item in the details. It returns nil if the error is not a gRPC status
// error or if no detail is found.
func DecodeError(err error) proto.Message {
st, ok := status.FromError(err)
if !ok {
return nil
}
details := st.Details()
if len(details) == 0 {
return nil
}
return details[0].(proto.Message)
}
// ErrInvalidType is the error returned when the wrong type is given to a
// encoder or decoder.
func ErrInvalidType(svc, m, expected string, actual interface{}) error {
msg := fmt.Sprintf("invalid value expected %s, got %v", expected, actual)
return &ClientError{Name: "invalid_type", Message: msg, Service: svc, Method: m}
}
// Error builds an error message.
func (c *ClientError) Error() string {
return fmt.Sprintf("[%s %s]: %s", c.Service, c.Method, c.Message)
}