Better error handling for API errors#596
Merged
jacobbednarz merged 8 commits into0.14from Mar 2, 2021
Merged
Conversation
This introduces a change to how we raise and interact with exceptions
raised by the API responses.
For quite some time, interacting with the API response errors has been
difficult due to them spitting out parsed JSON (as a string). This means
if you need to check the type of exception in order to recover or retry,
you need to use an unreliable `strings.Contains` check. This also
extends to internally when we want to show the user an error, we had a
`errorFromResponse` function which would return the HTTP status code and
whatever was in the `errors` array.
To improve the situation I'm proposing a revamp of our error handling by
exposing various methods on `APIRequestError` struct as helpers to get
the information clients may need to better handle errors. Here are some
examples:
### Return the whole error message
```go
fmt.Println(err.Error())
// => "HTTP status 400: Authentication error (10000)"
```
### Return only the HTTP status
```go
fmt.Println(err.(*cloudflare.APIRequestError).HTTPStatusCode())
// => 400
```
### Check if the response was rate limited
```go
fmt.Println(err.(*cloudflare.APIRequestError).HTTPStatusCode())
// => false
```
In order to achieve this, we'll need to update almost every method that
makes an API call to reflect the following (example from
`createAccessRule` in firewall.go):
```diff
@@ -218,7 +218,7 @@ func (api *API) createAccessRule(prefix string, accessRule AccessRule) (*AccessR
uri := prefix + "/firewall/access_rules/rules"
res, err := api.makeRequest("POST", uri, accessRule)
if err != nil {
- return nil, errors.Wrap(err, errMakeRequestError)
+ return nil, err.(*APIRequestError)
}
response := &AccessRuleResponse{}
```
Personally, I think this is a better situation for the maintainers and
consumers of this library as we'll be better equipped to handle
differing error scenarios. The Terraform provider will also benefit from
these changes for things like retries.
Contributor
Author
patryk
suggested changes
Feb 24, 2021
Updates the functions to assume > 1 error code may be returned in a single API response
patryk
approved these changes
Feb 26, 2021
patryk
left a comment
There was a problem hiding this comment.
Love the idea of concatenating multiple messages while stringifying.
Contributor
Author
|
Awesome, let me add some test coverage and update the methods themselves and we can get this shipped. |
firefart
reviewed
Feb 26, 2021
firefart
left a comment
There was a problem hiding this comment.
Thanks for the quick implementation @jacobbednarz !
Left you some comments on the changes
This was
linked to
issues
Feb 28, 2021
Contributor
Author
|
@firefart for your specific use case raised in #574, I envision your code looking something like this: resp, err := c.client.CreateAccountAccessRule(accountID, cloudflare.AccessRule{
Mode: "block",
Notes: comment,
Configuration: cloudflare.AccessRuleConfiguration{
Target: target,
Value: ip,
},
})
// BYO contains function
if err != nil {
if contains(err.(*APIRequestError).InternalErrorCodes(), 10009) {
// fine to ignore
} else {
// better look into the error
}
}
// ...or just access the index position directly
if err != nil {
if err.(*APIRequestError).InternalErrorCodes()[0] == 10009 {
// fine to ignore
} else {
// better look into the error
}
} |
…/cloudflare-go into better-error-handling-and-use
8 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This introduces a change to how we raise and interact with exceptions
raised by the API responses.
For quite some time, interacting with the API response errors has been
difficult due to them spitting out parsed JSON (as a string). This means
if you need to check the type of exception in order to recover or retry,
you need to use an unreliable
strings.Containscheck. This alsoextends to internally when we want to show the user an error, we had a
errorFromResponsefunction which would return the HTTP status code andwhatever was in the
errorsarray.To improve the situation I'm proposing a revamp of our error handling by
exposing various methods on
APIRequestErrorstruct as helpers to getthe information clients may need to better handle errors. Here are some
examples:
Return the whole error message
Return only the HTTP status
Check if the response was rate limited
Fetch just the internal error codes
In order to achieve this, we'll need to update almost every method that
makes an API call to reflect the following (example from
createAccessRulein firewall.go):Personally, I think this is a better situation for the maintainers and
consumers of this library as we'll be better equipped to handle
differing error scenarios. The Terraform provider will also benefit from
these changes for things like retries.
Recent issues related to this (more upstream in Terraform):
I haven't gone ahead and fixed any tests yet but if we agree this is
a good move, I'll take the usual steps with CI.