Skip to content

Commit 7c3ee6c

Browse files
committed
Auto merge of #4145 - Turbo87:mirage-invites, r=locks
mirage: Implement `GET /api/private/crate_owner_invitations` route handler This will be needed to address #4083
2 parents a658181 + fc175da commit 7c3ee6c

File tree

3 files changed

+280
-0
lines changed

3 files changed

+280
-0
lines changed

mirage/config.js

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as Categories from './route-handlers/categories';
22
import * as Crates from './route-handlers/crates';
33
import * as DocsRS from './route-handlers/docs-rs';
4+
import * as Invites from './route-handlers/invites';
45
import * as Keywords from './route-handlers/keywords';
56
import * as Me from './route-handlers/me';
67
import * as Session from './route-handlers/session';
@@ -12,6 +13,7 @@ export default function () {
1213
Categories.register(this);
1314
Crates.register(this);
1415
DocsRS.register(this);
16+
Invites.register(this);
1517
Keywords.register(this);
1618
Me.register(this);
1719
Session.register(this);

mirage/route-handlers/invites.js

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { Response } from 'ember-cli-mirage';
2+
3+
import { getSession } from '../utils/session';
4+
import { notFound } from './-utils';
5+
6+
export function register(server) {
7+
server.get('/api/private/crate_owner_invitations', function (schema, request) {
8+
let { user } = getSession(schema);
9+
if (!user) {
10+
return new Response(403, {}, { errors: [{ detail: 'must be logged in to perform that action' }] });
11+
}
12+
13+
let invites;
14+
if (request.queryParams['crate_name']) {
15+
let crate = schema.crates.findBy({ name: request.queryParams['crate_name'] });
16+
if (!crate) return notFound();
17+
18+
invites = schema.crateOwnerInvitations.where({ crateId: crate.id });
19+
} else if (request.queryParams['invitee_id']) {
20+
let inviteeId = request.queryParams['invitee_id'];
21+
if (inviteeId !== user.id) {
22+
return new Response(403, {}, { errors: [{ detail: 'must be logged in to perform that action' }] });
23+
}
24+
25+
invites = schema.crateOwnerInvitations.where({ inviteeId });
26+
} else {
27+
return new Response(400, {}, { errors: [{ detail: 'missing or invalid filter' }] });
28+
}
29+
30+
let perPage = 10;
31+
let start = request.queryParams['__start__'] ?? 0;
32+
let end = start + perPage;
33+
34+
let nextPage = null;
35+
if (invites.length > end) {
36+
let url = new URL(request.url, 'https://crates.io');
37+
url.searchParams.set('__start__', end);
38+
nextPage = url.search;
39+
}
40+
41+
invites = invites.slice(start, end);
42+
43+
let response = this.serialize(invites);
44+
response.users ??= [];
45+
response.meta = { next_page: nextPage };
46+
47+
return response;
48+
});
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
import { module, test } from 'qunit';
2+
3+
import fetch from 'fetch';
4+
5+
import { setupTest } from '../../../helpers';
6+
import setupMirage from '../../../helpers/setup-mirage';
7+
8+
module('Mirage | GET /api/private/crate_owner_invitations', function (hooks) {
9+
setupTest(hooks);
10+
setupMirage(hooks);
11+
12+
test('happy path (invitee_id)', async function (assert) {
13+
let nanomsg = this.server.create('crate', { name: 'nanomsg' });
14+
this.server.create('version', { crate: nanomsg });
15+
16+
let ember = this.server.create('crate', { name: 'ember-rs' });
17+
this.server.create('version', { crate: ember });
18+
19+
let user = this.server.create('user');
20+
this.server.create('mirage-session', { user });
21+
22+
let inviter = this.server.create('user', { name: 'janed' });
23+
this.server.create('crate-owner-invitation', {
24+
crate: nanomsg,
25+
createdAt: '2016-12-24T12:34:56Z',
26+
invitee: user,
27+
inviter,
28+
});
29+
30+
let inviter2 = this.server.create('user', { name: 'wycats' });
31+
this.server.create('crate-owner-invitation', {
32+
crate: ember,
33+
createdAt: '2020-12-31T12:34:56Z',
34+
invitee: user,
35+
inviter: inviter2,
36+
});
37+
38+
let response = await fetch(`/api/private/crate_owner_invitations?invitee_id=${user.id}`);
39+
assert.equal(response.status, 200);
40+
assert.deepEqual(await response.json(), {
41+
crate_owner_invitations: [
42+
{
43+
crate_id: Number(nanomsg.id),
44+
crate_name: 'nanomsg',
45+
created_at: '2016-12-24T12:34:56Z',
46+
invited_by_username: 'janed',
47+
invitee_id: Number(user.id),
48+
inviter_id: Number(inviter.id),
49+
},
50+
{
51+
crate_id: Number(ember.id),
52+
crate_name: 'ember-rs',
53+
created_at: '2020-12-31T12:34:56Z',
54+
invited_by_username: 'wycats',
55+
invitee_id: Number(user.id),
56+
inviter_id: Number(inviter2.id),
57+
},
58+
],
59+
users: [
60+
{
61+
avatar: user.avatar,
62+
id: Number(user.id),
63+
login: user.login,
64+
name: user.name,
65+
url: user.url,
66+
},
67+
{
68+
avatar: 'https://avatars1.githubusercontent.com/u/14631425?v=4',
69+
id: Number(inviter.id),
70+
login: 'janed',
71+
name: 'janed',
72+
url: 'https://github.com/janed',
73+
},
74+
{
75+
avatar: 'https://avatars1.githubusercontent.com/u/14631425?v=4',
76+
id: Number(inviter2.id),
77+
login: 'wycats',
78+
name: 'wycats',
79+
url: 'https://github.com/wycats',
80+
},
81+
],
82+
meta: {
83+
next_page: null,
84+
},
85+
});
86+
});
87+
88+
test('happy path with empty response (invitee_id)', async function (assert) {
89+
let user = this.server.create('user');
90+
this.server.create('mirage-session', { user });
91+
92+
let response = await fetch(`/api/private/crate_owner_invitations?invitee_id=${user.id}`);
93+
assert.equal(response.status, 200);
94+
assert.deepEqual(await response.json(), {
95+
crate_owner_invitations: [],
96+
users: [],
97+
meta: {
98+
next_page: null,
99+
},
100+
});
101+
});
102+
103+
test('happy path with pagination (invitee_id)', async function (assert) {
104+
let inviter = this.server.create('user');
105+
106+
let user = this.server.create('user');
107+
this.server.create('mirage-session', { user });
108+
109+
for (let i = 0; i < 15; i++) {
110+
let crate = this.server.create('crate');
111+
this.server.create('version', { crate });
112+
this.server.create('crate-owner-invitation', { crate, invitee: user, inviter });
113+
}
114+
115+
let response = await fetch(`/api/private/crate_owner_invitations?invitee_id=${user.id}`);
116+
assert.equal(response.status, 200);
117+
let responseJSON = await response.json();
118+
assert.strictEqual(responseJSON['crate_owner_invitations'].length, 10);
119+
assert.ok(responseJSON.meta['next_page']);
120+
121+
response = await fetch(`/api/private/crate_owner_invitations${responseJSON.meta['next_page']}`);
122+
assert.equal(response.status, 200);
123+
responseJSON = await response.json();
124+
assert.strictEqual(responseJSON['crate_owner_invitations'].length, 5);
125+
assert.strictEqual(responseJSON.meta['next_page'], null);
126+
});
127+
128+
test('happy path (crate_name)', async function (assert) {
129+
let nanomsg = this.server.create('crate', { name: 'nanomsg' });
130+
this.server.create('version', { crate: nanomsg });
131+
132+
let ember = this.server.create('crate', { name: 'ember-rs' });
133+
this.server.create('version', { crate: ember });
134+
135+
let user = this.server.create('user');
136+
this.server.create('mirage-session', { user });
137+
138+
let inviter = this.server.create('user', { name: 'janed' });
139+
this.server.create('crate-owner-invitation', {
140+
crate: nanomsg,
141+
createdAt: '2016-12-24T12:34:56Z',
142+
invitee: user,
143+
inviter,
144+
});
145+
146+
let inviter2 = this.server.create('user', { name: 'wycats' });
147+
this.server.create('crate-owner-invitation', {
148+
crate: ember,
149+
createdAt: '2020-12-31T12:34:56Z',
150+
invitee: user,
151+
inviter: inviter2,
152+
});
153+
154+
let response = await fetch(`/api/private/crate_owner_invitations?crate_name=ember-rs`);
155+
assert.equal(response.status, 200);
156+
assert.deepEqual(await response.json(), {
157+
crate_owner_invitations: [
158+
{
159+
crate_id: Number(ember.id),
160+
crate_name: 'ember-rs',
161+
created_at: '2020-12-31T12:34:56Z',
162+
invited_by_username: 'wycats',
163+
invitee_id: Number(user.id),
164+
inviter_id: Number(inviter2.id),
165+
},
166+
],
167+
users: [
168+
{
169+
avatar: user.avatar,
170+
id: Number(user.id),
171+
login: user.login,
172+
name: user.name,
173+
url: user.url,
174+
},
175+
{
176+
avatar: 'https://avatars1.githubusercontent.com/u/14631425?v=4',
177+
id: Number(inviter2.id),
178+
login: 'wycats',
179+
name: 'wycats',
180+
url: 'https://github.com/wycats',
181+
},
182+
],
183+
meta: {
184+
next_page: null,
185+
},
186+
});
187+
});
188+
189+
test('returns 403 if unauthenticated', async function (assert) {
190+
let response = await fetch(`/api/private/crate_owner_invitations?invitee_id=42`);
191+
assert.equal(response.status, 403);
192+
assert.deepEqual(await response.json(), {
193+
errors: [{ detail: 'must be logged in to perform that action' }],
194+
});
195+
});
196+
197+
test('returns 400 if query params are missing', async function (assert) {
198+
let user = this.server.create('user');
199+
this.server.create('mirage-session', { user });
200+
201+
let response = await fetch(`/api/private/crate_owner_invitations`);
202+
assert.equal(response.status, 400);
203+
assert.deepEqual(await response.json(), {
204+
errors: [{ detail: 'missing or invalid filter' }],
205+
});
206+
});
207+
208+
test("returns 404 if crate can't be found", async function (assert) {
209+
let user = this.server.create('user');
210+
this.server.create('mirage-session', { user });
211+
212+
let response = await fetch(`/api/private/crate_owner_invitations?crate_name=foo`);
213+
assert.equal(response.status, 404);
214+
assert.deepEqual(await response.json(), {
215+
errors: [{ detail: 'Not Found' }],
216+
});
217+
});
218+
219+
test('returns 403 if requesting for other user', async function (assert) {
220+
let user = this.server.create('user');
221+
this.server.create('mirage-session', { user });
222+
223+
let response = await fetch(`/api/private/crate_owner_invitations?invitee_id=${user.id + 1}`);
224+
assert.equal(response.status, 403);
225+
assert.deepEqual(await response.json(), {
226+
errors: [{ detail: 'must be logged in to perform that action' }],
227+
});
228+
});
229+
});

0 commit comments

Comments
 (0)