-
Notifications
You must be signed in to change notification settings - Fork 347
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add IncludesAny and IncludesAll methods to ValidationError #35
Conversation
Adds IncludesAny and IncludesAll convenience methods to test whether an error contains some (IncludesAny) or all (IncludesAll) of a set of error conditions. Adds tests for IncludesAny and IncludesAll. Updates doc comments to follow standard Go conventions and changes ValidationError.valid() to use a value receiver so that all methods have the same kind of receiver.
Thanks for this PR. I am still a little bit torn whether we want to push additional functionality into the struct. My long term plan would actually to refactor this alltogether with the new error wrapping possibilities that were introduced in Go 1.13. This way we could rely on the stdlib's approach of using |
I kinda agree with this as discussed on #64. Do we want to keep going the both the bitflag and 1.13 error route? in any case, I can see these refactored error types working both ways but I don't know if such complexity should be warranted here |
Fair concerns—something like this (specifically, “IncludesAny”) would make some code in my work nicer and I did this implementation as a “throw it at the wall and see what sticks” first version. It looks like this could be implemented in terms of
This seems like it could coexist with the bitmasked error forms (e.g. type AnyOf InvalidReason
// AnyReason is a convenience constructor so that you don't have to think about the bitmasking operations.
func AnyReason(...of InvalidReason) AnyOf {
out := AnyOf(0)
for _, r := range of {
out |= r
}
return out
}
func (ar AnyReason) Error() string { return "TODO" }
func (ar AnyReason) Is(err error) bool { /* do the intersection-check */ } or you could choose to abandon them and go for more conventional sequentially-enumerated errors (which would definitely be an API break). These are just some ideas after a little pondering. |
@thetorpedodog, regarding the comment
I would assume the error returned from func (s *Server) ClaimFromToken(token string) (*CustomClaim, error) {
claim := CustomClaim{}
if _, err := jwt.ParseWithClaims(token, &claim, s.withKey); err != nil {
var validationError *CustomValidationError
if errors.As(err, &validationError) {
fmt.Println("got invalid claim %d", validationError.UserID)
// do custom error handling here
}
return err
}
return &claim, nil
}
func (s *Server) withKey(token *jwt.Token) (interface{}, error) { return s.Key, nil }
type CustomClaim struct { UserID int }
func (c CustomClaim) Valid() error {
if c.UserID <= 0 {
return &CustomValidationError{c.UserID}
}
return nil
}
type CustomValidationError struct { UserID int }
func (e *CustomValidationError) Error() string {
return fmt.Sprintf("invalid user id %d", e.UserID)
} To get the above to work, I think the only change needed would be to add an func (e *InvalidClaimError) Unwrap() error {
return e.Inner
} @lggomez, regarding the go 1.13 error API support and error type bitfield support, would the pseudo-code I have below address both issues? We could inspect the value of the error passed in against the current error code. I don't know how idiomatic it is, but it should work. I can make a PR if this approach sounds good. I have some custom code I wrote to work around the lack of the go 1.13 error support and I would love to get rid of it. I'm happy to contribute if it would help. func (e *InvalidClaimError) Is(err error) bool {
claimError, ok := err.(*InvalidClaimError)
if !ok {
return false
}
// perhaps a different bitwise check?
return claimError.Errors & e.Errors == 0
}
func SomeExampleFunction() {
_, err := jwt.ParseWithClaims(token, &claim, s.withKey)
if errors.Is(err, &jwt.InvalidClaimError{}) {
// handle case when the type of error is an jwt.InvalidClaimError
}
if errors.Is(err, &jwt.InvalidClaimError{Errors: jwt.ValidationErrorExpired}) {
// handle case when the error is a jwt.ValidationErrorExpired
}
if errors.Is(err, &jwt.InvalidClaimError{Errors: jwt.ValidationErrorExpired | jwt.ValidationErrorIssuedAt}) {
// handle case with multiple errors
}
} |
I think this functionality should now be possible with #136 You can find an example in the examples: Lines 101 to 114 in b44ad0a
|
Closing this as stale and possibly superseded by #136. |
Adds IncludesAny and IncludesAll convenience methods to test whether
an error contains some (IncludesAny) or all (IncludesAll) of a set of
error conditions.
Adds tests for IncludesAny and IncludesAll.
Updates doc comments to follow standard Go conventions and changes
ValidationError.valid() to use a value receiver so that all methods
have the same kind of receiver.