Files
hanko/backend/flowpilot/errors.go
bjoern-m 601ffaae92 Introduce Flowpilot - integration (#1532)
This pull request introduces the new Flowpilot system along with several new features and various improvements. The key enhancements include configurable authorization, registration, and profile flows, as well as the ability to enable and disable user identifiers (e.g., email addresses and usernames) and login methods.

---------

Co-authored-by: Frederic Jahn <frederic.jahn@hanko.io>
Co-authored-by: Lennart Fleischmann <lennart.fleischmann@hanko.io>
Co-authored-by: lfleischmann <67686424+lfleischmann@users.noreply.github.com>
Co-authored-by: merlindru <hello@merlindru.com>
2024-08-06 16:07:29 +02:00

177 lines
4.8 KiB
Go

package flowpilot
import (
"fmt"
"net/http"
"strings"
)
// flowpilotError defines the interface for custom error types in the Flowpilot package.
type flowpilotError interface {
error
Unwrap() error
Code() string
Message() string
toResponseError(debug bool) *ResponseError
}
// FlowError is an interface representing flow-related errors.
type FlowError interface {
flowpilotError
Wrap(error) FlowError
Status() int
}
// InputError is an interface representing input-related errors.
type InputError interface {
flowpilotError
Wrap(error) InputError
}
// defaultError is a base struct for custom error types.
type defaultError struct {
cause error // The error cause.
code string // Unique error code.
message string // Contains a description of the error.
errorText string // The string representation of the error.
}
// Code returns the error code.
func (e *defaultError) Code() string {
return e.code
}
// Message returns the error message.
func (e *defaultError) Message() string {
return e.message
}
// Unwrap returns the wrapped error.
func (e *defaultError) Unwrap() error {
return e.cause
}
// Error returns the formatted error message.
func (e *defaultError) Error() string {
return e.errorText
}
// toResponseError converts the error to a ResponseError for public exposure.
func (e *defaultError) toResponseError(debug bool) *ResponseError {
publicError := &ResponseError{
Code: e.Code(),
Message: e.Message(),
}
if e.cause != nil {
cause := e.cause.Error()
publicError.Internal = &cause
if debug {
publicError.Cause = &cause
}
}
return publicError
}
// defaultFlowError is a struct for flow-related errors.
type defaultFlowError struct {
defaultError
status int // The suggested HTTP status code.
}
// createErrorText creates the text used as the string representation of the error.
func createErrorText(code, message string, cause error) string {
text := fmt.Sprintf("%s - %s", code, message)
if cause != nil {
text = fmt.Sprintf("%s: %s", text, cause.Error())
}
return text
}
// NewFlowError creates a new FlowError instance.
func NewFlowError(code, message string, status int) FlowError {
return newFlowErrorWithCause(code, message, status, nil)
}
// newFlowErrorWithCause creates a new FlowError instance with an error cause.
func newFlowErrorWithCause(code, message string, status int, cause error) FlowError {
errorText := createErrorText(code, message, cause)
e := defaultError{
cause: cause,
code: code,
message: message,
errorText: errorText,
}
return &defaultFlowError{defaultError: e, status: status}
}
// Status returns the suggested HTTP status code.
func (e *defaultFlowError) Status() int {
return e.status
}
// Wrap wraps the error with another error.
func (e *defaultFlowError) Wrap(err error) FlowError {
return newFlowErrorWithCause(e.code, e.message, e.status, err)
}
// defaultInputError is a struct for input-related errors.
type defaultInputError struct {
defaultError
}
// NewInputError creates a new InputError instance.
func NewInputError(code, message string) InputError {
return newInputErrorWithCause(code, message, nil)
}
// newInputErrorWithCause creates a new InputError instance with an error cause.
func newInputErrorWithCause(code, message string, cause error) InputError {
errorText := createErrorText(code, message, cause)
e := defaultError{
cause: cause,
code: code,
message: message,
errorText: errorText,
}
return &defaultInputError{defaultError: e}
}
// Wrap wraps the error with another error.
func (e *defaultInputError) Wrap(err error) InputError {
return newInputErrorWithCause(e.code, e.message, err)
}
// Predefined flow error types
var (
ErrorTechnical = NewFlowError("technical_error", "Something went wrong.", http.StatusInternalServerError)
ErrorFlowExpired = NewFlowError("flow_expired_error", "The flow has expired.", http.StatusGone)
ErrorFlowDiscontinuity = NewFlowError("flow_discontinuity_error", "The flow can't be continued.", http.StatusInternalServerError)
ErrorOperationNotPermitted = NewFlowError("operation_not_permitted_error", "The operation is not permitted.", http.StatusForbidden)
ErrorFormDataInvalid = NewFlowError("form_data_invalid_error", "Form data invalid.", http.StatusBadRequest)
)
// Predefined input error types
var (
ErrorValueMissing = NewInputError("value_missing_error", "The value is missing.")
ErrorValueInvalid = NewInputError("value_invalid_error", "The value is invalid.")
ErrorValueTooLong = NewInputError("value_too_long_error", "The value is too long.")
ErrorValueTooShort = NewInputError("value_too_short_error", "The value is too short.")
)
func createMustBeOneOfError(values []string) InputError {
return NewInputError("value_invalid_error", fmt.Sprintf("The value is invalid. Must be one of: %s", strings.Join(values, ",")))
}