Skip to content

Commit 6496df8

Browse files
authored
Merge pull request #2195 from Northeastern-Electric-Racing/#2060-get-single-dr
#2060 Get single DR endpoint
2 parents ccd4844 + 624f4d9 commit 6496df8

5 files changed

Lines changed: 99 additions & 19 deletions

File tree

src/backend/src/controllers/design-review.controllers.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,15 @@ export default class DesignReviewController {
2323
next(error);
2424
}
2525
}
26+
27+
static async getSingleDesignReview(req: Request, res: Response, next: NextFunction) {
28+
try {
29+
const drId: string = req.params.designReviewId;
30+
const user: User = await getCurrentUser(res);
31+
const designReview = await DesignReviewService.getSingleDesignReview(user, drId);
32+
return res.status(200).json(designReview);
33+
} catch (error: unknown) {
34+
next(error);
35+
}
36+
}
2637
}

src/backend/src/routes/design-review.routes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ const designReviewRouter = express.Router();
66
designReviewRouter.get('/', DesignReviewController.getAllDesignReviews);
77

88
designReviewRouter.delete('/:designReviewId/delete', DesignReviewController.deleteDesignReview);
9+
designReviewRouter.get('/:designReviewId', DesignReviewController.getSingleDesignReview);
910

1011
export default designReviewRouter;

src/backend/src/services/design-review.services.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,24 @@ export default class DesignReviewService {
4444

4545
return designReviewTransformer(deletedDesignReview);
4646
}
47+
48+
/**
49+
* Retrieves a single design review
50+
*
51+
* @param submitter the user who is trying to retrieve the design review
52+
* @param designReviewId the id of the design review to retrieve
53+
* @returns the design review
54+
*/
55+
static async getSingleDesignReview(submitter: User, designReviewId: string): Promise<DesignReview> {
56+
const designReview = await prisma.design_Review.findUnique({
57+
where: { designReviewId },
58+
...designReviewQueryArgs
59+
});
60+
61+
if (!designReview) throw new NotFoundException('Design Review', designReviewId);
62+
63+
if (designReview.dateDeleted) throw new DeletedException('Design Review', designReviewId);
64+
65+
return designReviewTransformer(designReview);
66+
}
4767
}

src/backend/tests/design-reviews.test.ts

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { designReview1, prismaDesignReview2 } from './test-data/design-reviews.test-data';
1+
import { prismaDesignReview1, prismaDesignReview2, sharedDesignReview1 } from './test-data/design-reviews.test-data';
22
import { batman, wonderwoman } from './test-data/users.test-data';
33
import DesignReviewService from '../src/services/design-review.services';
44
import prisma from '../src/prisma/prisma';
@@ -25,20 +25,20 @@ describe('Design Reviews', () => {
2525
describe('Delete Design Review Tests', () => {
2626
test('Delete Reimbursment Request fails when ID does not exist', async () => {
2727
vi.spyOn(prisma.design_Review, 'findUnique').mockResolvedValue(null);
28-
await expect(() => DesignReviewService.deleteDesignReview(batman, designReview1.designReviewId)).rejects.toThrow(
29-
new NotFoundException('Design Review', designReview1.designReviewId)
28+
await expect(() => DesignReviewService.deleteDesignReview(batman, prismaDesignReview1.designReviewId)).rejects.toThrow(
29+
new NotFoundException('Design Review', prismaDesignReview1.designReviewId)
3030
);
3131
});
3232
test('Delete Design Review fails when user is not an admin nor the user who created the design review', async () => {
33-
vi.spyOn(prisma.design_Review, 'findUnique').mockResolvedValue(designReview1);
34-
await expect(() => DesignReviewService.deleteDesignReview(wonderwoman, designReview1.designReviewId)).rejects.toThrow(
35-
new AccessDeniedAdminOnlyException('delete design reviews')
36-
);
33+
vi.spyOn(prisma.design_Review, 'findUnique').mockResolvedValue(prismaDesignReview1);
34+
await expect(() =>
35+
DesignReviewService.deleteDesignReview(wonderwoman, prismaDesignReview1.designReviewId)
36+
).rejects.toThrow(new AccessDeniedAdminOnlyException('delete design reviews'));
3737
});
3838
test('Delete Design Review fails when design review is already deleted', async () => {
39-
vi.spyOn(prisma.design_Review, 'findUnique').mockResolvedValue({ ...designReview1, dateDeleted: new Date() });
40-
await expect(() => DesignReviewService.deleteDesignReview(batman, designReview1.designReviewId)).rejects.toThrow(
41-
new DeletedException('Design Review', designReview1.designReviewId)
39+
vi.spyOn(prisma.design_Review, 'findUnique').mockResolvedValue({ ...prismaDesignReview1, dateDeleted: new Date() });
40+
await expect(() => DesignReviewService.deleteDesignReview(batman, prismaDesignReview1.designReviewId)).rejects.toThrow(
41+
new DeletedException('Design Review', prismaDesignReview1.designReviewId)
4242
);
4343
});
4444
test('Delete Design Review succeeds when user is admin', async () => {
@@ -67,4 +67,28 @@ describe('Design Reviews', () => {
6767
expect(prismaDesignReview2.dateDeleted).toBeDefined();
6868
});
6969
});
70+
71+
describe('Get Single Design Review Tests', () => {
72+
test('Get Single Design Review fails when ID does not exist', async () => {
73+
vi.spyOn(prisma.design_Review, 'findUnique').mockResolvedValue(null);
74+
await expect(() =>
75+
DesignReviewService.getSingleDesignReview(batman, prismaDesignReview1.designReviewId)
76+
).rejects.toThrow(new NotFoundException('Design Review', prismaDesignReview1.designReviewId));
77+
});
78+
test('Get Single Design Review fails when design review is already deleted', async () => {
79+
vi.spyOn(prisma.design_Review, 'findUnique').mockResolvedValue({ ...prismaDesignReview1, dateDeleted: new Date() });
80+
await expect(() =>
81+
DesignReviewService.getSingleDesignReview(batman, prismaDesignReview1.designReviewId)
82+
).rejects.toThrow(new DeletedException('Design Review', prismaDesignReview1.designReviewId));
83+
});
84+
85+
test('Get Single Design Review succeeds', async () => {
86+
vi.spyOn(prisma.design_Review, 'findUnique').mockResolvedValue(prismaDesignReview1);
87+
88+
const result = await DesignReviewService.getSingleDesignReview(wonderwoman, prismaDesignReview1.designReviewId);
89+
90+
expect(prisma.design_Review.findUnique).toHaveBeenCalledTimes(1);
91+
expect(result).toEqual(sharedDesignReview1);
92+
});
93+
});
7094
});

src/backend/tests/test-data/design-reviews.test-data.ts

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,39 @@
1-
import {
2-
Design_Review as PrismaDesignReview,
3-
Design_Review_Status as PrismaDesignReviewStatus,
4-
Prisma,
5-
TeamType
6-
} from '@prisma/client';
7-
import { batman, wonderwoman } from './users.test-data';
1+
import { Design_Review_Status as PrismaDesignReviewStatus, Prisma, TeamType } from '@prisma/client';
2+
import { batman, sharedBatman, wonderwoman } from './users.test-data';
83
import designReviewQueryArgs from '../../src/prisma-query-args/design-review.query-args';
94
import { prismaWbsElement1 } from './wbs-element.test-data';
5+
import { DesignReview as SharedDesignReview, DesignReviewStatus as sharedDesignReviewStatus } from 'shared';
106

117
export const teamType1: TeamType = {
128
teamTypeId: '1',
139
name: 'teamType1'
1410
};
1511

16-
export const designReview1: PrismaDesignReview = {
12+
export const prismaDesignReview1: Prisma.Design_ReviewGetPayload<typeof designReviewQueryArgs> = {
1713
designReviewId: '1',
1814
dateScheduled: new Date('2024-03-25'),
1915
meetingTimes: [0, 1, 2, 3],
2016
dateCreated: new Date('2024-03-10'),
2117
userCreatedId: batman.userId,
18+
userCreated: batman,
2219
status: PrismaDesignReviewStatus.CONFIRMED,
2320
teamTypeId: '1',
21+
teamType: teamType1,
2422
location: null,
2523
isOnline: true,
2624
isInPerson: false,
2725
zoomLink: null,
2826
dateDeleted: null,
2927
userDeletedId: null,
3028
docTemplateLink: null,
31-
wbsElementId: 1
29+
wbsElementId: 1,
30+
requiredMembers: [batman],
31+
optionalMembers: [],
32+
confirmedMembers: [batman],
33+
deniedMembers: [],
34+
attendees: [batman],
35+
userDeleted: null,
36+
wbsElement: prismaWbsElement1
3237
};
3338

3439
export const prismaDesignReview2: Prisma.Design_ReviewGetPayload<typeof designReviewQueryArgs> = {
@@ -57,3 +62,22 @@ export const prismaDesignReview2: Prisma.Design_ReviewGetPayload<typeof designRe
5762
wbsElement: prismaWbsElement1,
5863
teamType: teamType1
5964
};
65+
66+
export const sharedDesignReview1: SharedDesignReview = {
67+
designReviewId: '1',
68+
dateScheduled: new Date('2024-03-25'),
69+
meetingTimes: [0, 1, 2, 3],
70+
dateCreated: new Date('2024-03-10'),
71+
userCreated: sharedBatman,
72+
status: sharedDesignReviewStatus.CONFIRMED,
73+
isOnline: true,
74+
isInPerson: false,
75+
requiredMembers: [sharedBatman],
76+
optionalMembers: [],
77+
confirmedMembers: [sharedBatman],
78+
deniedMembers: [],
79+
attendees: [sharedBatman],
80+
teamType: teamType1,
81+
wbsName: 'car',
82+
wbsNum: { carNumber: 1, projectNumber: 2, workPackageNumber: 0 }
83+
};

0 commit comments

Comments
 (0)