[WIP] - refactor limit, sort, offset

- rename middleware.MayExceedLimit to CanExceedLimit
- update the DailyTribeStats/DailyPlayerStats repository and usecase
- rename utils.SanitizeSort to utils.SanitizeSortExpression
- add two new utils - SanitizeSortExpressions and FindStringWithPrefix
This commit is contained in:
Dawid Wysokiński 2020-11-21 09:13:25 +01:00
parent c32e988e71
commit 01fdd80f7f
27 changed files with 302 additions and 129 deletions

View File

@ -10,6 +10,9 @@ type FetchConfig struct {
Server string
Filter *models.DailyPlayerStatsFilter
Count bool
Sort []string
Limit int
Offset int
}
type Repository interface {

View File

@ -8,6 +8,7 @@ import (
"github.com/go-pg/pg/v10"
"github.com/pkg/errors"
"github.com/tribalwarshelp/api/dailyplayerstats"
"github.com/tribalwarshelp/api/utils"
"github.com/tribalwarshelp/shared/models"
)
@ -23,39 +24,36 @@ func (repo *pgRepository) Fetch(ctx context.Context, cfg dailyplayerstats.FetchC
var err error
data := []*models.DailyPlayerStats{}
total := 0
query := repo.WithParam("SERVER", pg.Safe(cfg.Server)).Model(&data).Context(ctx)
query := repo.
WithParam("SERVER", pg.Safe(cfg.Server)).
Model(&data).
Context(ctx).
Order(cfg.Sort...).
Limit(cfg.Limit).
Offset(cfg.Offset)
playerRequired := utils.FindStringWithPrefix(cfg.Sort, "player") != ""
tribeRequired := utils.FindStringWithPrefix(cfg.Sort, "tribe") != ""
if cfg.Filter != nil {
query = query.
WhereStruct(cfg.Filter).
Limit(cfg.Filter.Limit).
Offset(cfg.Filter.Offset)
order := []string{}
if cfg.Filter.Sort != "" {
order = append(order, cfg.Filter.Sort)
}
WhereStruct(cfg.Filter)
if cfg.Filter.PlayerFilter != nil {
query = query.Relation("Player._").WhereStruct(cfg.Filter.PlayerFilter)
if cfg.Filter.PlayerFilter.Sort != "" {
order = append(order, fmt.Sprintf("player.%s", cfg.Filter.PlayerFilter.Sort))
}
playerRequired = true
query = query.WhereStruct(cfg.Filter.PlayerFilter)
if cfg.Filter.PlayerFilter.TribeFilter != nil {
tribeRequired = true
query = query.
Join("LEFT JOIN ?SERVER.tribes AS tribe ON tribe.id = player.tribe_id").
WhereStruct(cfg.Filter.PlayerFilter.TribeFilter)
if cfg.Filter.PlayerFilter.TribeFilter.Sort != "" {
order = append(order, fmt.Sprintf("tribe.%s", cfg.Filter.PlayerFilter.TribeFilter.Sort))
}
}
}
query = query.Order(order...)
}
if playerRequired {
query = query.Relation("Player._")
}
if tribeRequired {
query = query.Join("LEFT JOIN ?SERVER.tribes AS tribe ON tribe.id = player.tribe_id")
}
if cfg.Count {

View File

@ -7,5 +7,5 @@ import (
)
type Usecase interface {
Fetch(ctx context.Context, server string, filter *models.DailyPlayerStatsFilter) ([]*models.DailyPlayerStats, int, error)
Fetch(ctx context.Context, cfg FetchConfig) ([]*models.DailyPlayerStats, int, error)
}

View File

@ -17,23 +17,32 @@ func New(repo dailyplayerstats.Repository) dailyplayerstats.Usecase {
return &usecase{repo}
}
func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.DailyPlayerStatsFilter) ([]*models.DailyPlayerStats, int, error) {
if filter == nil {
filter = &models.DailyPlayerStatsFilter{}
func (ucase *usecase) Fetch(ctx context.Context, cfg dailyplayerstats.FetchConfig) ([]*models.DailyPlayerStats, int, error) {
if cfg.Filter == nil {
cfg.Filter = &models.DailyPlayerStatsFilter{}
}
if !middleware.MayExceedLimit(ctx) && (filter.Limit > dailyplayerstats.PaginationLimit || filter.Limit <= 0) {
filter.Limit = dailyplayerstats.PaginationLimit
if cfg.Filter.Limit > 0 {
cfg.Limit = cfg.Filter.Limit
}
filter.Sort = utils.SanitizeSort(filter.Sort)
if filter.PlayerFilter != nil {
filter.PlayerFilter.Sort = utils.SanitizeSort(filter.PlayerFilter.Sort)
if filter.PlayerFilter.TribeFilter != nil {
filter.PlayerFilter.TribeFilter.Sort = utils.SanitizeSort(filter.PlayerFilter.TribeFilter.Sort)
if cfg.Filter.Offset > 0 {
cfg.Offset = cfg.Filter.Offset
}
if cfg.Filter.Sort != "" {
cfg.Sort = append(cfg.Sort, cfg.Filter.Sort)
}
if cfg.Filter.PlayerFilter != nil {
if cfg.Filter.PlayerFilter.Sort != "" {
cfg.Sort = append(cfg.Sort, "player."+cfg.Filter.PlayerFilter.Sort)
}
if cfg.Filter.PlayerFilter.TribeFilter != nil && cfg.Filter.PlayerFilter.TribeFilter.Sort != "" {
cfg.Sort = append(cfg.Sort, "tribe."+cfg.Filter.PlayerFilter.TribeFilter.Sort)
}
}
return ucase.repo.Fetch(ctx, dailyplayerstats.FetchConfig{
Server: server,
Filter: filter,
Count: true,
})
if !middleware.CanExceedLimit(ctx) && (cfg.Limit > dailyplayerstats.PaginationLimit || cfg.Limit <= 0) {
cfg.Limit = dailyplayerstats.PaginationLimit
}
cfg.Sort = utils.SanitizeSortExpressions(cfg.Sort)
return ucase.repo.Fetch(ctx, cfg)
}

View File

@ -10,6 +10,9 @@ type FetchConfig struct {
Server string
Filter *models.DailyTribeStatsFilter
Count bool
Sort []string
Limit int
Offset int
}
type Repository interface {

View File

@ -8,6 +8,7 @@ import (
"github.com/go-pg/pg/v10"
"github.com/pkg/errors"
"github.com/tribalwarshelp/api/dailytribestats"
"github.com/tribalwarshelp/api/utils"
"github.com/tribalwarshelp/shared/models"
)
@ -23,29 +24,26 @@ func (repo *pgRepository) Fetch(ctx context.Context, cfg dailytribestats.FetchCo
var err error
data := []*models.DailyTribeStats{}
total := 0
query := repo.WithParam("SERVER", pg.Safe(cfg.Server)).Model(&data).Context(ctx)
query := repo.
WithParam("SERVER", pg.Safe(cfg.Server)).
Model(&data).
Context(ctx).
Order(cfg.Sort...).
Limit(cfg.Limit).
Offset(cfg.Offset)
tribeRequired := utils.FindStringWithPrefix(cfg.Sort, "tribe") != ""
if cfg.Filter != nil {
query = query.
WhereStruct(cfg.Filter).
Limit(cfg.Filter.Limit).
Offset(cfg.Filter.Offset)
order := []string{}
if cfg.Filter.Sort != "" {
order = append(order, cfg.Filter.Sort)
}
WhereStruct(cfg.Filter)
if cfg.Filter.TribeFilter != nil {
query = query.Relation("Tribe._").WhereStruct(cfg.Filter.TribeFilter)
if cfg.Filter.TribeFilter.Sort != "" {
order = append(order, fmt.Sprintf("tribe.%s", cfg.Filter.TribeFilter.Sort))
}
query = query.WhereStruct(cfg.Filter.TribeFilter)
tribeRequired = true
}
query = query.Order(order...)
}
if tribeRequired {
query = query.Relation("Tribe._")
}
if cfg.Count {

View File

@ -7,5 +7,5 @@ import (
)
type Usecase interface {
Fetch(ctx context.Context, server string, filter *models.DailyTribeStatsFilter) ([]*models.DailyTribeStats, int, error)
Fetch(ctx context.Context, cfg FetchConfig) ([]*models.DailyTribeStats, int, error)
}

View File

@ -17,20 +17,28 @@ func New(repo dailytribestats.Repository) dailytribestats.Usecase {
return &usecase{repo}
}
func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.DailyTribeStatsFilter) ([]*models.DailyTribeStats, int, error) {
if filter == nil {
filter = &models.DailyTribeStatsFilter{}
func (ucase *usecase) Fetch(ctx context.Context, cfg dailytribestats.FetchConfig) ([]*models.DailyTribeStats, int, error) {
if cfg.Filter == nil {
cfg.Filter = &models.DailyTribeStatsFilter{}
}
if !middleware.MayExceedLimit(ctx) && (filter.Limit > dailytribestats.PaginationLimit || filter.Limit <= 0) {
filter.Limit = dailytribestats.PaginationLimit
if cfg.Filter.Limit > 0 {
cfg.Limit = cfg.Filter.Limit
}
filter.Sort = utils.SanitizeSort(filter.Sort)
if filter.TribeFilter != nil {
filter.TribeFilter.Sort = utils.SanitizeSort(filter.TribeFilter.Sort)
if cfg.Filter.Offset > 0 {
cfg.Offset = cfg.Filter.Offset
}
return ucase.repo.Fetch(ctx, dailytribestats.FetchConfig{
Server: server,
Filter: filter,
Count: true,
})
if cfg.Filter.Sort != "" {
cfg.Sort = append(cfg.Sort, cfg.Filter.Sort)
}
if cfg.Filter.TribeFilter != nil {
if cfg.Filter.TribeFilter.Sort != "" {
cfg.Sort = append(cfg.Sort, "tribe."+cfg.Filter.TribeFilter.Sort)
}
}
if !middleware.CanExceedLimit(ctx) && (cfg.Limit > dailytribestats.PaginationLimit || cfg.Limit <= 0) {
cfg.Limit = dailytribestats.PaginationLimit
}
cfg.Sort = utils.SanitizeSortExpressions(cfg.Sort)
return ucase.repo.Fetch(ctx, cfg)
}

2
dev.sh
View File

@ -1,4 +1,4 @@
#!/bin/sh
export MODE=development
export GIN_MODE=debug
go run main.go
go run -race main.go

View File

@ -21,10 +21,10 @@ func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.E
if filter == nil {
filter = &models.EnnoblementFilter{}
}
if !middleware.MayExceedLimit(ctx) && (filter.Limit > ennoblement.PaginationLimit || filter.Limit <= 0) {
if !middleware.CanExceedLimit(ctx) && (filter.Limit > ennoblement.PaginationLimit || filter.Limit <= 0) {
filter.Limit = ennoblement.PaginationLimit
}
filter.Sort = utils.SanitizeSort(filter.Sort)
filter.Sort = utils.SanitizeSortExpression(filter.Sort)
return ucase.repo.Fetch(ctx, ennoblement.FetchConfig{
Server: server,
Filter: filter,

View File

@ -216,8 +216,8 @@ type ComplexityRoot struct {
}
Query struct {
DailyPlayerStats func(childComplexity int, server string, filter *models.DailyPlayerStatsFilter) int
DailyTribeStats func(childComplexity int, server string, filter *models.DailyTribeStatsFilter) int
DailyPlayerStats func(childComplexity int, server string, filter *models.DailyPlayerStatsFilter, limit *int, offset *int, sort []string) int
DailyTribeStats func(childComplexity int, server string, filter *models.DailyTribeStatsFilter, limit *int, offset *int, sort []string) int
Ennoblements func(childComplexity int, server string, filter *models.EnnoblementFilter) int
LangVersion func(childComplexity int, tag models.VersionCode) int
LangVersions func(childComplexity int, filter *models.VersionFilter) int
@ -579,8 +579,8 @@ type PlayerHistoryRecordResolver interface {
Tribe(ctx context.Context, obj *models.PlayerHistory) (*models.Tribe, error)
}
type QueryResolver interface {
DailyPlayerStats(ctx context.Context, server string, filter *models.DailyPlayerStatsFilter) (*DailyPlayerStats, error)
DailyTribeStats(ctx context.Context, server string, filter *models.DailyTribeStatsFilter) (*DailyTribeStats, error)
DailyPlayerStats(ctx context.Context, server string, filter *models.DailyPlayerStatsFilter, limit *int, offset *int, sort []string) (*DailyPlayerStats, error)
DailyTribeStats(ctx context.Context, server string, filter *models.DailyTribeStatsFilter, limit *int, offset *int, sort []string) (*DailyTribeStats, error)
Ennoblements(ctx context.Context, server string, filter *models.EnnoblementFilter) (*EnnoblementList, error)
LiveEnnoblements(ctx context.Context, server string) ([]*models.LiveEnnoblement, error)
Players(ctx context.Context, server string, filter *models.PlayerFilter) (*PlayerList, error)
@ -1477,7 +1477,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return 0, false
}
return e.complexity.Query.DailyPlayerStats(childComplexity, args["server"].(string), args["filter"].(*models.DailyPlayerStatsFilter)), true
return e.complexity.Query.DailyPlayerStats(childComplexity, args["server"].(string), args["filter"].(*models.DailyPlayerStatsFilter), args["limit"].(*int), args["offset"].(*int), args["sort"].([]string)), true
case "Query.dailyTribeStats":
if e.complexity.Query.DailyTribeStats == nil {
@ -1489,7 +1489,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return 0, false
}
return e.complexity.Query.DailyTribeStats(childComplexity, args["server"].(string), args["filter"].(*models.DailyTribeStatsFilter)), true
return e.complexity.Query.DailyTribeStats(childComplexity, args["server"].(string), args["filter"].(*models.DailyTribeStatsFilter), args["limit"].(*int), args["offset"].(*int), args["sort"].([]string)), true
case "Query.ennoblements":
if e.complexity.Query.Ennoblements == nil {
@ -3371,15 +3371,18 @@ input DailyPlayerStatsFilter {
createDateLT: Time
createDateLTE: Time
offset: Int
limit: Int
sort: String
offset: Int @deprecated(reason: "Use a new variable added to the query dailyPlayerStats - ` + "`" + `offset` + "`" + `.")
limit: Int @deprecated(reason: "Use a new variable added to the query dailyPlayerStats - ` + "`" + `limit` + "`" + `.")
sort: String @deprecated(reason: "Use a new variable added to the query dailyPlayerStats - ` + "`" + `sort` + "`" + `.")
}
extend type Query {
dailyPlayerStats(
server: String!
filter: DailyPlayerStatsFilter
limit: Int
offset: Int
sort: [String!]
): DailyPlayerStats!
}
`, BuiltIn: false},
@ -3416,15 +3419,18 @@ input DailyTribeStatsFilter {
createDateLT: Time
createDateLTE: Time
offset: Int
limit: Int
sort: String
offset: Int @deprecated(reason: "Use a new variable added to the query dailyTribeStats - ` + "`" + `offset` + "`" + `.")
limit: Int @deprecated(reason: "Use a new variable added to the query dailyTribeStats - ` + "`" + `limit` + "`" + `.")
sort: String @deprecated(reason: "Use a new variable added to the query dailyTribeStats - ` + "`" + `sort` + "`" + `.")
}
extend type Query {
dailyTribeStats(
server: String!
filter: DailyTribeStatsFilter
limit: Int
offset: Int
sort: [String!]
): DailyTribeStats!
}
`, BuiltIn: false},
@ -4337,6 +4343,33 @@ func (ec *executionContext) field_Query_dailyPlayerStats_args(ctx context.Contex
}
}
args["filter"] = arg1
var arg2 *int
if tmp, ok := rawArgs["limit"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("limit"))
arg2, err = ec.unmarshalOInt2ᚖint(ctx, tmp)
if err != nil {
return nil, err
}
}
args["limit"] = arg2
var arg3 *int
if tmp, ok := rawArgs["offset"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("offset"))
arg3, err = ec.unmarshalOInt2ᚖint(ctx, tmp)
if err != nil {
return nil, err
}
}
args["offset"] = arg3
var arg4 []string
if tmp, ok := rawArgs["sort"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("sort"))
arg4, err = ec.unmarshalOString2ᚕstringᚄ(ctx, tmp)
if err != nil {
return nil, err
}
}
args["sort"] = arg4
return args, nil
}
@ -4361,6 +4394,33 @@ func (ec *executionContext) field_Query_dailyTribeStats_args(ctx context.Context
}
}
args["filter"] = arg1
var arg2 *int
if tmp, ok := rawArgs["limit"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("limit"))
arg2, err = ec.unmarshalOInt2ᚖint(ctx, tmp)
if err != nil {
return nil, err
}
}
args["limit"] = arg2
var arg3 *int
if tmp, ok := rawArgs["offset"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("offset"))
arg3, err = ec.unmarshalOInt2ᚖint(ctx, tmp)
if err != nil {
return nil, err
}
}
args["offset"] = arg3
var arg4 []string
if tmp, ok := rawArgs["sort"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("sort"))
arg4, err = ec.unmarshalOString2ᚕstringᚄ(ctx, tmp)
if err != nil {
return nil, err
}
}
args["sort"] = arg4
return args, nil
}
@ -8904,7 +8964,7 @@ func (ec *executionContext) _Query_dailyPlayerStats(ctx context.Context, field g
fc.Args = args
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Query().DailyPlayerStats(rctx, args["server"].(string), args["filter"].(*models.DailyPlayerStatsFilter))
return ec.resolvers.Query().DailyPlayerStats(rctx, args["server"].(string), args["filter"].(*models.DailyPlayerStatsFilter), args["limit"].(*int), args["offset"].(*int), args["sort"].([]string))
})
if err != nil {
ec.Error(ctx, err)
@ -8946,7 +9006,7 @@ func (ec *executionContext) _Query_dailyTribeStats(ctx context.Context, field gr
fc.Args = args
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Query().DailyTribeStats(rctx, args["server"].(string), args["filter"].(*models.DailyTribeStatsFilter))
return ec.resolvers.Query().DailyTribeStats(rctx, args["server"].(string), args["filter"].(*models.DailyTribeStatsFilter), args["limit"].(*int), args["offset"].(*int), args["sort"].([]string))
})
if err != nil {
ec.Error(ctx, err)
@ -25522,6 +25582,21 @@ func (ec *executionContext) marshalOInt2ᚕintᚄ(ctx context.Context, sel ast.S
return ret
}
func (ec *executionContext) unmarshalOInt2ᚖint(ctx context.Context, v interface{}) (*int, error) {
if v == nil {
return nil, nil
}
res, err := graphql.UnmarshalInt(v)
return &res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) marshalOInt2ᚖint(ctx context.Context, sel ast.SelectionSet, v *int) graphql.Marshaler {
if v == nil {
return graphql.Null
}
return graphql.MarshalInt(*v)
}
func (ec *executionContext) marshalOLiveEnnoblement2ᚕᚖgithubᚗcomᚋtribalwarshelpᚋsharedᚋmodelsᚐLiveEnnoblementᚄ(ctx context.Context, sel ast.SelectionSet, v []*models.LiveEnnoblement) graphql.Marshaler {
if v == nil {
return graphql.Null

View File

@ -3,6 +3,7 @@ package resolvers
import (
"context"
"github.com/tribalwarshelp/api/dailyplayerstats"
"github.com/tribalwarshelp/api/graphql/generated"
"github.com/tribalwarshelp/shared/models"
)
@ -15,9 +16,30 @@ func (r *dailyPlayerStatsRecordResolver) Player(ctx context.Context, obj *models
return getPlayer(ctx, obj.PlayerID), nil
}
func (r *queryResolver) DailyPlayerStats(ctx context.Context, server string, filter *models.DailyPlayerStatsFilter) (*generated.DailyPlayerStats, error) {
func (r *queryResolver) DailyPlayerStats(ctx context.Context,
server string,
filter *models.DailyPlayerStatsFilter,
limit *int,
offset *int,
sort []string) (*generated.DailyPlayerStats, error) {
defLimit := 0
defOffset := 0
if limit == nil {
limit = &defLimit
}
if offset == nil {
offset = &defOffset
}
var err error
list := &generated.DailyPlayerStats{}
list.Items, list.Total, err = r.DailyPlayerStatsUcase.Fetch(ctx, server, filter)
list.Items, list.Total, err = r.DailyPlayerStatsUcase.Fetch(ctx, dailyplayerstats.FetchConfig{
Server: server,
Filter: filter,
Sort: sort,
Limit: *limit,
Offset: *offset,
Count: true,
})
return list, err
}

View File

@ -3,6 +3,7 @@ package resolvers
import (
"context"
"github.com/tribalwarshelp/api/dailytribestats"
"github.com/tribalwarshelp/api/graphql/generated"
"github.com/tribalwarshelp/shared/models"
)
@ -15,9 +16,30 @@ func (r *dailyTribeStatsRecordResolver) Tribe(ctx context.Context, obj *models.D
return getTribe(ctx, obj.TribeID), nil
}
func (r *queryResolver) DailyTribeStats(ctx context.Context, server string, filter *models.DailyTribeStatsFilter) (*generated.DailyTribeStats, error) {
func (r *queryResolver) DailyTribeStats(ctx context.Context,
server string,
filter *models.DailyTribeStatsFilter,
limit *int,
offset *int,
sort []string) (*generated.DailyTribeStats, error) {
defLimit := 0
defOffset := 0
if limit == nil {
limit = &defLimit
}
if offset == nil {
offset = &defOffset
}
var err error
list := &generated.DailyTribeStats{}
list.Items, list.Total, err = r.DailyTribeStatsUcase.Fetch(ctx, server, filter)
list.Items, list.Total, err = r.DailyTribeStatsUcase.Fetch(ctx, dailytribestats.FetchConfig{
Server: server,
Filter: filter,
Sort: sort,
Limit: *limit,
Offset: *offset,
Count: true,
})
return list, err
}

View File

@ -30,14 +30,17 @@ input DailyPlayerStatsFilter {
createDateLT: Time
createDateLTE: Time
offset: Int
limit: Int
sort: String
offset: Int @deprecated(reason: "Use a new variable added to the query dailyPlayerStats - `offset`.")
limit: Int @deprecated(reason: "Use a new variable added to the query dailyPlayerStats - `limit`.")
sort: String @deprecated(reason: "Use a new variable added to the query dailyPlayerStats - `sort`.")
}
extend type Query {
dailyPlayerStats(
server: String!
filter: DailyPlayerStatsFilter
limit: Int
offset: Int
sort: [String!]
): DailyPlayerStats!
}

View File

@ -31,14 +31,17 @@ input DailyTribeStatsFilter {
createDateLT: Time
createDateLTE: Time
offset: Int
limit: Int
sort: String
offset: Int @deprecated(reason: "Use a new variable added to the query dailyTribeStats - `offset`.")
limit: Int @deprecated(reason: "Use a new variable added to the query dailyTribeStats - `limit`.")
sort: String @deprecated(reason: "Use a new variable added to the query dailyTribeStats - `sort`.")
}
extend type Query {
dailyTribeStats(
server: String!
filter: DailyTribeStatsFilter
limit: Int
offset: Int
sort: [String!]
): DailyTribeStats!
}

View File

@ -16,24 +16,24 @@ func LimitWhitelist(cfg LimitWhitelistConfig) gin.HandlerFunc {
return func(c *gin.Context) {
ctx := c.Request.Context()
clientIP := c.ClientIP()
mayExceedLimit := false
canExceedLimit := false
for _, ip := range cfg.IPAddresses {
if ip == clientIP {
mayExceedLimit = true
canExceedLimit = true
break
}
}
ctx = StoreLimitWhitelistDataInContext(ctx, mayExceedLimit)
ctx = StoreLimitWhitelistDataInContext(ctx, canExceedLimit)
c.Request = c.Request.WithContext(ctx)
c.Next()
}
}
func StoreLimitWhitelistDataInContext(ctx context.Context, mayExceedLimit bool) context.Context {
return context.WithValue(ctx, limitWhitelistContextKey, mayExceedLimit)
func StoreLimitWhitelistDataInContext(ctx context.Context, canExceedLimit bool) context.Context {
return context.WithValue(ctx, limitWhitelistContextKey, canExceedLimit)
}
func MayExceedLimit(ctx context.Context) bool {
func CanExceedLimit(ctx context.Context) bool {
whitelisted := ctx.Value(limitWhitelistContextKey)
if whitelisted == nil {
return false

View File

@ -22,12 +22,12 @@ func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.P
if filter == nil {
filter = &models.PlayerFilter{}
}
if !middleware.MayExceedLimit(ctx) && (filter.Limit > player.PaginationLimit || filter.Limit <= 0) {
if !middleware.CanExceedLimit(ctx) && (filter.Limit > player.PaginationLimit || filter.Limit <= 0) {
filter.Limit = player.PaginationLimit
}
filter.Sort = utils.SanitizeSort(filter.Sort)
filter.Sort = utils.SanitizeSortExpression(filter.Sort)
if filter.TribeFilter != nil {
filter.TribeFilter.Sort = utils.SanitizeSort(filter.TribeFilter.Sort)
filter.TribeFilter.Sort = utils.SanitizeSortExpression(filter.TribeFilter.Sort)
}
return ucase.repo.Fetch(ctx, player.FetchConfig{
Server: server,

View File

@ -21,10 +21,10 @@ func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.P
if filter == nil {
filter = &models.PlayerHistoryFilter{}
}
if !middleware.MayExceedLimit(ctx) && (filter.Limit > playerhistory.PaginationLimit || filter.Limit <= 0) {
if !middleware.CanExceedLimit(ctx) && (filter.Limit > playerhistory.PaginationLimit || filter.Limit <= 0) {
filter.Limit = playerhistory.PaginationLimit
}
filter.Sort = utils.SanitizeSort(filter.Sort)
filter.Sort = utils.SanitizeSortExpression(filter.Sort)
return ucase.repo.Fetch(ctx, playerhistory.FetchConfig{
Server: server,
Filter: filter,

View File

@ -6,7 +6,6 @@ import (
"github.com/tribalwarshelp/api/middleware"
"github.com/tribalwarshelp/api/server"
"github.com/tribalwarshelp/api/utils"
"github.com/tribalwarshelp/shared/models"
)
@ -22,7 +21,7 @@ func (ucase *usecase) Fetch(ctx context.Context, filter *models.ServerFilter) ([
if filter == nil {
filter = &models.ServerFilter{}
}
if !middleware.MayExceedLimit(ctx) && (filter.Limit > server.PaginationLimit || filter.Limit <= 0) {
if !middleware.CanExceedLimit(ctx) && (filter.Limit > server.PaginationLimit || filter.Limit <= 0) {
filter.Limit = server.PaginationLimit
}
if len(filter.LangVersionTag) > 0 {
@ -31,7 +30,7 @@ func (ucase *usecase) Fetch(ctx context.Context, filter *models.ServerFilter) ([
if len(filter.LangVersionTagNEQ) > 0 {
filter.VersionCodeNEQ = append(filter.VersionCode, filter.LangVersionTagNEQ...)
}
filter.Sort = utils.SanitizeSort(filter.Sort)
// filter.Sort = utils.SanitizeSortExpression(filter.Sort)
return ucase.repo.Fetch(ctx, server.FetchConfig{
Count: true,
Filter: filter,

View File

@ -21,10 +21,10 @@ func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.S
if filter == nil {
filter = &models.ServerStatsFilter{}
}
if !middleware.MayExceedLimit(ctx) && (filter.Limit > serverstats.PaginationLimit || filter.Limit <= 0) {
if !middleware.CanExceedLimit(ctx) && (filter.Limit > serverstats.PaginationLimit || filter.Limit <= 0) {
filter.Limit = serverstats.PaginationLimit
}
filter.Sort = utils.SanitizeSort(filter.Sort)
filter.Sort = utils.SanitizeSortExpression(filter.Sort)
return ucase.repo.Fetch(ctx, serverstats.FetchConfig{
Server: server,
Filter: filter,

View File

@ -22,10 +22,10 @@ func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.T
if filter == nil {
filter = &models.TribeFilter{}
}
if !middleware.MayExceedLimit(ctx) && (filter.Limit > tribe.PaginationLimit || filter.Limit <= 0) {
if !middleware.CanExceedLimit(ctx) && (filter.Limit > tribe.PaginationLimit || filter.Limit <= 0) {
filter.Limit = tribe.PaginationLimit
}
filter.Sort = utils.SanitizeSort(filter.Sort)
filter.Sort = utils.SanitizeSortExpression(filter.Sort)
return ucase.repo.Fetch(ctx, tribe.FetchConfig{
Filter: filter,
Server: server,

View File

@ -21,10 +21,10 @@ func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.T
if filter == nil {
filter = &models.TribeChangeFilter{}
}
if !middleware.MayExceedLimit(ctx) && (filter.Limit > tribechange.PaginationLimit || filter.Limit <= 0) {
if !middleware.CanExceedLimit(ctx) && (filter.Limit > tribechange.PaginationLimit || filter.Limit <= 0) {
filter.Limit = tribechange.PaginationLimit
}
filter.Sort = utils.SanitizeSort(filter.Sort)
filter.Sort = utils.SanitizeSortExpression(filter.Sort)
return ucase.repo.Fetch(ctx, tribechange.FetchConfig{
Server: server,
Filter: filter,

View File

@ -21,10 +21,10 @@ func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.T
if filter == nil {
filter = &models.TribeHistoryFilter{}
}
if !middleware.MayExceedLimit(ctx) && (filter.Limit > tribehistory.PaginationLimit || filter.Limit <= 0) {
if !middleware.CanExceedLimit(ctx) && (filter.Limit > tribehistory.PaginationLimit || filter.Limit <= 0) {
filter.Limit = tribehistory.PaginationLimit
}
filter.Sort = utils.SanitizeSort(filter.Sort)
filter.Sort = utils.SanitizeSortExpression(filter.Sort)
return ucase.repo.Fetch(ctx, tribehistory.FetchConfig{
Server: server,
Filter: filter,

View File

@ -0,0 +1,12 @@
package utils
import "strings"
func FindStringWithPrefix(sl []string, prefix string) string {
for _, s := range sl {
if strings.HasPrefix(s, prefix) {
return s
}
}
return ""
}

View File

@ -6,19 +6,37 @@ import (
)
var (
sortRegex = regexp.MustCompile(`^[\p{L}\_]+$`)
sortexprRegex = regexp.MustCompile(`^[\p{L}\_\.]+$`)
)
func SanitizeSort(sort string) string {
trimmed := strings.TrimSpace(sort)
func SanitizeSortExpression(expr string) string {
trimmed := strings.TrimSpace(expr)
splitted := strings.Split(trimmed, " ")
length := len(splitted)
if length != 2 || !sortRegex.Match([]byte(splitted[0])) {
if length != 2 || !sortexprRegex.Match([]byte(splitted[0])) {
return ""
}
table := ""
column := splitted[0]
if strings.Contains(splitted[0], ".") {
columnAndTable := strings.Split(splitted[0], ".")
table = columnAndTable[0] + "."
column = columnAndTable[1]
}
keyword := "ASC"
if strings.ToUpper(splitted[1]) == "DESC" {
keyword = "DESC"
}
return strings.ToLower(Underscore(splitted[0])) + " " + keyword
return strings.ToLower(table+Underscore(column)) + " " + keyword
}
func SanitizeSortExpressions(exprs []string) []string {
filtered := []string{}
for _, expr := range exprs {
sanitized := SanitizeSortExpression(expr)
if sanitized != "" {
filtered = append(filtered, sanitized)
}
}
return filtered
}

View File

@ -25,7 +25,7 @@ func (ucase *usecase) Fetch(ctx context.Context, filter *models.VersionFilter) (
if filter == nil {
filter = &models.VersionFilter{}
}
if !middleware.MayExceedLimit(ctx) && (filter.Limit > version.PaginationLimit || filter.Limit <= 0) {
if !middleware.CanExceedLimit(ctx) && (filter.Limit > version.PaginationLimit || filter.Limit <= 0) {
filter.Limit = version.PaginationLimit
}
if len(filter.Tag) > 0 {
@ -34,7 +34,7 @@ func (ucase *usecase) Fetch(ctx context.Context, filter *models.VersionFilter) (
if len(filter.TagNEQ) > 0 {
filter.CodeNEQ = append(filter.Code, filter.TagNEQ...)
}
filter.Sort = utils.SanitizeSort(filter.Sort)
filter.Sort = utils.SanitizeSortExpression(filter.Sort)
return ucase.repo.Fetch(ctx, version.FetchConfig{
Filter: filter,
Count: true,

View File

@ -22,14 +22,14 @@ func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.V
if filter == nil {
filter = &models.VillageFilter{}
}
if !middleware.MayExceedLimit(ctx) && (filter.Limit > village.PaginationLimit || filter.Limit <= 0) {
if !middleware.CanExceedLimit(ctx) && (filter.Limit > village.PaginationLimit || filter.Limit <= 0) {
filter.Limit = village.PaginationLimit
}
filter.Sort = utils.SanitizeSort(filter.Sort)
filter.Sort = utils.SanitizeSortExpression(filter.Sort)
if filter.PlayerFilter != nil {
filter.PlayerFilter.Sort = utils.SanitizeSort(filter.PlayerFilter.Sort)
filter.PlayerFilter.Sort = utils.SanitizeSortExpression(filter.PlayerFilter.Sort)
if filter.PlayerFilter.TribeFilter != nil {
filter.PlayerFilter.TribeFilter.Sort = utils.SanitizeSort(filter.PlayerFilter.TribeFilter.Sort)
filter.PlayerFilter.TribeFilter.Sort = utils.SanitizeSortExpression(filter.PlayerFilter.TribeFilter.Sort)
}
}
return ucase.repo.Fetch(ctx, village.FetchConfig{