Skip to content

Commit edabd5c

Browse files
committed
msw: Implement GET /api/v1/crates/:name/reverse_dependencies request handler
1 parent e3a2ebc commit edabd5c

File tree

3 files changed

+190
-0
lines changed

3 files changed

+190
-0
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import followCrate from './crates/follow.js';
44
import following from './crates/following.js';
55
import getCrate from './crates/get.js';
66
import listCrates from './crates/list.js';
7+
import reverseDependencies from './crates/reverse-dependencies.js';
78
import teamOwners from './crates/team-owners.js';
89
import unfollowCrate from './crates/unfollow.js';
910
import userOwners from './crates/user-owners.js';
@@ -17,5 +18,6 @@ export default [
1718
unfollowCrate,
1819
userOwners,
1920
teamOwners,
21+
reverseDependencies,
2022
downloads,
2123
];
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { http, HttpResponse } from 'msw';
2+
3+
import { db } from '../../index.js';
4+
import { serializeDependency } from '../../serializers/dependency.js';
5+
import { serializeVersion } from '../../serializers/version.js';
6+
import { notFound, pageParams } from '../../utils/handlers.js';
7+
8+
export default http.get('/api/v1/crates/:name/reverse_dependencies', async ({ request, params }) => {
9+
let crate = db.crate.findFirst({ where: { name: { equals: params.name } } });
10+
if (!crate) return notFound();
11+
12+
let { start, end } = pageParams(request);
13+
14+
let allDependencies = db.dependency.findMany({
15+
where: { crate: { id: { equals: crate.id } } },
16+
orderBy: { version: { crate: { downloads: 'desc' } } },
17+
});
18+
19+
let dependencies = allDependencies.slice(start, end);
20+
let total = allDependencies.length;
21+
22+
let versions = dependencies.map(d => d.version);
23+
24+
return HttpResponse.json({
25+
dependencies: dependencies.map(d => serializeDependency(d)),
26+
versions: versions.map(v => serializeVersion(v)),
27+
meta: { total },
28+
});
29+
});
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import { assert, test } from 'vitest';
2+
3+
import { db } from '../../index.js';
4+
5+
test('returns 404 for unknown crates', async function () {
6+
let response = await fetch('/api/v1/crates/foo/reverse_dependencies');
7+
assert.strictEqual(response.status, 404);
8+
assert.deepEqual(await response.json(), { errors: [{ detail: 'Not Found' }] });
9+
});
10+
11+
test('empty case', async function () {
12+
db.crate.create({ name: 'rand' });
13+
14+
let response = await fetch('/api/v1/crates/rand/reverse_dependencies');
15+
assert.strictEqual(response.status, 200);
16+
assert.deepEqual(await response.json(), {
17+
dependencies: [],
18+
versions: [],
19+
meta: {
20+
total: 0,
21+
},
22+
});
23+
});
24+
25+
test('returns a paginated list of crate versions depending to the specified crate', async function () {
26+
let crate = db.crate.create({ name: 'foo' });
27+
28+
db.dependency.create({
29+
crate,
30+
version: db.version.create({
31+
crate: db.crate.create({ name: 'bar' }),
32+
}),
33+
});
34+
35+
db.dependency.create({
36+
crate,
37+
version: db.version.create({
38+
crate: db.crate.create({ name: 'baz' }),
39+
}),
40+
});
41+
42+
let response = await fetch('/api/v1/crates/foo/reverse_dependencies');
43+
assert.strictEqual(response.status, 200);
44+
assert.deepEqual(await response.json(), {
45+
dependencies: [
46+
{
47+
id: 2,
48+
crate_id: 'foo',
49+
default_features: false,
50+
features: [],
51+
kind: 'normal',
52+
optional: true,
53+
req: '0.3.7',
54+
target: null,
55+
version_id: 2,
56+
},
57+
{
58+
id: 1,
59+
crate_id: 'foo',
60+
default_features: false,
61+
features: [],
62+
kind: 'normal',
63+
optional: true,
64+
req: '^2.1.3',
65+
target: null,
66+
version_id: 1,
67+
},
68+
],
69+
versions: [
70+
{
71+
id: 2,
72+
crate: 'baz',
73+
crate_size: 325_926,
74+
created_at: '2010-06-16T21:30:45Z',
75+
dl_path: '/api/v1/crates/baz/1.0.1/download',
76+
downloads: 7404,
77+
features: {},
78+
license: 'Apache-2.0',
79+
links: {
80+
dependencies: '/api/v1/crates/baz/1.0.1/dependencies',
81+
version_downloads: '/api/v1/crates/baz/1.0.1/downloads',
82+
},
83+
num: '1.0.1',
84+
published_by: null,
85+
readme_path: '/api/v1/crates/baz/1.0.1/readme',
86+
rust_version: null,
87+
updated_at: '2017-02-24T12:34:56Z',
88+
yanked: false,
89+
yank_message: null,
90+
},
91+
{
92+
id: 1,
93+
crate: 'bar',
94+
crate_size: 162_963,
95+
created_at: '2010-06-16T21:30:45Z',
96+
dl_path: '/api/v1/crates/bar/1.0.0/download',
97+
downloads: 3702,
98+
features: {},
99+
license: 'MIT',
100+
links: {
101+
dependencies: '/api/v1/crates/bar/1.0.0/dependencies',
102+
version_downloads: '/api/v1/crates/bar/1.0.0/downloads',
103+
},
104+
num: '1.0.0',
105+
published_by: null,
106+
readme_path: '/api/v1/crates/bar/1.0.0/readme',
107+
rust_version: null,
108+
updated_at: '2017-02-24T12:34:56Z',
109+
yanked: false,
110+
yank_message: null,
111+
},
112+
],
113+
meta: {
114+
total: 2,
115+
},
116+
});
117+
});
118+
119+
test('never returns more than 10 results', async function () {
120+
let crate = db.crate.create({ name: 'foo' });
121+
122+
Array.from({ length: 25 }, () =>
123+
db.dependency.create({
124+
crate,
125+
version: db.version.create({
126+
crate: db.crate.create({ name: 'bar' }),
127+
}),
128+
}),
129+
);
130+
131+
let response = await fetch('/api/v1/crates/foo/reverse_dependencies');
132+
assert.strictEqual(response.status, 200);
133+
134+
let responsePayload = await response.json();
135+
assert.strictEqual(responsePayload.dependencies.length, 10);
136+
assert.strictEqual(responsePayload.versions.length, 10);
137+
assert.strictEqual(responsePayload.meta.total, 25);
138+
});
139+
140+
test('supports `page` and `per_page` parameters', async function () {
141+
let crate = db.crate.create({ name: 'foo' });
142+
143+
let crates = Array.from({ length: 25 }, (_, i) =>
144+
db.crate.create({ name: `crate-${String(i + 1).padStart(2, '0')}` }),
145+
);
146+
let versions = crates.map(crate => db.version.create({ crate }));
147+
versions.forEach(version => db.dependency.create({ crate, version }));
148+
149+
let response = await fetch('/api/v1/crates/foo/reverse_dependencies?page=2&per_page=5');
150+
assert.strictEqual(response.status, 200);
151+
152+
let responsePayload = await response.json();
153+
assert.strictEqual(responsePayload.dependencies.length, 5);
154+
assert.deepEqual(
155+
responsePayload.versions.map(it => it.crate),
156+
['crate-24', 'crate-02', 'crate-15', 'crate-06', 'crate-19'],
157+
);
158+
assert.strictEqual(responsePayload.meta.total, 25);
159+
});

0 commit comments

Comments
 (0)