-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathautoPagination.ts
109 lines (101 loc) · 3.23 KB
/
autoPagination.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// Copyright 2020 Cognite AS
import type {
AutoPagingEach,
AutoPagingEachHandler,
AutoPagingToArrayOptions,
CogniteAsyncIterator,
ListResponse,
} from './types';
/**
* The `makeAutoPaginationMethods` function generates methods for automatically
* paginating through API responses. This is particularly useful when dealing with
* APIs that return large sets of data across multiple pages, allowing you to
* seamlessly iterate through all available data without manually handling pagination.
*
* This function typically creates methods that:
* - Fetch the next page of results based on the current page's metadata.
* - Aggregate results from multiple pages into a single iterable or array.
*
* @param apiClient - The API client instance used to make requests.
* @param initialRequest - The initial request object containing the parameters for the first page.
* @returns An iterable or async iterable that yields items from the paginated response.
*
* @hidden
*/
export function makeAutoPaginationMethods<T>(
firstPagePromise: Promise<ListResponse<T[]>>
) {
let listPromise = firstPagePromise;
let i = 0;
function iterate(
listResult: ListResponse<T[]>
): IteratorResult<T> | Promise<IteratorResult<T>> {
if (i < listResult.items.length) {
return {
value: listResult.items[i++],
done: false,
};
}
if (listResult.next) {
// reset counter, request next page, and recurse.
i = 0;
listPromise = listResult.next();
return listPromise.then(iterate);
}
return { value: undefined, done: true };
}
async function asyncIteratorNext(): Promise<IteratorResult<T>> {
const listResult = await listPromise;
return iterate(listResult);
}
const autoPagingEach = makeAutoPagingEach(asyncIteratorNext);
const autoPagingToArray = makeAutoPagingToArray(autoPagingEach);
const autoPaginationMethods: CogniteAsyncIterator<T> = {
autoPagingEach,
autoPagingToArray,
// Async iterator functions:
next: asyncIteratorNext,
// biome-ignore lint/suspicious/noExplicitAny: didn't find the right type in reasonable timeframe
return(): any {
// This is required for `break`.
return {};
},
[Symbol.asyncIterator]: () => autoPaginationMethods,
};
return autoPaginationMethods;
}
function makeAutoPagingEach<T>(
asyncIteratorNext: () => Promise<IteratorResult<T>>
): AutoPagingEach<T> {
return async function autoPagingEach(handler: AutoPagingEachHandler<T>) {
async function iterate() {
const iterResult = await asyncIteratorNext();
if (iterResult.done) {
return;
}
const item = iterResult.value;
const shouldContinue = await handler(item);
if (shouldContinue === false) {
return;
}
await iterate();
}
await iterate();
};
}
function makeAutoPagingToArray<T>(autoPagingEach: AutoPagingEach<T>) {
return async function autoPagingToArray(options?: AutoPagingToArrayOptions) {
let limit = options?.limit || 25;
if (limit === -1) {
limit = Number.POSITIVE_INFINITY;
}
const items: T[] = [];
await autoPagingEach(async (item) => {
items.push(item);
if (items.length >= limit) {
return false;
}
});
return items;
};
}