-
Notifications
You must be signed in to change notification settings - Fork 165
/
errors.go
154 lines (127 loc) · 6.38 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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Package errors contains custom error codes that are sent to clients.
package errors
import (
"errors"
"fmt"
openfgav1 "github.com/openfga/api/proto/openfga/v1"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/openfga/openfga/pkg/storage"
"github.com/openfga/openfga/pkg/tuple"
)
const InternalServerErrorMsg = "Internal Server Error"
var (
// AuthorizationModelResolutionTooComplex is used to avoid stack overflows
AuthorizationModelResolutionTooComplex = status.Error(codes.Code(openfgav1.ErrorCode_authorization_model_resolution_too_complex), "Authorization Model resolution required too many rewrite rules to be resolved. Check your authorization model for infinite recursion or too much nesting")
InvalidWriteInput = status.Error(codes.Code(openfgav1.ErrorCode_invalid_write_input), "Invalid input. Make sure you provide at least one write, or at least one delete")
InvalidContinuationToken = status.Error(codes.Code(openfgav1.ErrorCode_invalid_continuation_token), "Invalid continuation token")
InvalidExpandInput = status.Error(codes.Code(openfgav1.ErrorCode_invalid_expand_input), "Invalid input. Make sure you provide an object and a relation")
UnsupportedUserSet = status.Error(codes.Code(openfgav1.ErrorCode_unsupported_user_set), "Userset is not supported (right now)")
StoreIDNotFound = status.Error(codes.Code(openfgav1.NotFoundErrorCode_store_id_not_found), "Store ID not found")
MismatchObjectType = status.Error(codes.Code(openfgav1.ErrorCode_query_string_type_continuation_token_mismatch), "The type in the querystring and the continuation token don't match")
RequestCancelled = status.Error(codes.Code(openfgav1.InternalErrorCode_cancelled), "Request Cancelled")
RequestDeadlineExceeded = status.Error(codes.Code(openfgav1.InternalErrorCode_deadline_exceeded), "Request Deadline Exceeded")
)
type InternalError struct {
public error
internal error
}
func (e InternalError) Error() string {
return e.public.Error()
}
func (e InternalError) Is(target error) bool {
return target.Error() == e.Error()
}
func (e InternalError) InternalError() string {
return e.internal.Error()
}
func (e InternalError) Internal() error {
return e.internal
}
// NewInternalError returns an error that is decorated with a public-facing error message.
// It is only meant to be called by HandleError.
func NewInternalError(public string, internal error) InternalError {
if public == "" {
public = InternalServerErrorMsg
}
return InternalError{
public: status.Error(codes.Code(openfgav1.InternalErrorCode_internal_error), public),
internal: internal,
}
}
func ValidationError(cause error) error {
return status.Error(codes.Code(openfgav1.ErrorCode_validation_error), cause.Error())
}
func AssertionsNotForAuthorizationModelFound(modelID string) error {
return status.Error(codes.Code(openfgav1.ErrorCode_authorization_model_assertions_not_found), fmt.Sprintf("No assertions found for authorization model '%s'", modelID))
}
func AuthorizationModelNotFound(modelID string) error {
return status.Error(codes.Code(openfgav1.ErrorCode_authorization_model_not_found), fmt.Sprintf("Authorization Model '%s' not found", modelID))
}
func LatestAuthorizationModelNotFound(store string) error {
return status.Error(codes.Code(openfgav1.ErrorCode_latest_authorization_model_not_found), fmt.Sprintf("No authorization models found for store '%s'", store))
}
func TypeNotFound(objectType string) error {
return status.Error(codes.Code(openfgav1.ErrorCode_type_not_found), fmt.Sprintf("type '%s' not found", objectType))
}
func RelationNotFound(relation string, objectType string, tk *openfgav1.TupleKey) error {
msg := fmt.Sprintf("relation '%s#%s' not found", objectType, relation)
if tk != nil {
msg += fmt.Sprintf(" for tuple '%s'", tuple.TupleKeyToString(tk))
}
return status.Error(codes.Code(openfgav1.ErrorCode_relation_not_found), msg)
}
func ExceededEntityLimit(entity string, limit int) error {
return status.Error(codes.Code(openfgav1.ErrorCode_exceeded_entity_limit),
fmt.Sprintf("The number of %s exceeds the allowed limit of %d", entity, limit))
}
func DuplicateTupleInWrite(tk tuple.TupleWithoutCondition) error {
return status.Error(codes.Code(openfgav1.ErrorCode_cannot_allow_duplicate_tuples_in_one_request), fmt.Sprintf("duplicate tuple in write: user: '%s', relation: '%s', object: '%s'", tk.GetUser(), tk.GetRelation(), tk.GetObject()))
}
func WriteFailedDueToInvalidInput(err error) error {
if err != nil {
return status.Error(codes.Code(openfgav1.ErrorCode_write_failed_due_to_invalid_input), err.Error())
}
return status.Error(codes.Code(openfgav1.ErrorCode_write_failed_due_to_invalid_input), "Write failed due to invalid input")
}
func InvalidAuthorizationModelInput(err error) error {
return status.Error(codes.Code(openfgav1.ErrorCode_invalid_authorization_model), err.Error())
}
// HandleError is used to surface some errors, and hide others.
// Use `public` if you want to return a useful error message to the user.
func HandleError(public string, err error) error {
if errors.Is(err, storage.ErrTransactionalWriteFailed) {
return status.Error(codes.Aborted, err.Error())
} else if errors.Is(err, storage.ErrInvalidWriteInput) {
return WriteFailedDueToInvalidInput(err)
} else if errors.Is(err, storage.ErrInvalidContinuationToken) {
return InvalidContinuationToken
} else if errors.Is(err, storage.ErrMismatchObjectType) {
return MismatchObjectType
} else if errors.Is(err, storage.ErrCancelled) {
return RequestCancelled
} else if errors.Is(err, storage.ErrDeadlineExceeded) {
return RequestDeadlineExceeded
}
return NewInternalError(public, err)
}
// HandleTupleValidateError provide common routines for handling tuples validation error
func HandleTupleValidateError(err error) error {
switch t := err.(type) {
case *tuple.InvalidTupleError:
return status.Error(
codes.Code(openfgav1.ErrorCode_invalid_tuple),
fmt.Sprintf("Invalid tuple '%s'. Reason: %s", t.TupleKey, t.Cause.Error()),
)
case *tuple.TypeNotFoundError:
return TypeNotFound(t.TypeName)
case *tuple.RelationNotFoundError:
return RelationNotFound(t.Relation, t.TypeName, t.TupleKey)
case *tuple.InvalidConditionalTupleError:
return status.Error(
codes.Code(openfgav1.ErrorCode_validation_error),
err.Error(),
)
}
return HandleError("", err)
}