api/pkg/handler/errors.go (86 lines of code) (raw):
// Copyright 2021 Google LLC
//
// 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 handler
import (
"context"
"fmt"
"net/http"
"github.com/rs/zerolog/log"
)
type httpError struct {
err error
status int // http status code
msg string // http response message
}
// Error returns internal message of e.
func (e *httpError) Error() string {
return fmt.Sprintf("%s (%d): %s", e.msg, e.status, e.err)
}
// Errorf constructs a new error for handler.
func Errorf(ctx context.Context, status int, msg string, format string, a ...interface{}) error {
he := &httpError{
err: fmt.Errorf(format, a...),
status: status,
msg: msg,
}
first := false
if len(a) > 0 {
pe, ok := a[len(a)-1].(*httpError)
if ok {
he.status = pe.status
he.msg = pe.msg
} else {
first = true
}
} else {
first = true
}
if first {
logger := log.Ctx(ctx).With().CallerWithSkipFrameCount(3).Logger()
logger.Error().Err(he).Msg(he.Error())
}
return he
}
// Wrapf wraps an error.
func Wrapf(format string, a ...interface{}) error {
he := &httpError{
err: fmt.Errorf(format, a...),
status: http.StatusInternalServerError,
msg: http.StatusText(http.StatusInternalServerError),
}
pe, ok := a[len(a)-1].(*httpError)
if ok {
he.status = pe.status
he.msg = pe.msg
}
return he
}
// RespondError responds an error.
func RespondError(w http.ResponseWriter, r *http.Request, err error) {
e, ok := err.(*httpError)
if ok {
http.Error(w, e.msg, e.status)
} else {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
}
type errjson struct {
Status int `json:"status"`
Kind string `json:"kind"`
Message string `json:"message"`
}
// RespondErrorJSON responds error as JSON.
func RespondErrorJSON(w http.ResponseWriter, r *http.Request, err error) {
ej := &errjson{}
e, ok := err.(*httpError)
if ok {
ej.Status = e.status
ej.Message = e.msg
} else {
ej.Status = http.StatusInternalServerError
ej.Message = http.StatusText(ej.Status)
}
ej.Kind = http.StatusText(ej.Status)
RespondJSON(w, r, ej.Status, ej)
}
// RespondErrorMessage responds error messages as JSON.
func RespondErrorMessage(w http.ResponseWriter, r *http.Request, status int, msg string) {
ej := &errjson{
Status: status,
Kind: http.StatusText(status),
Message: msg,
}
RespondJSON(w, r, status, ej)
}