feat: list players - new query param - name
All checks were successful
continuous-integration/drone/pr Build is passing

This commit is contained in:
Dawid Wysokiński 2023-05-19 07:05:56 +02:00
parent 0ba8a57220
commit fe207c934c
Signed by: Kichiyaki
GPG Key ID: B5445E357FB8B892
17 changed files with 157 additions and 57 deletions

View File

@ -158,6 +158,10 @@ func (l listPlayersParamsApplier) applyFilters(q *bun.SelectQuery) *bun.SelectQu
q = q.Where("player.id > ?", l.params.IDGT.Int64)
}
if len(l.params.Names) > 0 {
q = q.Where("player.name IN (?)", bun.In(l.params.Names))
}
if len(l.params.ServerKeys) > 0 {
q = q.Where("player.server_key IN (?)", bun.In(l.params.ServerKeys))
}

View File

@ -625,6 +625,25 @@ func TestPlayer_List_ListCountWithRelations(t *testing.T) {
expectedPlayers: playersExceptPL169,
expectedCount: int64(len(playersExceptPL169)),
},
{
name: "Names=[Zero-Two,makobex],Sort=[{By=ID,Direction=ASC}],ServerKeys=[en113]",
params: domain.ListPlayersParams{
Names: []string{"Zero-Two", "makobex"},
ServerKeys: []string{"en113"},
Sort: []domain.PlayerSort{{By: domain.PlayerSortByID, Direction: domain.SortDirectionASC}},
},
expectedPlayers: []expectedPlayer{
{
id: 8625053,
serverKey: "en113",
},
{
id: 8625053,
serverKey: "en113",
},
},
expectedCount: 2,
},
{
name: "ERR: unsupported sort by",
params: domain.ListPlayersParams{

View File

@ -248,7 +248,7 @@ func TestVillage_List_ListCountWithRelations(t *testing.T) {
expectedCount: 3,
},
{
name: "Coords=[{X: 533, Y: 548}, {X: 633, Y: 548}],ServerKeys=[it70]",
name: "Coords=[{X=533,Y=548},{X=633,Y=548}],ServerKeys=[it70]",
params: domain.ListVillagesParams{
Coords: []domain.Coords{
{X: 533, Y: 548},

View File

@ -136,6 +136,7 @@ type ListPlayersParams struct {
ServerKeys []string
ServerKeysNIN []string
VersionCodes []string
Names []string
Deleted NullBool
TribeID NullInt64
Pagination Pagination

View File

@ -60,7 +60,6 @@ func TestEnnoblement_list(t *testing.T) {
expectedParams := domain.ListEnnoblementsParams{
ServerKeys: []string{server.Key},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
@ -277,7 +276,6 @@ func TestEnnoblement_list(t *testing.T) {
Offset: 100,
},
}
if diff := cmp.Diff(params, expectedParams, cmpopts.IgnoreUnexported(time.Time{})); diff != "" {
return nil, 0, errors.New("invalid params: " + diff)
}
@ -652,7 +650,6 @@ func TestEnnoblement_listPlayer(t *testing.T) {
Valid: true,
},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
@ -880,7 +877,6 @@ func TestEnnoblement_listPlayer(t *testing.T) {
Offset: 100,
},
}
if diff := cmp.Diff(params, expectedParams, cmpopts.IgnoreUnexported(time.Time{})); diff != "" {
return nil, 0, errors.New("invalid params: " + diff)
}
@ -1372,7 +1368,6 @@ func TestEnnoblement_listVillage(t *testing.T) {
Valid: true,
},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
@ -1600,7 +1595,6 @@ func TestEnnoblement_listVillage(t *testing.T) {
Offset: 100,
},
}
if diff := cmp.Diff(params, expectedParams, cmpopts.IgnoreUnexported(time.Time{})); diff != "" {
return nil, 0, errors.New("invalid params: " + diff)
}
@ -2088,7 +2082,6 @@ func TestEnnoblement_listTribe(t *testing.T) {
Valid: true,
},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
@ -2316,7 +2309,6 @@ func TestEnnoblement_listTribe(t *testing.T) {
Offset: 100,
},
}
if diff := cmp.Diff(params, expectedParams, cmpopts.IgnoreUnexported(time.Time{})); diff != "" {
return nil, 0, errors.New("invalid params: " + diff)
}

View File

@ -35,8 +35,9 @@ type player struct {
// @Param versionCode path string true "Version code"
// @Param serverKey path string true "Server key"
// @Param deleted query boolean false "true=only deleted players, false=only existing players, by default both existing and deleted players are returned"
// @Param offset query int false "specifies where to start a page" minimum(0) default(0)
// @Param limit query int false "page size" minimum(1) maximum(200) default(200)
// @Param offset query int false "specifies where to start a page" minimum(0) default(0)
// @Param limit query int false "page size" minimum(1) maximum(200) default(200)
// @Param name query []string false "player names, max 20"
// @Param sort query []string false "format: field:direction, default: [id:asc]" Enums(id:asc,id:desc,scoreAtt:asc,scoreAtt:desc,scoreDef:asc,scoreDef:desc,scoreSup:asc,scoreSup:desc,scoreTotal:asc,scoreTotal:desc,points:asc,points:desc,deletedAt:asc,deletedAt:desc)
// @Router /versions/{versionCode}/servers/{serverKey}/players [get]
func (p *player) list(w http.ResponseWriter, r *http.Request) {
@ -51,6 +52,8 @@ func (p *player) list(w http.ResponseWriter, r *http.Request) {
newSort: domain.NewPlayerSort,
}
params.Names = query.strings("name")
params.Deleted, err = query.nullBool("deleted")
if err != nil {
renderErr(w, err)

View File

@ -77,7 +77,6 @@ func TestPlayerSnapshot_list(t *testing.T) {
ServerKeys: []string{server.Key},
PlayerIDs: []int64{player.ID},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
@ -223,7 +222,6 @@ func TestPlayerSnapshot_list(t *testing.T) {
{By: domain.PlayerSnapshotSortByID, Direction: domain.SortDirectionDESC},
},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}

View File

@ -57,7 +57,6 @@ func TestPlayer_list(t *testing.T) {
expectedParams := domain.ListPlayersParams{
ServerKeys: []string{server.Key},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
@ -229,7 +228,6 @@ func TestPlayer_list(t *testing.T) {
{By: domain.PlayerSortByDeletedAt, Direction: domain.SortDirectionDESC},
},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
@ -328,14 +326,9 @@ func TestPlayer_list(t *testing.T) {
params domain.ListPlayersParams,
) ([]domain.PlayerWithRelations, int64, error) {
expectedParams := domain.ListPlayersParams{
Deleted: domain.NullBool{
Valid: false,
Bool: false,
},
ServerKeys: []string{server.Key},
Pagination: domain.Pagination{
Limit: 1,
Offset: 0,
Limit: 1,
},
Sort: []domain.PlayerSort{
{By: domain.PlayerSortByID, Direction: domain.SortDirectionASC},
@ -343,7 +336,6 @@ func TestPlayer_list(t *testing.T) {
{By: domain.PlayerSortByScoreTotal, Direction: domain.SortDirectionASC},
},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
@ -430,6 +422,103 @@ func TestPlayer_list(t *testing.T) {
},
expectedTotalCount: "1523",
},
{
name: "OK: name=[name 998]",
setup: func(versionSvc *mock.FakeVersionService, serverSvc *mock.FakeServerService, playerSvc *mock.FakePlayerService) {
versionSvc.GetByCodeReturns(version, nil)
serverSvc.GetNormalByVersionCodeAndKeyReturns(server, nil)
playerSvc.ListCountWithRelationsCalls(func(
_ context.Context,
params domain.ListPlayersParams,
) ([]domain.PlayerWithRelations, int64, error) {
expectedParams := domain.ListPlayersParams{
ServerKeys: []string{server.Key},
Names: []string{"name 998"},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
return []domain.PlayerWithRelations{
{
Player: domain.Player{
OpponentsDefeated: domain.OpponentsDefeated{
RankAtt: 1,
ScoreAtt: 2,
RankDef: 3,
ScoreDef: 4,
RankSup: 5,
ScoreSup: 6,
RankTotal: 7,
ScoreTotal: 8,
},
ID: 998,
Name: "name 998",
NumVillages: 2,
Points: 3,
Rank: 4,
TribeID: 0,
ProfileURL: "profile-998",
BestRank: 1117,
BestRankAt: now,
MostPoints: 1115,
MostPointsAt: now.Add(-5 * time.Second),
MostVillages: 1114,
MostVillagesAt: now.Add(time.Hour),
LastActivityAt: now.Add(time.Minute),
ServerKey: server.Key,
CreatedAt: now,
DeletedAt: now,
},
Tribe: domain.NullTribeMeta{},
},
}, 1, nil
})
},
versionCode: version.Code,
serverKey: server.Key,
queryParams: url.Values{
"name": []string{"name 998"},
},
expectedStatus: http.StatusOK,
target: &model.ListPlayersResp{},
expectedResponse: &model.ListPlayersResp{
Data: []model.Player{
{
RankAtt: 1,
ScoreAtt: 2,
RankDef: 3,
ScoreDef: 4,
RankSup: 5,
ScoreSup: 6,
RankTotal: 7,
ScoreTotal: 8,
ID: 998,
Name: "name 998",
NumVillages: 2,
Points: 3,
Rank: 4,
ProfileURL: "profile-998",
BestRank: 1117,
BestRankAt: now,
MostPoints: 1115,
MostPointsAt: now.Add(-5 * time.Second),
MostVillages: 1114,
MostVillagesAt: now.Add(time.Hour),
LastActivityAt: now.Add(time.Minute),
Tribe: model.NullTribeMeta{
Valid: false,
},
CreatedAt: now,
DeletedAt: model.NullTime{
Valid: true,
Time: now,
},
},
},
},
expectedTotalCount: "1",
},
{
name: "ERR: limit is not a valid int32",
setup: func(versionSvc *mock.FakeVersionService, serverSvc *mock.FakeServerService, playerSvc *mock.FakePlayerService) {
@ -923,7 +1012,6 @@ func TestPlayer_listOtherServers(t *testing.T) {
IDs: []int64{playerID},
ServerKeysNIN: []string{serverPL151.Key},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
@ -1108,7 +1196,6 @@ func TestPlayer_listOtherServers(t *testing.T) {
Offset: 1,
},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
@ -1404,7 +1491,6 @@ func TestPlayer_listTribeMembers(t *testing.T) {
Int64: tribe.ID,
},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
@ -1600,7 +1686,6 @@ func TestPlayer_listTribeMembers(t *testing.T) {
{By: domain.PlayerSortByScoreTotal, Direction: domain.SortDirectionASC},
},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}

View File

@ -20,14 +20,6 @@ type queryParams[Sort any] struct {
newSort func(by string, direction string) (Sort, error)
}
func (q queryParams[_]) sliceStrings(key string) ([]string, error) {
v, ok := q.values[key]
if !ok {
return nil, nil
}
return v, nil
}
func (q queryParams[_]) nullBool(key string) (domain.NullBool, error) {
if !q.values.Has(key) {
return domain.NullBool{}, nil
@ -89,7 +81,7 @@ func (q queryParams[Sort]) sort() ([]Sort, error) {
return nil, errors.New("newSort is nil, can't proceed")
}
vals := q.values[sortKey]
vals := q.strings(sortKey)
if len(vals) == 0 {
return nil, nil
}
@ -140,7 +132,7 @@ func (q queryParams[_]) nullTime(key string) (domain.NullTime, error) {
}
func (q queryParams[_]) coords(key string) ([]domain.Coords, error) {
vals := q.values[key]
vals := q.strings(key)
if len(vals) == 0 {
return nil, nil
}
@ -160,3 +152,7 @@ func (q queryParams[_]) coords(key string) ([]domain.Coords, error) {
return coords, nil
}
func (q queryParams[_]) strings(key string) []string {
return q.values[key]
}

View File

@ -52,7 +52,6 @@ func TestServer_list(t *testing.T) {
},
VersionCodes: []string{version.Code},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
@ -190,7 +189,6 @@ func TestServer_list(t *testing.T) {
Offset: 25,
},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
@ -333,7 +331,6 @@ func TestServer_list(t *testing.T) {
Offset: 0,
},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}

View File

@ -52,11 +52,7 @@ func (t *tribe) list(w http.ResponseWriter, r *http.Request) {
newSort: domain.NewTribeSort,
}
params.Tags, err = query.sliceStrings("tag")
if err != nil {
renderErr(w, err)
return
}
params.Tags = query.strings("tag")
params.Deleted, err = query.nullBool("deleted")
if err != nil {

View File

@ -82,7 +82,6 @@ func TestTribeChange_listPlayer(t *testing.T) {
ServerKeys: []string{server.Key},
PlayerIDs: []int64{player.ID},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
@ -269,7 +268,6 @@ func TestTribeChange_listPlayer(t *testing.T) {
{By: domain.TribeChangeSortByID, Direction: domain.SortDirectionDESC},
},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
@ -690,7 +688,6 @@ func TestTribeChange_listTribe(t *testing.T) {
ServerKeys: []string{server.Key},
TribeIDs: []int64{tribe.ID},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
@ -874,7 +871,6 @@ func TestTribeChange_listTribe(t *testing.T) {
{By: domain.TribeChangeSortByID, Direction: domain.SortDirectionDESC},
},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}

View File

@ -76,7 +76,6 @@ func TestTribeSnapshot_list(t *testing.T) {
ServerKeys: []string{server.Key},
TribeIDs: []int64{tribe.ID},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
@ -203,7 +202,6 @@ func TestTribeSnapshot_list(t *testing.T) {
{By: domain.TribeSnapshotSortByID, Direction: domain.SortDirectionDESC},
},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}

View File

@ -57,7 +57,6 @@ func TestTribe_list(t *testing.T) {
},
ServerKeys: []string{server.Key},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
@ -220,7 +219,6 @@ func TestTribe_list(t *testing.T) {
},
},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
@ -375,7 +373,6 @@ func TestTribe_list(t *testing.T) {
Offset: 15,
},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
@ -465,7 +462,6 @@ func TestTribe_list(t *testing.T) {
ServerKeys: []string{server.Key},
Tags: []string{"tag997", "tag998"},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}

View File

@ -53,7 +53,6 @@ func TestVillage_list(t *testing.T) {
expectedParams := domain.ListVillagesParams{
ServerKeys: []string{server.Key},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
@ -184,7 +183,6 @@ func TestVillage_list(t *testing.T) {
Offset: 15,
},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
@ -253,7 +251,6 @@ func TestVillage_list(t *testing.T) {
ServerKeys: []string{server.Key},
Coords: []domain.Coords{{X: 22, Y: 23}},
}
if diff := cmp.Diff(params, expectedParams); diff != "" {
fmt.Println(diff)
return nil, 0, fmt.Errorf("validation failed: %s", diff)

View File

@ -13,6 +13,7 @@ import (
const (
playerChunkSize = 500
playerMaxLimit = 200
playerNameMaxLen = 20
playerSortMaxLen = 3
)
@ -350,6 +351,15 @@ func (l listPlayersParamsBuilder) build() (domain.ListPlayersParams, error) {
l.Pagination.Limit = playerMaxLimit
}
if len(l.Names) > playerNameMaxLen {
return domain.ListPlayersParams{}, domain.ValidationError{
Field: "name",
Err: domain.MaxLenError{
Max: playerNameMaxLen,
},
}
}
if len(l.Sort) > playerSortMaxLen {
return domain.ListPlayersParams{}, domain.ValidationError{
Field: "sort",

View File

@ -795,6 +795,18 @@ func TestPlayer_List_ListCountWithRelations(t *testing.T) {
},
},
},
{
name: "ERR: len(params.Names) > 20",
params: domain.ListPlayersParams{
Names: make([]string, 21),
},
expectedErr: domain.ValidationError{
Field: "name",
Err: domain.MaxLenError{
Max: 20,
},
},
},
}
for _, tt := range tests {