Skip to content

Commit eb1c746

Browse files
Merge pull request #13 from mittwald/feat/catch-validation-errors
feat: catch validation errors explicitly, and expose their response body
2 parents f13f6dc + e4b4eba commit eb1c746

File tree

4 files changed

+83
-1
lines changed

4 files changed

+83
-1
lines changed

README.md

+22
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import "github.com/mittwald/api-client-go/mittwaldv2"
3030

3131
## Usage
3232

33+
### Initialization and authentication
34+
3335
Use the `mittwaldv2.New` function to instantiate a new client:
3436

3537
```go
@@ -59,6 +61,26 @@ Other options include:
5961
Have a look at our [API introduction][api-getting-started] for more information
6062
on how to obtain an API token and how to get started with the API.
6163

64+
### Error handling
65+
66+
Each API call returns a response object and an error. The response object contains the HTTP status code and the response body, which can be used to check for errors.
67+
68+
Some error conditions are also mapped to specific Go error types that you can check for:
69+
70+
- `httperr.ErrValidation` for validation errors
71+
- `httperr.ErrBadRequest` for 400 errors (except for validation errors)
72+
- `httperr.ErrNotFound` for 404 errors
73+
- `httperr.ErrPermissionDenied` for 403 errors
74+
75+
Use `errors.As` to check for these errors:
76+
77+
```go
78+
projects, res, err := client.Projects().ListProjects(ctx, req)
79+
if validationError := new(httperr.ErrValidation); errors.As(err, &validationError) {
80+
// ...
81+
}
82+
```
83+
6284
## Example
6385

6486
```go

pkg/httperr/bad_request_error.go

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package httperr
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
)
7+
8+
// ErrBadRequest represents the error of an invalid request to the server.
9+
type ErrBadRequest struct {
10+
Response *http.Response
11+
}
12+
13+
func (e *ErrBadRequest) Error() string {
14+
if e == nil || e.Response == nil {
15+
return "bad request: <nil>"
16+
}
17+
return fmt.Sprintf("bad request: %s", e.Response.Request.URL)
18+
}

pkg/httperr/from_response.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package httperr
22

3-
import "net/http"
3+
import (
4+
"net/http"
5+
)
46

57
// ErrFromResponse maps an HTTP response (with an error status code) to a (more
68
// or less) specific error type.
@@ -10,6 +12,11 @@ func ErrFromResponse(res *http.Response) error {
1012
return &ErrNotFound{Response: res}
1113
case http.StatusForbidden:
1214
return &ErrPermissionDenied{Response: res}
15+
case http.StatusBadRequest:
16+
if validationError, isValidationError := IsValidationErrorResponse(res); isValidationError {
17+
return &ErrValidation{Response: res, ValidationError: validationError}
18+
}
19+
return &ErrBadRequest{Response: res}
1320
default:
1421
return &ErrUnexpectedResponse{Response: res}
1522
}

pkg/httperr/validation_error.go

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package httperr
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"github.com/mittwald/api-client-go/mittwaldv2/generated/schemas/commonsv2"
7+
"net/http"
8+
)
9+
10+
func IsValidationErrorResponse(res *http.Response) (*commonsv2.ValidationErrors, bool) {
11+
target := commonsv2.ValidationErrors{}
12+
13+
if err := json.NewDecoder(res.Body).Decode(&target); err != nil {
14+
return nil, false
15+
}
16+
17+
if target.Type != commonsv2.ValidationErrorsTypeValidationError {
18+
return nil, false
19+
}
20+
21+
return &target, true
22+
}
23+
24+
// ErrValidation represents a validation error response from the server.
25+
type ErrValidation struct {
26+
Response *http.Response
27+
ValidationError *commonsv2.ValidationErrors
28+
}
29+
30+
func (e *ErrValidation) Error() string {
31+
if e.ValidationError != nil && e.ValidationError.Message != nil {
32+
return fmt.Sprintf("invalid request: %s", *e.ValidationError.Message)
33+
}
34+
return "invalid request: <nil>"
35+
}

0 commit comments

Comments
 (0)