refactor: introduce domain.ErrorWithPath interface
This commit is contained in:
parent
c173bcdba6
commit
0f4611962f
|
@ -1,5 +1,7 @@
|
|||
package domain
|
||||
|
||||
import "strconv"
|
||||
|
||||
type ErrorType uint8
|
||||
|
||||
const (
|
||||
|
@ -32,6 +34,33 @@ type ErrorWithParams interface {
|
|||
Params() map[string]any
|
||||
}
|
||||
|
||||
type ErrorPathSegment struct {
|
||||
Model string
|
||||
Field string
|
||||
Index int // Index may be <0 and this means that it is unset
|
||||
}
|
||||
|
||||
func (s ErrorPathSegment) String() string {
|
||||
path := s.Model
|
||||
|
||||
if s.Field != "" {
|
||||
if len(path) > 0 {
|
||||
path += "."
|
||||
}
|
||||
path += s.Field
|
||||
if s.Index > 0 {
|
||||
path += "[" + strconv.Itoa(s.Index) + "]"
|
||||
}
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
type ErrorWithPath interface {
|
||||
Error
|
||||
Path() ErrorPathSegment
|
||||
}
|
||||
|
||||
type simpleError struct {
|
||||
msg string
|
||||
typ ErrorType
|
||||
|
|
|
@ -3,7 +3,6 @@ package domain
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type ValidationError struct {
|
||||
|
@ -13,9 +12,10 @@ type ValidationError struct {
|
|||
}
|
||||
|
||||
var _ ErrorWithParams = ValidationError{}
|
||||
var _ ErrorWithPath = ValidationError{}
|
||||
|
||||
func (e ValidationError) Error() string {
|
||||
prefix := e.Path()
|
||||
prefix := e.Path().String()
|
||||
|
||||
if prefix != "" {
|
||||
prefix += ": "
|
||||
|
@ -24,17 +24,12 @@ func (e ValidationError) Error() string {
|
|||
return prefix + e.Err.Error()
|
||||
}
|
||||
|
||||
func (e ValidationError) Path() string {
|
||||
path := e.Model
|
||||
|
||||
if e.Field != "" {
|
||||
if len(path) > 0 {
|
||||
path += "."
|
||||
}
|
||||
path += e.Field
|
||||
func (e ValidationError) Path() ErrorPathSegment {
|
||||
return ErrorPathSegment{
|
||||
Model: e.Model,
|
||||
Field: e.Field,
|
||||
Index: -1,
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
func (e ValidationError) Type() ErrorType {
|
||||
|
@ -73,9 +68,10 @@ type SliceElementValidationError struct {
|
|||
}
|
||||
|
||||
var _ ErrorWithParams = SliceElementValidationError{}
|
||||
var _ ErrorWithPath = SliceElementValidationError{}
|
||||
|
||||
func (e SliceElementValidationError) Error() string {
|
||||
prefix := e.Path()
|
||||
prefix := e.Path().String()
|
||||
|
||||
if prefix != "" {
|
||||
prefix += ": "
|
||||
|
@ -84,17 +80,12 @@ func (e SliceElementValidationError) Error() string {
|
|||
return prefix + e.Err.Error()
|
||||
}
|
||||
|
||||
func (e SliceElementValidationError) Path() string {
|
||||
path := e.Model
|
||||
|
||||
if e.Field != "" {
|
||||
if len(path) > 0 {
|
||||
path += "."
|
||||
}
|
||||
path += e.Field + "[" + strconv.Itoa(e.Index) + "]"
|
||||
func (e SliceElementValidationError) Path() ErrorPathSegment {
|
||||
return ErrorPathSegment{
|
||||
Model: e.Model,
|
||||
Field: e.Field,
|
||||
Index: e.Index,
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
func (e SliceElementValidationError) Type() ErrorType {
|
||||
|
|
|
@ -65,13 +65,7 @@ func (es apiErrors) toResponse() apimodel.ErrorResponse {
|
|||
return resp
|
||||
}
|
||||
|
||||
type errorPathSegment struct {
|
||||
model string
|
||||
field string
|
||||
index int // index may be <0 and this means that it is unset
|
||||
}
|
||||
|
||||
type errorPathFormatter func(segments []errorPathSegment) []string
|
||||
type errorPathFormatter func(segments []domain.ErrorPathSegment) []string
|
||||
|
||||
type apiErrorRenderer struct {
|
||||
// errorPathFormatter allows to override the default path formatter
|
||||
|
@ -149,31 +143,25 @@ func (re apiErrorRenderer) invalidParamFormatErrorToAPIError(
|
|||
|
||||
func (re apiErrorRenderer) domainErrorToAPIError(domainErr domain.Error) apiError {
|
||||
message := domainErr.Error()
|
||||
var pathSegments []errorPathSegment
|
||||
var pathSegments []domain.ErrorPathSegment
|
||||
|
||||
var err error = domainErr
|
||||
for err != nil {
|
||||
var validationErr domain.ValidationError
|
||||
var sliceElementValidationErr domain.SliceElementValidationError
|
||||
for {
|
||||
var withPath domain.ErrorWithPath
|
||||
|
||||
switch {
|
||||
case errors.As(err, &validationErr):
|
||||
pathSegments = append(pathSegments, errorPathSegment{
|
||||
model: validationErr.Model,
|
||||
field: validationErr.Field,
|
||||
index: -1,
|
||||
})
|
||||
err = validationErr.Unwrap()
|
||||
if !errors.As(err, &withPath) {
|
||||
break
|
||||
}
|
||||
|
||||
pathSegments = append(pathSegments, withPath.Path())
|
||||
|
||||
u, ok := err.(interface {
|
||||
Unwrap() error
|
||||
})
|
||||
if ok {
|
||||
err = u.Unwrap()
|
||||
message = err.Error()
|
||||
case errors.As(err, &sliceElementValidationErr):
|
||||
pathSegments = append(pathSegments, errorPathSegment{
|
||||
model: sliceElementValidationErr.Model,
|
||||
field: sliceElementValidationErr.Field,
|
||||
index: sliceElementValidationErr.Index,
|
||||
})
|
||||
err = sliceElementValidationErr.Unwrap()
|
||||
message = err.Error()
|
||||
default:
|
||||
} else {
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -131,12 +131,12 @@ func playerFromContext(ctx context.Context) (domain.PlayerWithRelations, bool) {
|
|||
return p, ok
|
||||
}
|
||||
|
||||
func formatListPlayersErrorPath(segments []errorPathSegment) []string {
|
||||
if segments[0].model != "ListPlayersParams" {
|
||||
func formatListPlayersErrorPath(segments []domain.ErrorPathSegment) []string {
|
||||
if segments[0].Model != "ListPlayersParams" {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch segments[0].field {
|
||||
switch segments[0].Field {
|
||||
case "cursor":
|
||||
return []string{"$query", "cursor"}
|
||||
case "limit":
|
||||
|
@ -145,14 +145,14 @@ func formatListPlayersErrorPath(segments []errorPathSegment) []string {
|
|||
return []string{"$query", "deleted"}
|
||||
case "names":
|
||||
path := []string{"$query", "name"}
|
||||
if segments[0].index >= 0 {
|
||||
path = append(path, strconv.Itoa(segments[0].index))
|
||||
if segments[0].Index >= 0 {
|
||||
path = append(path, strconv.Itoa(segments[0].Index))
|
||||
}
|
||||
return path
|
||||
case "sort":
|
||||
path := []string{"$query", "sort"}
|
||||
if segments[0].index >= 0 {
|
||||
path = append(path, strconv.Itoa(segments[0].index))
|
||||
if segments[0].Index >= 0 {
|
||||
path = append(path, strconv.Itoa(segments[0].Index))
|
||||
}
|
||||
return path
|
||||
default:
|
||||
|
@ -160,12 +160,12 @@ func formatListPlayersErrorPath(segments []errorPathSegment) []string {
|
|||
}
|
||||
}
|
||||
|
||||
func formatGetPlayerErrorPath(segments []errorPathSegment) []string {
|
||||
if segments[0].model != "ListPlayersParams" {
|
||||
func formatGetPlayerErrorPath(segments []domain.ErrorPathSegment) []string {
|
||||
if segments[0].Model != "ListPlayersParams" {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch segments[0].field {
|
||||
switch segments[0].Field {
|
||||
case "ids":
|
||||
return []string{"$path", "playerId"}
|
||||
default:
|
||||
|
|
|
@ -138,12 +138,12 @@ func serverFromContext(ctx context.Context) (domain.Server, bool) {
|
|||
return s, ok
|
||||
}
|
||||
|
||||
func formatListServersErrorPath(segments []errorPathSegment) []string {
|
||||
if segments[0].model != "ListServersParams" {
|
||||
func formatListServersErrorPath(segments []domain.ErrorPathSegment) []string {
|
||||
if segments[0].Model != "ListServersParams" {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch segments[0].field {
|
||||
switch segments[0].Field {
|
||||
case "cursor":
|
||||
return []string{"$query", "cursor"}
|
||||
case "limit":
|
||||
|
@ -155,12 +155,12 @@ func formatListServersErrorPath(segments []errorPathSegment) []string {
|
|||
}
|
||||
}
|
||||
|
||||
func formatGetServerErrorPath(segments []errorPathSegment) []string {
|
||||
if segments[0].model != "ListServersParams" {
|
||||
func formatGetServerErrorPath(segments []domain.ErrorPathSegment) []string {
|
||||
if segments[0].Model != "ListServersParams" {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch segments[0].field {
|
||||
switch segments[0].Field {
|
||||
case "keys":
|
||||
return []string{"$path", "serverKey"}
|
||||
default:
|
||||
|
|
|
@ -131,12 +131,12 @@ func tribeFromContext(ctx context.Context) (domain.Tribe, bool) {
|
|||
return t, ok
|
||||
}
|
||||
|
||||
func formatListTribesErrorPath(segments []errorPathSegment) []string {
|
||||
if segments[0].model != "ListTribesParams" {
|
||||
func formatListTribesErrorPath(segments []domain.ErrorPathSegment) []string {
|
||||
if segments[0].Model != "ListTribesParams" {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch segments[0].field {
|
||||
switch segments[0].Field {
|
||||
case "cursor":
|
||||
return []string{"$query", "cursor"}
|
||||
case "limit":
|
||||
|
@ -147,8 +147,8 @@ func formatListTribesErrorPath(segments []errorPathSegment) []string {
|
|||
return []string{"$query", "tag"}
|
||||
case "sort":
|
||||
path := []string{"$query", "sort"}
|
||||
if segments[0].index >= 0 {
|
||||
path = append(path, strconv.Itoa(segments[0].index))
|
||||
if segments[0].Index >= 0 {
|
||||
path = append(path, strconv.Itoa(segments[0].Index))
|
||||
}
|
||||
return path
|
||||
default:
|
||||
|
@ -156,12 +156,12 @@ func formatListTribesErrorPath(segments []errorPathSegment) []string {
|
|||
}
|
||||
}
|
||||
|
||||
func formatGetTribeErrorPath(segments []errorPathSegment) []string {
|
||||
if segments[0].model != "ListTribesParams" {
|
||||
func formatGetTribeErrorPath(segments []domain.ErrorPathSegment) []string {
|
||||
if segments[0].Model != "ListTribesParams" {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch segments[0].field {
|
||||
switch segments[0].Field {
|
||||
case "ids":
|
||||
return []string{"$path", "tribeId"}
|
||||
default:
|
||||
|
|
|
@ -73,12 +73,12 @@ func versionFromContext(ctx context.Context) (domain.Version, bool) {
|
|||
return v, ok
|
||||
}
|
||||
|
||||
func formatListVersionsErrorPath(segments []errorPathSegment) []string {
|
||||
if segments[0].model != "ListVersionsParams" {
|
||||
func formatListVersionsErrorPath(segments []domain.ErrorPathSegment) []string {
|
||||
if segments[0].Model != "ListVersionsParams" {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch segments[0].field {
|
||||
switch segments[0].Field {
|
||||
case "cursor":
|
||||
return []string{"$query", "cursor"}
|
||||
case "limit":
|
||||
|
@ -88,12 +88,12 @@ func formatListVersionsErrorPath(segments []errorPathSegment) []string {
|
|||
}
|
||||
}
|
||||
|
||||
func formatGetVersionErrorPath(segments []errorPathSegment) []string {
|
||||
if segments[0].model != "ListVersionsParams" {
|
||||
func formatGetVersionErrorPath(segments []domain.ErrorPathSegment) []string {
|
||||
if segments[0].Model != "ListVersionsParams" {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch segments[0].field {
|
||||
switch segments[0].Field {
|
||||
case "codes":
|
||||
return []string{"$path", "versionCode"}
|
||||
default:
|
||||
|
|
Loading…
Reference in New Issue
Block a user