blob: 0061d418f18f894be674e039ce589c0547e56e4a [file] [log] [blame]
package endpoints
import (
"encoding/json"
"fmt"
"net/http"
"strings"
)
var (
// Pre-defined API errors.
// Use NewAPIError() method to create your own.
// InternalServerError is default error with http.StatusInternalServerError (500)
InternalServerError = NewInternalServerError("")
// BadRequestError is default error with http.StatusBadRequest (400)
BadRequestError = NewBadRequestError("")
// UnauthorizedError is default error with http.StatusUnauthorized (401)
UnauthorizedError = NewUnauthorizedError("")
// ForbiddenError is default error with http.StatusForbidden (403)
ForbiddenError = NewForbiddenError("")
// NotFoundError is default error with http.StatusNotFound (404)
NotFoundError = NewNotFoundError("")
// ConflictError is default error with http.StatusConflict (409)
ConflictError = NewConflictError("")
// knownErrors is a list of all known errors.
knownErrors = [...]int{
http.StatusInternalServerError,
http.StatusBadRequest,
http.StatusUnauthorized,
http.StatusForbidden,
http.StatusNotFound,
http.StatusConflict,
}
)
// APIError is a user custom API's error
type APIError struct {
Name string
Msg string
Code int
}
// APIError is an error
func (a *APIError) Error() string {
return a.Msg
}
// NewAPIError Create a new APIError for custom error
func NewAPIError(name string, msg string, code int) error {
return &APIError{name, msg, code}
}
// errorf creates a new APIError given its status code, a format string and its arguments.
func errorf(code int, format string, args ...interface{}) error {
return &APIError{http.StatusText(code), fmt.Sprintf(format, args...), code}
}
// NewInternalServerError creates a new APIError with Internal Server Error status (500)
func NewInternalServerError(format string, args ...interface{}) error {
return errorf(http.StatusInternalServerError, format, args...)
}
// NewBadRequestError creates a new APIError with Bad Request status (400)
func NewBadRequestError(format string, args ...interface{}) error {
return errorf(http.StatusBadRequest, format, args...)
}
// NewUnauthorizedError creates a new APIError with Unauthorized status (401)
func NewUnauthorizedError(format string, args ...interface{}) error {
return errorf(http.StatusUnauthorized, format, args...)
}
// NewNotFoundError creates a new APIError with Not Found status (404)
func NewNotFoundError(format string, args ...interface{}) error {
return errorf(http.StatusNotFound, format, args...)
}
// NewForbiddenError creates a new APIError with Forbidden status (403)
func NewForbiddenError(format string, args ...interface{}) error {
return errorf(http.StatusForbidden, format, args...)
}
// NewConflictError creates a new APIError with Conflict status (409)
func NewConflictError(format string, args ...interface{}) error {
return errorf(http.StatusConflict, format, args...)
}
// errorResponse is SPI-compatible error response
type errorResponse struct {
// Currently always "APPLICATION_ERROR"
State string `json:"state"`
Name string `json:"error_name"`
Msg string `json:"error_message,omitempty"`
Code int `json:"-"`
}
// Creates and initializes a new errorResponse.
// If msg contains any of knownErrors then errorResponse.Name will be set
// to that name and the rest of the msg becomes errorResponse.Msg.
// Otherwise, a default error name is used and msg argument
// is errorResponse.Msg.
func newErrorResponse(err error) *errorResponse {
if e, ok := err.(*APIError); ok {
return &errorResponse{"APPLICATION_ERROR", e.Name, e.Msg, e.Code}
}
msg := err.Error()
for _, code := range knownErrors {
if name := http.StatusText(code); strings.HasPrefix(msg, name) {
return &errorResponse{"APPLICATION_ERROR", name, strings.Trim(msg[len(name):], " :"), code}
}
}
//for compatibility, Before behavior, always return 400 HTTP Status Code.
// TODO(alex): where is 400 coming from?
return &errorResponse{"APPLICATION_ERROR", http.StatusText(http.StatusInternalServerError), msg, http.StatusBadRequest}
}
// writeError writes SPI-compatible error response.
func writeError(w http.ResponseWriter, err error) {
errResp := newErrorResponse(err)
w.WriteHeader(errResp.Code)
json.NewEncoder(w).Encode(errResp)
}