type complexity measurement improvement + misc corrections
This commit is contained in:
parent
cf28f6c8df
commit
17b24f2a16
@ -110,7 +110,7 @@ func NewFootball(c *graphql.Client) *Football {
|
||||
c,
|
||||
"players",
|
||||
[]string{"football"},
|
||||
),
|
||||
).WithMaxPageSize(100),
|
||||
Season: graphql.NewQuery[Season, SeasonParams](
|
||||
c,
|
||||
"season",
|
||||
|
@ -4,8 +4,14 @@ import "git.lehouerou.net/laurent/sorare/graphql"
|
||||
|
||||
type Rivals struct {
|
||||
c *graphql.Client
|
||||
|
||||
PastGames *graphql.PaginatedQuery[RivalsGame, graphql.EmptyParams]
|
||||
}
|
||||
|
||||
func NewRivals(c *graphql.Client) *Rivals {
|
||||
return &Rivals{c: c}
|
||||
return &Rivals{
|
||||
c: c,
|
||||
|
||||
PastGames: graphql.NewPaginatedQuery[RivalsGame, graphql.EmptyParams](c, "pastGamesPaginated", []string{"football", "rivals"}).WithOverrideComplexity(107),
|
||||
}
|
||||
}
|
||||
|
32
football/rivals_game.go
Normal file
32
football/rivals_game.go
Normal file
@ -0,0 +1,32 @@
|
||||
package football
|
||||
|
||||
import (
|
||||
"git.lehouerou.net/laurent/sorare/graphql"
|
||||
"git.lehouerou.net/laurent/sorare/types"
|
||||
)
|
||||
|
||||
type RivalsDraftableObject struct {
|
||||
Id graphql.Id `graphql:"id"`
|
||||
CapValue float64 `graphql:"capValue"`
|
||||
Player struct {
|
||||
Slug string `graphql:"slug"`
|
||||
ActiveClub struct {
|
||||
Slug string `graphql:"slug"`
|
||||
}
|
||||
ActiveNationalTeam struct {
|
||||
Slug string `graphql:"slug"`
|
||||
}
|
||||
} `graphql:"player"`
|
||||
Position types.Position `graphql:"position"`
|
||||
}
|
||||
|
||||
type RivalsGame struct {
|
||||
Id graphql.Id `graphql:"id"`
|
||||
Slug string `graphql:"slug"`
|
||||
Cap int `graphql:"cap"`
|
||||
DraftablePlayers []RivalsDraftableObject `graphql:"draftablePlayers"`
|
||||
FormationKnown bool `graphql:"formationKnown"`
|
||||
Game struct {
|
||||
Id graphql.Id `graphql:"id"`
|
||||
} `graphql:"game"`
|
||||
}
|
@ -22,28 +22,28 @@ func NewSo5(c *graphql.Client) *So5 {
|
||||
So5Fixture: graphql.NewQuery[So5Fixture, So5FixtureParams](
|
||||
c,
|
||||
"so5Fixture",
|
||||
[]string{"football", "so5"},
|
||||
[]string{"so5"},
|
||||
),
|
||||
So5Fixtures: graphql.NewPaginatedQuery[So5Fixture, So5FixturesParams](
|
||||
c,
|
||||
"so5Fixtures",
|
||||
[]string{"football", "so5"},
|
||||
[]string{"so5"},
|
||||
),
|
||||
So5Score: graphql.NewQuery[So5Score, graphql.IdParams](
|
||||
c,
|
||||
"so5Score",
|
||||
[]string{"football", "so5"},
|
||||
[]string{"so5"},
|
||||
),
|
||||
So5Leaderboard: graphql.NewQuery[So5Leaderboard, graphql.SlugParams](
|
||||
c,
|
||||
"so5Leaderboard",
|
||||
[]string{"football", "so5"},
|
||||
[]string{"so5"},
|
||||
),
|
||||
|
||||
UpcomingLeaderboards: graphql.NewQuery[[]So5Leaderboard, UpcomingLeaderboardsParams](
|
||||
c,
|
||||
"upcomingLeaderboards",
|
||||
[]string{"football", "so5"},
|
||||
[]string{"so5"},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package graphql
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -60,12 +61,11 @@ func (c *Client) SetApiKey(apiKey string) {
|
||||
c.apiKey = apiKey
|
||||
}
|
||||
|
||||
func (c *Client) MaxComplexity() int {
|
||||
if c.authenticated {
|
||||
func (c *Client) MaxQueryComplexity() int {
|
||||
if c.apiKey != "" || c.authenticated {
|
||||
return MaxAuthenticatedQueryComplexity
|
||||
} else {
|
||||
return MaxAnonymousQueryComplexity
|
||||
}
|
||||
return MaxAnonymousQueryComplexity
|
||||
}
|
||||
|
||||
func (c *Client) ConstructRawQuery(
|
||||
@ -140,3 +140,52 @@ func (c *Client) Mutate(
|
||||
defer c.lock.Unlock()
|
||||
return c.gql.Mutate(ctx, q, variables, options...)
|
||||
}
|
||||
|
||||
func GetTypeComplexity(t reflect.Type) int {
|
||||
var checkStruct func(t reflect.Type, complexity *int)
|
||||
checkStruct = func(t reflect.Type, complexity *int) {
|
||||
if t.Kind() != reflect.Struct {
|
||||
return
|
||||
}
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
tag := field.Tag.Get("graphql")
|
||||
if field.Type.Kind() == reflect.Struct {
|
||||
*complexity++
|
||||
if len(tag) > 6 && tag[:6] == "... on" {
|
||||
*complexity--
|
||||
}
|
||||
checkStruct(field.Type, complexity)
|
||||
continue
|
||||
}
|
||||
if field.Type.Kind() == reflect.Slice {
|
||||
if isSimpleType(field.Type.Elem()) {
|
||||
*complexity++
|
||||
} else {
|
||||
*complexity++
|
||||
tmpcomplexity := 0
|
||||
checkStruct(field.Type.Elem(), &tmpcomplexity)
|
||||
*complexity += tmpcomplexity * 10
|
||||
}
|
||||
continue
|
||||
}
|
||||
if tag != "" && tag != "__typename" {
|
||||
*complexity++
|
||||
}
|
||||
}
|
||||
}
|
||||
complexity := 0
|
||||
checkStruct(t, &complexity)
|
||||
return complexity
|
||||
}
|
||||
|
||||
func isSimpleType(t reflect.Type) bool {
|
||||
switch t.Kind() {
|
||||
case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
|
||||
reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128, reflect.String:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ type PaginatedQuery[ResultType any, Params any] struct {
|
||||
|
||||
additionalPayloadParams map[string]interface{}
|
||||
additionalQueryParams map[string]interface{}
|
||||
|
||||
overrideComplexity int
|
||||
}
|
||||
|
||||
func NewPaginatedQuery[ResultType any, Params any](
|
||||
@ -25,6 +27,7 @@ func NewPaginatedQuery[ResultType any, Params any](
|
||||
containerLayers: containerLayers,
|
||||
additionalPayloadParams: make(map[string]interface{}),
|
||||
additionalQueryParams: make(map[string]interface{}),
|
||||
overrideComplexity: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,6 +47,11 @@ func (pq *PaginatedQuery[ResultType, Params]) WithQueryParam(
|
||||
return pq
|
||||
}
|
||||
|
||||
func (pq *PaginatedQuery[ResultType, Params]) WithOverrideComplexity(complexity int) *PaginatedQuery[ResultType, Params] {
|
||||
pq.overrideComplexity = complexity
|
||||
return pq
|
||||
}
|
||||
|
||||
type PaginatedQueryGetOptions struct {
|
||||
Limit int
|
||||
}
|
||||
@ -68,17 +76,26 @@ func (pq *PaginatedQuery[ResultType, Params]) Get(
|
||||
var res []ResultType
|
||||
after := ""
|
||||
var noop ResultType
|
||||
pageSize := (pq.c.MaxComplexity() - 9) / (GetComplexity(reflect.TypeOf(noop)) + 1)
|
||||
complexity := pq.overrideComplexity
|
||||
if complexity == 0 {
|
||||
complexity = GetTypeComplexity(reflect.TypeOf(noop))
|
||||
}
|
||||
maxPageSize := (pq.c.MaxQueryComplexity() - 7 - len(pq.containerLayers)) / complexity
|
||||
for {
|
||||
pageSize := maxPageSize
|
||||
if opts.Limit > 0 {
|
||||
remaining := opts.Limit - len(res)
|
||||
if remaining < pageSize {
|
||||
pageSize = remaining
|
||||
}
|
||||
}
|
||||
page, pi, err := pq.getPage(ctx, params, after, pageSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, item := range page {
|
||||
res = append(res, item)
|
||||
if opts.Limit > 0 && len(res) >= opts.Limit {
|
||||
return res, nil
|
||||
}
|
||||
res = append(res, page...)
|
||||
if opts.Limit > 0 && len(res) >= opts.Limit {
|
||||
return res[:opts.Limit], nil
|
||||
}
|
||||
if !pi.HasNextPage {
|
||||
break
|
||||
@ -122,35 +139,3 @@ func (pq *PaginatedQuery[ResultType, Params]) getPage(
|
||||
}
|
||||
return res.Nodes, res.PageInfo, nil
|
||||
}
|
||||
|
||||
func GetComplexity(t reflect.Type) int {
|
||||
var checkStruct func(t reflect.Type, complexity *int)
|
||||
checkStruct = func(t reflect.Type, complexity *int) {
|
||||
if t.Kind() != reflect.Struct {
|
||||
return
|
||||
}
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
if field.Type.Kind() == reflect.Struct {
|
||||
*complexity++
|
||||
tag := field.Tag.Get("graphql")
|
||||
if len(tag) > 6 && tag[:6] == "... on" {
|
||||
*complexity--
|
||||
}
|
||||
checkStruct(field.Type, complexity)
|
||||
continue
|
||||
}
|
||||
if field.Type.Kind() == reflect.Slice {
|
||||
*complexity++
|
||||
tmpcomplexity := 0
|
||||
checkStruct(field.Type.Elem(), &tmpcomplexity)
|
||||
*complexity += tmpcomplexity * 10
|
||||
continue
|
||||
}
|
||||
*complexity++
|
||||
}
|
||||
}
|
||||
complexity := 0
|
||||
checkStruct(t, &complexity)
|
||||
return complexity
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package graphql
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@ -15,6 +16,8 @@ type Query[ReturnType any, Params any] struct {
|
||||
|
||||
additionalPayloadParams map[string]interface{}
|
||||
additionalQueryParams map[string]interface{}
|
||||
|
||||
maxPageSize int
|
||||
}
|
||||
|
||||
func NewQuery[ReturnType any, Params any](
|
||||
@ -29,6 +32,7 @@ func NewQuery[ReturnType any, Params any](
|
||||
|
||||
additionalPayloadParams: make(map[string]interface{}),
|
||||
additionalQueryParams: make(map[string]interface{}),
|
||||
maxPageSize: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,6 +52,27 @@ func (r *Query[ReturnType, Params]) WithQueryParam(
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Query[ReturnType, Params]) WithMaxPageSize(maxPageSize int) *Query[ReturnType, Params] {
|
||||
r.maxPageSize = maxPageSize
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Query[ReturnType, Params]) GetPageSize() int {
|
||||
t := reflect.TypeOf((*ReturnType)(nil)).Elem()
|
||||
if t.Kind() == reflect.Slice {
|
||||
t = t.Elem()
|
||||
}
|
||||
complexity := GetTypeComplexity(t)
|
||||
maxComplexity := r.c.MaxQueryComplexity()
|
||||
layers := len(r.containerLayers)
|
||||
computedPageSize := (maxComplexity - layers - 1) / complexity
|
||||
|
||||
if r.maxPageSize > 0 && r.maxPageSize < computedPageSize {
|
||||
return r.maxPageSize
|
||||
}
|
||||
return computedPageSize
|
||||
}
|
||||
|
||||
func (r *Query[ReturnType, Params]) Get(ctx context.Context, params Params) (ReturnType, error) {
|
||||
|
||||
paramsMap := convertParamsToMap(params)
|
||||
|
Loading…
Reference in New Issue
Block a user