Skip to content

Commit 1cddd52

Browse files
jaulzszmarczaksindresorhus
authoredMar 9, 2020
Pass allItems and currentItems to _pagination.paginate() (#1100)
Co-authored-by: Szymon Marczak <36894700+szmarczak@users.noreply.github.com> Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
1 parent 88f973f commit 1cddd52

File tree

4 files changed

+101
-12
lines changed

4 files changed

+101
-12
lines changed
 

‎readme.md

+45-4
Original file line numberDiff line numberDiff line change
@@ -682,23 +682,64 @@ A function that transform [`Response`](#response) into an array of items. This i
682682
Type: `Function`\
683683
Default: [`Link` header logic](source/index.ts)
684684

685-
A function that returns an object representing Got options pointing to the next page. If there are no more pages, `false` should be returned.
685+
The function takes three arguments:
686+
- `response` - The current response object.
687+
- `allItems` - An array of the emitted items.
688+
- `currentItems` - Items from the current response.
689+
690+
It should return an object representing Got options pointing to the next page. If there are no more pages, `false` should be returned.
691+
692+
For example, if you want to stop when the response contains less items than expected, you can use something like this:
693+
694+
```js
695+
const got = require('got');
696+
697+
(async () => {
698+
const limit = 10;
699+
700+
const items = got.paginate('https://example.com/items', {
701+
searchParams: {
702+
limit,
703+
offset: 0
704+
},
705+
_pagination: {
706+
paginate: (response, allItems, currentItems) => {
707+
const previousSearchParams = response.request.options.searchParams;
708+
const {offset: previousOffset} = previousSearchParams;
709+
710+
if (currentItems.length < limit) {
711+
return false;
712+
}
713+
714+
return {
715+
searchParams: {
716+
...previousSearchParams,
717+
offset: previousOffset + limit,
718+
}
719+
};
720+
}
721+
}
722+
});
723+
724+
console.log('Items from all pages:', items);
725+
})();
726+
```
686727

687728
###### \_pagination.filter
688729

689730
Type: `Function`\
690-
Default: `(item, allItems) => true`
731+
Default: `(item, allItems, currentItems) => true`
691732

692733
Checks whether the item should be emitted or not.
693734

694735
###### \_pagination.shouldContinue
695736

696737
Type: `Function`\
697-
Default: `(item, allItems) => true`
738+
Default: `(item, allItems, currentItems) => true`
698739

699740
Checks whether the pagination should continue.
700741

701-
For example, if you need to stop **before** emitting an entry with some flag, you should use `(item, allItems) => !item.flag`. If you want to stop **after** emitting the entry, you should use `(item, allItems) => allItems.some(entry => entry.flag)` instead.
742+
For example, if you need to stop **before** emitting an entry with some flag, you should use `(item, allItems, currentItems) => !item.flag`. If you want to stop **after** emitting the entry, you should use `(item, allItems, currentItems) => allItems.some(entry => entry.flag)` instead.
702743

703744
###### \_pagination.countLimit
704745

‎source/create.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -216,24 +216,26 @@ const create = (defaults: Defaults): Got => {
216216

217217
// eslint-disable-next-line no-await-in-loop
218218
const parsed = await pagination.transform!(result);
219+
const current: T[] = [];
219220

220221
for (const item of parsed) {
221-
if (pagination.filter!(item, all)) {
222-
if (!pagination.shouldContinue!(item, all)) {
222+
if (pagination.filter!(item, all, current)) {
223+
if (!pagination.shouldContinue!(item, all, current)) {
223224
return;
224225
}
225226

226227
yield item;
227228

228229
all.push(item as T);
230+
current.push(item as T);
229231

230232
if (all.length === pagination.countLimit) {
231233
return;
232234
}
233235
}
234236
}
235237

236-
const optionsToMerge = pagination.paginate!(result);
238+
const optionsToMerge = pagination.paginate!(result, all, current);
237239

238240
if (optionsToMerge === false) {
239241
return;

‎source/types.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,9 @@ export type DefaultOptions = Merge<
163163
export interface PaginationOptions<T> {
164164
_pagination?: {
165165
transform?: (response: Response) => Promise<T[]> | T[];
166-
filter?: (item: T, allItems: T[]) => boolean;
167-
paginate?: (response: Response) => Options | false;
168-
shouldContinue?: (item: T, allItems: T[]) => boolean;
166+
filter?: (item: T, allItems: T[], currentItems: T[]) => boolean;
167+
paginate?: (response: Response, allItems: T[], currentItems: T[]) => Options | false;
168+
shouldContinue?: (item: T, allItems: T[], currentItems: T[]) => boolean;
169169
countLimit?: number;
170170
};
171171
}

‎test/pagination.ts

+48-2
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,12 @@ test('filters elements', withServer, async (t, server, got) => {
8282

8383
const result = await got.paginate.all({
8484
_pagination: {
85-
filter: element => element !== 2
85+
filter: (element, allItems, currentItems) => {
86+
t.true(Array.isArray(allItems));
87+
t.true(Array.isArray(currentItems));
88+
89+
return element !== 2;
90+
}
8691
}
8792
});
8893

@@ -131,6 +136,42 @@ test('custom paginate function', withServer, async (t, server, got) => {
131136
t.deepEqual(result, [1, 3]);
132137
});
133138

139+
test('custom paginate function using allItems', withServer, async (t, server, got) => {
140+
attachHandler(server, 3);
141+
142+
const result = await got.paginate.all({
143+
_pagination: {
144+
paginate: (_response, allItems) => {
145+
if (allItems.length === 2) {
146+
return false;
147+
}
148+
149+
return {path: '/?page=3'};
150+
}
151+
}
152+
});
153+
154+
t.deepEqual(result, [1, 3]);
155+
});
156+
157+
test('custom paginate function using currentItems', withServer, async (t, server, got) => {
158+
attachHandler(server, 3);
159+
160+
const result = await got.paginate.all({
161+
_pagination: {
162+
paginate: (_response, _allItems, currentItems) => {
163+
if (currentItems[0] === 3) {
164+
return false;
165+
}
166+
167+
return {path: '/?page=3'};
168+
}
169+
}
170+
});
171+
172+
t.deepEqual(result, [1, 3]);
173+
});
174+
134175
test('iterator works', withServer, async (t, server, got) => {
135176
attachHandler(server, 5);
136177

@@ -148,7 +189,12 @@ test('`shouldContinue` works', withServer, async (t, server, got) => {
148189

149190
const options = {
150191
_pagination: {
151-
shouldContinue: () => false
192+
shouldContinue: (_element: unknown, allItems: unknown[], currentItems: unknown[]) => {
193+
t.true(Array.isArray(allItems));
194+
t.true(Array.isArray(currentItems));
195+
196+
return false;
197+
}
152198
}
153199
};
154200

0 commit comments

Comments
 (0)
Please sign in to comment.