feat: api - GET /api/v2/versions/{versionCode}/servers/{serverKey}/tribes - add more sort options
This commit is contained in:
parent
e5d8ba5390
commit
bf3a7b11e5
|
@ -142,6 +142,7 @@ paths:
|
|||
- $ref: "#/components/parameters/CursorQueryParam"
|
||||
- $ref: "#/components/parameters/LimitQueryParam"
|
||||
- $ref: "#/components/parameters/TribeDeletedQueryParam"
|
||||
- $ref: "#/components/parameters/TribeSortQueryParam"
|
||||
responses:
|
||||
200:
|
||||
$ref: "#/components/responses/ListTribesResponse"
|
||||
|
@ -949,6 +950,28 @@ components:
|
|||
schema:
|
||||
type: boolean
|
||||
required: false
|
||||
TribeSortQueryParam:
|
||||
name: sort
|
||||
in: query
|
||||
description: Order matters!
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
enum:
|
||||
- odScoreAtt:ASC
|
||||
- odScoreAtt:DESC
|
||||
- odScoreDef:ASC
|
||||
- odScoreDef:DESC
|
||||
- odScoreTotal:ASC
|
||||
- odScoreTotal:DESC
|
||||
- points:ASC
|
||||
- points:DESC
|
||||
- dominance:ASC
|
||||
- dominance:DESC
|
||||
- deletedAt:ASC
|
||||
- deletedAt:DESC
|
||||
maxItems: 2
|
||||
VersionCodePathParam:
|
||||
in: path
|
||||
name: versionCode
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"math"
|
||||
"net/url"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -380,6 +381,40 @@ const (
|
|||
TribeSortDeletedAtDESC
|
||||
)
|
||||
|
||||
//nolint:gocyclo
|
||||
func newTribeSortFromString(s string) (TribeSort, error) {
|
||||
switch strings.ToLower(s) {
|
||||
case "odscoreatt:asc":
|
||||
return TribeSortODScoreAttASC, nil
|
||||
case "odscoreatt:desc":
|
||||
return TribeSortODScoreAttDESC, nil
|
||||
case "odscoredef:asc":
|
||||
return TribeSortODScoreDefASC, nil
|
||||
case "odscoredef:desc":
|
||||
return TribeSortODScoreDefDESC, nil
|
||||
case "odscoretotal:asc":
|
||||
return TribeSortODScoreTotalASC, nil
|
||||
case "odscoretotal:desc":
|
||||
return TribeSortODScoreTotalDESC, nil
|
||||
case "points:asc":
|
||||
return TribeSortPointsASC, nil
|
||||
case "points:desc":
|
||||
return TribeSortPointsDESC, nil
|
||||
case "dominance:asc":
|
||||
return TribeSortDominanceASC, nil
|
||||
case "dominance:desc":
|
||||
return TribeSortDominanceDESC, nil
|
||||
case "deletedat:asc":
|
||||
return TribeSortDeletedAtASC, nil
|
||||
case "deletedat:desc":
|
||||
return TribeSortDeletedAtDESC, nil
|
||||
default:
|
||||
return 0, UnsupportedSortStringError{
|
||||
Sort: s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type TribeCursor struct {
|
||||
id int
|
||||
serverKey string
|
||||
|
@ -666,6 +701,31 @@ func (params *ListTribesParams) SetSort(sort []TribeSort) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (params *ListTribesParams) PrependSortString(sort []string) error {
|
||||
if err := validateSliceLen(sort, tribeSortMinLength, max(tribeSortMaxLength-len(params.sort), 0)); err != nil {
|
||||
return ValidationError{
|
||||
Model: listTribesParamsModelName,
|
||||
Field: "sort",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
for i := len(sort) - 1; i >= 0; i-- {
|
||||
converted, err := newTribeSortFromString(sort[i])
|
||||
if err != nil {
|
||||
return SliceElementValidationError{
|
||||
Model: listTribesParamsModelName,
|
||||
Field: "sort",
|
||||
Index: i,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
params.sort = append([]TribeSort{converted}, params.sort...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (params *ListTribesParams) Cursor() TribeCursor {
|
||||
return params.cursor
|
||||
}
|
||||
|
|
|
@ -510,6 +510,185 @@ func TestListTribesParams_SetSort(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestListTribesParams_PrependSortString(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
defaultNewParams := func(t *testing.T) domain.ListTribesParams {
|
||||
t.Helper()
|
||||
return domain.ListTribesParams{}
|
||||
}
|
||||
|
||||
type args struct {
|
||||
sort []string
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
newParams func(t *testing.T) domain.ListTribesParams
|
||||
args args
|
||||
expectedSort []domain.TribeSort
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "OK: [odScoreAtt:ASC, odScoreDef:ASC, odScoreTotal:ASC]",
|
||||
args: args{
|
||||
sort: []string{
|
||||
"odScoreAtt:ASC",
|
||||
"odScoreDef:ASC",
|
||||
"odScoreTotal:ASC",
|
||||
},
|
||||
},
|
||||
expectedSort: []domain.TribeSort{
|
||||
domain.TribeSortODScoreAttASC,
|
||||
domain.TribeSortODScoreDefASC,
|
||||
domain.TribeSortODScoreTotalASC,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "OK: [odScoreAtt:DESC, odScoreDef:DESC, odScoreTotal:DESC]",
|
||||
args: args{
|
||||
sort: []string{
|
||||
"odScoreAtt:DESC",
|
||||
"odScoreDef:DESC",
|
||||
"odScoreTotal:DESC",
|
||||
},
|
||||
},
|
||||
expectedSort: []domain.TribeSort{
|
||||
domain.TribeSortODScoreAttDESC,
|
||||
domain.TribeSortODScoreDefDESC,
|
||||
domain.TribeSortODScoreTotalDESC,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "OK: [points:ASC, dominance:ASC, deletedAt:ASC]",
|
||||
args: args{
|
||||
sort: []string{
|
||||
"points:ASC",
|
||||
"dominance:ASC",
|
||||
"deletedAt:ASC",
|
||||
},
|
||||
},
|
||||
expectedSort: []domain.TribeSort{
|
||||
domain.TribeSortPointsASC,
|
||||
domain.TribeSortDominanceASC,
|
||||
domain.TribeSortDeletedAtASC,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "OK: [points:DESC, dominance:DESC, deletedAt:DESC]",
|
||||
args: args{
|
||||
sort: []string{
|
||||
"points:DESC",
|
||||
"dominance:DESC",
|
||||
"deletedAt:DESC",
|
||||
},
|
||||
},
|
||||
expectedSort: []domain.TribeSort{
|
||||
domain.TribeSortPointsDESC,
|
||||
domain.TribeSortDominanceDESC,
|
||||
domain.TribeSortDeletedAtDESC,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ERR: len(sort) < 1",
|
||||
args: args{
|
||||
sort: nil,
|
||||
},
|
||||
expectedErr: domain.ValidationError{
|
||||
Model: "ListTribesParams",
|
||||
Field: "sort",
|
||||
Err: domain.LenOutOfRangeError{
|
||||
Min: 1,
|
||||
Max: 3,
|
||||
Current: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ERR: len(sort) > 3",
|
||||
args: args{
|
||||
sort: []string{
|
||||
"odScoreAtt:ASC",
|
||||
"odScoreDef:ASC",
|
||||
"odScoreTotal:ASC",
|
||||
"points:ASC",
|
||||
},
|
||||
},
|
||||
expectedErr: domain.ValidationError{
|
||||
Model: "ListTribesParams",
|
||||
Field: "sort",
|
||||
Err: domain.LenOutOfRangeError{
|
||||
Min: 1,
|
||||
Max: 3,
|
||||
Current: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ERR: custom params + len(sort) > 2",
|
||||
newParams: func(t *testing.T) domain.ListTribesParams {
|
||||
t.Helper()
|
||||
params := domain.NewListTribesParams()
|
||||
require.NoError(t, params.SetSort([]domain.TribeSort{domain.TribeSortIDASC}))
|
||||
return params
|
||||
},
|
||||
args: args{
|
||||
sort: []string{
|
||||
"odScoreAtt:ASC",
|
||||
"odScoreDef:ASC",
|
||||
"odScoreTotal:ASC",
|
||||
},
|
||||
},
|
||||
expectedErr: domain.ValidationError{
|
||||
Model: "ListTribesParams",
|
||||
Field: "sort",
|
||||
Err: domain.LenOutOfRangeError{
|
||||
Min: 1,
|
||||
Max: 2,
|
||||
Current: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ERR: unsupported sort string",
|
||||
newParams: defaultNewParams,
|
||||
args: args{
|
||||
sort: []string{
|
||||
"odScoreAtt:ASC",
|
||||
"odScoreDef:ASC",
|
||||
"odScoreTotal:",
|
||||
},
|
||||
},
|
||||
expectedErr: domain.SliceElementValidationError{
|
||||
Model: "ListTribesParams",
|
||||
Field: "sort",
|
||||
Index: 2,
|
||||
Err: domain.UnsupportedSortStringError{
|
||||
Sort: "odScoreTotal:",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
newParams := defaultNewParams
|
||||
if tt.newParams != nil {
|
||||
newParams = tt.newParams
|
||||
}
|
||||
params := newParams(t)
|
||||
|
||||
require.ErrorIs(t, params.PrependSortString(tt.args.sort), tt.expectedErr)
|
||||
if tt.expectedErr != nil {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, tt.expectedSort, params.Sort())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListTribesParams_SetEncodedCursor(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -229,6 +229,30 @@ func (e InvalidURLError) Params() map[string]any {
|
|||
}
|
||||
}
|
||||
|
||||
type UnsupportedSortStringError struct {
|
||||
Sort string
|
||||
}
|
||||
|
||||
var _ ErrorWithParams = UnsupportedSortStringError{}
|
||||
|
||||
func (e UnsupportedSortStringError) Error() string {
|
||||
return fmt.Sprintf("sort %s is unsupported", e.Sort)
|
||||
}
|
||||
|
||||
func (e UnsupportedSortStringError) Type() ErrorType {
|
||||
return ErrorTypeIncorrectInput
|
||||
}
|
||||
|
||||
func (e UnsupportedSortStringError) Code() string {
|
||||
return "unsupported-sort-string"
|
||||
}
|
||||
|
||||
func (e UnsupportedSortStringError) Params() map[string]any {
|
||||
return map[string]any{
|
||||
"Sort": e.Sort,
|
||||
}
|
||||
}
|
||||
|
||||
var ErrRequired error = simpleError{
|
||||
msg: "can't be blank",
|
||||
typ: ErrorTypeIncorrectInput,
|
||||
|
|
|
@ -2,11 +2,13 @@ package port
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||
"gitea.dwysokinski.me/twhelp/corev3/internal/port/internal/apimodel"
|
||||
)
|
||||
|
||||
//nolint:gocyclo
|
||||
func (h *apiHTTPHandler) ListTribes(
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
|
@ -21,6 +23,13 @@ func (h *apiHTTPHandler) ListTribes(
|
|||
return
|
||||
}
|
||||
|
||||
if params.Sort != nil {
|
||||
if err := domainParams.PrependSortString(*params.Sort); err != nil {
|
||||
apiErrorRenderer{errors: []error{err}, formatErrorPath: formatListTribesParamsErrorPath}.render(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := domainParams.SetServerKeys([]string{serverKey}); err != nil {
|
||||
apiErrorRenderer{errors: []error{err}, formatErrorPath: formatListTribesParamsErrorPath}.render(w, r)
|
||||
return
|
||||
|
@ -72,7 +81,11 @@ func formatListTribesParamsErrorPath(segments []errorPathSegment) []string {
|
|||
case "deleted":
|
||||
return []string{"$query", "deleted"}
|
||||
case "sort":
|
||||
return []string{"$query", "sort"}
|
||||
path := []string{"$query", "sort"}
|
||||
if segments[0].index >= 0 {
|
||||
path = append(path, strconv.Itoa(segments[0].index))
|
||||
}
|
||||
return path
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue