From 82d313d2ae4056b3db5e9df8ad5f56e864a27c2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Wysoki=C5=84ski?= Date: Sun, 3 Mar 2024 07:43:07 +0100 Subject: [PATCH] feat: api - GET /api/v2/versions/{versionCode}/servers/{serverKey}/players - new sort option --- api/openapi3.yml | 2 ++ internal/adapter/repository_bun_player.go | 24 +++++++------- internal/adapter/repository_bun_tribe.go | 14 -------- internal/adapter/repository_player_test.go | 29 +++++++++++++++++ internal/domain/domaintest/player.go | 3 ++ internal/domain/player.go | 34 +++++++++++++++++++ internal/domain/player_test.go | 38 ++++++++++++++++++++-- 7 files changed, 116 insertions(+), 28 deletions(-) diff --git a/api/openapi3.yml b/api/openapi3.yml index 39d86c7..b8b59a0 100644 --- a/api/openapi3.yml +++ b/api/openapi3.yml @@ -1162,6 +1162,8 @@ components: - odScoreAtt:DESC - odScoreDef:ASC - odScoreDef:DESC + - odScoreSup:ASC + - odScoreSup:DESC - odScoreTotal:ASC - odScoreTotal:DESC - points:ASC diff --git a/internal/adapter/repository_bun_player.go b/internal/adapter/repository_bun_player.go index 7677c3c..fc06289 100644 --- a/internal/adapter/repository_bun_player.go +++ b/internal/adapter/repository_bun_player.go @@ -198,6 +198,10 @@ func (a listPlayersParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery { q = q.Order("player.score_def ASC") case domain.PlayerSortODScoreDefDESC: q = q.Order("player.score_def DESC") + case domain.PlayerSortODScoreSupASC: + q = q.Order("player.score_sup ASC") + case domain.PlayerSortODScoreSupDESC: + q = q.Order("player.score_sup DESC") case domain.PlayerSortODScoreTotalASC: q = q.Order("player.score_total ASC") case domain.PlayerSortODScoreTotalDESC: @@ -247,62 +251,58 @@ func (a listPlayersParamsApplier) applyCursor(q *bun.SelectQuery) *bun.SelectQue el.direction = sortDirectionDESC case domain.PlayerSortServerKeyASC: el.value = cursor.ServerKey() - el.unique = false el.column = "player.server_key" el.direction = sortDirectionASC case domain.PlayerSortServerKeyDESC: el.value = cursor.ServerKey() - el.unique = false el.column = "player.server_key" el.direction = sortDirectionDESC case domain.PlayerSortODScoreAttASC: el.value = cursor.ODScoreAtt() - el.unique = false el.column = "player.score_att" el.direction = sortDirectionASC case domain.PlayerSortODScoreAttDESC: el.value = cursor.ODScoreAtt() - el.unique = false el.column = "player.score_att" el.direction = sortDirectionDESC case domain.PlayerSortODScoreDefASC: el.value = cursor.ODScoreDef() - el.unique = false el.column = "player.score_def" el.direction = sortDirectionASC case domain.PlayerSortODScoreDefDESC: el.value = cursor.ODScoreDef() - el.unique = false el.column = "player.score_def" el.direction = sortDirectionDESC + case domain.PlayerSortODScoreSupASC: + el.value = cursor.ODScoreSup() + el.column = "player.score_sup" + el.direction = sortDirectionASC + case domain.PlayerSortODScoreSupDESC: + el.value = cursor.ODScoreSup() + el.column = "player.score_sup" + el.direction = sortDirectionDESC case domain.PlayerSortODScoreTotalASC: el.value = cursor.ODScoreTotal() - el.unique = false el.column = "player.score_total" el.direction = sortDirectionASC case domain.PlayerSortODScoreTotalDESC: el.value = cursor.ODScoreTotal() - el.unique = false el.column = "player.score_total" el.direction = sortDirectionDESC case domain.PlayerSortPointsASC: el.value = cursor.Points() - el.unique = false el.column = "player.points" el.direction = sortDirectionASC case domain.PlayerSortPointsDESC: el.value = cursor.Points() - el.unique = false el.column = "player.points" el.direction = sortDirectionDESC case domain.PlayerSortDeletedAtASC: el.value = cursor.DeletedAt() - el.unique = false el.column = "COALESCE(player.deleted_at, '0001-01-01 00:00:00+00:00')" el.direction = sortDirectionASC case domain.PlayerSortDeletedAtDESC: el.value = cursor.DeletedAt() - el.unique = false el.column = "COALESCE(player.deleted_at, '0001-01-01 00:00:00+00:00')" el.direction = sortDirectionDESC default: diff --git a/internal/adapter/repository_bun_tribe.go b/internal/adapter/repository_bun_tribe.go index a6a4e4a..f1e699a 100644 --- a/internal/adapter/repository_bun_tribe.go +++ b/internal/adapter/repository_bun_tribe.go @@ -248,72 +248,58 @@ func (a listTribesParamsApplier) applyCursor(q *bun.SelectQuery) *bun.SelectQuer el.direction = sortDirectionDESC case domain.TribeSortServerKeyASC: el.value = cursor.ServerKey() - el.unique = false el.column = "tribe.server_key" el.direction = sortDirectionASC case domain.TribeSortServerKeyDESC: el.value = cursor.ServerKey() - el.unique = false el.column = "tribe.server_key" el.direction = sortDirectionDESC case domain.TribeSortODScoreAttASC: el.value = cursor.ODScoreAtt() - el.unique = false el.column = "tribe.score_att" el.direction = sortDirectionASC case domain.TribeSortODScoreAttDESC: el.value = cursor.ODScoreAtt() - el.unique = false el.column = "tribe.score_att" el.direction = sortDirectionDESC case domain.TribeSortODScoreDefASC: el.value = cursor.ODScoreDef() - el.unique = false el.column = "tribe.score_def" el.direction = sortDirectionASC case domain.TribeSortODScoreDefDESC: el.value = cursor.ODScoreDef() - el.unique = false el.column = "tribe.score_def" el.direction = sortDirectionDESC case domain.TribeSortODScoreTotalASC: el.value = cursor.ODScoreTotal() - el.unique = false el.column = "tribe.score_total" el.direction = sortDirectionASC case domain.TribeSortODScoreTotalDESC: el.value = cursor.ODScoreTotal() - el.unique = false el.column = "tribe.score_total" el.direction = sortDirectionDESC case domain.TribeSortPointsASC: el.value = cursor.Points() - el.unique = false el.column = "tribe.points" el.direction = sortDirectionASC case domain.TribeSortPointsDESC: el.value = cursor.Points() - el.unique = false el.column = "tribe.points" el.direction = sortDirectionDESC case domain.TribeSortDominanceASC: el.value = cursor.Dominance() - el.unique = false el.column = "tribe.dominance" el.direction = sortDirectionASC case domain.TribeSortDominanceDESC: el.value = cursor.Dominance() - el.unique = false el.column = "tribe.dominance" el.direction = sortDirectionDESC case domain.TribeSortDeletedAtASC: el.value = cursor.DeletedAt() - el.unique = false el.column = "COALESCE(tribe.deleted_at, '0001-01-01 00:00:00+00:00')" el.direction = sortDirectionASC case domain.TribeSortDeletedAtDESC: el.value = cursor.DeletedAt() - el.unique = false el.column = "COALESCE(tribe.deleted_at, '0001-01-01 00:00:00+00:00')" el.direction = sortDirectionDESC default: diff --git a/internal/adapter/repository_player_test.go b/internal/adapter/repository_player_test.go index d0842a7..23e2ae3 100644 --- a/internal/adapter/repository_player_test.go +++ b/internal/adapter/repository_player_test.go @@ -217,6 +217,35 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories require.NoError(t, err) }, }, + { + name: "OK: sort=[scoreSup DESC, serverKey ASC, id ASC]", + params: func(t *testing.T) domain.ListPlayersParams { + t.Helper() + params := domain.NewListPlayersParams() + require.NoError(t, params.SetSort([]domain.PlayerSort{ + domain.PlayerSortODScoreSupDESC, + domain.PlayerSortServerKeyASC, + domain.PlayerSortIDASC, + })) + return params + }, + assertResult: func(t *testing.T, _ domain.ListPlayersParams, res domain.ListPlayersResult) { + t.Helper() + players := res.Players() + assert.NotEmpty(t, len(players)) + assert.True(t, slices.IsSortedFunc(players, func(a, b domain.Player) int { + return cmp.Or( + cmp.Compare(a.OD().ScoreSup(), b.OD().ScoreSup())*-1, + cmp.Compare(a.ServerKey(), b.ServerKey()), + cmp.Compare(a.ID(), b.ID()), + ) + })) + }, + assertError: func(t *testing.T, err error) { + t.Helper() + require.NoError(t, err) + }, + }, { name: "OK: sort=[scoreTotal DESC, serverKey ASC, id ASC]", params: func(t *testing.T) domain.ListPlayersParams { diff --git a/internal/domain/domaintest/player.go b/internal/domain/domaintest/player.go index 9a5eba6..f9414ff 100644 --- a/internal/domain/domaintest/player.go +++ b/internal/domain/domaintest/player.go @@ -14,6 +14,7 @@ type PlayerCursorConfig struct { ServerKey string ODScoreAtt int ODScoreDef int + ODScoreSup int ODScoreTotal int Points int DeletedAt time.Time @@ -27,6 +28,7 @@ func NewPlayerCursor(tb TestingTB, opts ...func(cfg *PlayerCursorConfig)) domain ServerKey: RandServerKey(), ODScoreAtt: gofakeit.IntRange(0, math.MaxInt), ODScoreDef: gofakeit.IntRange(0, math.MaxInt), + ODScoreSup: gofakeit.IntRange(0, math.MaxInt), ODScoreTotal: gofakeit.IntRange(0, math.MaxInt), Points: gofakeit.IntRange(0, math.MaxInt), DeletedAt: time.Time{}, @@ -41,6 +43,7 @@ func NewPlayerCursor(tb TestingTB, opts ...func(cfg *PlayerCursorConfig)) domain cfg.ServerKey, cfg.ODScoreAtt, cfg.ODScoreDef, + cfg.ODScoreSup, cfg.ODScoreTotal, cfg.Points, cfg.DeletedAt, diff --git a/internal/domain/player.go b/internal/domain/player.go index 24eea00..4957134 100644 --- a/internal/domain/player.go +++ b/internal/domain/player.go @@ -402,6 +402,8 @@ const ( PlayerSortODScoreAttDESC PlayerSortODScoreDefASC PlayerSortODScoreDefDESC + PlayerSortODScoreSupASC + PlayerSortODScoreSupDESC PlayerSortODScoreTotalASC PlayerSortODScoreTotalDESC PlayerSortPointsASC @@ -437,6 +439,10 @@ func (s PlayerSort) String() string { return "odScoreDef:ASC" case PlayerSortODScoreDefDESC: return "odScoreDef:DESC" + case PlayerSortODScoreSupASC: + return "odScoreSup:ASC" + case PlayerSortODScoreSupDESC: + return "odScoreSup:DESC" case PlayerSortODScoreTotalASC: return "odScoreTotal:ASC" case PlayerSortODScoreTotalDESC: @@ -460,6 +466,8 @@ func newPlayerSortFromString(s string) (PlayerSort, error) { PlayerSortODScoreAttDESC, PlayerSortODScoreDefASC, PlayerSortODScoreDefDESC, + PlayerSortODScoreSupASC, + PlayerSortODScoreSupDESC, PlayerSortODScoreTotalASC, PlayerSortODScoreTotalDESC, PlayerSortPointsASC, @@ -484,6 +492,7 @@ type PlayerCursor struct { serverKey string odScoreAtt int odScoreDef int + odScoreSup int odScoreTotal int points int deletedAt time.Time @@ -496,6 +505,7 @@ func NewPlayerCursor( serverKey string, odScoreAtt int, odScoreDef int, + odScoreSup int, odScoreTotal int, points int, deletedAt time.Time, @@ -532,6 +542,14 @@ func NewPlayerCursor( } } + if err := validateIntInRange(odScoreSup, 0, math.MaxInt); err != nil { + return PlayerCursor{}, ValidationError{ + Model: playerCursorModelName, + Field: "odScoreSup", + Err: err, + } + } + if err := validateIntInRange(odScoreTotal, 0, math.MaxInt); err != nil { return PlayerCursor{}, ValidationError{ Model: playerCursorModelName, @@ -553,6 +571,7 @@ func NewPlayerCursor( serverKey: serverKey, odScoreAtt: odScoreAtt, odScoreDef: odScoreDef, + odScoreSup: odScoreSup, odScoreTotal: odScoreTotal, points: points, deletedAt: deletedAt, @@ -586,6 +605,11 @@ func decodePlayerCursor(encoded string) (PlayerCursor, error) { return PlayerCursor{}, ErrInvalidCursor } + odScoreSup, err := m.int("odScoreSup") + if err != nil { + return PlayerCursor{}, ErrInvalidCursor + } + odScoreTotal, err := m.int("odScoreTotal") if err != nil { return PlayerCursor{}, ErrInvalidCursor @@ -606,6 +630,7 @@ func decodePlayerCursor(encoded string) (PlayerCursor, error) { serverKey, odScoreAtt, odScoreDef, + odScoreSup, odScoreTotal, points, deletedAt, @@ -633,6 +658,10 @@ func (pc PlayerCursor) ODScoreDef() int { return pc.odScoreDef } +func (pc PlayerCursor) ODScoreSup() int { + return pc.odScoreSup +} + func (pc PlayerCursor) ODScoreTotal() int { return pc.odScoreTotal } @@ -659,6 +688,7 @@ func (pc PlayerCursor) Encode() string { {"serverKey", pc.serverKey}, {"odScoreAtt", pc.odScoreAtt}, {"odScoreDef", pc.odScoreDef}, + {"odScoreSup", pc.odScoreSup}, {"odScoreTotal", pc.odScoreTotal}, {"points", pc.points}, {"deletedAt", pc.deletedAt}, @@ -888,6 +918,7 @@ func NewListPlayersResult(players Players, next Player) (ListPlayersResult, erro players[0].ServerKey(), od.ScoreAtt(), od.ScoreDef(), + od.ScoreSup(), od.ScoreTotal(), players[0].Points(), players[0].DeletedAt(), @@ -908,6 +939,7 @@ func NewListPlayersResult(players Players, next Player) (ListPlayersResult, erro next.ServerKey(), od.ScoreAtt(), od.ScoreDef(), + od.ScoreSup(), od.ScoreTotal(), next.Points(), next.DeletedAt(), @@ -961,6 +993,7 @@ func NewListPlayersWithRelationsResult( player.ServerKey(), od.ScoreAtt(), od.ScoreDef(), + od.ScoreSup(), od.ScoreTotal(), player.Points(), player.DeletedAt(), @@ -983,6 +1016,7 @@ func NewListPlayersWithRelationsResult( player.ServerKey(), od.ScoreAtt(), od.ScoreDef(), + od.ScoreSup(), od.ScoreTotal(), player.Points(), player.DeletedAt(), diff --git a/internal/domain/player_test.go b/internal/domain/player_test.go index 841a920..ebe073a 100644 --- a/internal/domain/player_test.go +++ b/internal/domain/player_test.go @@ -331,6 +331,7 @@ func TestNewPlayerCursor(t *testing.T) { serverKey string odScoreAtt int odScoreDef int + odScoreSup int odScoreTotal int points int deletedAt time.Time @@ -350,6 +351,7 @@ func TestNewPlayerCursor(t *testing.T) { serverKey: validPlayerCursor.ServerKey(), odScoreAtt: validPlayerCursor.ODScoreAtt(), odScoreDef: validPlayerCursor.ODScoreDef(), + odScoreSup: validPlayerCursor.ODScoreSup(), odScoreTotal: validPlayerCursor.ODScoreTotal(), points: validPlayerCursor.Points(), deletedAt: validPlayerCursor.DeletedAt(), @@ -363,6 +365,7 @@ func TestNewPlayerCursor(t *testing.T) { serverKey: validPlayerCursor.ServerKey(), odScoreAtt: validPlayerCursor.ODScoreAtt(), odScoreDef: validPlayerCursor.ODScoreDef(), + odScoreSup: validPlayerCursor.ODScoreSup(), odScoreTotal: validPlayerCursor.ODScoreTotal(), points: validPlayerCursor.Points(), deletedAt: validPlayerCursor.DeletedAt(), @@ -383,6 +386,7 @@ func TestNewPlayerCursor(t *testing.T) { serverKey: validPlayerCursor.ServerKey(), odScoreAtt: -1, odScoreDef: validPlayerCursor.ODScoreDef(), + odScoreSup: validPlayerCursor.ODScoreSup(), odScoreTotal: validPlayerCursor.ODScoreTotal(), points: validPlayerCursor.Points(), deletedAt: validPlayerCursor.DeletedAt(), @@ -403,6 +407,7 @@ func TestNewPlayerCursor(t *testing.T) { serverKey: validPlayerCursor.ServerKey(), odScoreAtt: validPlayerCursor.ODScoreAtt(), odScoreDef: -1, + odScoreSup: validPlayerCursor.ODScoreSup(), odScoreTotal: validPlayerCursor.ODScoreTotal(), points: validPlayerCursor.Points(), deletedAt: validPlayerCursor.DeletedAt(), @@ -416,6 +421,27 @@ func TestNewPlayerCursor(t *testing.T) { }, }, }, + { + name: "ERR: odScoreSup < 0", + args: args{ + id: validPlayerCursor.ID(), + serverKey: validPlayerCursor.ServerKey(), + odScoreAtt: validPlayerCursor.ODScoreAtt(), + odScoreDef: validPlayerCursor.ODScoreDef(), + odScoreSup: -1, + odScoreTotal: validPlayerCursor.ODScoreTotal(), + points: validPlayerCursor.Points(), + deletedAt: validPlayerCursor.DeletedAt(), + }, + expectedErr: domain.ValidationError{ + Model: "PlayerCursor", + Field: "odScoreSup", + Err: domain.MinGreaterEqualError{ + Min: 0, + Current: -1, + }, + }, + }, { name: "ERR: odScoreTotal < 0", args: args{ @@ -423,6 +449,7 @@ func TestNewPlayerCursor(t *testing.T) { serverKey: validPlayerCursor.ServerKey(), odScoreAtt: validPlayerCursor.ODScoreAtt(), odScoreDef: validPlayerCursor.ODScoreDef(), + odScoreSup: validPlayerCursor.ODScoreSup(), odScoreTotal: -1, points: validPlayerCursor.Points(), deletedAt: validPlayerCursor.DeletedAt(), @@ -443,6 +470,7 @@ func TestNewPlayerCursor(t *testing.T) { serverKey: validPlayerCursor.ServerKey(), odScoreAtt: validPlayerCursor.ODScoreAtt(), odScoreDef: validPlayerCursor.ODScoreDef(), + odScoreSup: validPlayerCursor.ODScoreSup(), odScoreTotal: validPlayerCursor.ODScoreTotal(), points: -1, deletedAt: validPlayerCursor.DeletedAt(), @@ -487,6 +515,7 @@ func TestNewPlayerCursor(t *testing.T) { tt.args.serverKey, tt.args.odScoreAtt, tt.args.odScoreDef, + tt.args.odScoreSup, tt.args.odScoreTotal, tt.args.points, tt.args.deletedAt, @@ -499,6 +528,7 @@ func TestNewPlayerCursor(t *testing.T) { assert.Equal(t, tt.args.serverKey, tc.ServerKey()) assert.Equal(t, tt.args.odScoreAtt, tc.ODScoreAtt()) assert.Equal(t, tt.args.odScoreDef, tc.ODScoreDef()) + assert.Equal(t, tt.args.odScoreSup, tc.ODScoreSup()) assert.Equal(t, tt.args.odScoreTotal, tc.ODScoreTotal()) assert.Equal(t, tt.args.points, tc.Points()) assert.Equal(t, tt.args.deletedAt, tc.DeletedAt()) @@ -879,27 +909,31 @@ func TestListPlayersParams_PrependSortString(t *testing.T) { }, }, { - name: "OK: [points:ASC, deletedAt:ASC]", + name: "OK: [odScoreSup:ASC, points:ASC, deletedAt:ASC]", args: args{ sort: []string{ + "odScoreSup:ASC", "points:ASC", "deletedAt:ASC", }, }, expectedSort: []domain.PlayerSort{ + domain.PlayerSortODScoreSupASC, domain.PlayerSortPointsASC, domain.PlayerSortDeletedAtASC, }, }, { - name: "OK: [points:DESC, deletedAt:DESC]", + name: "OK: [odScoreSup:DESC, points:DESC, deletedAt:DESC]", args: args{ sort: []string{ + "odScoreSup:DESC", "points:DESC", "deletedAt:DESC", }, }, expectedSort: []domain.PlayerSort{ + domain.PlayerSortODScoreSupDESC, domain.PlayerSortPointsDESC, domain.PlayerSortDeletedAtDESC, },