Skip to content

Commit

Permalink
Merge pull request #73 from madscience/fixed-offset
Browse files Browse the repository at this point in the history
Support fixed offset pagination (page = 1...n)
  • Loading branch information
sgarner committed Jun 1, 2020
2 parents e84152f + dd1c91b commit 5c017ec
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 5 deletions.
19 changes: 19 additions & 0 deletions src/cursor/OffsetCursorPaginator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,23 @@ describe('OffsetCursorPaginator', () => {
expect(pageInfo.endCursor).toBeDefined();
expect(OffsetCursor.create(pageInfo.endCursor!).parameters.offset).toStrictEqual(49);
});

test('PageInfo is correct for fixed offset pagination', () => {
const paginator = OffsetCursorPaginator.createFromConnectionArgs(
{
first: 20,
page: 4,
},
100,
);
const pageInfo = paginator.createPageInfo(20);

expect(pageInfo.totalEdges).toBe(100);
expect(pageInfo.hasPreviousPage).toBe(true);
expect(pageInfo.hasNextPage).toBe(true);
expect(pageInfo.startCursor).toBeDefined();
expect(OffsetCursor.create(pageInfo.startCursor!).parameters.offset).toStrictEqual(60);
expect(pageInfo.endCursor).toBeDefined();
expect(OffsetCursor.create(pageInfo.endCursor!).parameters.offset).toStrictEqual(79);
});
});
37 changes: 32 additions & 5 deletions src/cursor/OffsetCursorPaginator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ export class OffsetCursor extends Cursor {
}
}

interface CreateFromConnectionArgsOptions {
defaultEdgesPerPage?: number;
maxEdgesPerPage?: number;
}

export class OffsetCursorPaginator {
public take: number = 20;
public skip: number = 0;
Expand All @@ -62,21 +67,41 @@ export class OffsetCursorPaginator {
}

public static createFromConnectionArgs(
{ first, last, before, after }: ConnectionArgs,
{ page, first, last, before, after }: ConnectionArgs,
totalEdges: number,
options: CreateFromConnectionArgsOptions = {},
): OffsetCursorPaginator {
let take: number = 20;
const { defaultEdgesPerPage = 20, maxEdgesPerPage = 100 } = options;
let take: number = defaultEdgesPerPage;
let skip: number = 0;

if (first != null) {
if (first > 100 || first < 1) {
throw new ConnectionArgsValidationError('The "first" argument accepts a value between 1 and 100, inclusive.');
if (first > maxEdgesPerPage || first < 1) {
throw new ConnectionArgsValidationError(
`The "first" argument accepts a value between 1 and ${maxEdgesPerPage}, inclusive.`,
);
}

take = first;
skip = 0;
}

if (page != null) {
if (last != null || after != null || before != null) {
throw new ConnectionArgsValidationError(
`The "page" argument cannot be used together with "last", "after" or "before".`,
);
}

if (page < 1) {
throw new ConnectionArgsValidationError(
`The "page" argument accepts only a positive integer greater than zero.`,
);
}

skip = take * (page - 1);
}

if (last != null) {
if (first != null) {
throw new ConnectionArgsValidationError(
Expand All @@ -85,7 +110,9 @@ export class OffsetCursorPaginator {
}

if (last > 100 || last < 1) {
throw new ConnectionArgsValidationError('The "last" argument accepts a value between 1 and 100, inclusive.');
throw new ConnectionArgsValidationError(
`The "last" argument accepts a value between 1 and ${maxEdgesPerPage}, inclusive.`,
);
}

take = last;
Expand Down
6 changes: 6 additions & 0 deletions src/type/ConnectionArgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ import * as GQL from '@nestjs/graphql';

@GQL.ArgsType()
export class ConnectionArgs {
@GQL.Field(_type => GQL.Int, {
nullable: true,
description: 'Retrieve page of edges by fixed offset page number.',
})
public page?: number;

@GQL.Field(_type => String, {
nullable: true,
description: 'Retrieve page of edges before opaque cursor.',
Expand Down

0 comments on commit 5c017ec

Please sign in to comment.