Skip to content

Commit

Permalink
Create a third options argument and allow users to specify a `maxPa…
Browse files Browse the repository at this point in the history
…ges` setting
  • Loading branch information
jetzhou committed Feb 13, 2024
1 parent 80b4777 commit 5045792
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 1 deletion.
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,35 @@ await octokit.graphql.paginate(
);
```

### Options

You can provide a third argument to `paginate` or `iterator` to modify the behavior of the pagination.

`maxPages` will stop the iteration at the specified number of pages, useful when you don't need all the items in the response but still want to take advantage of the automatic merging.

```
const { repository } = await octokit.graphql.paginate(
`query paginate($cursor: String) {
repository(owner: "octokit", name: "rest.js") {
issues(first: 10, after: $cursor) {
nodes {
title
}
pageInfo {
hasNextPage
endCursor
}
}
}
}`,
{ },
{ maxPages: 10 },
);
```

### Pagination Direction

You can control the pagination direction by the properties deinfed in the `pageInfo` resource.
You can control the pagination direction by the properties defined in the `pageInfo` resource.

For a forward pagination, use:

Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Octokit } from "@octokit/core";
import { createIterator } from "./iterator";
import { createPaginate } from "./paginate";
export type { PageInfoForward, PageInfoBackward } from "./page-info";
export type { Options } from "./options";

export function paginateGraphql(octokit: Octokit) {
octokit.graphql;
Expand Down
9 changes: 9 additions & 0 deletions src/iterator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,28 @@ import { extractPageInfos } from "./extract-page-info";
import { Octokit } from "@octokit/core";
import { getCursorFrom, hasAnotherPage } from "./page-info";
import { MissingCursorChange } from "./errors";
import type { Options } from "./options";

const createIterator = (octokit: Octokit) => {
return <ResponseType = any>(
query: string,
initialParameters: Record<string, any> = {},
options: Options = {},
) => {
let nextPageExists = true;
let parameters = { ...initialParameters };
const { maxPages } = options;
let page = 0;

return {
[Symbol.asyncIterator]: () => ({
async next() {
if (!nextPageExists) return { done: true, value: {} as ResponseType };
if (maxPages && page >= maxPages) {
return { done: true, value: {} as ResponseType };
}

page += 1;

const response = await octokit.graphql<ResponseType>(
query,
Expand Down
3 changes: 3 additions & 0 deletions src/options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type Options = {
maxPages?: number;
};
3 changes: 3 additions & 0 deletions src/paginate.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { Octokit } from "@octokit/core";
import { mergeResponses } from "./merge-responses";
import { createIterator } from "./iterator";
import type { Options } from "./options";

const createPaginate = (octokit: Octokit) => {
const iterator = createIterator(octokit);
return async <ResponseType extends object = any>(
query: string,
initialParameters: Record<string, any> = {},
options: Options = {},
): Promise<ResponseType> => {
let mergedResponse: ResponseType = {} as ResponseType;
for await (const response of iterator<ResponseType>(
query,
initialParameters,
options,
)) {
mergedResponse = mergeResponses<ResponseType>(mergedResponse, response);
}
Expand Down
39 changes: 39 additions & 0 deletions test/paginate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,45 @@ describe("pagination", () => {
]);
});

it(".paginate.iterator() allows users to pass `maxPages` parameter and stops at the right place.", async (): Promise<void> => {
const responses = createResponsePages({ amount: 3 });

const { octokit, getCallCount, getPassedVariablesForCall } = MockOctokit({
responses,
});

const actualResponse = await octokit.graphql.paginate<TestResponseType>(
`
query paginate ($cursor: String) {
repository(owner: "octokit", name: "rest.js") {
issues(first: 10, after: $cursor) {
nodes {
title
}
pageInfo {
hasNextPage
endCursor
}
}
}
}`,
{},
{ maxPages: 2 },
);

expect(actualResponse).toEqual({
repository: {
issues: {
nodes: [{ title: "Issue 1" }, { title: "Issue 2" }],
pageInfo: { hasNextPage: true, endCursor: "endCursor2" },
},
},
});
expect(getCallCount()).toBe(2);
expect(getPassedVariablesForCall(1)).toBeUndefined();
expect(getPassedVariablesForCall(2)).toEqual({ cursor: "endCursor1" });
});

it("paginate() throws error with path and variable name if cursors do not change between calls.", async (): Promise<void> => {
const [responsePage1, responsePage2] = createResponsePages({ amount: 2 });
responsePage2.repository.issues.pageInfo = {
Expand Down

0 comments on commit 5045792

Please sign in to comment.