From 0f4611962f0647a31f729d4f14f24f3a7564637b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Wysoki=C5=84ski?= Date: Fri, 1 Mar 2024 07:46:42 +0100 Subject: [PATCH] refactor: introduce domain.ErrorWithPath interface --- internal/domain/error.go | 29 +++++++++++++++ internal/domain/validation.go | 37 ++++++++----------- internal/port/handler_http_api_error.go | 44 +++++++++-------------- internal/port/handler_http_api_player.go | 20 +++++------ internal/port/handler_http_api_server.go | 12 +++---- internal/port/handler_http_api_tribe.go | 16 ++++----- internal/port/handler_http_api_version.go | 12 +++---- 7 files changed, 89 insertions(+), 81 deletions(-) diff --git a/internal/domain/error.go b/internal/domain/error.go index 738ac75..f27a614 100644 --- a/internal/domain/error.go +++ b/internal/domain/error.go @@ -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 diff --git a/internal/domain/validation.go b/internal/domain/validation.go index 37edfb5..79a61ea 100644 --- a/internal/domain/validation.go +++ b/internal/domain/validation.go @@ -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 { diff --git a/internal/port/handler_http_api_error.go b/internal/port/handler_http_api_error.go index c227335..6ba834c 100644 --- a/internal/port/handler_http_api_error.go +++ b/internal/port/handler_http_api_error.go @@ -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 } } diff --git a/internal/port/handler_http_api_player.go b/internal/port/handler_http_api_player.go index d7b5b21..c09b092 100644 --- a/internal/port/handler_http_api_player.go +++ b/internal/port/handler_http_api_player.go @@ -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: diff --git a/internal/port/handler_http_api_server.go b/internal/port/handler_http_api_server.go index a2a4a37..7311a90 100644 --- a/internal/port/handler_http_api_server.go +++ b/internal/port/handler_http_api_server.go @@ -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: diff --git a/internal/port/handler_http_api_tribe.go b/internal/port/handler_http_api_tribe.go index a14d5b6..de9f6c9 100644 --- a/internal/port/handler_http_api_tribe.go +++ b/internal/port/handler_http_api_tribe.go @@ -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: diff --git a/internal/port/handler_http_api_version.go b/internal/port/handler_http_api_version.go index baa5193..a6a7030 100644 --- a/internal/port/handler_http_api_version.go +++ b/internal/port/handler_http_api_version.go @@ -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: