wormhole-explorer/api/response/error.go

166 lines
4.3 KiB
Go

// The response package defines the success and error response type.
// It define a type [AppError] that represent the api error response.
// Its define a custom error handling for the api.
package response
import (
"fmt"
"github.com/gofiber/fiber/v2"
"github.com/wormhole-foundation/wormhole-explorer/api/internal/config"
)
// API error codes. These error code are the same used in guardian API.
// https://github.com/grpc/grpc-go/blob/master/codes/codes.go
const (
OK = iota
Canceled
Unknown
InvalidParam
DeadlineExceeded
NotFound
AlreadyExists
PermissionDenied
ResourceExhausted
FailedPrecondition
Aborted
OutOfRange
Unimplemented
Internal
Unavailable
DataLoss
Unauthenticated
)
var enableStackTrace bool
// SetEnableStackTrace enable/disable send the stacktrace field in the response.
func SetEnableStackTrace(cfg config.AppConfig) {
if cfg.RunMode == config.RunModeDevelopmernt {
enableStackTrace = true
return
}
enableStackTrace = false
}
// APIError api error response.
// This structure is defined to be aligned with the way the guardian API handles the error response.
type APIError struct {
StatusCode int `json:"-"`
Code int `json:"code"` // support to guardian-api code.
Message string `json:"message"`
Details []ErrorDetail `json:"details"`
}
// ErrorDetail definition.
// This structure contains the requestID and the stacktrace of the error.
type ErrorDetail struct {
RequestID string `json:"request_id"`
StackTrace string `json:"stack_trace,omitempty"`
}
// Error interface implementation.
func (a APIError) Error() string {
return fmt.Sprintf("code: %d, message: %s, details: %v", a.Code, a.Message, a.Details)
}
// NewApiError create a new api response.
func NewApiError(ctx *fiber.Ctx, statusCode, code int, message string, err error) APIError {
detail := ErrorDetail{
RequestID: fmt.Sprintf("%v", ctx.Locals("requestid")),
}
if enableStackTrace && err != nil {
detail.StackTrace = fmt.Sprintf("%+v\n", err)
}
return APIError{
StatusCode: statusCode,
Code: code,
Message: message,
Details: []ErrorDetail{detail},
}
}
// NewInvalidParamError create a invalid param Error.
func NewInvalidParamError(ctx *fiber.Ctx, message string, err error) APIError {
if message == "" {
message = "INVALID PARAM"
}
detail := ErrorDetail{
RequestID: fmt.Sprintf("%v", ctx.Locals("requestid")),
}
if enableStackTrace && err != nil {
detail.StackTrace = fmt.Sprintf("%+v\n", err)
}
return APIError{
StatusCode: fiber.StatusBadRequest,
Code: InvalidParam,
Message: message,
Details: []ErrorDetail{detail},
}
}
// NewInternalError create a new APIError for Internal Errors.
func NewInternalError(ctx *fiber.Ctx, err error) APIError {
detail := ErrorDetail{
RequestID: fmt.Sprintf("%v", ctx.Locals("requestid")),
}
if enableStackTrace && err != nil {
detail.StackTrace = fmt.Sprintf("%+v\n", err)
}
return APIError{
StatusCode: fiber.StatusInternalServerError,
Code: Internal,
Message: "INTERNAL ERROR",
Details: []ErrorDetail{detail},
}
}
// NewNotFoundError create a new APIError for Not Found errors.
func NewNotFoundError(ctx *fiber.Ctx) APIError {
return APIError{
StatusCode: fiber.StatusNotFound,
Code: NotFound,
Message: "NOT FOUND",
Details: []ErrorDetail{{
RequestID: fmt.Sprintf("%v", ctx.Locals("requestid")),
}},
}
}
// NewInvalidQueryParamError create a query param error
func NewInvalidQueryParamError(ctx *fiber.Ctx, message string, err error) APIError {
if message == "" {
message = "INVALID QUERY PARAM"
}
detail := ErrorDetail{
RequestID: fmt.Sprintf("%v", ctx.Locals("requestid")),
}
if enableStackTrace && err != nil {
detail.StackTrace = fmt.Sprintf("%+v\n", err)
}
return APIError{
StatusCode: fiber.StatusBadRequest,
Code: InvalidParam,
Message: message,
Details: []ErrorDetail{detail},
}
}
func NewRequestBodyError(ctx *fiber.Ctx, message string, err error) APIError {
if message == "" {
message = "INVALID BODY"
}
detail := ErrorDetail{
RequestID: fmt.Sprintf("%v", ctx.Locals("requestid")),
}
if enableStackTrace && err != nil {
detail.StackTrace = fmt.Sprintf("%+v\n", err)
}
return APIError{
StatusCode: fiber.StatusBadRequest,
Code: InvalidParam,
Message: message,
Details: []ErrorDetail{detail},
}
}