import { Resolver , inspect } from 'graphql-compose' ; import type { ObjectTypeComposer , InterfaceTypeComposer , UnionTypeComposer , ScalarTypeComposer , EnumTypeComposer , ResolverResolveParams , ObjectTypeComposerArgumentConfigMap , } from 'graphql-compose' ; import { preparePaginationTC } from './types' ; export const DEFAULT_RESOLVER_NAME = 'pagination' ; export const DEFAULT_PER_PAGE = 20 ; const ALLOWED_TYPE_COMPOSERS = [ 'ObjectTypeComposer' , 'InterfaceTypeComposer' , 'UnionTypeComposer' , 'ScalarTypeComposer' , 'EnumTypeComposer' , ] ; export type PaginationResolverOpts = { findManyResolver : Resolver ; countResolver : Resolver ; name ? : string ; perPage ? : number ; } ; export type PaginationType = { count : number ; items : any [ ] ; pageInfo : PaginationInfoType ; } ; export type PaginationInfoType = { currentPage : number ; perPage : number ; itemCount : number ; pageCount : number ; hasPreviousPage : boolean ; hasNextPage : boolean ; } ; export interface PaginationTArgs { page ? : number ; perPage ? : number ; filter ? : any ; sort ? : any ; } export function preparePaginationResolver < TSource , TContext > ( tc , opts ) { if ( ! tc || ! ALLOWED_TYPE_COMPOSERS . includes ( tc . constructor . name ) ) { throw new Error ( `First arg for preparePaginationResolver() should be instance of ${ ALLOWED_TYPE_COMPOSERS . join ( ' or ' ) }` ) ; } const resolverName = opts . name || DEFAULT_RESOLVER_NAME ; if ( ! opts . countResolver || ! ( opts . countResolver instanceof Resolver ) ) { throw new Error ( `Option 'opts.countResolver' must be a Resolver instance. Received ${ inspect ( opts . countResolver ) }` ) ; } const countResolve = opts . countResolver . getResolve ( ) ; if ( ! opts . findManyResolver || ! ( opts . findManyResolver instanceof Resolver ) ) { throw new Error ( `Option 'opts.findManyResolver' must be a Resolver instance. Received ${ inspect ( opts . findManyResolver ) }` ) ; } const findManyResolver = opts . findManyResolver ; const findManyResolve = findManyResolver . getResolve ( ) ; const additionalArgs = { } ; if ( findManyResolver . hasArg ( 'filter' ) ) { const filter = findManyResolver . getArg ( 'filter' ) ; if ( filter ) { additionalArgs . filter = filter ; } } if ( findManyResolver . hasArg ( 'sort' ) ) { const sort = findManyResolver . getArg ( 'sort' ) ; if ( sort ) { additionalArgs . sort = sort ; } } return tc . schemaComposer . createResolver ( { type : preparePaginationTC ( tc , resolverName ) , name : resolverName , kind : 'query' , args : { page : { type : 'Int' , description : 'Page number for displaying' , } , perPage : { type : 'Int' , description : '' , defaultValue : opts . perPage || DEFAULT_PER_PAGE , } , ... ( additionalArgs as any ) , } , resolve : async ( rp ) => { let countPromise ; let findManyPromise ; const { projection = { } , args , rawQuery } = rp ; const page = parseInt ( args . page as any , 10 ) || 1 ; if ( page <= 0 ) { throw new Error ( 'Argument `page` should be positive number.' ) ; } const perPage = parseInt ( args . perPage as any , 10 ) || opts . perPage || DEFAULT_PER_PAGE ; if ( perPage <= 0 ) { throw new Error ( 'Argument `perPage` should be positive number.' ) ; } const countParams = { ... rp , rawQuery , args : { ... rp . args , filter : { ... rp . args . filter } , } , } ; if ( projection . count || ( projection . pageInfo && ( projection . pageInfo . itemCount || projection . pageInfo . pageCount ) ) ) { countPromise = countResolve ( countParams ) ; } else { countPromise = Promise . resolve ( 0 ) ; } const findManyParams = { ... rp , } ; if ( projection && projection . items ) { findManyParams . projection = { ... projection , ... projection . items } ; } else { findManyParams . projection = { ... projection } ; } const limit = perPage ; const skip = ( page - 1 ) * perPage ; findManyParams . args . limit = limit + 1 ; if ( skip > 0 ) { findManyParams . args . skip = skip ; } rp . findManyResolveParams = findManyParams ; rp . countResolveParams = countParams ; if ( ( projection . count || projection . pageInfo ) && Object . keys ( projection ) . length === 1 ) { findManyPromise = Promise . resolve ( [ ] ) ; } else { findManyPromise = findManyResolve ( findManyParams ) ; } return Promise . all ( [ findManyPromise , countPromise ] ) . then ( ( [ items , count ] ) => { const result = { count , items : items . length > limit ? items . slice ( 0 , limit ) : items , pageInfo : { currentPage : page , perPage , itemCount : count , pageCount : Math . ceil ( count / perPage ) , hasPreviousPage : page > 1 , hasNextPage : items . length > limit || page * perPage < count , } , } ; return result ; } ) ; } , } ) ; }