157 lines
3.7 KiB
Go
157 lines
3.7 KiB
Go
|
package graphql
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"reflect"
|
||
|
)
|
||
|
|
||
|
type PaginatedQuery[ResultType any, Params any] struct {
|
||
|
c *Client
|
||
|
gqltype string
|
||
|
containerLayers []string
|
||
|
|
||
|
additionalPayloadParams map[string]interface{}
|
||
|
additionalQueryParams map[string]interface{}
|
||
|
}
|
||
|
|
||
|
func NewPaginatedQuery[ResultType any, Params any](
|
||
|
c *Client,
|
||
|
gqlType string,
|
||
|
containerLayers []string,
|
||
|
) *PaginatedQuery[ResultType, Params] {
|
||
|
return &PaginatedQuery[ResultType, Params]{
|
||
|
c: c,
|
||
|
gqltype: gqlType,
|
||
|
containerLayers: containerLayers,
|
||
|
additionalPayloadParams: make(map[string]interface{}),
|
||
|
additionalQueryParams: make(map[string]interface{}),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (pq *PaginatedQuery[ResultType, Params]) WithPayloadParam(
|
||
|
name string,
|
||
|
value interface{},
|
||
|
) *PaginatedQuery[ResultType, Params] {
|
||
|
pq.additionalPayloadParams[name] = value
|
||
|
return pq
|
||
|
}
|
||
|
|
||
|
func (pq *PaginatedQuery[ResultType, Params]) WithQueryParam(
|
||
|
name string,
|
||
|
value interface{},
|
||
|
) *PaginatedQuery[ResultType, Params] {
|
||
|
pq.additionalQueryParams[name] = value
|
||
|
return pq
|
||
|
}
|
||
|
|
||
|
type PaginatedQueryGetOptions struct {
|
||
|
Limit int
|
||
|
}
|
||
|
|
||
|
func WithPaginatedQueryLimit(limit int) func(options *PaginatedQueryGetOptions) {
|
||
|
return func(options *PaginatedQueryGetOptions) {
|
||
|
options.Limit = limit
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (pq *PaginatedQuery[ResultType, Params]) Get(
|
||
|
ctx context.Context,
|
||
|
params Params,
|
||
|
options ...func(options *PaginatedQueryGetOptions),
|
||
|
) ([]ResultType, error) {
|
||
|
opts := &PaginatedQueryGetOptions{
|
||
|
Limit: 0,
|
||
|
}
|
||
|
for _, opt := range options {
|
||
|
opt(opts)
|
||
|
}
|
||
|
var res []ResultType
|
||
|
after := ""
|
||
|
var noop ResultType
|
||
|
pageSize := (pq.c.MaxComplexity() - 9) / (GetComplexity(reflect.TypeOf(noop)) + 1)
|
||
|
for {
|
||
|
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
|
||
|
}
|
||
|
}
|
||
|
if !pi.HasNextPage {
|
||
|
break
|
||
|
}
|
||
|
after = pi.EndCursor
|
||
|
}
|
||
|
return res, nil
|
||
|
}
|
||
|
|
||
|
type PageInfo struct {
|
||
|
EndCursor string `graphql:"endCursor"`
|
||
|
HasNextPage bool `graphql:"hasNextPage"`
|
||
|
HasPreviousPage bool `graphql:"hasPreviousPage"`
|
||
|
StartCursor string `graphql:"startCursor"`
|
||
|
}
|
||
|
|
||
|
func (pq *PaginatedQuery[ResultType, Params]) getPage(
|
||
|
ctx context.Context,
|
||
|
params Params,
|
||
|
after string,
|
||
|
first int,
|
||
|
) ([]ResultType, PageInfo, error) {
|
||
|
type PageResult struct {
|
||
|
Nodes []ResultType `graphql:"nodes"`
|
||
|
PageInfo PageInfo `graphql:"pageInfo"`
|
||
|
}
|
||
|
q := NewQuery[PageResult, Params](pq.c, pq.gqltype, pq.containerLayers).
|
||
|
WithPayloadParam("first", first)
|
||
|
if after != "" {
|
||
|
q = q.WithPayloadParam("after", after)
|
||
|
}
|
||
|
for k, v := range pq.additionalPayloadParams {
|
||
|
q.WithPayloadParam(k, v)
|
||
|
}
|
||
|
for k, v := range pq.additionalQueryParams {
|
||
|
q.WithQueryParam(k, v)
|
||
|
}
|
||
|
res, err := q.Get(ctx, params)
|
||
|
if err != nil {
|
||
|
return nil, PageInfo{}, err
|
||
|
}
|
||
|
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
|
||
|
}
|