refactor: introduce domain.ErrorWithPath interface
ci/woodpecker/push/govulncheck Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details

This commit is contained in:
Dawid Wysokiński 2024-03-01 07:46:42 +01:00
parent c173bcdba6
commit 0f4611962f
Signed by: Kichiyaki
GPG Key ID: B5445E357FB8B892
7 changed files with 89 additions and 81 deletions

View File

@ -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

View File

@ -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 {

View File

@ -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
} }
} }

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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: