Skip to content

Commit 23f5e51

Browse files
imranolasCopilot
andauthored
fix(cli): handle pagination without total metadata (#249)
Rather than rely on the self reported pages and totals which appears to be inconsistent, we can instead infer if there are any remaining pages by counting the results. Resolves #238 Resolves #248 --------- Co-authored-by: Copilot <[email protected]>
1 parent f0af3ba commit 23f5e51

File tree

2 files changed

+56
-6
lines changed

2 files changed

+56
-6
lines changed

packages/cli/src/commands/stories/actions.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,52 @@ describe('stories/actions', () => {
270270
);
271271
});
272272
});
273+
274+
it('should handle pagination when per_page and total are missing from response', async () => {
275+
// Mock pages
276+
const page1Stories = Array.from({ length: 100 }, (_, i) => ({
277+
...mockStories[0],
278+
id: i + 1,
279+
name: `Story ${i + 1}`,
280+
uuid: `uuid-${i + 1}`,
281+
}));
282+
const page2Stories = Array.from({ length: 50 }, (_, i) => ({
283+
...mockStories[0],
284+
id: i + 101,
285+
name: `Story ${i + 101}`,
286+
uuid: `uuid-${i + 101}`,
287+
}));
288+
289+
// Override handler to simulate API without pagination metadata
290+
server.use(
291+
http.get('https://api.storyblok.com/v1/spaces/:spaceId/stories', ({ request }) => {
292+
const url = new URL(request.url);
293+
const page = Number.parseInt(url.searchParams.get('page') || '1', 10);
294+
295+
if (page === 1) {
296+
// First page returns 100 stories (no per_page and total fields)
297+
return HttpResponse.json({ stories: page1Stories });
298+
}
299+
else if (page === 2) {
300+
// Second page returns 50 stories (less than per_page, indicating end)
301+
return HttpResponse.json({ stories: page2Stories });
302+
}
303+
else {
304+
// Third page returns empty array
305+
return HttpResponse.json({ stories: [] });
306+
}
307+
}),
308+
);
309+
310+
const result = await fetchStories(mockSpace);
311+
312+
// Should fetch all stories from both pages
313+
expect(result).toHaveLength(150);
314+
expect(result?.[0].id).toBe(1);
315+
expect(result?.[99].id).toBe(100);
316+
expect(result?.[100].id).toBe(101);
317+
expect(result?.[149].id).toBe(150);
318+
});
273319
});
274320

275321
describe('fetchStoriesByComponent', () => {

packages/cli/src/commands/stories/actions.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@ export const fetchStories = async (
1919
const allStories: Story[] = [];
2020
let currentPage = 1;
2121
let hasMorePages = true;
22+
const perPage = 100;
2223

2324
while (hasMorePages) {
2425
// Extract filter_query params to handle them separately
2526
const { filter_query, ...restParams } = params || {};
2627

2728
// Handle regular params with URLSearchParams
2829
const regularParams = new URLSearchParams({
29-
...objectToStringParams({ ...restParams, per_page: 100 }),
30+
...objectToStringParams({ ...restParams, per_page: perPage }),
3031
...(currentPage > 1 && { page: currentPage.toString() }),
3132
}).toString();
3233

@@ -39,16 +40,19 @@ export const fetchStories = async (
3940

4041
const { data } = await client.get<{
4142
stories: Story[];
42-
per_page: number;
43-
total: number;
43+
per_page?: number;
44+
total?: number;
4445
}>(endpoint, {
4546
});
4647

4748
allStories.push(...data.stories);
4849

49-
// Check if we have more pages to fetch
50-
const totalPages = Math.ceil(data.total / data.per_page);
51-
hasMorePages = currentPage < totalPages;
50+
// Since per_page and total may not be available, check if we got fewer stories than requested
51+
// If we got fewer stories than per_page, we've reached the end
52+
hasMorePages = data.stories.length === perPage && data.stories.length > 0;
53+
if (data.stories.length < perPage) {
54+
break;
55+
}
5256
currentPage++;
5357
}
5458

0 commit comments

Comments
 (0)