Add httputils.FromStatusCode()
This utility allows a client to convert an API response back to a typed error; allowing the client to perform different actions based on the type of error, without having to resort to string-matching the error. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
87d593639c
commit
1af30c50ca
2 changed files with 139 additions and 0 deletions
|
@ -76,6 +76,52 @@ func GetHTTPErrorStatusCode(err error) int {
|
|||
return statusCode
|
||||
}
|
||||
|
||||
// FromStatusCode creates an errdef error, based on the provided status-code
|
||||
func FromStatusCode(err error, statusCode int) error {
|
||||
if err == nil {
|
||||
return err
|
||||
}
|
||||
switch statusCode {
|
||||
case http.StatusNotFound:
|
||||
err = errdefs.NotFound(err)
|
||||
case http.StatusBadRequest:
|
||||
err = errdefs.InvalidParameter(err)
|
||||
case http.StatusConflict:
|
||||
err = errdefs.Conflict(err)
|
||||
case http.StatusUnauthorized:
|
||||
err = errdefs.Unauthorized(err)
|
||||
case http.StatusServiceUnavailable:
|
||||
err = errdefs.Unavailable(err)
|
||||
case http.StatusForbidden:
|
||||
err = errdefs.Forbidden(err)
|
||||
case http.StatusNotModified:
|
||||
err = errdefs.NotModified(err)
|
||||
case http.StatusNotImplemented:
|
||||
err = errdefs.NotImplemented(err)
|
||||
case http.StatusInternalServerError:
|
||||
if !errdefs.IsSystem(err) && !errdefs.IsUnknown(err) && !errdefs.IsDataLoss(err) && !errdefs.IsDeadline(err) && !errdefs.IsCancelled(err) {
|
||||
err = errdefs.System(err)
|
||||
}
|
||||
default:
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"module": "api",
|
||||
"status_code": fmt.Sprintf("%d", statusCode),
|
||||
}).Debugf("FIXME: Got an status-code for which error does not match any expected type!!!: %d", statusCode)
|
||||
|
||||
switch {
|
||||
case statusCode >= 200 && statusCode < 400:
|
||||
// it's a client error
|
||||
case statusCode >= 400 && statusCode < 500:
|
||||
err = errdefs.InvalidParameter(err)
|
||||
case statusCode >= 500 && statusCode < 600:
|
||||
err = errdefs.System(err)
|
||||
default:
|
||||
err = errdefs.Unknown(err)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func apiVersionSupportsJSONErrors(version string) bool {
|
||||
const firstAPIVersionWithJSONErrors = "1.23"
|
||||
return version == "" || versions.GreaterThan(version, firstAPIVersionWithJSONErrors)
|
||||
|
|
93
api/server/httputils/errors_test.go
Normal file
93
api/server/httputils/errors_test.go
Normal file
|
@ -0,0 +1,93 @@
|
|||
package httputils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/errdefs"
|
||||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
func TestFromStatusCode(t *testing.T) {
|
||||
testErr := fmt.Errorf("some error occurred")
|
||||
|
||||
testCases := []struct {
|
||||
err error
|
||||
status int
|
||||
check func(error) bool
|
||||
}{
|
||||
{
|
||||
err: testErr,
|
||||
status: http.StatusNotFound,
|
||||
check: errdefs.IsNotFound,
|
||||
},
|
||||
{
|
||||
err: testErr,
|
||||
status: http.StatusBadRequest,
|
||||
check: errdefs.IsInvalidParameter,
|
||||
},
|
||||
{
|
||||
err: testErr,
|
||||
status: http.StatusConflict,
|
||||
check: errdefs.IsConflict,
|
||||
},
|
||||
{
|
||||
err: testErr,
|
||||
status: http.StatusUnauthorized,
|
||||
check: errdefs.IsUnauthorized,
|
||||
},
|
||||
{
|
||||
err: testErr,
|
||||
status: http.StatusServiceUnavailable,
|
||||
check: errdefs.IsUnavailable,
|
||||
},
|
||||
{
|
||||
err: testErr,
|
||||
status: http.StatusForbidden,
|
||||
check: errdefs.IsForbidden,
|
||||
},
|
||||
{
|
||||
err: testErr,
|
||||
status: http.StatusNotModified,
|
||||
check: errdefs.IsNotModified,
|
||||
},
|
||||
{
|
||||
err: testErr,
|
||||
status: http.StatusNotImplemented,
|
||||
check: errdefs.IsNotImplemented,
|
||||
},
|
||||
{
|
||||
err: testErr,
|
||||
status: http.StatusInternalServerError,
|
||||
check: errdefs.IsSystem,
|
||||
},
|
||||
{
|
||||
err: errdefs.Unknown(testErr),
|
||||
status: http.StatusInternalServerError,
|
||||
check: errdefs.IsUnknown,
|
||||
},
|
||||
{
|
||||
err: errdefs.DataLoss(testErr),
|
||||
status: http.StatusInternalServerError,
|
||||
check: errdefs.IsDataLoss,
|
||||
},
|
||||
{
|
||||
err: errdefs.Deadline(testErr),
|
||||
status: http.StatusInternalServerError,
|
||||
check: errdefs.IsDeadline,
|
||||
},
|
||||
{
|
||||
err: errdefs.Cancelled(testErr),
|
||||
status: http.StatusInternalServerError,
|
||||
check: errdefs.IsCancelled,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(http.StatusText(tc.status), func(t *testing.T) {
|
||||
err := FromStatusCode(tc.err, tc.status)
|
||||
assert.Check(t, tc.check(err), "unexpected error-type %T", err)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue