142 lines
3.4 KiB
Go
142 lines
3.4 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{}
|
|
|
|
overrideComplexity int
|
|
}
|
|
|
|
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{}),
|
|
overrideComplexity: 0,
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
func (pq *PaginatedQuery[ResultType, Params]) WithOverrideComplexity(complexity int) *PaginatedQuery[ResultType, Params] {
|
|
pq.overrideComplexity = complexity
|
|
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
|
|
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
|
|
}
|
|
res = append(res, page...)
|
|
if opts.Limit > 0 && len(res) >= opts.Limit {
|
|
return res[:opts.Limit], 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
|
|
}
|