Skip to content

Commit 08f2649

Browse files
authored
Merge pull request #1700 from Northeastern-Electric-Racing/#1527-Delete-Material-Endpoint
Issue #1527 initial push
2 parents ffa28d5 + 579c7c6 commit 08f2649

5 files changed

Lines changed: 77 additions & 0 deletions

File tree

src/backend/src/controllers/projects.controllers.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,17 @@ export default class ProjectsController {
269269
}
270270
}
271271

272+
static async deleteMaterial(req: Request, res: Response, next: NextFunction) {
273+
try {
274+
const { materialId } = req.params;
275+
const user: User = await getCurrentUser(res);
276+
const updatedMaterial = await ProjectsService.deleteMaterial(user, materialId);
277+
res.status(200).json(updatedMaterial);
278+
} catch (error: unknown) {
279+
next(error);
280+
}
281+
}
282+
272283
static async editMaterial(req: Request, res: Response, next: NextFunction) {
273284
try {
274285
const user = await getCurrentUser(res);

src/backend/src/routes/projects.routes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,5 +112,6 @@ projectRouter.post(
112112
projectRouter.delete('/bom/material-type/:materialTypeId/delete', ProjectsController.deleteMaterialType);
113113

114114
projectRouter.delete('/bom/assembly/:assemblyId/delete', ProjectsController.deleteAssemblyType);
115+
projectRouter.post('/bom/material/:materialId/delete', ProjectsController.deleteMaterial);
115116

116117
export default projectRouter;

src/backend/src/services/projects.services.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,32 @@ export default class ProjectsService {
10381038
return deletedMaterialType;
10391039
}
10401040

1041+
/**
1042+
* Delete material in the database
1043+
* @param materialId the id number of the given material
1044+
* @param currentUser the current user currently accessing the material
1045+
* @returns the deleted material
1046+
* @throws if the user does not have permission, or materidal already deleted
1047+
*/
1048+
static async deleteMaterial(currentUser: User, materialId: string): Promise<Material> {
1049+
if (!isLeadership(currentUser.role)) {
1050+
throw new AccessDeniedException('Only Leadership can delete materials');
1051+
}
1052+
1053+
const material = await prisma.material.findUnique({ where: { materialId } });
1054+
1055+
if (!material) throw new NotFoundException('Material', materialId);
1056+
1057+
if (material.dateDeleted) throw new DeletedException('Material', materialId);
1058+
1059+
const deletedMaterial = await prisma.material.update({
1060+
where: { materialId },
1061+
data: { dateDeleted: new Date(), userDeletedId: currentUser.userId }
1062+
});
1063+
1064+
return deletedMaterial;
1065+
}
1066+
10411067
/**
10421068
* Update a material
10431069
* @param submitter the submitter of the request

src/backend/src/utils/errors.utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ type ExceptionObjectNames =
111111
| 'Expense Type'
112112
| 'Reimbursement Request'
113113
| 'User Secure Settings'
114+
| 'Material'
114115
| 'Image File'
115116
| 'Material'
116117
| 'Assembly'

src/backend/tests/projects.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,7 @@ describe('Projects', () => {
683683
new DeletedException('Manufacturer', prismaManufacturer2.name)
684684
);
685685
});
686+
686687
test('Get all Manufacturer works', async () => {
687688
vi.spyOn(prisma.manufacturer, 'findMany').mockResolvedValue([]);
688689

@@ -1014,5 +1015,42 @@ describe('Projects', () => {
10141015
expect(updatedMaterial.name).toBe('name2');
10151016
expect(prisma.material.update).toBeCalledTimes(1);
10161017
});
1018+
1019+
test('deleteMaterial works', async () => {
1020+
vi.spyOn(prisma.material, 'findUnique').mockResolvedValue(prismaMaterial);
1021+
const deletedMaterial = { ...prismaMaterial, dateDeleted: new Date() };
1022+
vi.spyOn(prisma.material, 'update').mockResolvedValue(deletedMaterial);
1023+
1024+
const material = await ProjectsService.deleteMaterial(batman, prismaMaterial.materialId);
1025+
1026+
expect(material).toStrictEqual(deletedMaterial);
1027+
});
1028+
1029+
test('deleteMaterial fails when user is not at least Lead', async () => {
1030+
vi.spyOn(prisma.material, 'findUnique').mockResolvedValue(prismaMaterial);
1031+
vi.spyOn(prisma.material, 'update').mockResolvedValue(prismaMaterial);
1032+
1033+
await expect(async () => await ProjectsService.deleteMaterial(wonderwoman, prismaMaterial.materialId)).rejects.toThrow(
1034+
new AccessDeniedException('Only Leadership can delete materials')
1035+
);
1036+
});
1037+
1038+
test('deleteMaterial fails when material is not found', async () => {
1039+
vi.spyOn(prisma.material, 'findUnique').mockResolvedValue(null);
1040+
vi.spyOn(prisma.material, 'update').mockResolvedValue(prismaMaterial);
1041+
1042+
await expect(async () => await ProjectsService.deleteMaterial(batman, prismaMaterial.materialId)).rejects.toThrow(
1043+
new NotFoundException('Material', prismaMaterial.materialId)
1044+
);
1045+
});
1046+
1047+
test('deleteMaterial fails when material has been deleted', async () => {
1048+
vi.spyOn(prisma.material, 'findUnique').mockResolvedValue({ ...prismaMaterial, dateDeleted: new Date() });
1049+
vi.spyOn(prisma.material, 'update').mockResolvedValue(prismaMaterial);
1050+
1051+
await expect(async () => await ProjectsService.deleteMaterial(batman, prismaMaterial.materialId)).rejects.toThrow(
1052+
new DeletedException('Material', prismaMaterial.materialId)
1053+
);
1054+
});
10171055
});
10181056
});

0 commit comments

Comments
 (0)