683b0ddbf4
Prepare the extracted library for external consumption with grouped godoc, extension point docs, and setup requirements. Wire ErrBadCursorString into decode paths and drop redundant Paginate*Conds aliases. Co-authored-by: Cursor <cursoragent@cursor.com>
84 lines
2.2 KiB
Go
84 lines
2.2 KiB
Go
package cursor
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"gitea.auvem.com/go-toolkit/dbx"
|
|
"github.com/go-jet/jet/v2/mysql"
|
|
)
|
|
|
|
// PageQuery runs a paginated SELECT and builds a Connection from the results.
|
|
type PageQuery[T any, IE mysql.Expression, IC mysql.Column] struct {
|
|
Sqlo dbx.Queryable
|
|
Stmt mysql.SelectStatement
|
|
Conds mysql.BoolExpression
|
|
Cursor *Cursor[IE, IC]
|
|
Default func() *Cursor[IE, IC]
|
|
Limit int
|
|
CountFn QueryCountFn
|
|
ToEdge func(active *Cursor[IE, IC], item *T) (GenericCursor, error)
|
|
// Scan runs the paginated query into dest. When nil, Run uses Stmt.Query.
|
|
Scan func(stmt mysql.SelectStatement, dest *[]*T) error
|
|
// AfterScan optionally transforms rows after the query and before building edges.
|
|
AfterScan func(items []*T) ([]*T, error)
|
|
}
|
|
|
|
// Run executes the paginated query and returns a Connection.
|
|
func (q PageQuery[T, IE, IC]) Run() (*Connection[T], error) {
|
|
active := q.Cursor
|
|
if active == nil {
|
|
active = q.Default()
|
|
}
|
|
|
|
stmt := q.Stmt
|
|
stmt.WHERE(q.Conds.AND(PaginateConds(q.Cursor)))
|
|
stmt.ORDER_BY(OrderByClauses(active)...)
|
|
if q.Limit > 0 {
|
|
stmt = stmt.LIMIT(int64(q.Limit))
|
|
}
|
|
|
|
var items []*T
|
|
scan := q.Scan
|
|
if scan == nil {
|
|
scan = func(s mysql.SelectStatement, dest *[]*T) error {
|
|
return s.Query(q.Sqlo, dest)
|
|
}
|
|
}
|
|
if err := scan(stmt, &items); err != nil {
|
|
return nil, fmt.Errorf("failed to run paginated query: %w", err)
|
|
}
|
|
|
|
if q.AfterScan != nil {
|
|
var err error
|
|
items, err = q.AfterScan(items)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return BuildEdges(q.Sqlo, q.CountFn, items, func(item *T) (GenericCursor, error) {
|
|
return q.ToEdge(active, item)
|
|
})
|
|
}
|
|
|
|
// ConnectionFromRelayArgs parses Relay after/first args and calls list with the
|
|
// decoded cursor and limit. newZero returns a template cursor used to decode after
|
|
// (same shape as the cursor factory for the resource, e.g. newUserCursor).
|
|
func ConnectionFromRelayArgs[T any, IE mysql.Expression, IC mysql.Column](
|
|
after *string,
|
|
first *int,
|
|
newZero func() *Cursor[IE, IC],
|
|
list func(cursor *Cursor[IE, IC], limit int) (*Connection[T], error),
|
|
) (*Connection[T], error) {
|
|
limit := 0
|
|
if first != nil {
|
|
limit = *first
|
|
}
|
|
|
|
c, err := NewCursorFromAfterPtr(newZero, after)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return list(c, limit)
|
|
}
|