// 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}, } }