Skip to content

Commit

Permalink
Add totalCount to paginate
Browse files Browse the repository at this point in the history
  • Loading branch information
danielrearden committed May 16, 2020
1 parent c0298f8 commit 32fe4be
Show file tree
Hide file tree
Showing 11 changed files with 171 additions and 31 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased](https://github.com/danielrearden/sqlmancer/compare/v0.2.1...HEAD)

### Added
- `totalCount` property to object returned by `paginate`

### Fixed
- Return type generated by @paginate

Expand Down
6 changes: 6 additions & 0 deletions src/__tests__/mysql/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ describe('integration (mysql)', () => {
id
}
hasMore
totalCount
}
filmsPaginated {
aggregate {
Expand All @@ -216,6 +217,7 @@ describe('integration (mysql)', () => {
id
}
hasMore
totalCount
}
actor(id: 1) {
filmsPaginated {
Expand All @@ -242,6 +244,7 @@ describe('integration (mysql)', () => {
id
}
hasMore
totalCount
}
}
}
Expand All @@ -255,6 +258,7 @@ describe('integration (mysql)', () => {
expect(data?.actorsPaginated.aggregate.max.lastUpdate).toBeDefined()
expect(data?.actorsPaginated.results.length).toBeGreaterThan(0)
expect(data?.actorsPaginated.hasMore).toBeDefined()
expect(data?.actorsPaginated.totalCount).toBeDefined()
expect(data?.filmsPaginated.aggregate.count).toBeGreaterThan(0)
expect(data?.filmsPaginated.aggregate.min.title).toBeDefined()
expect(data?.filmsPaginated.aggregate.min.length).toBeDefined()
Expand All @@ -266,6 +270,7 @@ describe('integration (mysql)', () => {
expect(data?.filmsPaginated.aggregate.sum.length).toBeDefined()
expect(data?.filmsPaginated.results.length).toBeGreaterThan(0)
expect(data?.filmsPaginated.hasMore).toBeDefined()
expect(data?.filmsPaginated.totalCount).toBeDefined()
expect(data?.actor.filmsPaginated.aggregate.count).toBeGreaterThan(0)
expect(data?.actor.filmsPaginated.aggregate.min.title).toBeDefined()
expect(data?.actor.filmsPaginated.aggregate.min.length).toBeDefined()
Expand All @@ -277,6 +282,7 @@ describe('integration (mysql)', () => {
expect(data?.actor.filmsPaginated.aggregate.sum.length).toBeDefined()
expect(data?.actor.filmsPaginated.results.length).toBeGreaterThan(0)
expect(data?.actor.filmsPaginated.hasMore).toBeDefined()
expect(data?.actor.filmsPaginated.totalCount).toBeDefined()
})

test('abstract types', async () => {
Expand Down
6 changes: 6 additions & 0 deletions src/__tests__/postgres/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ describe('integration (postgres)', () => {
id
}
hasMore
totalCount
}
filmsPaginated {
aggregate {
Expand All @@ -234,6 +235,7 @@ describe('integration (postgres)', () => {
id
}
hasMore
totalCount
}
actor(id: 1) {
filmsPaginated {
Expand All @@ -260,6 +262,7 @@ describe('integration (postgres)', () => {
id
}
hasMore
totalCount
}
}
}
Expand All @@ -273,6 +276,7 @@ describe('integration (postgres)', () => {
expect(data?.actorsPaginated.aggregate.max.lastUpdate).toBeDefined()
expect(data?.actorsPaginated.results.length).toBeGreaterThan(0)
expect(data?.actorsPaginated.hasMore).toBeDefined()
expect(data?.actorsPaginated.totalCount).toBeDefined()
expect(data?.filmsPaginated.aggregate.count).toBeGreaterThan(0)
expect(data?.filmsPaginated.aggregate.min.title).toBeDefined()
expect(data?.filmsPaginated.aggregate.min.length).toBeDefined()
Expand All @@ -284,6 +288,7 @@ describe('integration (postgres)', () => {
expect(data?.filmsPaginated.aggregate.sum.length).toBeDefined()
expect(data?.filmsPaginated.results.length).toBeGreaterThan(0)
expect(data?.filmsPaginated.hasMore).toBeDefined()
expect(data?.filmsPaginated.totalCount).toBeDefined()
expect(data?.actor.filmsPaginated.aggregate.count).toBeGreaterThan(0)
expect(data?.actor.filmsPaginated.aggregate.min.title).toBeDefined()
expect(data?.actor.filmsPaginated.aggregate.min.length).toBeDefined()
Expand All @@ -295,6 +300,7 @@ describe('integration (postgres)', () => {
expect(data?.actor.filmsPaginated.aggregate.sum.length).toBeDefined()
expect(data?.actor.filmsPaginated.results.length).toBeGreaterThan(0)
expect(data?.actor.filmsPaginated.hasMore).toBeDefined()
expect(data?.actor.filmsPaginated.totalCount).toBeDefined()
})

test('abstract types', async () => {
Expand Down
6 changes: 6 additions & 0 deletions src/__tests__/sqlite/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ describe('integration (sqlite)', () => {
id
}
hasMore
totalCount
}
filmsPaginated {
aggregate {
Expand All @@ -218,6 +219,7 @@ describe('integration (sqlite)', () => {
id
}
hasMore
totalCount
}
actor(id: 1) {
filmsPaginated {
Expand All @@ -244,6 +246,7 @@ describe('integration (sqlite)', () => {
id
}
hasMore
totalCount
}
}
}
Expand All @@ -257,6 +260,7 @@ describe('integration (sqlite)', () => {
expect(data?.actorsPaginated.aggregate.max.lastUpdate).toBeDefined()
expect(data?.actorsPaginated.results.length).toBeGreaterThan(0)
expect(data?.actorsPaginated.hasMore).toBeDefined()
expect(data?.actorsPaginated.totalCount).toBeDefined()
expect(data?.filmsPaginated.aggregate.count).toBeGreaterThan(0)
expect(data?.filmsPaginated.aggregate.min.title).toBeDefined()
expect(data?.filmsPaginated.aggregate.min.length).toBeDefined()
Expand All @@ -268,6 +272,7 @@ describe('integration (sqlite)', () => {
expect(data?.filmsPaginated.aggregate.sum.length).toBeDefined()
expect(data?.filmsPaginated.results.length).toBeGreaterThan(0)
expect(data?.filmsPaginated.hasMore).toBeDefined()
expect(data?.filmsPaginated.totalCount).toBeDefined()
expect(data?.actor.filmsPaginated.aggregate.count).toBeGreaterThan(0)
expect(data?.actor.filmsPaginated.aggregate.min.title).toBeDefined()
expect(data?.actor.filmsPaginated.aggregate.min.length).toBeDefined()
Expand All @@ -279,6 +284,7 @@ describe('integration (sqlite)', () => {
expect(data?.actor.filmsPaginated.aggregate.sum.length).toBeDefined()
expect(data?.actor.filmsPaginated.results.length).toBeGreaterThan(0)
expect(data?.actor.filmsPaginated.hasMore).toBeDefined()
expect(data?.actor.filmsPaginated.totalCount).toBeDefined()
})

test('abstract types', async () => {
Expand Down
1 change: 1 addition & 0 deletions src/directives/__tests__/__fixtures__/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const typeDefs = `
widgets: [Widget!]! @limit @offset @orderBy(model: "Widget") @where(model: "Widget")
alsoWidgets: [Widget!]! @orderBy @where
someMoreWidgets: [Widget!]! @many
paginatedWidgets: Widget @paginate
}
type Mutation {
Expand Down
55 changes: 33 additions & 22 deletions src/directives/__tests__/directives.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,29 +147,40 @@ describe('directives', () => {

describe('@paginate', () => {
test('correct usage', async () => {
const { createWidget, createWidgets, updateWidget, updateWidgets } = (schema.getType(
'Mutation'
const { paginatedWidgets } = (schema.getType('Query') as GraphQLObjectType).getFields()
const { results, aggregate, hasMore, totalCount } = (schema.getType(
'WidgetPage'
) as GraphQLObjectType).getFields()
const CreateWidgetInput = schema.getType('CreateWidgetInput') as GraphQLInputObjectType
const UpdateWidgetInput = schema.getType('UpdateWidgetInput') as GraphQLInputObjectType
const createWidgetInputArgType = createWidget.args.find((arg) => arg.name === 'input')!.type
const createWidgetsInputArgType = createWidgets.args.find((arg) => arg.name === 'input')!.type
const updateWidgetInputArgType = updateWidget.args.find((arg) => arg.name === 'input')!.type
const updateWidgetsInputArgType = updateWidgets.args.find((arg) => arg.name === 'input')!.type
expect(createWidgetInputArgType.toString()).toBe('CreateWidgetInput!')
expect(createWidgetsInputArgType.toString()).toBe('[CreateWidgetInput!]!')
expect(updateWidgetInputArgType.toString()).toBe('UpdateWidgetInput!')
expect(updateWidgetsInputArgType.toString()).toBe('UpdateWidgetInput!')
expect(CreateWidgetInput.getFields().id.type.toString()).toBe('ID')
expect(CreateWidgetInput.getFields().idNullable.type.toString()).toBe('ID')
expect(CreateWidgetInput.getFields().idList.type.toString()).toBe('[ID!]!')
expect(CreateWidgetInput.getFields().string.type.toString()).toBe('String!')
expect(CreateWidgetInput.getFields().json.type.toString()).toBe('JSON!')
expect(UpdateWidgetInput.getFields().idNullable.type.toString()).toBe('ID')
expect(UpdateWidgetInput.getFields().idList.type.toString()).toBe('[ID!]')
expect(UpdateWidgetInput.getFields().string.type.toString()).toBe('String')
expect(UpdateWidgetInput.getFields().json.type.toString()).toBe('JSON')
expect(UpdateWidgetInput.getFields().id).toBeUndefined()
const { count } = (schema.getType('WidgetAggregate') as GraphQLObjectType).getFields()
const minFields = (schema.getType('WidgetAggregateMin') as GraphQLObjectType).getFields()
const maxFields = (schema.getType('WidgetAggregateMax') as GraphQLObjectType).getFields()
const sumFields = (schema.getType('WidgetAggregateSum') as GraphQLObjectType).getFields()
const avgFields = (schema.getType('WidgetAggregateAvg') as GraphQLObjectType).getFields()

expect(paginatedWidgets.type.toString()).toBe('WidgetPage!')
expect(results.type.toString()).toBe('[Widget!]!')
expect(aggregate.type.toString()).toBe('WidgetAggregate!')
expect(hasMore.type.toString()).toBe('Boolean!')
expect(totalCount.type.toString()).toBe('Int!')
expect(count.type.toString()).toBe('Int!')

expect(minFields.id).toBeDefined()
expect(minFields.string).toBeDefined()
expect(minFields.int).toBeDefined()
expect(minFields.float).toBeDefined()
expect(maxFields.id).toBeDefined()
expect(maxFields.string).toBeDefined()
expect(maxFields.int).toBeDefined()
expect(maxFields.float).toBeDefined()
expect(sumFields.int).toBeDefined()
expect(sumFields.float).toBeDefined()
expect(avgFields.int).toBeDefined()
expect(avgFields.float).toBeDefined()

expect(sumFields.id).toBeUndefined()
expect(sumFields.string).toBeUndefined()
expect(avgFields.id).toBeUndefined()
expect(avgFields.string).toBeUndefined()
})
})
})
3 changes: 3 additions & 0 deletions src/directives/paginate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ export class PaginateDirective extends SchemaDirectiveVisitor<any, any> {
hasMore: {
type: new GraphQLNonNull(GraphQLBoolean),
},
totalCount: {
type: new GraphQLNonNull(GraphQLInt),
},
}),
})
}
Expand Down
12 changes: 12 additions & 0 deletions src/queryBuilder/__tests__/__snapshots__/paginate.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ Array [
]
`;

exports[`PaginateBuilder mysql totalCount correct usage 1`] = `"select json_object('totalCount', (select coalesce(\`f1\`.\`count\`, 0) from (select count(\`f2\`.\`film_id\`) as count from \`film\` as \`f2\`) as \`f1\`)) as \`page\`"`;

exports[`PaginateBuilder mysql totalCount correct usage 2`] = `Array []`;

exports[`PaginateBuilder postgres aggregate avg 1`] = `"select json_build_object('aggregate', (select json_build_object('avg', json_build_object('rentalRate', coalesce(avg(\\"f1\\".\\"rental_rate\\"), 0))) from (select \\"f2\\".\\"rental_rate\\" from \\"film\\" as \\"f2\\") as \\"f1\\")) as \\"page\\""`;

exports[`PaginateBuilder postgres aggregate avg 2`] = `Array []`;
Expand Down Expand Up @@ -252,6 +256,10 @@ Array [
]
`;

exports[`PaginateBuilder postgres totalCount correct usage 1`] = `"select json_build_object('totalCount', (select coalesce(\\"f1\\".\\"count\\", 0) from (select count(\\"f2\\".\\"film_id\\") as count from \\"film\\" as \\"f2\\") as \\"f1\\")) as \\"page\\""`;

exports[`PaginateBuilder postgres totalCount correct usage 2`] = `Array []`;

exports[`PaginateBuilder sqlite aggregate avg 1`] = `"select json_object('aggregate', (select json_object('avg', json_object('rentalRate', coalesce(avg(\`f1\`.\`rental_rate\`), 0))) from (select \`f2\`.\`rental_rate\` from \`film\` as \`f2\`) as \`f1\`)) as \`page\`"`;

exports[`PaginateBuilder sqlite aggregate avg 2`] = `Array []`;
Expand Down Expand Up @@ -379,3 +387,7 @@ Array [
"FILM",
]
`;

exports[`PaginateBuilder sqlite totalCount correct usage 1`] = `"select json_object('totalCount', (select coalesce(\`f1\`.\`count\`, 0) from (select count(\`f2\`.\`film_id\`) as count from \`film\` as \`f2\`) as \`f1\`)) as \`page\`"`;

exports[`PaginateBuilder sqlite totalCount correct usage 2`] = `Array []`;
17 changes: 17 additions & 0 deletions src/queryBuilder/__tests__/paginate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,5 +226,22 @@ describe('PaginateBuilder', () => {
expect(bindings).toMatchSnapshot()
})
})

describe('totalCount', () => {
test('correct usage', async () => {
const builder = client.models.Film.paginate().totalCount()
const builder2 = client.models.Film.paginate().offset(10).totalCount()
const builder3 = client.models.Film.paginate().limit(10).totalCount()
const { sql, bindings } = builder.toQueryBuilder().toSQL()
const result = await builder.execute()
const result2 = await builder2.execute()
const result3 = await builder3.execute()
expect(result.totalCount).toBeNumber()
expect(result.totalCount).toBe(result2.totalCount)
expect(result.totalCount).toBe(result3.totalCount)
expect(sql).toMatchSnapshot()
expect(bindings).toMatchSnapshot()
})
})
})
})
6 changes: 3 additions & 3 deletions src/queryBuilder/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ export abstract class BaseBuilder {
return { orderByColumnName, direction, selectExpression }
}

protected _applyExpressions(query: Knex.QueryBuilder, expressions: Expressions) {
protected _applyExpressions(query: Knex.QueryBuilder, expressions: Expressions, includeLimitOffset = true) {
expressions.join.forEach((join) => (query as any)[join.type](join.table, join.on))

expressions.where.forEach((whereArgs) => (query.where as any)(...whereArgs))
Expand All @@ -495,11 +495,11 @@ export abstract class BaseBuilder {
query.orderBy(expressions.orderBy as any)
}

if (this._limit) {
if (this._limit && includeLimitOffset) {
query.limit(this._limit)
}

if (this._offset) {
if (this._offset && includeLimitOffset) {
query.offset(this._offset)
}
}
Expand Down

0 comments on commit 32fe4be

Please sign in to comment.