127 lines
2.7 KiB
Go
127 lines
2.7 KiB
Go
package usecase
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
type ErrorKind string
|
|
|
|
const (
|
|
ErrorKindInvalid ErrorKind = "INVALID"
|
|
ErrorKindNotFound ErrorKind = "NOT_FOUND"
|
|
ErrorKindConflict ErrorKind = "CONFLICT"
|
|
ErrorKindUnavailable ErrorKind = "UNAVAILABLE"
|
|
ErrorKindInternal ErrorKind = "INTERNAL"
|
|
)
|
|
|
|
type Error struct {
|
|
Kind ErrorKind
|
|
Code string
|
|
Message string
|
|
Retryable bool
|
|
Fields map[string]string
|
|
Cause error
|
|
}
|
|
|
|
func (e *Error) Error() string {
|
|
if e.Message != "" {
|
|
return e.Message
|
|
}
|
|
return "use case failed"
|
|
}
|
|
|
|
func (e *Error) Unwrap() error {
|
|
return e.Cause
|
|
}
|
|
|
|
func newError(
|
|
kind ErrorKind,
|
|
code string,
|
|
message string,
|
|
cause error,
|
|
) *Error {
|
|
return &Error{
|
|
Kind: kind,
|
|
Code: code,
|
|
Message: message,
|
|
Cause: cause,
|
|
Fields: map[string]string{},
|
|
}
|
|
}
|
|
|
|
func invalidError(code, message string, fields map[string]string) *Error {
|
|
return &Error{
|
|
Kind: ErrorKindInvalid,
|
|
Code: code,
|
|
Message: message,
|
|
Fields: fields,
|
|
}
|
|
}
|
|
|
|
var (
|
|
ErrRepositoryNotFound = errors.New("repository resource not found")
|
|
ErrIdempotencyConflict = errors.New("idempotency key payload conflict")
|
|
ErrSourceReferenceConflict = errors.New("source reference conflict")
|
|
ErrAssetUnavailable = errors.New("asset is unavailable")
|
|
ErrTaskStateConflict = errors.New("task state conflict")
|
|
ErrRepositoryUnavailable = errors.New("repository unavailable")
|
|
ErrRepositoryInvariant = errors.New("repository invariant failed")
|
|
)
|
|
|
|
func wrapRepositoryError(err error) error {
|
|
switch {
|
|
case errors.Is(err, ErrRepositoryNotFound):
|
|
return newError(
|
|
ErrorKindNotFound,
|
|
"RESOURCE_NOT_FOUND",
|
|
"resource not found",
|
|
err,
|
|
)
|
|
case errors.Is(err, ErrIdempotencyConflict):
|
|
return newError(
|
|
ErrorKindConflict,
|
|
"IDEMPOTENCY_KEY_CONFLICT",
|
|
"idempotency key was already used for a different request",
|
|
err,
|
|
)
|
|
case errors.Is(err, ErrSourceReferenceConflict):
|
|
return newError(
|
|
ErrorKindConflict,
|
|
"TASK_SOURCE_REF_CONFLICT",
|
|
"source reference already exists",
|
|
err,
|
|
)
|
|
case errors.Is(err, ErrAssetUnavailable):
|
|
return newError(
|
|
ErrorKindConflict,
|
|
"TASK_ASSET_UNAVAILABLE",
|
|
"reference image is not available for this task",
|
|
err,
|
|
)
|
|
case errors.Is(err, ErrTaskStateConflict):
|
|
return newError(
|
|
ErrorKindConflict,
|
|
"TASK_STATE_CONFLICT",
|
|
"task state does not allow this operation",
|
|
err,
|
|
)
|
|
case errors.Is(err, ErrRepositoryUnavailable):
|
|
result := newError(
|
|
ErrorKindUnavailable,
|
|
"STORAGE_UNAVAILABLE",
|
|
"storage is temporarily unavailable",
|
|
err,
|
|
)
|
|
result.Retryable = true
|
|
return result
|
|
default:
|
|
return newError(
|
|
ErrorKindInternal,
|
|
"INTERNAL_ERROR",
|
|
"internal server error",
|
|
fmt.Errorf("%w: %v", ErrRepositoryInvariant, err),
|
|
)
|
|
}
|
|
}
|