Refactor host_repo -> domain errors (#786)
* Refactor domain errors: New -> E, create helper New and Wrap * Update host repo to use domain errors
This commit is contained in:
1 parent
aa4157639c
commit
ec0020de90
33 files changed
+996
-374
No files matched your search
+89
-19
@@ -13,9 +13,8 @@ import (
|
||||
type Op string
|
||||
|
||||
// Err provides the ability to specify a Msg, Op, Code and Wrapped error.
|
||||
// Errs must have a Code and all other fields are optional. We've chosen Err
|
||||
// over Error for the identifier to support the easy embedding of Errs. Errs
|
||||
// can be embedded without a conflict between the embedded Err and Err.Error().
|
||||
// We've chosen Err over Error for the identifier to support the easy embedding of Errs.
|
||||
// Errs can be embedded without a conflict between the embedded Err and Err.Error().
|
||||
type Err struct {
|
||||
// Code is the error's code, which can be used to get the error's
|
||||
// errorCodeInfo, which contains the error's Kind and Message
|
||||
@@ -32,22 +31,85 @@ type Err struct {
|
||||
Wrapped error
|
||||
}
|
||||
|
||||
// New creates a new Err and supports the options of:
|
||||
// WithOp - allows you to specify an optional Op (operation)
|
||||
// WithMsg() - allows you to specify an optional error msg, if the default
|
||||
// E creates a new Err with provided code and supports the options of:
|
||||
//
|
||||
// * WithOp() - allows you to specify an optional Op (operation).
|
||||
//
|
||||
// * WithMsg() - allows you to specify an optional error msg, if the default
|
||||
// msg for the error Code is not sufficient.
|
||||
// WithWrap() - allows you to specify
|
||||
// an error to wrap
|
||||
func New(c Code, opt ...Option) error {
|
||||
//
|
||||
// * WithWrap() - allows you to specify an error to wrap.
|
||||
// If the wrapped error is a boundary domain error, the wrapped error code
|
||||
// will be used as the returned error's code.
|
||||
//
|
||||
// * WithCode() - allows you to specify an optional Code, this code will be prioritized
|
||||
// over a code used from WithWrap().
|
||||
func E(opt ...Option) error {
|
||||
opts := GetOpts(opt...)
|
||||
var code Code
|
||||
|
||||
// check if options includes a wrapped error to take code from
|
||||
var err *Err
|
||||
if As(opts.withErrWrapped, &err) {
|
||||
code = err.Code
|
||||
}
|
||||
|
||||
// if options include withCode prioritize using that code
|
||||
// even if one was set via wrapped error above
|
||||
if opts.withCode != Unknown {
|
||||
code = opts.withCode
|
||||
}
|
||||
|
||||
return &Err{
|
||||
Code: c,
|
||||
Code: code,
|
||||
Op: opts.withOp,
|
||||
Wrapped: opts.withErrWrapped,
|
||||
Msg: opts.withErrMsg,
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a new Err with provided code, op and msg
|
||||
// It supports the options of:
|
||||
//
|
||||
// * WithWrap() - allows you to specify an error to wrap
|
||||
func New(c Code, op Op, msg string, opt ...Option) error {
|
||||
if c != Unknown {
|
||||
opt = append(opt, WithCode(c))
|
||||
}
|
||||
if op != "" {
|
||||
opt = append(opt, WithOp(op))
|
||||
}
|
||||
if msg != "" {
|
||||
opt = append(opt, WithMsg(msg))
|
||||
}
|
||||
|
||||
return E(opt...)
|
||||
}
|
||||
|
||||
// Wrap creates a new Err from the provided err and op,
|
||||
// preserving the code from the originating error.
|
||||
// It supports the options of:
|
||||
//
|
||||
// * WithMsg() - allows you to specify an optional error msg, if the default
|
||||
// msg for the error Code is not sufficient.
|
||||
func Wrap(e error, op Op, opt ...Option) error {
|
||||
if op != "" {
|
||||
opt = append(opt, WithOp(op))
|
||||
}
|
||||
if e != nil {
|
||||
// TODO: once db package has been refactored to only return domain errors,
|
||||
// this convert can be removed
|
||||
err := Convert(e)
|
||||
if err != nil {
|
||||
// wrapped converted error
|
||||
e = err
|
||||
}
|
||||
opt = append(opt, WithWrap(e))
|
||||
}
|
||||
|
||||
return E(opt...)
|
||||
}
|
||||
|
||||
// Convert will convert the error to a Boundary *Err (returning it as an error)
|
||||
// and attempt to add a helpful error msg as well. If that's not possible, it
|
||||
// will return nil
|
||||
@@ -55,7 +117,8 @@ func Convert(e error) *Err {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
if err, ok := e.(*Err); ok {
|
||||
var err *Err
|
||||
if As(e, &err) {
|
||||
return err
|
||||
}
|
||||
var pqError *pq.Error
|
||||
@@ -63,19 +126,19 @@ func Convert(e error) *Err {
|
||||
if pqError.Code.Class() == "23" { // class of integrity constraint violations
|
||||
switch pqError.Code {
|
||||
case "23505": // unique_violation
|
||||
return New(NotUnique, WithMsg(pqError.Detail), WithWrap(ErrNotUnique)).(*Err)
|
||||
return E(WithMsg(pqError.Detail), WithWrap(ErrNotUnique)).(*Err)
|
||||
case "23502": // not_null_violation
|
||||
msg := fmt.Sprintf("%s must not be empty", pqError.Column)
|
||||
return New(NotNull, WithMsg(msg), WithWrap(ErrNotNull)).(*Err)
|
||||
return E(WithMsg(msg), WithWrap(ErrNotNull)).(*Err)
|
||||
case "23514": // check_violation
|
||||
msg := fmt.Sprintf("%s constraint failed", pqError.Constraint)
|
||||
return New(CheckConstraint, WithMsg(msg), WithWrap(ErrCheckConstraint)).(*Err)
|
||||
return E(WithMsg(msg), WithWrap(ErrCheckConstraint)).(*Err)
|
||||
default:
|
||||
return New(NotSpecificIntegrity, WithMsg(pqError.Message)).(*Err)
|
||||
return E(WithCode(NotSpecificIntegrity), WithMsg(pqError.Message)).(*Err)
|
||||
}
|
||||
}
|
||||
if pqError.Code == "42P01" {
|
||||
return New(MissingTable, WithMsg(pqError.Message)).(*Err)
|
||||
return E(WithCode(MissingTable), WithMsg(pqError.Message)).(*Err)
|
||||
}
|
||||
}
|
||||
// unfortunately, we can't help.
|
||||
@@ -104,18 +167,25 @@ func (e *Err) Error() string {
|
||||
join(&s, ": ", e.Msg)
|
||||
}
|
||||
|
||||
if info, ok := errorCodeInfo[e.Code]; ok {
|
||||
var skipInfo bool
|
||||
var wrapped *Err
|
||||
if As(e.Wrapped, &wrapped) {
|
||||
// if wrapped error code is the same as this error, don't print redundant info
|
||||
skipInfo = wrapped.Code == e.Code
|
||||
}
|
||||
|
||||
if info, ok := errorCodeInfo[e.Code]; ok && !skipInfo {
|
||||
if e.Msg == "" {
|
||||
join(&s, ": ", info.Message) // provide a default.
|
||||
join(&s, ", ", info.Kind.String())
|
||||
} else {
|
||||
join(&s, ": ", info.Kind.String())
|
||||
}
|
||||
join(&s, ": ", fmt.Sprintf("error #%d", e.Code))
|
||||
}
|
||||
join(&s, ": ", fmt.Sprintf("error #%d", e.Code))
|
||||
|
||||
if e.Wrapped != nil {
|
||||
join(&s, ": \n", e.Wrapped.Error())
|
||||
join(&s, ": ", e.Wrapped.Error())
|
||||
}
|
||||
return s.String()
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user