parent
646dfedde4
commit
48b87eea81
|
@ -92,7 +92,7 @@ func (a listEnnoblementsParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuer
|
||||||
case domain.EnnoblementSortServerKeyDESC:
|
case domain.EnnoblementSortServerKeyDESC:
|
||||||
q = q.Order("ennoblement.server_key DESC")
|
q = q.Order("ennoblement.server_key DESC")
|
||||||
default:
|
default:
|
||||||
return q.Err(errInvalidSortValue)
|
return q.Err(fmt.Errorf("%s: %w", s.String(), errInvalidSortValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -211,7 +211,7 @@ func (a listPlayersParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery {
|
||||||
case domain.PlayerSortDeletedAtDESC:
|
case domain.PlayerSortDeletedAtDESC:
|
||||||
q = q.OrderExpr("COALESCE(player.deleted_at, ?) DESC", time.Time{})
|
q = q.OrderExpr("COALESCE(player.deleted_at, ?) DESC", time.Time{})
|
||||||
default:
|
default:
|
||||||
return q.Err(errInvalidSortValue)
|
return q.Err(fmt.Errorf("%s: %w", s.String(), errInvalidSortValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,7 +306,7 @@ func (a listPlayersParamsApplier) applyCursor(q *bun.SelectQuery) *bun.SelectQue
|
||||||
el.column = "COALESCE(player.deleted_at, '0001-01-01 00:00:00+00:00')"
|
el.column = "COALESCE(player.deleted_at, '0001-01-01 00:00:00+00:00')"
|
||||||
el.direction = sortDirectionDESC
|
el.direction = sortDirectionDESC
|
||||||
default:
|
default:
|
||||||
return q.Err(errInvalidSortValue)
|
return q.Err(fmt.Errorf("%s: %w", s.String(), errInvalidSortValue))
|
||||||
}
|
}
|
||||||
|
|
||||||
cursorApplier.data = append(cursorApplier.data, el)
|
cursorApplier.data = append(cursorApplier.data, el)
|
||||||
|
|
|
@ -97,7 +97,7 @@ func (a listPlayerSnapshotsParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQ
|
||||||
case domain.PlayerSnapshotSortServerKeyDESC:
|
case domain.PlayerSnapshotSortServerKeyDESC:
|
||||||
q = q.Order("ps.server_key DESC")
|
q = q.Order("ps.server_key DESC")
|
||||||
default:
|
default:
|
||||||
return q.Err(errInvalidSortValue)
|
return q.Err(fmt.Errorf("%s: %w", s.String(), errInvalidSortValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -228,7 +228,7 @@ func (a listServersParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery {
|
||||||
case domain.ServerSortOpenDESC:
|
case domain.ServerSortOpenDESC:
|
||||||
q = q.Order("server.open DESC")
|
q = q.Order("server.open DESC")
|
||||||
default:
|
default:
|
||||||
return q.Err(errInvalidSortValue)
|
return q.Err(fmt.Errorf("%s: %w", s.String(), errInvalidSortValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,7 +272,7 @@ func (a listServersParamsApplier) applyCursor(q *bun.SelectQuery) *bun.SelectQue
|
||||||
el.column = "server.open"
|
el.column = "server.open"
|
||||||
el.direction = sortDirectionDESC
|
el.direction = sortDirectionDESC
|
||||||
default:
|
default:
|
||||||
return q.Err(errInvalidSortValue)
|
return q.Err(fmt.Errorf("%s: %w", s.String(), errInvalidSortValue))
|
||||||
}
|
}
|
||||||
|
|
||||||
cursorApplier.data = append(cursorApplier.data, el)
|
cursorApplier.data = append(cursorApplier.data, el)
|
||||||
|
|
|
@ -212,7 +212,7 @@ func (a listTribesParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery {
|
||||||
case domain.TribeSortDeletedAtDESC:
|
case domain.TribeSortDeletedAtDESC:
|
||||||
q = q.OrderExpr("COALESCE(tribe.deleted_at, ?) DESC", time.Time{})
|
q = q.OrderExpr("COALESCE(tribe.deleted_at, ?) DESC", time.Time{})
|
||||||
default:
|
default:
|
||||||
return q.Err(errInvalidSortValue)
|
return q.Err(fmt.Errorf("%s: %w", s.String(), errInvalidSortValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,7 +317,7 @@ func (a listTribesParamsApplier) applyCursor(q *bun.SelectQuery) *bun.SelectQuer
|
||||||
el.column = "COALESCE(tribe.deleted_at, '0001-01-01 00:00:00+00:00')"
|
el.column = "COALESCE(tribe.deleted_at, '0001-01-01 00:00:00+00:00')"
|
||||||
el.direction = sortDirectionDESC
|
el.direction = sortDirectionDESC
|
||||||
default:
|
default:
|
||||||
return q.Err(errInvalidSortValue)
|
return q.Err(fmt.Errorf("%s: %w", s.String(), errInvalidSortValue))
|
||||||
}
|
}
|
||||||
|
|
||||||
cursorApplier.data = append(cursorApplier.data, el)
|
cursorApplier.data = append(cursorApplier.data, el)
|
||||||
|
|
|
@ -90,7 +90,7 @@ func (a listTribeChangesParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuer
|
||||||
case domain.TribeChangeSortServerKeyDESC:
|
case domain.TribeChangeSortServerKeyDESC:
|
||||||
q = q.Order("tc.server_key DESC")
|
q = q.Order("tc.server_key DESC")
|
||||||
default:
|
default:
|
||||||
return q.Err(errInvalidSortValue)
|
return q.Err(fmt.Errorf("%s: %w", s.String(), errInvalidSortValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,7 @@ func (a listTribeSnapshotsParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQu
|
||||||
case domain.TribeSnapshotSortServerKeyDESC:
|
case domain.TribeSnapshotSortServerKeyDESC:
|
||||||
q = q.Order("ts.server_key DESC")
|
q = q.Order("ts.server_key DESC")
|
||||||
default:
|
default:
|
||||||
return q.Err(errInvalidSortValue)
|
return q.Err(fmt.Errorf("%s: %w", s.String(), errInvalidSortValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ func (a listVersionsParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery {
|
||||||
case domain.VersionSortCodeDESC:
|
case domain.VersionSortCodeDESC:
|
||||||
q = q.Order("version.code DESC")
|
q = q.Order("version.code DESC")
|
||||||
default:
|
default:
|
||||||
return q.Err(errInvalidSortValue)
|
return q.Err(fmt.Errorf("%s: %w", s.String(), errInvalidSortValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ func (a listVersionsParamsApplier) applyCursor(q *bun.SelectQuery) *bun.SelectQu
|
||||||
el.column = "version.code"
|
el.column = "version.code"
|
||||||
el.direction = sortDirectionDESC
|
el.direction = sortDirectionDESC
|
||||||
default:
|
default:
|
||||||
return q.Err(errInvalidSortValue)
|
return q.Err(fmt.Errorf("%s: %w", s.String(), errInvalidSortValue))
|
||||||
}
|
}
|
||||||
|
|
||||||
cursorApplier.data = append(cursorApplier.data, el)
|
cursorApplier.data = append(cursorApplier.data, el)
|
||||||
|
|
|
@ -135,7 +135,7 @@ func (a listVillagesParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery {
|
||||||
case domain.VillageSortServerKeyDESC:
|
case domain.VillageSortServerKeyDESC:
|
||||||
q = q.Order("village.server_key DESC")
|
q = q.Order("village.server_key DESC")
|
||||||
default:
|
default:
|
||||||
return q.Err(errInvalidSortValue)
|
return q.Err(fmt.Errorf("%s: %w", s.String(), errInvalidSortValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package domain
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -165,6 +166,34 @@ const (
|
||||||
EnnoblementSortServerKeyDESC
|
EnnoblementSortServerKeyDESC
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IsInConflict returns true if two sorts can't be used together (e.g. EnnoblementSortIDASC and EnnoblementSortIDDESC).
|
||||||
|
func (s EnnoblementSort) IsInConflict(s2 EnnoblementSort) bool {
|
||||||
|
ss := []EnnoblementSort{s, s2}
|
||||||
|
slices.Sort(ss)
|
||||||
|
// ASC is always an odd number, DESC is always an even number
|
||||||
|
return (ss[0]%2 == 1 && ss[0] == ss[1]-1) || ss[0] == ss[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:gocyclo
|
||||||
|
func (s EnnoblementSort) String() string {
|
||||||
|
switch s {
|
||||||
|
case EnnoblementSortCreatedAtASC:
|
||||||
|
return "createdAt:ASC"
|
||||||
|
case EnnoblementSortCreatedAtDESC:
|
||||||
|
return "createdAt:DESC"
|
||||||
|
case EnnoblementSortIDASC:
|
||||||
|
return "id:ASC"
|
||||||
|
case EnnoblementSortIDDESC:
|
||||||
|
return "id:DESC"
|
||||||
|
case EnnoblementSortServerKeyASC:
|
||||||
|
return "serverKey:ASC"
|
||||||
|
case EnnoblementSortServerKeyDESC:
|
||||||
|
return "serverKey:DESC"
|
||||||
|
default:
|
||||||
|
return "unknown ennoblement sort"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type ListEnnoblementsParams struct {
|
type ListEnnoblementsParams struct {
|
||||||
serverKeys []string
|
serverKeys []string
|
||||||
sort []EnnoblementSort
|
sort []EnnoblementSort
|
||||||
|
@ -207,7 +236,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (params *ListEnnoblementsParams) SetSort(sort []EnnoblementSort) error {
|
func (params *ListEnnoblementsParams) SetSort(sort []EnnoblementSort) error {
|
||||||
if err := validateSliceLen(sort, ennoblementSortMinLength, ennoblementSortMaxLength); err != nil {
|
if err := validateSort(sort, ennoblementSortMinLength, ennoblementSortMaxLength); err != nil {
|
||||||
return ValidationError{
|
return ValidationError{
|
||||||
Model: listEnnoblementsParamsModelName,
|
Model: listEnnoblementsParamsModelName,
|
||||||
Field: "sort",
|
Field: "sort",
|
||||||
|
|
|
@ -37,6 +37,71 @@ func TestNewCreateEnnoblementParams(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEnnoblementSort_IsInConflict(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
sorts [2]domain.EnnoblementSort
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedRes bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.EnnoblementSort{domain.EnnoblementSortIDASC, domain.EnnoblementSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:DESC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.EnnoblementSort{domain.EnnoblementSortIDDESC, domain.EnnoblementSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC id:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.EnnoblementSort{domain.EnnoblementSortIDASC, domain.EnnoblementSortIDASC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC id:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.EnnoblementSort{domain.EnnoblementSortIDDESC, domain.EnnoblementSortIDDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: createdAt:ASC createdAt:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.EnnoblementSort{domain.EnnoblementSortCreatedAtDESC, domain.EnnoblementSortCreatedAtDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: serverKey:DESC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.EnnoblementSort{domain.EnnoblementSortServerKeyDESC, domain.EnnoblementSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert.Equal(t, tt.expectedRes, tt.args.sorts[0].IsInConflict(tt.args.sorts[1]))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestListEnnoblementsParams_SetSort(t *testing.T) {
|
func TestListEnnoblementsParams_SetSort(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
@ -94,6 +159,22 @@ func TestListEnnoblementsParams_SetSort(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: conflict",
|
||||||
|
args: args{
|
||||||
|
sort: []domain.EnnoblementSort{
|
||||||
|
domain.EnnoblementSortIDASC,
|
||||||
|
domain.EnnoblementSortIDDESC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListEnnoblementsParams",
|
||||||
|
Field: "sort",
|
||||||
|
Err: domain.SortConflictError{
|
||||||
|
Sort: [2]string{domain.EnnoblementSortIDASC.String(), domain.EnnoblementSortIDDESC.String()},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
|
@ -410,34 +410,73 @@ const (
|
||||||
PlayerSortDeletedAtDESC
|
PlayerSortDeletedAtDESC
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IsInConflict returns true if two sorts can't be used together (e.g. PlayerSortIDASC and PlayerSortIDDESC).
|
||||||
|
func (s PlayerSort) IsInConflict(s2 PlayerSort) bool {
|
||||||
|
ss := []PlayerSort{s, s2}
|
||||||
|
slices.Sort(ss)
|
||||||
|
// ASC is always an odd number, DESC is always an even number
|
||||||
|
return (ss[0]%2 == 1 && ss[0] == ss[1]-1) || ss[0] == ss[1]
|
||||||
|
}
|
||||||
|
|
||||||
//nolint:gocyclo
|
//nolint:gocyclo
|
||||||
func newPlayerSortFromString(s string) (PlayerSort, error) {
|
func (s PlayerSort) String() string {
|
||||||
switch strings.ToLower(s) {
|
switch s {
|
||||||
case "odscoreatt:asc":
|
case PlayerSortIDASC:
|
||||||
return PlayerSortODScoreAttASC, nil
|
return "id:ASC"
|
||||||
case "odscoreatt:desc":
|
case PlayerSortIDDESC:
|
||||||
return PlayerSortODScoreAttDESC, nil
|
return "id:DESC"
|
||||||
case "odscoredef:asc":
|
case PlayerSortServerKeyASC:
|
||||||
return PlayerSortODScoreDefASC, nil
|
return "serverKey:ASC"
|
||||||
case "odscoredef:desc":
|
case PlayerSortServerKeyDESC:
|
||||||
return PlayerSortODScoreDefDESC, nil
|
return "serverKey:DESC"
|
||||||
case "odscoretotal:asc":
|
case PlayerSortODScoreAttASC:
|
||||||
return PlayerSortODScoreTotalASC, nil
|
return "odScoreAtt:ASC"
|
||||||
case "odscoretotal:desc":
|
case PlayerSortODScoreAttDESC:
|
||||||
return PlayerSortODScoreTotalDESC, nil
|
return "odScoreAtt:DESC"
|
||||||
case "points:asc":
|
case PlayerSortODScoreDefASC:
|
||||||
return PlayerSortPointsASC, nil
|
return "odScoreDef:ASC"
|
||||||
case "points:desc":
|
case PlayerSortODScoreDefDESC:
|
||||||
return PlayerSortPointsDESC, nil
|
return "odScoreDef:DESC"
|
||||||
case "deletedat:asc":
|
case PlayerSortODScoreTotalASC:
|
||||||
return PlayerSortDeletedAtASC, nil
|
return "odScoreTotal:ASC"
|
||||||
case "deletedat:desc":
|
case PlayerSortODScoreTotalDESC:
|
||||||
return PlayerSortDeletedAtDESC, nil
|
return "odScoreTotal:DESC"
|
||||||
|
case PlayerSortPointsASC:
|
||||||
|
return "points:ASC"
|
||||||
|
case PlayerSortPointsDESC:
|
||||||
|
return "points:DESC"
|
||||||
|
case PlayerSortDeletedAtASC:
|
||||||
|
return "deletedAt:ASC"
|
||||||
|
case PlayerSortDeletedAtDESC:
|
||||||
|
return "deletedAt:DESC"
|
||||||
default:
|
default:
|
||||||
return 0, UnsupportedSortStringError{
|
return "unknown player sort"
|
||||||
Sort: s,
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPlayerSortFromString(s string) (PlayerSort, error) {
|
||||||
|
allowed := []PlayerSort{
|
||||||
|
PlayerSortODScoreAttASC,
|
||||||
|
PlayerSortODScoreAttDESC,
|
||||||
|
PlayerSortODScoreDefASC,
|
||||||
|
PlayerSortODScoreDefDESC,
|
||||||
|
PlayerSortODScoreTotalASC,
|
||||||
|
PlayerSortODScoreTotalDESC,
|
||||||
|
PlayerSortPointsASC,
|
||||||
|
PlayerSortPointsDESC,
|
||||||
|
PlayerSortDeletedAtASC,
|
||||||
|
PlayerSortDeletedAtDESC,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, a := range allowed {
|
||||||
|
if strings.EqualFold(a.String(), s) {
|
||||||
|
return a, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0, UnsupportedSortStringError{
|
||||||
|
Sort: s,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type PlayerCursor struct {
|
type PlayerCursor struct {
|
||||||
|
@ -734,7 +773,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (params *ListPlayersParams) SetSort(sort []PlayerSort) error {
|
func (params *ListPlayersParams) SetSort(sort []PlayerSort) error {
|
||||||
if err := validateSliceLen(sort, playerSortMinLength, playerSortMaxLength); err != nil {
|
if err := validateSort(sort, playerSortMinLength, playerSortMaxLength); err != nil {
|
||||||
return ValidationError{
|
return ValidationError{
|
||||||
Model: listPlayersParamsModelName,
|
Model: listPlayersParamsModelName,
|
||||||
Field: "sort",
|
Field: "sort",
|
||||||
|
@ -756,8 +795,10 @@ func (params *ListPlayersParams) PrependSortString(sort []string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := len(sort) - 1; i >= 0; i-- {
|
toPrepend := make([]PlayerSort, 0, len(sort))
|
||||||
converted, err := newPlayerSortFromString(sort[i])
|
|
||||||
|
for i, s := range sort {
|
||||||
|
converted, err := newPlayerSortFromString(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return SliceElementValidationError{
|
return SliceElementValidationError{
|
||||||
Model: listPlayersParamsModelName,
|
Model: listPlayersParamsModelName,
|
||||||
|
@ -766,10 +807,10 @@ func (params *ListPlayersParams) PrependSortString(sort []string) error {
|
||||||
Err: err,
|
Err: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
params.sort = append([]PlayerSort{converted}, params.sort...)
|
toPrepend = append(toPrepend, converted)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return params.SetSort(append(toPrepend, params.sort...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (params *ListPlayersParams) Cursor() PlayerCursor {
|
func (params *ListPlayersParams) Cursor() PlayerCursor {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package domain
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -198,6 +199,35 @@ const (
|
||||||
PlayerSnapshotSortServerKeyDESC
|
PlayerSnapshotSortServerKeyDESC
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IsInConflict returns true if two sorts can't be used together
|
||||||
|
// (e.g. PlayerSnapshotSortIDASC and PlayerSnapshotSortIDDESC).
|
||||||
|
func (s PlayerSnapshotSort) IsInConflict(s2 PlayerSnapshotSort) bool {
|
||||||
|
ss := []PlayerSnapshotSort{s, s2}
|
||||||
|
slices.Sort(ss)
|
||||||
|
// ASC is always an odd number, DESC is always an even number
|
||||||
|
return (ss[0]%2 == 1 && ss[0] == ss[1]-1) || ss[0] == ss[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:gocyclo
|
||||||
|
func (s PlayerSnapshotSort) String() string {
|
||||||
|
switch s {
|
||||||
|
case PlayerSnapshotSortDateASC:
|
||||||
|
return "date:ASC"
|
||||||
|
case PlayerSnapshotSortDateDESC:
|
||||||
|
return "date:DESC"
|
||||||
|
case PlayerSnapshotSortIDASC:
|
||||||
|
return "id:ASC"
|
||||||
|
case PlayerSnapshotSortIDDESC:
|
||||||
|
return "id:DESC"
|
||||||
|
case PlayerSnapshotSortServerKeyASC:
|
||||||
|
return "serverKey:ASC"
|
||||||
|
case PlayerSnapshotSortServerKeyDESC:
|
||||||
|
return "serverKey:DESC"
|
||||||
|
default:
|
||||||
|
return "unknown player snapshot sort"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const PlayerSnapshotListMaxLimit = 200
|
const PlayerSnapshotListMaxLimit = 200
|
||||||
|
|
||||||
type ListPlayerSnapshotsParams struct {
|
type ListPlayerSnapshotsParams struct {
|
||||||
|
@ -239,7 +269,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (params *ListPlayerSnapshotsParams) SetSort(sort []PlayerSnapshotSort) error {
|
func (params *ListPlayerSnapshotsParams) SetSort(sort []PlayerSnapshotSort) error {
|
||||||
if err := validateSliceLen(sort, playerSnapshotSortMinLength, playerSnapshotSortMaxLength); err != nil {
|
if err := validateSort(sort, playerSnapshotSortMinLength, playerSnapshotSortMaxLength); err != nil {
|
||||||
return ValidationError{
|
return ValidationError{
|
||||||
Model: listPlayerSnapshotsParamsModelName,
|
Model: listPlayerSnapshotsParamsModelName,
|
||||||
Field: "sort",
|
Field: "sort",
|
||||||
|
|
|
@ -44,6 +44,71 @@ func TestNewCreatePlayerSnapshotParams(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPlayerSnapshotSort_IsInConflict(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
sorts [2]domain.PlayerSnapshotSort
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedRes bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.PlayerSnapshotSort{domain.PlayerSnapshotSortIDASC, domain.PlayerSnapshotSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:DESC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.PlayerSnapshotSort{domain.PlayerSnapshotSortIDDESC, domain.PlayerSnapshotSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC id:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.PlayerSnapshotSort{domain.PlayerSnapshotSortIDASC, domain.PlayerSnapshotSortIDASC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC id:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.PlayerSnapshotSort{domain.PlayerSnapshotSortIDASC, domain.PlayerSnapshotSortIDDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: date:ASC date:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.PlayerSnapshotSort{domain.PlayerSnapshotSortDateASC, domain.PlayerSnapshotSortDateDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: serverKey:DESC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.PlayerSnapshotSort{domain.PlayerSnapshotSortServerKeyDESC, domain.PlayerSnapshotSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert.Equal(t, tt.expectedRes, tt.args.sorts[0].IsInConflict(tt.args.sorts[1]))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestListPlayerSnapshotsParams_SetSort(t *testing.T) {
|
func TestListPlayerSnapshotsParams_SetSort(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
@ -100,6 +165,22 @@ func TestListPlayerSnapshotsParams_SetSort(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: conflict",
|
||||||
|
args: args{
|
||||||
|
sort: []domain.PlayerSnapshotSort{
|
||||||
|
domain.PlayerSnapshotSortIDASC,
|
||||||
|
domain.PlayerSnapshotSortIDDESC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListPlayerSnapshotsParams",
|
||||||
|
Field: "sort",
|
||||||
|
Err: domain.SortConflictError{
|
||||||
|
Sort: [2]string{domain.PlayerSnapshotSortIDASC.String(), domain.PlayerSnapshotSortIDDESC.String()},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
|
@ -228,6 +228,99 @@ func TestNewCreatePlayerParams(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPlayerSort_IsInConflict(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
sorts [2]domain.PlayerSort
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedRes bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.PlayerSort{domain.PlayerSortIDASC, domain.PlayerSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:DESC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.PlayerSort{domain.PlayerSortIDDESC, domain.PlayerSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC id:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.PlayerSort{domain.PlayerSortIDASC, domain.PlayerSortIDASC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC id:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.PlayerSort{domain.PlayerSortIDASC, domain.PlayerSortIDDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: serverKey:DESC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.PlayerSort{domain.PlayerSortServerKeyDESC, domain.PlayerSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: odScoreAtt:ASC odScoreAtt:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.PlayerSort{domain.PlayerSortODScoreAttASC, domain.PlayerSortODScoreAttDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: odScoreDef:ASC odScoreDef:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.PlayerSort{domain.PlayerSortODScoreDefASC, domain.PlayerSortODScoreDefDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: odScoreTotal:ASC odScoreTotal:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.PlayerSort{domain.PlayerSortODScoreTotalASC, domain.PlayerSortODScoreTotalDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: points:ASC points:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.PlayerSort{domain.PlayerSortPointsASC, domain.PlayerSortPointsDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: deletedAt:ASC deletedAt:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.PlayerSort{domain.PlayerSortDeletedAtASC, domain.PlayerSortDeletedAtDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert.Equal(t, tt.expectedRes, tt.args.sorts[0].IsInConflict(tt.args.sorts[1]))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewPlayerCursor(t *testing.T) {
|
func TestNewPlayerCursor(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
@ -649,6 +742,22 @@ func TestListPlayersParams_SetSort(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: conflict",
|
||||||
|
args: args{
|
||||||
|
sort: []domain.PlayerSort{
|
||||||
|
domain.PlayerSortIDASC,
|
||||||
|
domain.PlayerSortIDDESC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListPlayersParams",
|
||||||
|
Field: "sort",
|
||||||
|
Err: domain.SortConflictError{
|
||||||
|
Sort: [2]string{domain.PlayerSortIDASC.String(), domain.PlayerSortIDDESC.String()},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
@ -820,6 +929,22 @@ func TestListPlayersParams_PrependSortString(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: conflict",
|
||||||
|
args: args{
|
||||||
|
sort: []string{
|
||||||
|
"odScoreAtt:ASC",
|
||||||
|
"odScoreAtt:DESC",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListPlayersParams",
|
||||||
|
Field: "sort",
|
||||||
|
Err: domain.SortConflictError{
|
||||||
|
Sort: [2]string{domain.PlayerSortODScoreAttASC.String(), domain.PlayerSortODScoreAttDESC.String()},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
|
@ -521,6 +521,29 @@ const (
|
||||||
ServerSortOpenDESC
|
ServerSortOpenDESC
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IsInConflict returns true if two sorts can't be used together (e.g. ServerSortKeyASC and ServerSortKeyDESC).
|
||||||
|
func (s ServerSort) IsInConflict(s2 ServerSort) bool {
|
||||||
|
ss := []ServerSort{s, s2}
|
||||||
|
slices.Sort(ss)
|
||||||
|
// ASC is always an odd number, DESC is always an even number
|
||||||
|
return (ss[0]%2 == 1 && ss[0] == ss[1]-1) || ss[0] == ss[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s ServerSort) String() string {
|
||||||
|
switch s {
|
||||||
|
case ServerSortKeyASC:
|
||||||
|
return "key:ASC"
|
||||||
|
case ServerSortKeyDESC:
|
||||||
|
return "key:DESC"
|
||||||
|
case ServerSortOpenASC:
|
||||||
|
return "open:ASC"
|
||||||
|
case ServerSortOpenDESC:
|
||||||
|
return "open:DESC"
|
||||||
|
default:
|
||||||
|
return "unknown server sort"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type ServerCursor struct {
|
type ServerCursor struct {
|
||||||
key string
|
key string
|
||||||
open bool
|
open bool
|
||||||
|
@ -703,7 +726,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (params *ListServersParams) SetSort(sort []ServerSort) error {
|
func (params *ListServersParams) SetSort(sort []ServerSort) error {
|
||||||
if err := validateSliceLen(sort, serverSortMinLength, serverSortMaxLength); err != nil {
|
if err := validateSort(sort, serverSortMinLength, serverSortMaxLength); err != nil {
|
||||||
return ValidationError{
|
return ValidationError{
|
||||||
Model: listServersParamsModelName,
|
Model: listServersParamsModelName,
|
||||||
Field: "sort",
|
Field: "sort",
|
||||||
|
|
|
@ -498,6 +498,71 @@ func TestUpdateServerParams_SetNumBonusVillages(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestServerSort_IsInConflict(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
sorts [2]domain.ServerSort
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedRes bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK: key:ASC open:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.ServerSort{domain.ServerSortKeyASC, domain.ServerSortOpenASC},
|
||||||
|
},
|
||||||
|
expectedRes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: key:ASC open:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.ServerSort{domain.ServerSortKeyASC, domain.ServerSortOpenDESC},
|
||||||
|
},
|
||||||
|
expectedRes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: key:DESC open:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.ServerSort{domain.ServerSortKeyDESC, domain.ServerSortOpenASC},
|
||||||
|
},
|
||||||
|
expectedRes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: key:ASC key:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.ServerSort{domain.ServerSortKeyASC, domain.ServerSortKeyDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: open:DESC open:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.ServerSort{domain.ServerSortOpenDESC, domain.ServerSortOpenASC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: open:DESC open:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.ServerSort{domain.ServerSortOpenDESC, domain.ServerSortOpenDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert.Equal(t, tt.expectedRes, tt.args.sorts[0].IsInConflict(tt.args.sorts[1]))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewServerCursor(t *testing.T) {
|
func TestNewServerCursor(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
@ -609,6 +674,22 @@ func TestListServersParams_SetSort(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: conflict",
|
||||||
|
args: args{
|
||||||
|
sort: []domain.ServerSort{
|
||||||
|
domain.ServerSortKeyASC,
|
||||||
|
domain.ServerSortKeyDESC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListServersParams",
|
||||||
|
Field: "sort",
|
||||||
|
Err: domain.SortConflictError{
|
||||||
|
Sort: [2]string{domain.ServerSortKeyASC.String(), domain.ServerSortKeyDESC.String()},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
|
@ -450,38 +450,79 @@ const (
|
||||||
TribeSortDeletedAtDESC
|
TribeSortDeletedAtDESC
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IsInConflict returns true if two sorts can't be used together (e.g. TribeSortIDASC and TribeSortIDDESC).
|
||||||
|
func (s TribeSort) IsInConflict(s2 TribeSort) bool {
|
||||||
|
ss := []TribeSort{s, s2}
|
||||||
|
slices.Sort(ss)
|
||||||
|
// ASC is always an odd number, DESC is always an even number
|
||||||
|
return (ss[0]%2 == 1 && ss[0] == ss[1]-1) || ss[0] == ss[1]
|
||||||
|
}
|
||||||
|
|
||||||
//nolint:gocyclo
|
//nolint:gocyclo
|
||||||
func newTribeSortFromString(s string) (TribeSort, error) {
|
func (s TribeSort) String() string {
|
||||||
switch strings.ToLower(s) {
|
switch s {
|
||||||
case "odscoreatt:asc":
|
case TribeSortIDASC:
|
||||||
return TribeSortODScoreAttASC, nil
|
return "id:ASC"
|
||||||
case "odscoreatt:desc":
|
case TribeSortIDDESC:
|
||||||
return TribeSortODScoreAttDESC, nil
|
return "id:DESC"
|
||||||
case "odscoredef:asc":
|
case TribeSortServerKeyASC:
|
||||||
return TribeSortODScoreDefASC, nil
|
return "serverKey:ASC"
|
||||||
case "odscoredef:desc":
|
case TribeSortServerKeyDESC:
|
||||||
return TribeSortODScoreDefDESC, nil
|
return "serverKey:DESC"
|
||||||
case "odscoretotal:asc":
|
case TribeSortODScoreAttASC:
|
||||||
return TribeSortODScoreTotalASC, nil
|
return "odScoreAtt:ASC"
|
||||||
case "odscoretotal:desc":
|
case TribeSortODScoreAttDESC:
|
||||||
return TribeSortODScoreTotalDESC, nil
|
return "odScoreAtt:DESC"
|
||||||
case "points:asc":
|
case TribeSortODScoreDefASC:
|
||||||
return TribeSortPointsASC, nil
|
return "odScoreDef:ASC"
|
||||||
case "points:desc":
|
case TribeSortODScoreDefDESC:
|
||||||
return TribeSortPointsDESC, nil
|
return "odScoreDef:DESC"
|
||||||
case "dominance:asc":
|
case TribeSortODScoreTotalASC:
|
||||||
return TribeSortDominanceASC, nil
|
return "odScoreTotal:ASC"
|
||||||
case "dominance:desc":
|
case TribeSortODScoreTotalDESC:
|
||||||
return TribeSortDominanceDESC, nil
|
return "odScoreTotal:DESC"
|
||||||
case "deletedat:asc":
|
case TribeSortPointsASC:
|
||||||
return TribeSortDeletedAtASC, nil
|
return "points:ASC"
|
||||||
case "deletedat:desc":
|
case TribeSortPointsDESC:
|
||||||
return TribeSortDeletedAtDESC, nil
|
return "points:DESC"
|
||||||
|
case TribeSortDominanceASC:
|
||||||
|
return "dominance:ASC"
|
||||||
|
case TribeSortDominanceDESC:
|
||||||
|
return "dominance:DESC"
|
||||||
|
case TribeSortDeletedAtASC:
|
||||||
|
return "deletedAt:ASC"
|
||||||
|
case TribeSortDeletedAtDESC:
|
||||||
|
return "deletedAt:DESC"
|
||||||
default:
|
default:
|
||||||
return 0, UnsupportedSortStringError{
|
return "unknown tribe sort"
|
||||||
Sort: s,
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTribeSortFromString(s string) (TribeSort, error) {
|
||||||
|
allowed := []TribeSort{
|
||||||
|
TribeSortODScoreAttASC,
|
||||||
|
TribeSortODScoreAttDESC,
|
||||||
|
TribeSortODScoreDefASC,
|
||||||
|
TribeSortODScoreDefDESC,
|
||||||
|
TribeSortODScoreTotalASC,
|
||||||
|
TribeSortODScoreTotalDESC,
|
||||||
|
TribeSortPointsASC,
|
||||||
|
TribeSortPointsDESC,
|
||||||
|
TribeSortDominanceASC,
|
||||||
|
TribeSortDominanceDESC,
|
||||||
|
TribeSortDeletedAtASC,
|
||||||
|
TribeSortDeletedAtDESC,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, a := range allowed {
|
||||||
|
if strings.EqualFold(a.String(), s) {
|
||||||
|
return a, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0, UnsupportedSortStringError{
|
||||||
|
Sort: s,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type TribeCursor struct {
|
type TribeCursor struct {
|
||||||
|
@ -735,7 +776,19 @@ func (params *ListTribesParams) ServerKeys() []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (params *ListTribesParams) SetServerKeys(serverKeys []string) error {
|
func (params *ListTribesParams) SetServerKeys(serverKeys []string) error {
|
||||||
|
for i, sk := range serverKeys {
|
||||||
|
if err := validateServerKey(sk); err != nil {
|
||||||
|
return SliceElementValidationError{
|
||||||
|
Model: listTribesParamsModelName,
|
||||||
|
Field: "serverKeys",
|
||||||
|
Index: i,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
params.serverKeys = serverKeys
|
params.serverKeys = serverKeys
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -781,7 +834,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (params *ListTribesParams) SetSort(sort []TribeSort) error {
|
func (params *ListTribesParams) SetSort(sort []TribeSort) error {
|
||||||
if err := validateSliceLen(sort, tribeSortMinLength, tribeSortMaxLength); err != nil {
|
if err := validateSort(sort, tribeSortMinLength, tribeSortMaxLength); err != nil {
|
||||||
return ValidationError{
|
return ValidationError{
|
||||||
Model: listTribesParamsModelName,
|
Model: listTribesParamsModelName,
|
||||||
Field: "sort",
|
Field: "sort",
|
||||||
|
@ -803,8 +856,10 @@ func (params *ListTribesParams) PrependSortString(sort []string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := len(sort) - 1; i >= 0; i-- {
|
toPrepend := make([]TribeSort, 0, len(sort))
|
||||||
converted, err := newTribeSortFromString(sort[i])
|
|
||||||
|
for i, s := range sort {
|
||||||
|
converted, err := newTribeSortFromString(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return SliceElementValidationError{
|
return SliceElementValidationError{
|
||||||
Model: listTribesParamsModelName,
|
Model: listTribesParamsModelName,
|
||||||
|
@ -813,10 +868,10 @@ func (params *ListTribesParams) PrependSortString(sort []string) error {
|
||||||
Err: err,
|
Err: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
params.sort = append([]TribeSort{converted}, params.sort...)
|
toPrepend = append(toPrepend, converted)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return params.SetSort(append(toPrepend, params.sort...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (params *ListTribesParams) Cursor() TribeCursor {
|
func (params *ListTribesParams) Cursor() TribeCursor {
|
||||||
|
|
|
@ -213,6 +213,34 @@ const (
|
||||||
TribeChangeSortServerKeyDESC
|
TribeChangeSortServerKeyDESC
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IsInConflict returns true if two sorts can't be used together (e.g. TribeChangeSortIDASC and TribeChangeSortIDDESC).
|
||||||
|
func (s TribeChangeSort) IsInConflict(s2 TribeChangeSort) bool {
|
||||||
|
ss := []TribeChangeSort{s, s2}
|
||||||
|
slices.Sort(ss)
|
||||||
|
// ASC is always an odd number, DESC is always an even number
|
||||||
|
return (ss[0]%2 == 1 && ss[0] == ss[1]-1) || ss[0] == ss[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:gocyclo
|
||||||
|
func (s TribeChangeSort) String() string {
|
||||||
|
switch s {
|
||||||
|
case TribeChangeSortCreatedAtASC:
|
||||||
|
return "createdAt:ASC"
|
||||||
|
case TribeChangeSortCreatedAtDESC:
|
||||||
|
return "createdAt:DESC"
|
||||||
|
case TribeChangeSortIDASC:
|
||||||
|
return "id:ASC"
|
||||||
|
case TribeChangeSortIDDESC:
|
||||||
|
return "id:DESC"
|
||||||
|
case TribeChangeSortServerKeyASC:
|
||||||
|
return "serverKey:ASC"
|
||||||
|
case TribeChangeSortServerKeyDESC:
|
||||||
|
return "serverKey:DESC"
|
||||||
|
default:
|
||||||
|
return "unknown tribe change sort"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type ListTribeChangesParams struct {
|
type ListTribeChangesParams struct {
|
||||||
serverKeys []string
|
serverKeys []string
|
||||||
sort []TribeChangeSort
|
sort []TribeChangeSort
|
||||||
|
@ -255,7 +283,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (params *ListTribeChangesParams) SetSort(sort []TribeChangeSort) error {
|
func (params *ListTribeChangesParams) SetSort(sort []TribeChangeSort) error {
|
||||||
if err := validateSliceLen(sort, tribeChangeSortMinLength, tribeChangeSortMaxLength); err != nil {
|
if err := validateSort(sort, tribeChangeSortMinLength, tribeChangeSortMaxLength); err != nil {
|
||||||
return ValidationError{
|
return ValidationError{
|
||||||
Model: listTribeChangesParamsModelName,
|
Model: listTribeChangesParamsModelName,
|
||||||
Field: "sort",
|
Field: "sort",
|
||||||
|
|
|
@ -214,6 +214,71 @@ func TestNewCreateTribeChangeParamsFromPlayers(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTribeChangeSort_IsInConflict(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
sorts [2]domain.TribeChangeSort
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedRes bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.TribeChangeSort{domain.TribeChangeSortIDASC, domain.TribeChangeSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:DESC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.TribeChangeSort{domain.TribeChangeSortIDDESC, domain.TribeChangeSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC id:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.TribeChangeSort{domain.TribeChangeSortIDASC, domain.TribeChangeSortIDASC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC id:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.TribeChangeSort{domain.TribeChangeSortIDDESC, domain.TribeChangeSortIDDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: createdAt:ASC createdAt:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.TribeChangeSort{domain.TribeChangeSortCreatedAtDESC, domain.TribeChangeSortCreatedAtDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: serverKey:DESC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.TribeChangeSort{domain.TribeChangeSortServerKeyDESC, domain.TribeChangeSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert.Equal(t, tt.expectedRes, tt.args.sorts[0].IsInConflict(tt.args.sorts[1]))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestListTribeChangesParams_SetSort(t *testing.T) {
|
func TestListTribeChangesParams_SetSort(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
@ -270,6 +335,22 @@ func TestListTribeChangesParams_SetSort(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: conflict",
|
||||||
|
args: args{
|
||||||
|
sort: []domain.TribeChangeSort{
|
||||||
|
domain.TribeChangeSortIDASC,
|
||||||
|
domain.TribeChangeSortIDDESC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListTribeChangesParams",
|
||||||
|
Field: "sort",
|
||||||
|
Err: domain.SortConflictError{
|
||||||
|
Sort: [2]string{domain.TribeChangeSortIDASC.String(), domain.TribeChangeSortIDDESC.String()},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package domain
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -224,6 +225,35 @@ const (
|
||||||
TribeSnapshotSortServerKeyDESC
|
TribeSnapshotSortServerKeyDESC
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IsInConflict returns true if two sorts can't be used together
|
||||||
|
// (e.g. TribeSnapshotSortIDASC and TribeSnapshotSortIDDESC).
|
||||||
|
func (s TribeSnapshotSort) IsInConflict(s2 TribeSnapshotSort) bool {
|
||||||
|
ss := []TribeSnapshotSort{s, s2}
|
||||||
|
slices.Sort(ss)
|
||||||
|
// ASC is always an odd number, DESC is always an even number
|
||||||
|
return (ss[0]%2 == 1 && ss[0] == ss[1]-1) || ss[0] == ss[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:gocyclo
|
||||||
|
func (s TribeSnapshotSort) String() string {
|
||||||
|
switch s {
|
||||||
|
case TribeSnapshotSortDateASC:
|
||||||
|
return "date:ASC"
|
||||||
|
case TribeSnapshotSortDateDESC:
|
||||||
|
return "date:DESC"
|
||||||
|
case TribeSnapshotSortIDASC:
|
||||||
|
return "id:ASC"
|
||||||
|
case TribeSnapshotSortIDDESC:
|
||||||
|
return "id:DESC"
|
||||||
|
case TribeSnapshotSortServerKeyASC:
|
||||||
|
return "serverKey:ASC"
|
||||||
|
case TribeSnapshotSortServerKeyDESC:
|
||||||
|
return "serverKey:DESC"
|
||||||
|
default:
|
||||||
|
return "unknown tribe snapshot sort"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type ListTribeSnapshotsParams struct {
|
type ListTribeSnapshotsParams struct {
|
||||||
serverKeys []string
|
serverKeys []string
|
||||||
sort []TribeSnapshotSort
|
sort []TribeSnapshotSort
|
||||||
|
@ -266,7 +296,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (params *ListTribeSnapshotsParams) SetSort(sort []TribeSnapshotSort) error {
|
func (params *ListTribeSnapshotsParams) SetSort(sort []TribeSnapshotSort) error {
|
||||||
if err := validateSliceLen(sort, tribeSnapshotSortMinLength, tribeSnapshotSortMaxLength); err != nil {
|
if err := validateSort(sort, tribeSnapshotSortMinLength, tribeSnapshotSortMaxLength); err != nil {
|
||||||
return ValidationError{
|
return ValidationError{
|
||||||
Model: listTribeSnapshotsParamsModelName,
|
Model: listTribeSnapshotsParamsModelName,
|
||||||
Field: "sort",
|
Field: "sort",
|
||||||
|
|
|
@ -46,6 +46,71 @@ func TestNewCreateTribeSnapshotParams(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTribeSnapshotSort_IsInConflict(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
sorts [2]domain.TribeSnapshotSort
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedRes bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.TribeSnapshotSort{domain.TribeSnapshotSortIDASC, domain.TribeSnapshotSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:DESC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.TribeSnapshotSort{domain.TribeSnapshotSortIDDESC, domain.TribeSnapshotSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC id:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.TribeSnapshotSort{domain.TribeSnapshotSortIDASC, domain.TribeSnapshotSortIDASC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC id:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.TribeSnapshotSort{domain.TribeSnapshotSortIDASC, domain.TribeSnapshotSortIDDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: date:ASC date:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.TribeSnapshotSort{domain.TribeSnapshotSortDateASC, domain.TribeSnapshotSortDateDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: serverKey:DESC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.TribeSnapshotSort{domain.TribeSnapshotSortServerKeyDESC, domain.TribeSnapshotSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert.Equal(t, tt.expectedRes, tt.args.sorts[0].IsInConflict(tt.args.sorts[1]))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestListTribeSnapshotsParams_SetSort(t *testing.T) {
|
func TestListTribeSnapshotsParams_SetSort(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
@ -102,6 +167,22 @@ func TestListTribeSnapshotsParams_SetSort(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: conflict",
|
||||||
|
args: args{
|
||||||
|
sort: []domain.TribeSnapshotSort{
|
||||||
|
domain.TribeSnapshotSortIDASC,
|
||||||
|
domain.TribeSnapshotSortIDDESC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListTribeSnapshotsParams",
|
||||||
|
Field: "sort",
|
||||||
|
Err: domain.SortConflictError{
|
||||||
|
Sort: [2]string{domain.TribeSnapshotSortIDASC.String(), domain.TribeSnapshotSortIDDESC.String()},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
|
@ -180,6 +180,106 @@ func TestNewCreateTribeParams(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTribeSort_IsInConflict(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
sorts [2]domain.TribeSort
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedRes bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.TribeSort{domain.TribeSortIDASC, domain.TribeSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:DESC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.TribeSort{domain.TribeSortIDDESC, domain.TribeSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC id:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.TribeSort{domain.TribeSortIDASC, domain.TribeSortIDASC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC id:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.TribeSort{domain.TribeSortIDASC, domain.TribeSortIDDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: serverKey:DESC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.TribeSort{domain.TribeSortServerKeyDESC, domain.TribeSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: odScoreAtt:ASC odScoreAtt:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.TribeSort{domain.TribeSortODScoreAttASC, domain.TribeSortODScoreAttDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: odScoreDef:ASC odScoreDef:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.TribeSort{domain.TribeSortODScoreDefASC, domain.TribeSortODScoreDefDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: odScoreTotal:ASC odScoreTotal:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.TribeSort{domain.TribeSortODScoreTotalASC, domain.TribeSortODScoreTotalDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: points:ASC points:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.TribeSort{domain.TribeSortPointsASC, domain.TribeSortPointsDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: dominance:ASC dominance:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.TribeSort{domain.TribeSortDominanceASC, domain.TribeSortDominanceDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: deletedAt:ASC deletedAt:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.TribeSort{domain.TribeSortDeletedAtASC, domain.TribeSortDeletedAtDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert.Equal(t, tt.expectedRes, tt.args.sorts[0].IsInConflict(tt.args.sorts[1]))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewTribeCursor(t *testing.T) {
|
func TestNewTribeCursor(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
@ -436,6 +536,60 @@ func TestListTribesParams_SetIDs(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestListTribesParams_SetServerKeys(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
serverKeys []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type test struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []test{
|
||||||
|
{
|
||||||
|
name: "OK",
|
||||||
|
args: args{
|
||||||
|
serverKeys: []string{
|
||||||
|
domaintest.RandServerKey(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, serverKeyTest := range newServerKeyValidationTests() {
|
||||||
|
tests = append(tests, test{
|
||||||
|
name: serverKeyTest.name,
|
||||||
|
args: args{
|
||||||
|
serverKeys: []string{serverKeyTest.key},
|
||||||
|
},
|
||||||
|
expectedErr: domain.SliceElementValidationError{
|
||||||
|
Model: "ListTribesParams",
|
||||||
|
Field: "serverKeys",
|
||||||
|
Index: 0,
|
||||||
|
Err: serverKeyTest.expectedErr,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
params := domain.NewListTribesParams()
|
||||||
|
|
||||||
|
require.ErrorIs(t, params.SetServerKeys(tt.args.serverKeys), tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.args.serverKeys, params.ServerKeys())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestListTribesParams_SetTags(t *testing.T) {
|
func TestListTribesParams_SetTags(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
@ -570,6 +724,22 @@ func TestListTribesParams_SetSort(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: conflict",
|
||||||
|
args: args{
|
||||||
|
sort: []domain.TribeSort{
|
||||||
|
domain.TribeSortIDASC,
|
||||||
|
domain.TribeSortIDDESC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListTribesParams",
|
||||||
|
Field: "sort",
|
||||||
|
Err: domain.SortConflictError{
|
||||||
|
Sort: [2]string{domain.TribeSortIDASC.String(), domain.TribeSortIDDESC.String()},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
@ -745,6 +915,22 @@ func TestListTribesParams_PrependSortString(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: conflict",
|
||||||
|
args: args{
|
||||||
|
sort: []string{
|
||||||
|
"odScoreAtt:ASC",
|
||||||
|
"odScoreAtt:DESC",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListTribesParams",
|
||||||
|
Field: "sort",
|
||||||
|
Err: domain.SortConflictError{
|
||||||
|
Sort: [2]string{domain.TribeSortODScoreAttASC.String(), domain.TribeSortODScoreAttDESC.String()},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
|
@ -244,6 +244,30 @@ func (e UnsupportedSortStringError) Params() map[string]any {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SortConflictError struct {
|
||||||
|
Sort [2]string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ErrorWithParams = SortConflictError{}
|
||||||
|
|
||||||
|
func (e SortConflictError) Error() string {
|
||||||
|
return fmt.Sprintf("sort values %s are in conflict and can't be used together", e.Sort[0]+" and "+e.Sort[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e SortConflictError) Type() ErrorType {
|
||||||
|
return ErrorTypeIncorrectInput
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e SortConflictError) Code() string {
|
||||||
|
return "sort-conflict"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e SortConflictError) Params() map[string]any {
|
||||||
|
return map[string]any{
|
||||||
|
"Sort": e.Sort,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var ErrRequired error = simpleError{
|
var ErrRequired error = simpleError{
|
||||||
msg: "can't be blank",
|
msg: "can't be blank",
|
||||||
typ: ErrorTypeIncorrectInput,
|
typ: ErrorTypeIncorrectInput,
|
||||||
|
@ -322,3 +346,25 @@ func validateIntInRange(current, min, max int) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateSort[S ~[]E, E interface {
|
||||||
|
IsInConflict(s E) bool
|
||||||
|
String() string
|
||||||
|
}](sort S, min, max int) error {
|
||||||
|
if err := validateSliceLen(sort, min, max); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, s1 := range sort {
|
||||||
|
for j := i + 1; j < len(sort); j++ {
|
||||||
|
s2 := sort[j]
|
||||||
|
if s1.IsInConflict(s2) {
|
||||||
|
return SortConflictError{
|
||||||
|
Sort: [2]string{s1.String(), s2.String()},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package domain
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Version struct {
|
type Version struct {
|
||||||
|
@ -100,6 +101,25 @@ const (
|
||||||
VersionSortCodeDESC
|
VersionSortCodeDESC
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IsInConflict returns true if two sorts can't be used together (e.g. VersionSortCodeASC and VersionSortCodeDESC).
|
||||||
|
func (s VersionSort) IsInConflict(s2 VersionSort) bool {
|
||||||
|
ss := []VersionSort{s, s2}
|
||||||
|
slices.Sort(ss)
|
||||||
|
// ASC is always an odd number, DESC is always an even number
|
||||||
|
return (ss[0]%2 == 1 && ss[0] == ss[1]-1) || ss[0] == ss[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s VersionSort) String() string {
|
||||||
|
switch s {
|
||||||
|
case VersionSortCodeASC:
|
||||||
|
return "code:ASC"
|
||||||
|
case VersionSortCodeDESC:
|
||||||
|
return "code:DESC"
|
||||||
|
default:
|
||||||
|
return "unknown version sort"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type VersionCursor struct {
|
type VersionCursor struct {
|
||||||
code string
|
code string
|
||||||
}
|
}
|
||||||
|
@ -207,7 +227,7 @@ func (params *ListVersionsParams) Sort() []VersionSort {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (params *ListVersionsParams) SetSort(sort []VersionSort) error {
|
func (params *ListVersionsParams) SetSort(sort []VersionSort) error {
|
||||||
if err := validateSliceLen(sort, versionSortMinLength, versionSortMaxLength); err != nil {
|
if err := validateSort(sort, versionSortMinLength, versionSortMaxLength); err != nil {
|
||||||
return ValidationError{
|
return ValidationError{
|
||||||
Model: listVersionsParamsModelName,
|
Model: listVersionsParamsModelName,
|
||||||
Field: "sort",
|
Field: "sort",
|
||||||
|
|
|
@ -11,6 +11,43 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestVersionSort_IsInConflict(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
sorts [2]domain.VersionSort
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedRes bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK: code:ASC code:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.VersionSort{domain.VersionSortCodeASC, domain.VersionSortCodeASC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: code:ASC code:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.VersionSort{domain.VersionSortCodeASC, domain.VersionSortCodeDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert.Equal(t, tt.expectedRes, tt.args.sorts[0].IsInConflict(tt.args.sorts[1]))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewVersionCursor(t *testing.T) {
|
func TestNewVersionCursor(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
|
@ -224,6 +224,30 @@ const (
|
||||||
VillageSortServerKeyDESC
|
VillageSortServerKeyDESC
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IsInConflict returns true if two sorts can't be used together (e.g. VillageSortIDASC and VillageSortIDDESC).
|
||||||
|
func (s VillageSort) IsInConflict(s2 VillageSort) bool {
|
||||||
|
ss := []VillageSort{s, s2}
|
||||||
|
slices.Sort(ss)
|
||||||
|
// ASC is always an odd number, DESC is always an even number
|
||||||
|
return (ss[0]%2 == 1 && ss[0] == ss[1]-1) || ss[0] == ss[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:gocyclo
|
||||||
|
func (s VillageSort) String() string {
|
||||||
|
switch s {
|
||||||
|
case VillageSortIDASC:
|
||||||
|
return "id:ASC"
|
||||||
|
case VillageSortIDDESC:
|
||||||
|
return "id:DESC"
|
||||||
|
case VillageSortServerKeyASC:
|
||||||
|
return "serverKey:ASC"
|
||||||
|
case VillageSortServerKeyDESC:
|
||||||
|
return "serverKey:DESC"
|
||||||
|
default:
|
||||||
|
return "unknown village sort"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type ListVillagesParams struct {
|
type ListVillagesParams struct {
|
||||||
ids []int
|
ids []int
|
||||||
idGT NullInt
|
idGT NullInt
|
||||||
|
@ -308,7 +332,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (params *ListVillagesParams) SetSort(sort []VillageSort) error {
|
func (params *ListVillagesParams) SetSort(sort []VillageSort) error {
|
||||||
if err := validateSliceLen(sort, villageSortMinLength, villageSortMaxLength); err != nil {
|
if err := validateSort(sort, villageSortMinLength, villageSortMaxLength); err != nil {
|
||||||
return ValidationError{
|
return ValidationError{
|
||||||
Model: listVillagesParamsModelName,
|
Model: listVillagesParamsModelName,
|
||||||
Field: "sort",
|
Field: "sort",
|
||||||
|
|
|
@ -88,6 +88,64 @@ func TestNewCreateVillageParams(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVillageSort_IsInConflict(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
sorts [2]domain.VillageSort
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedRes bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.VillageSort{domain.VillageSortIDASC, domain.VillageSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:DESC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.VillageSort{domain.VillageSortIDDESC, domain.VillageSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC id:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.VillageSort{domain.VillageSortIDASC, domain.VillageSortIDASC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: id:ASC id:DESC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.VillageSort{domain.VillageSortIDASC, domain.VillageSortIDDESC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: serverKey:DESC serverKey:ASC",
|
||||||
|
args: args{
|
||||||
|
sorts: [2]domain.VillageSort{domain.VillageSortServerKeyDESC, domain.VillageSortServerKeyASC},
|
||||||
|
},
|
||||||
|
expectedRes: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert.Equal(t, tt.expectedRes, tt.args.sorts[0].IsInConflict(tt.args.sorts[1]))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestListVillagesParams_SetIDs(t *testing.T) {
|
func TestListVillagesParams_SetIDs(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
@ -258,6 +316,22 @@ func TestListVillagesParams_SetSort(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: conflict",
|
||||||
|
args: args{
|
||||||
|
sort: []domain.VillageSort{
|
||||||
|
domain.VillageSortIDASC,
|
||||||
|
domain.VillageSortIDDESC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListVillagesParams",
|
||||||
|
Field: "sort",
|
||||||
|
Err: domain.SortConflictError{
|
||||||
|
Sort: [2]string{domain.VillageSortIDASC.String(), domain.VillageSortIDDESC.String()},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
|
@ -527,6 +527,44 @@ func TestListPlayers(t *testing.T) {
|
||||||
}, body)
|
}, body)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: sort conflict",
|
||||||
|
reqModifier: func(t *testing.T, req *http.Request) {
|
||||||
|
t.Helper()
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Add("sort", "odScoreAtt:ASC")
|
||||||
|
q.Add("sort", "odScoreAtt:DESC")
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
},
|
||||||
|
assertResp: func(t *testing.T, req *http.Request, resp *http.Response) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
||||||
|
|
||||||
|
// body
|
||||||
|
body := decodeJSON[apimodel.ErrorResponse](t, resp.Body)
|
||||||
|
q := req.URL.Query()
|
||||||
|
domainErr := domain.SortConflictError{
|
||||||
|
Sort: [2]string{q["sort"][0], q["sort"][1]},
|
||||||
|
}
|
||||||
|
paramSort := make([]any, len(domainErr.Sort))
|
||||||
|
for i, s := range domainErr.Sort {
|
||||||
|
paramSort[i] = s
|
||||||
|
}
|
||||||
|
assert.Equal(t, apimodel.ErrorResponse{
|
||||||
|
Errors: []apimodel.Error{
|
||||||
|
{
|
||||||
|
Code: domainErr.Code(),
|
||||||
|
Message: domainErr.Error(),
|
||||||
|
Params: map[string]any{
|
||||||
|
"sort": paramSort,
|
||||||
|
},
|
||||||
|
Path: []string{"$query", "sort"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, body)
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "ERR: len(name) > 100",
|
name: "ERR: len(name) > 100",
|
||||||
reqModifier: func(t *testing.T, req *http.Request) {
|
reqModifier: func(t *testing.T, req *http.Request) {
|
||||||
|
|
|
@ -527,6 +527,44 @@ func TestListTribes(t *testing.T) {
|
||||||
}, body)
|
}, body)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: sort conflict",
|
||||||
|
reqModifier: func(t *testing.T, req *http.Request) {
|
||||||
|
t.Helper()
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Add("sort", "odScoreAtt:ASC")
|
||||||
|
q.Add("sort", "odScoreAtt:DESC")
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
},
|
||||||
|
assertResp: func(t *testing.T, req *http.Request, resp *http.Response) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
||||||
|
|
||||||
|
// body
|
||||||
|
body := decodeJSON[apimodel.ErrorResponse](t, resp.Body)
|
||||||
|
q := req.URL.Query()
|
||||||
|
domainErr := domain.SortConflictError{
|
||||||
|
Sort: [2]string{q["sort"][0], q["sort"][1]},
|
||||||
|
}
|
||||||
|
paramSort := make([]any, len(domainErr.Sort))
|
||||||
|
for i, s := range domainErr.Sort {
|
||||||
|
paramSort[i] = s
|
||||||
|
}
|
||||||
|
assert.Equal(t, apimodel.ErrorResponse{
|
||||||
|
Errors: []apimodel.Error{
|
||||||
|
{
|
||||||
|
Code: domainErr.Code(),
|
||||||
|
Message: domainErr.Error(),
|
||||||
|
Params: map[string]any{
|
||||||
|
"sort": paramSort,
|
||||||
|
},
|
||||||
|
Path: []string{"$query", "sort"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, body)
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "ERR: len(tag) > 100",
|
name: "ERR: len(tag) > 100",
|
||||||
reqModifier: func(t *testing.T, req *http.Request) {
|
reqModifier: func(t *testing.T, req *http.Request) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user