import type { Plugin } from "graphile-build" ; import type { SQL , QueryBuilder } from "graphile-build-pg" ; import type { GraphQLResolveInfo , GraphQLEnumType , GraphQLObjectType , } from "graphql" ; function isValidEnum ( enumType ) { try { if ( ! enumType ) { return false ; } if ( Object . keys ( enumType . getValues ( ) ) . length === 0 ) { return false ; } } catch ( e ) { return false ; } return true ; } const AddConnectionGroupedAggregatesPlugin = ( builder ) => { builder . hook ( "GraphQLObjectType:fields" , ( fields , build , context ) => { const { graphql : { GraphQLList , GraphQLNonNull } , inflection , getSafeAliasFromResolveInfo , pgSql : sql , getSafeAliasFromAlias , pgQueryFromResolveData : queryFromResolveData , } = build ; const { fieldWithHooks , scope : { isPgRowConnectionType , pgIntrospection : table } , } = context ; if ( ! isPgRowConnectionType || ! table || table . kind !== "class" || ! table . namespace ) { return fields ; } const AggregateContainerType = build . getTypeByName ( inflection . aggregateContainerType ( table ) ) ; if ( ! AggregateContainerType || Object . keys ( AggregateContainerType . getFields ( ) ) . length === 0 ) { return fields ; } const fieldName = inflection . groupedAggregatesContainerField ( table ) ; const TableGroupByType = build . getTypeByName ( inflection . aggregateGroupByType ( table ) ) ; const TableHavingInputType = build . getTypeByName ( inflection . aggregateHavingInputType ( table ) ) ; const tableTypeName = inflection . tableType ( table ) ; if ( ! isValidEnum ( TableGroupByType ) ) { return fields ; } return { ... fields , [ fieldName ] : fieldWithHooks ( fieldName , ( { addDataGenerator , getDataFromParsedResolveInfoFragment } ) => { addDataGenerator ( ( parsedResolveInfoFragment ) => { const safeAlias = getSafeAliasFromAlias ( parsedResolveInfoFragment . alias ) ; const resolveData = getDataFromParsedResolveInfoFragment ( parsedResolveInfoFragment , AggregateContainerType ) ; return { pgNamedQueryContainer : { name : safeAlias , query : ( { queryBuilder , innerQueryBuilder , options , } ) => { const args = parsedResolveInfoFragment . args ; const groupBy = args . groupBy . map ( ( b ) => b . spec ( queryBuilder . getTableAlias ( ) ) ) ; const having = args . having ? TableHavingInputType . extensions . graphile . toSql ( args . having , { tableAlias : queryBuilder . getTableAlias ( ) } ) : null ; if ( having && groupBy . length === 0 ) { throw new Error ( "Must not provide having without also providing groupBy" ) ; } innerQueryBuilder . select ( ( ) => sql . fragment `json_build_array(${ sql . join ( groupBy . map ( ( b ) => sql . fragment `(${ b })::text` ) , ", " ) })` , "keys" ) ; return sql . fragment `\
coalesce((select json_agg(j.data) from (
  select ${ innerQueryBuilder . build ( { onlyJsonField : true } ) } as data
  from ${ queryBuilder . getTableExpression ( ) } as ${ queryBuilder . getTableAlias ( ) }
  where ${ queryBuilder . buildWhereClause ( false , false , options ) }
  ${ groupBy . length > 0 ? sql . fragment `group by ${ sql . join ( groupBy , ", " ) }` : sql . blank }
  ${ having ? sql . fragment `having ${ having }` : sql . empty }
) j), '[]'::json)` ; } , } , pgNamedQuery : { name : safeAlias , query : ( aggregateQueryBuilder ) => { aggregateQueryBuilder . select ( ( ) => { const query = queryFromResolveData ( sql . identifier ( Symbol ( ) ) , aggregateQueryBuilder . getTableAlias ( ) , resolveData , { onlyJsonField : true } , ( innerQueryBuilder ) => { innerQueryBuilder . parentQueryBuilder = aggregateQueryBuilder ; innerQueryBuilder . select ( sql . fragment `sum(1)` , "__force_aggregate__" ) ; } , aggregateQueryBuilder . context ) ; return sql . fragment `(${ query })` ; } , safeAlias ) ; } , } , } ; } ) ; return { description : `Grouped aggregates across the matching connection (ignoring before/after/first/last/offset)` , type : new GraphQLList ( new GraphQLNonNull ( AggregateContainerType ) ) , args : { groupBy : { type : new GraphQLNonNull ( new GraphQLList ( new GraphQLNonNull ( TableGroupByType ) ) ) , description : build . wrapDescription ( `The method to use when grouping \`${ tableTypeName }\` for these aggregates.` , "arg" ) , } , ... ( TableHavingInputType ? { having : { type : TableHavingInputType , description : build . wrapDescription ( `Conditions on the grouped aggregates.` , "arg" ) , } , } : null ) , } , resolve ( parent , _args , _context , resolveInfo ) { const safeAlias = getSafeAliasFromResolveInfo ( resolveInfo ) ; return parent [ safeAlias ] . map ( ( entry ) => ( { ... entry [ safeAlias ] , keys : entry . keys , } ) ) ; } , } ; } , { } ) , } ; } ) ; } ; export default AddConnectionGroupedAggregatesPlugin ;