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