From 773b31f92aff8192aba50fb667d0451a098ed07d Mon Sep 17 00:00:00 2001 From: Libor Krzyzanek Date: Tue, 5 Dec 2023 15:28:43 +0100 Subject: [PATCH] feat: Better support for Offset Pagination #140 --- .gitignore | 1 + examples/in-memory/server.js | 2 +- examples/mongodb/package.json | 2 +- examples/mongodb/src/datasources/booksApi.ts | 1 + packages/core/__tests__/test-offset-ds.ts | 33 +++++++++++++------ packages/core/src/CursorPagerSpec.ts | 2 ++ packages/core/src/DataSourcePager.ts | 6 ++-- .../src/datasource/OffsetDataSourceWrapper.ts | 6 ++-- packages/mongodb/package.json | 2 +- 9 files changed, 38 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 0aa39c5..00217ea 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ junit.xml # Node modules node_modules/ +.nx # Mac OS .DS_Store diff --git a/examples/in-memory/server.js b/examples/in-memory/server.js index c10ae7b..3b885a1 100644 --- a/examples/in-memory/server.js +++ b/examples/in-memory/server.js @@ -92,7 +92,7 @@ const typeDefs = gql` booksByTitle(first: Int = 10 after: String title: String): BookConnection booksByAuthor(first: Int = 10 after: String author: String): BookConnection - booksByOffset(first: Int = 10 after: String): BookConnection + booksByOffset(first: Int = 10, after: String, page: Int): BookConnection booksDynamic(first: Int = 10 after: String): BookConnection } diff --git a/examples/mongodb/package.json b/examples/mongodb/package.json index 561b43b..80a09a8 100644 --- a/examples/mongodb/package.json +++ b/examples/mongodb/package.json @@ -21,7 +21,7 @@ "graphql": "^16.6.0", "graphql-tag": "2.12.6", "mongodb": "^5.1.0", - "mongodb-memory-server": "^8.12.1" + "mongodb-memory-server": "^9.1.1" }, "devDependencies": { "@graphql-codegen/cli": "^3.2.2", diff --git a/examples/mongodb/src/datasources/booksApi.ts b/examples/mongodb/src/datasources/booksApi.ts index 0fdebb2..336b608 100644 --- a/examples/mongodb/src/datasources/booksApi.ts +++ b/examples/mongodb/src/datasources/booksApi.ts @@ -8,6 +8,7 @@ const collectionName = "books"; export async function createBooksDataSource(mongoDb: Db) { const ds = new MongoDbDataSource({ collectionName, + //@ts-ignore mongoDb, filters: (args) => { const filters = []; diff --git a/packages/core/__tests__/test-offset-ds.ts b/packages/core/__tests__/test-offset-ds.ts index 8629a5f..05836ae 100644 --- a/packages/core/__tests__/test-offset-ds.ts +++ b/packages/core/__tests__/test-offset-ds.ts @@ -33,35 +33,48 @@ describe("offset-totalCount", () => { }); describe("offset-after", () => { + const array = [{"id": 1}, {"id": 2}, {"id": 3}, {"id": 4}]; test("no-offset", async () => { const ds = new ArrayOffsetDs([{"id": 1}]); const offset = new OffsetDataSourceWrapper(ds); return expect(offset.after(undefined, 10, { first: 0 })).resolves.toStrictEqual([{"_index": 0, "id": 1}]); }) - test("offset", async () => { - const ds = new ArrayOffsetDs([{"id": 1}, {"id": 2}, {"id": 3}, {"id": 4}]); + test("offset-arg-first", async () => { + const ds = new ArrayOffsetDs(array); + const offset = new OffsetDataSourceWrapper(ds); + return expect(offset.after(2, 1, { first: 0 })).resolves.toStrictEqual([{"_index": 3, "id": 4}]); + }) + test("offset-arg-page-1", async () => { + const ds = new ArrayOffsetDs(array); + const offset = new OffsetDataSourceWrapper(ds); + return expect(offset.after(undefined, 2, { page: 1 })).resolves.toStrictEqual([{"_index": 0, "id": 1}, { "_index": 1, "id": 2 }]); + }) + test("offset-arg-page-3", async () => { + const ds = new ArrayOffsetDs(array); const offset = new OffsetDataSourceWrapper(ds); - return expect(offset.after(2, 1, { first: 0 })).resolves.toStrictEqual([ - {"_index": 3, "id": 4} - ]); + return expect(offset.after(undefined, 3, { page: 2 })).resolves.toStrictEqual([{ "_index": 2, "id": 3 }, {"_index": 3,"id": 4}]); }) }); describe("offset-before", () => { + const array = [{"id": 1}, {"id": 2}, {"id": 3}, {"id": 4}]; test("no-offset", async () => { const ds = new ArrayOffsetDs([{"id": 1}]); const offset = new OffsetDataSourceWrapper(ds); return expect(offset.before(undefined, 10, { last: 0 })).resolves.toStrictEqual([{"_index": 0, "id": 1}]); }) - test("offset", async () => { - const ds = new ArrayOffsetDs([{"id": 1}, {"id": 2}, {"id": 3}, {"id": 4}]); + test("offset-arg-last", async () => { + const ds = new ArrayOffsetDs(array); + const offset = new OffsetDataSourceWrapper(ds); + return expect(offset.before(2, 1, { last: 0 })).resolves.toStrictEqual([{"_index": 3, "id": 1}]); + }) + test("offset-arg-page-1", async () => { + const ds = new ArrayOffsetDs(array); const offset = new OffsetDataSourceWrapper(ds); - return expect(offset.before(2, 1, { last: 0 })).resolves.toStrictEqual([ - {"_index": 3, "id": 1}, - ]); + return expect(offset.before(undefined, 3, { page: 2 })).resolves.toStrictEqual([{ "_index": 2, "id": 2 }, {"_index": 3,"id": 1}]); }) }); diff --git a/packages/core/src/CursorPagerSpec.ts b/packages/core/src/CursorPagerSpec.ts index 264cc79..705b45d 100644 --- a/packages/core/src/CursorPagerSpec.ts +++ b/packages/core/src/CursorPagerSpec.ts @@ -39,6 +39,7 @@ export interface PageInfo { export interface ArgsForward { first: number after?: string + page?: number } /** @@ -48,6 +49,7 @@ export interface ArgsForward { export interface ArgsBackward { last: number, before?: string + page?: number } diff --git a/packages/core/src/DataSourcePager.ts b/packages/core/src/DataSourcePager.ts index 2c236b1..e24aacb 100644 --- a/packages/core/src/DataSourcePager.ts +++ b/packages/core/src/DataSourcePager.ts @@ -88,7 +88,8 @@ export function dataSourcePager args.first; - const hasPreviousPage = !!args.after; + let hasPreviousPage = !!args.after; + if (args.page) hasPreviousPage = args.page > 1; let count; if (fetchTotalCountInResolver) count = await totalCount(args, ds); @@ -110,7 +111,8 @@ export function dataSourcePager args.last; - const hasPreviousPage = !!args.before; + let hasPreviousPage = !!args.before; + if (args.page) hasPreviousPage = args.page > 1; let count; if (fetchTotalCountInResolver) count = await totalCount(args, ds); diff --git a/packages/core/src/datasource/OffsetDataSourceWrapper.ts b/packages/core/src/datasource/OffsetDataSourceWrapper.ts index 8f39ba6..fe8e79f 100644 --- a/packages/core/src/datasource/OffsetDataSourceWrapper.ts +++ b/packages/core/src/datasource/OffsetDataSourceWrapper.ts @@ -19,12 +19,14 @@ export class OffsetDataSourceWrapper { - const start = afterIndex != null ? afterIndex + 1 : 0; + let start = afterIndex != null ? afterIndex + 1 : 0; + start = args.page && args.page > 1 ? ((args.page - 1) * (size - 1)) : start; return this.ds.after(start, size, args).then(data => this.addIndexField(start, data)); } async before(beforeIndex: number | undefined, size: number, args: ArgsBackwardType): Promise { - const start = beforeIndex != null ? beforeIndex + 1 : 0; + let start = beforeIndex != null ? beforeIndex + 1 : 0; + start = args.page && args.page > 1 ? ((args.page - 1) * (size - 1)) : start; return this.ds.before(start, size, args).then(data => this.addIndexField(start, data)); } diff --git a/packages/mongodb/package.json b/packages/mongodb/package.json index 0e151cd..536c088 100644 --- a/packages/mongodb/package.json +++ b/packages/mongodb/package.json @@ -50,6 +50,6 @@ "devDependencies": { "@types/node": "^18.15.11", "mongodb": "^5.1.0", - "mongodb-memory-server": "^9.0.0" + "mongodb-memory-server": "^9.1.1" } }