diff --git a/package-lock.json b/package-lock.json index f369302..db05f61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ra-data-hasura", - "version": "0.5.1", + "version": "0.5.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ra-data-hasura", - "version": "0.5.1", + "version": "0.5.2", "license": "MIT", "dependencies": { "graphql": "^15.8.0", diff --git a/package.json b/package.json index 0123e88..8babe12 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ra-data-hasura", - "version": "0.5.1", + "version": "0.5.2", "description": "A data provider for connecting react-admin to a Hasura endpoint", "main": "dist/index.js", "typings": "dist/index.d.ts", diff --git a/src/buildGqlQuery/index.ts b/src/buildGqlQuery/index.ts index d4fe1f4..20776b5 100644 --- a/src/buildGqlQuery/index.ts +++ b/src/buildGqlQuery/index.ts @@ -59,35 +59,40 @@ export const buildGqlQuery: BuildGqlQuery = aorFetchType === GET_MANY || aorFetchType === GET_MANY_REFERENCE ) { + let gqlArray = [ + gqlTypes.field( + gqlTypes.name(queryType.name), + gqlTypes.name('items'), + args, + null, + gqlTypes.selectionSet(fields) + ), + ]; + // Skip aggregate calls when provided aggregateFieldName function returns NO_COUNT. + // This is useful to avoid expensive count queries. + if (aggregateFieldName(queryType.name) !== 'NO_COUNT') { + gqlArray.push( + gqlTypes.field( + gqlTypes.name(aggregateFieldName(queryType.name)), + gqlTypes.name('total'), + metaArgs, + null, + gqlTypes.selectionSet([ + gqlTypes.field( + gqlTypes.name('aggregate'), + null, + null, + null, + gqlTypes.selectionSet([gqlTypes.field(gqlTypes.name('count'))]) + ), + ]) + ) + ); + } return gqlTypes.document([ gqlTypes.operationDefinition( 'query', - gqlTypes.selectionSet([ - gqlTypes.field( - gqlTypes.name(queryType.name), - gqlTypes.name('items'), - args, - null, - gqlTypes.selectionSet(fields) - ), - gqlTypes.field( - gqlTypes.name(aggregateFieldName(queryType.name)), - gqlTypes.name('total'), - metaArgs, - null, - gqlTypes.selectionSet([ - gqlTypes.field( - gqlTypes.name('aggregate'), - null, - null, - null, - gqlTypes.selectionSet([ - gqlTypes.field(gqlTypes.name('count')), - ]) - ), - ]) - ), - ]), + gqlTypes.selectionSet(gqlArray), gqlTypes.name(queryType.name), apolloArgs ), diff --git a/src/buildQuery/index.ts b/src/buildQuery/index.ts index 5555806..ce90614 100644 --- a/src/buildQuery/index.ts +++ b/src/buildQuery/index.ts @@ -3,6 +3,15 @@ import buildGqlQuery, { BuildGqlQueryFactory } from '../buildGqlQuery'; import { getResponseParser, GetResponseParser } from '../getResponseParser'; import type { FetchType, IntrospectionResult } from '../types'; +export type QueryResponse = { + data: any; + total?: number; + pageInfo?: { + hasNextPage?: boolean; + hasPreviousPage?: boolean; + }; +}; + export type BuildQuery = (introspectionResults: IntrospectionResult) => ( aorFetchType: FetchType, resourceName: string, @@ -10,7 +19,7 @@ export type BuildQuery = (introspectionResults: IntrospectionResult) => ( ) => { query: any; variables: any; - parseResponse: ({ data }: any) => { data: any; total?: number }; + parseResponse: ({ data }: any) => QueryResponse; }; export type BuildQueryFactory = ( diff --git a/src/getResponseParser/index.ts b/src/getResponseParser/index.ts index 6dcb16c..12e9d58 100644 --- a/src/getResponseParser/index.ts +++ b/src/getResponseParser/index.ts @@ -10,15 +10,15 @@ import { DELETE_MANY, } from '../helpers/fetchActions'; import { IntrospectionResult, IntrospectedResource, FetchType } from '../types'; +import { QueryResponse } from '../buildQuery'; import { sanitizeResource } from './sanitizeResource'; -export type GetResponseParser = (introspectionResults: IntrospectionResult) => ( +export type GetResponseParser = ( + introspectionResults: IntrospectionResult +) => ( aorFetchType: FetchType, resource?: IntrospectedResource -) => (res: { data: any }) => { - data: any; - total?: number; -}; +) => (res: { data: any }) => QueryResponse; export const getResponseParser: GetResponseParser = () => (aorFetchType) => (res) => { @@ -27,10 +27,19 @@ export const getResponseParser: GetResponseParser = switch (aorFetchType) { case GET_MANY_REFERENCE: case GET_LIST: - return { + let output: QueryResponse = { data: response.items.map(sanitizeResource), - total: response.total.aggregate.count, }; + if (typeof response.total !== 'undefined') { + output.total = response.total.aggregate.count; + } else { + // TODO: behave smarter and set hasNextPage=false when no more records exist. + output.pageInfo = { + hasPreviousPage: true, + hasNextPage: true, + }; + } + return output; case GET_MANY: return { data: response.items.map(sanitizeResource) };