소스 검색

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>
Sebastiaan van Stijn 6 년 전
부모
커밋
1af30c50ca
2개의 변경된 파일139개의 추가작업 그리고 0개의 파일을 삭제
  1. 46 0
      api/server/httputils/errors.go
  2. 93 0
      api/server/httputils/errors_test.go

+ 46 - 0
api/server/httputils/errors.go

@@ -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 - 0
api/server/httputils/errors_test.go

@@ -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)
+		})
+	}
+}