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 }