-
Notifications
You must be signed in to change notification settings - Fork 178
/
errors.go
136 lines (112 loc) · 3.69 KB
/
errors.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 rpc
import (
"context"
"errors"
"github.com/hashicorp/go-multierror"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/onflow/flow-go/module/state_synchronization/indexer"
"github.com/onflow/flow-go/storage"
)
// ConvertError converts a generic error into a grpc status error. The input may either
// be a status.Error already, or standard error type. Any error that matches on of the
// common status code mappings will be converted, all unmatched errors will be converted
// to the provided defaultCode.
func ConvertError(err error, msg string, defaultCode codes.Code) error {
if err == nil {
return nil
}
// Handle multierrors separately
if multiErr, ok := err.(*multierror.Error); ok {
return ConvertMultiError(multiErr, msg, defaultCode)
}
// Already converted
if status.Code(err) != codes.Unknown {
return err
}
if msg != "" {
msg += ": "
}
var returnCode codes.Code
switch {
case errors.Is(err, context.Canceled):
returnCode = codes.Canceled
case errors.Is(err, context.DeadlineExceeded):
returnCode = codes.DeadlineExceeded
default:
returnCode = defaultCode
}
return status.Errorf(returnCode, "%s%v", msg, err)
}
// ConvertStorageError converts a generic error into a grpc status error, converting storage errors
// into codes.NotFound
func ConvertStorageError(err error) error {
if err == nil {
return nil
}
// Already converted
if status.Code(err) == codes.NotFound {
return err
}
if errors.Is(err, storage.ErrNotFound) {
return status.Errorf(codes.NotFound, "not found: %v", err)
}
return status.Errorf(codes.Internal, "failed to find: %v", err)
}
// ConvertIndexError converts errors related to index and storage to appropriate gRPC status errors.
// If the error is nil, it returns nil. If the error is not recognized, it falls back to ConvertError
// with the provided default message and Internal gRPC code.
func ConvertIndexError(err error, height uint64, defaultMsg string) error {
if err == nil {
return nil
}
if errors.Is(err, indexer.ErrIndexNotInitialized) {
return status.Errorf(codes.FailedPrecondition, "data for block is not available: %v", err)
}
if errors.Is(err, storage.ErrHeightNotIndexed) {
return status.Errorf(codes.OutOfRange, "data for block height %d is not available", height)
}
if errors.Is(err, storage.ErrNotFound) {
return status.Errorf(codes.NotFound, "data not found: %v", err)
}
return ConvertError(err, defaultMsg, codes.Internal)
}
// ConvertMultiError converts a multierror to a grpc status error.
// If the errors have related status codes, the common code is returned, otherwise defaultCode is used.
func ConvertMultiError(err *multierror.Error, msg string, defaultCode codes.Code) error {
allErrors := err.WrappedErrors()
if len(allErrors) == 0 {
return nil
}
if msg != "" {
msg += ": "
}
// get a list of all of status codes
allCodes := make(map[codes.Code]struct{})
for _, err := range allErrors {
allCodes[status.Code(err)] = struct{}{}
}
// if they all match, return that
if len(allCodes) == 1 {
code := status.Code(allErrors[0])
return status.Errorf(code, "%s%v", msg, err)
}
// if they mostly match, ignore Unavailable and DeadlineExceeded since any other code is
// more descriptive
if len(allCodes) == 2 {
if _, ok := allCodes[codes.Unavailable]; ok {
delete(allCodes, codes.Unavailable)
for code := range allCodes {
return status.Errorf(code, "%s%v", msg, err)
}
}
if _, ok := allCodes[codes.DeadlineExceeded]; ok {
delete(allCodes, codes.DeadlineExceeded)
for code := range allCodes {
return status.Errorf(code, "%s%v", msg, err)
}
}
}
// otherwise, return the default code
return status.Errorf(defaultCode, "%s%v", msg, err)
}