Skip to content

Commit 07230f4

Browse files
authored
Merge pull request #41 from GTBitsOfGood/alexchen/list-partners-query
created list partners route handler + tests
2 parents d02f67c + b55f010 commit 07230f4

File tree

2 files changed

+218
-0
lines changed

2 files changed

+218
-0
lines changed

src/app/api/partners/route.test.ts

+166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
2+
//Lint gets angry when "as any" is used, but it is necessary for mocking Prisma responses using the "select" parameter (for now).
3+
import { testApiHandler } from "next-test-api-route-handler";
4+
import * as appHandler from "./route";
5+
import { expect, test } from "@jest/globals";
6+
import { dbMock } from "@/test/dbMock";
7+
import { invalidateSession, validateSession } from "@/test/util/authMockUtils";
8+
import { UserType } from "@prisma/client";
9+
10+
test("returns 401 on invalid session", async () => {
11+
await testApiHandler({
12+
appHandler,
13+
async test({ fetch }) {
14+
// Mock invalid session
15+
invalidateSession();
16+
const res = await fetch({ method: "GET", body: null });
17+
await expect(res.status).toBe(401);
18+
await expect(res.json()).resolves.toStrictEqual({
19+
message: "Session required",
20+
});
21+
},
22+
});
23+
});
24+
25+
test("returns 403 on unauthorized", async () => {
26+
await testApiHandler({
27+
appHandler,
28+
async test({ fetch }) {
29+
validateSession(UserType.PARTNER);
30+
const res = await fetch({ method: "GET", body: null });
31+
await expect(res.status).toBe(403);
32+
await expect(res.json()).resolves.toStrictEqual({
33+
message: "You are not allowed to view this",
34+
});
35+
},
36+
});
37+
});
38+
39+
test("returns 200 on authorized use by admin", async () => {
40+
await testApiHandler({
41+
appHandler,
42+
async test({ fetch }) {
43+
validateSession(UserType.ADMIN);
44+
const testData = [
45+
{
46+
name: "Partner A",
47+
email: "partner@partner.com",
48+
_count: {
49+
unallocatedItemRequests: 1,
50+
},
51+
},
52+
{
53+
name: "Partner B",
54+
email: "partner1@partner.com",
55+
_count: {
56+
unallocatedItemRequests: 2,
57+
},
58+
},
59+
];
60+
const expectedResponse = {
61+
partners: [
62+
{
63+
name: "Partner A",
64+
email: "partner@partner.com",
65+
unallocatedItemRequestCount: 1,
66+
},
67+
{
68+
name: "Partner B",
69+
email: "partner1@partner.com",
70+
unallocatedItemRequestCount: 2,
71+
},
72+
],
73+
};
74+
dbMock.user.findMany.mockResolvedValue(testData as any);
75+
const res = await fetch({ method: "GET", body: null });
76+
await expect(res.status).toBe(200);
77+
await expect(res.json()).resolves.toStrictEqual(expectedResponse);
78+
},
79+
});
80+
});
81+
82+
test("returns 200 on authorized use by super admin", async () => {
83+
await testApiHandler({
84+
appHandler,
85+
async test({ fetch }) {
86+
validateSession(UserType.SUPER_ADMIN);
87+
const testData = [
88+
{
89+
name: "Partner A",
90+
email: "partner@partner.com",
91+
_count: {
92+
unallocatedItemRequests: 1,
93+
},
94+
},
95+
{
96+
name: "Partner B",
97+
email: "partner1@partner.com",
98+
_count: {
99+
unallocatedItemRequests: 2,
100+
},
101+
},
102+
];
103+
const expectedResponse = {
104+
partners: [
105+
{
106+
name: "Partner A",
107+
email: "partner@partner.com",
108+
unallocatedItemRequestCount: 1,
109+
},
110+
{
111+
name: "Partner B",
112+
email: "partner1@partner.com",
113+
unallocatedItemRequestCount: 2,
114+
},
115+
],
116+
};
117+
dbMock.user.findMany.mockResolvedValue(testData as any);
118+
const res = await fetch({ method: "GET", body: null });
119+
await expect(res.status).toBe(200);
120+
await expect(res.json()).resolves.toStrictEqual(expectedResponse);
121+
},
122+
});
123+
});
124+
125+
test("returns 200 on authorized use by staff", async () => {
126+
await testApiHandler({
127+
appHandler,
128+
async test({ fetch }) {
129+
validateSession(UserType.STAFF);
130+
const testData = [
131+
{
132+
name: "Partner A",
133+
email: "partner@partner.com",
134+
_count: {
135+
unallocatedItemRequests: 1,
136+
},
137+
},
138+
{
139+
name: "Partner B",
140+
email: "partner1@partner.com",
141+
_count: {
142+
unallocatedItemRequests: 2,
143+
},
144+
},
145+
];
146+
const expectedResponse = {
147+
partners: [
148+
{
149+
name: "Partner A",
150+
email: "partner@partner.com",
151+
unallocatedItemRequestCount: 1,
152+
},
153+
{
154+
name: "Partner B",
155+
email: "partner1@partner.com",
156+
unallocatedItemRequestCount: 2,
157+
},
158+
],
159+
};
160+
dbMock.user.findMany.mockResolvedValue(testData as any);
161+
const res = await fetch({ method: "GET", body: null });
162+
await expect(res.status).toBe(200);
163+
await expect(res.json()).resolves.toStrictEqual(expectedResponse);
164+
},
165+
});
166+
});

src/app/api/partners/route.ts

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { auth } from "@/auth";
2+
import { authenticationError, authorizationError } from "@/util/responses";
3+
import { NextResponse } from "next/server";
4+
import { UserType } from "@prisma/client";
5+
import { db } from "@/db";
6+
7+
interface PartnersResponse {
8+
partners: {
9+
name: string;
10+
email: string;
11+
unallocatedItemRequestCount: number;
12+
}[];
13+
}
14+
15+
const AUTHORIZED_USER_TYPES = [
16+
UserType.STAFF,
17+
UserType.ADMIN,
18+
UserType.SUPER_ADMIN,
19+
] as UserType[];
20+
21+
/**
22+
* Retrieves a list of names, emails, and number of unallocated item requests for all partners.
23+
* @returns 401 if the request is not authenticated
24+
* @returns 403 if the user is not STAFF, ADMIN, or SUPER_ADMIN
25+
* @returns 200 and a list of partner names, details, and unallocated item counts
26+
*/
27+
export async function GET(): Promise<NextResponse> {
28+
const session = await auth();
29+
if (!session?.user) return authenticationError("Session required");
30+
if (!AUTHORIZED_USER_TYPES.includes(session.user.type)) {
31+
return authorizationError("You are not allowed to view this");
32+
}
33+
34+
const partners = await db.user.findMany({
35+
where: { type: UserType.PARTNER },
36+
select: {
37+
email: true,
38+
name: true,
39+
_count: {
40+
select: { unallocatedItemRequests: true },
41+
},
42+
},
43+
});
44+
45+
return NextResponse.json({
46+
partners: partners.map((partner) => ({
47+
name: partner.name,
48+
email: partner.email,
49+
unallocatedItemRequestCount: partner._count.unallocatedItemRequests,
50+
})),
51+
} as PartnersResponse);
52+
}

0 commit comments

Comments
 (0)