Skip to content

Commit 46011f0

Browse files
committed
msw: Add seek pagination support for GET /api/v1/crates/:name/versions
1 parent 22124a8 commit 46011f0

File tree

2 files changed

+103
-2
lines changed

2 files changed

+103
-2
lines changed

packages/crates-io-msw/handlers/versions/list.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,35 @@ export default http.get('/api/v1/crates/:name/versions', async ({ request, param
2727

2828
let include = url.searchParams.get('include') ?? '';
2929
let includes = include ? include.split(',') : [];
30-
31-
let serializedVersions = versions.map(v => serializeVersion(v, { includePublishedBy: true }));
3230
let meta = { total, next_page: null };
3331

3432
if (includes.includes('release_tracks')) {
3533
meta.release_tracks = calculateReleaseTracks(versions);
3634
}
3735

36+
// seek pagination
37+
// A simplified seek encoding is applied here for testing purposes only. It should be opaque in
38+
// real-world scenarios.
39+
let next_seek = null;
40+
let per_page = url.searchParams.get('per_page');
41+
if (per_page != null) {
42+
let seek = url.searchParams.get('seek');
43+
if (seek != null) {
44+
let start = versions.findIndex(it => it.num === seek);
45+
versions = versions.slice(start + 1);
46+
}
47+
versions = versions.slice(0, parseInt(per_page));
48+
49+
if (versions.length == per_page) {
50+
next_seek = versions.at(-1).num;
51+
}
52+
}
53+
if (next_seek) {
54+
let next_params = new URLSearchParams(url.searchParams);
55+
next_params.set('seek', next_seek);
56+
meta.next_page = `?${next_params}`;
57+
}
58+
59+
let serializedVersions = versions.map(v => serializeVersion(v, { includePublishedBy: true }));
3860
return HttpResponse.json({ versions: serializedVersions, meta });
3961
});

packages/crates-io-msw/handlers/versions/list.test.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,85 @@ test('supports multiple `ids[]` parameters', async function () {
158158
);
159159
});
160160

161+
test('supports seek pagination', async function () {
162+
let user = db.user.create();
163+
let crate = db.crate.create({ name: 'rand' });
164+
db.version.create({ crate, num: '1.0.0' });
165+
db.version.create({ crate, num: '2.0.0-alpha', publishedBy: user });
166+
db.version.create({ crate, num: '1.1.0', rust_version: '1.69' });
167+
168+
async function seek_forwards(queryParams) {
169+
let calls = 0;
170+
let next_page;
171+
let responses = [];
172+
let base_url = '/api/v1/crates/rand/versions';
173+
let params = new URLSearchParams(queryParams);
174+
let url = `${base_url}?${params}`;
175+
while ((calls == 0 || next_page) && calls < 50) {
176+
if (next_page) {
177+
url = `${base_url}${next_page}`;
178+
}
179+
let response = await fetch(url);
180+
calls += 1;
181+
assert.strictEqual(response.status, 200);
182+
let json = await response.json();
183+
responses.push(json);
184+
next_page = json.meta.next_page;
185+
if (next_page == null) {
186+
break;
187+
}
188+
}
189+
return responses;
190+
}
191+
192+
// sort by `semver` by default
193+
{
194+
let responses = await seek_forwards({ per_page: 1 });
195+
assert.deepEqual(
196+
responses.map(it => it.versions.map(v => v.num)),
197+
[['2.0.0-alpha'], ['1.1.0'], ['1.0.0'], []],
198+
);
199+
assert.deepEqual(
200+
responses.map(it => it.meta.next_page),
201+
['?per_page=1&seek=2.0.0-alpha', '?per_page=1&seek=1.1.0', '?per_page=1&seek=1.0.0', null],
202+
);
203+
}
204+
205+
{
206+
let responses = await seek_forwards({ per_page: 1, sort: 'semver' });
207+
assert.deepEqual(
208+
responses.map(it => it.versions.map(v => v.num)),
209+
[['2.0.0-alpha'], ['1.1.0'], ['1.0.0'], []],
210+
);
211+
assert.deepEqual(
212+
responses.map(it => it.meta.next_page),
213+
[
214+
'?per_page=1&sort=semver&seek=2.0.0-alpha',
215+
'?per_page=1&sort=semver&seek=1.1.0',
216+
'?per_page=1&sort=semver&seek=1.0.0',
217+
null,
218+
],
219+
);
220+
}
221+
222+
{
223+
let responses = await seek_forwards({ per_page: 1, sort: 'date' });
224+
assert.deepEqual(
225+
responses.map(it => it.versions.map(v => v.num)),
226+
[['1.1.0'], ['2.0.0-alpha'], ['1.0.0'], []],
227+
);
228+
assert.deepEqual(
229+
responses.map(it => it.meta.next_page),
230+
[
231+
'?per_page=1&sort=date&seek=1.1.0',
232+
'?per_page=1&sort=date&seek=2.0.0-alpha',
233+
'?per_page=1&sort=date&seek=1.0.0',
234+
null,
235+
],
236+
);
237+
}
238+
});
239+
161240
test('include `release_tracks` meta', async function () {
162241
let user = db.user.create();
163242
let crate = db.crate.create({ name: 'rand' });

0 commit comments

Comments
 (0)