diff --git a/src/internal/error/errors.go b/src/internal/error/errors.go index 4b4d36131..4a7c8ac97 100644 --- a/src/internal/error/errors.go +++ b/src/internal/error/errors.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/goharbor/harbor/src/common/utils/log" ) @@ -155,3 +156,19 @@ func IsErr(err error, code string) bool { func IsConflictErr(err error) bool { return IsErr(err, ConflictCode) } + +// ErrCode returns code of err +func ErrCode(err error) string { + if err == nil { + return "" + } + + var e *Error + if ok := errors.As(err, &e); ok && e.Code != "" { + return e.Code + } else if ok && e.Cause != nil { + return ErrCode(e.Cause) + } + + return GeneralCode +} diff --git a/src/internal/error/errors_test.go b/src/internal/error/errors_test.go new file mode 100644 index 000000000..89db07087 --- /dev/null +++ b/src/internal/error/errors_test.go @@ -0,0 +1,43 @@ +// Copyright Project Harbor Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package error + +import ( + "errors" + "testing" +) + +func TestErrCode(t *testing.T) { + type args struct { + err error + } + tests := []struct { + name string + args args + want string + }{ + {"nil", args{nil}, ""}, + {"general err", args{errors.New("general err")}, GeneralCode}, + {"code in err", args{&Error{Code: "code in err"}}, "code in err"}, + {"code in cause", args{&Error{Cause: &Error{Code: "code in cause"}}}, "code in cause"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := ErrCode(tt.args.err); got != tt.want { + t.Errorf("ErrCode() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/src/server/error/error.go b/src/server/error/error.go index 6a94b7e47..8343a3a71 100644 --- a/src/server/error/error.go +++ b/src/server/error/error.go @@ -16,8 +16,11 @@ package error import ( "errors" - ierror "github.com/goharbor/harbor/src/internal/error" "net/http" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + ierror "github.com/goharbor/harbor/src/internal/error" ) var ( @@ -34,14 +37,7 @@ var ( // APIError generates the HTTP status code and error payload based on the input err func APIError(err error) (int, string) { - var e *ierror.Error - statusCode := 0 - if errors.As(err, &e) { - statusCode = getHTTPStatusCode(e.Code) - } else { - statusCode = http.StatusInternalServerError - } - return statusCode, ierror.NewErrs(err).Error() + return getHTTPStatusCode(ierror.ErrCode(err)), ierror.NewErrs(err).Error() } func getHTTPStatusCode(errCode string) int { @@ -51,3 +47,33 @@ func getHTTPStatusCode(errCode string) int { } return statusCode } + +var _ middleware.Responder = &ErrResponder{} + +// ErrResponder error responder +type ErrResponder struct { + err error +} + +// WriteResponse ... +func (r *ErrResponder) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + code := ierror.ErrCode(r.err) + rw.WriteHeader(getHTTPStatusCode(code)) + + var e *ierror.Error + if !errors.As(r.err, &e) { + e = &ierror.Error{ + Code: code, + Message: r.err.Error(), + } + } + + if err := producer.Produce(rw, e); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// NewErrResponder returns responder for err +func NewErrResponder(err error) *ErrResponder { + return &ErrResponder{err: err} +} diff --git a/src/server/v2.0/handler/base.go b/src/server/v2.0/handler/base.go index 0f1495c88..c8f3683ec 100644 --- a/src/server/v2.0/handler/base.go +++ b/src/server/v2.0/handler/base.go @@ -18,6 +18,7 @@ import ( "context" "github.com/go-openapi/runtime/middleware" + errs "github.com/goharbor/harbor/src/server/error" ) // BaseAPI base API handler @@ -30,6 +31,5 @@ func (*BaseAPI) Prepare(ctx context.Context, operation string, params interface{ // SendError returns response for the err func (*BaseAPI) SendError(ctx context.Context, err error) middleware.Responder { - // TODO: implement send error - return nil + return errs.NewErrResponder(err) }