From 569695245fd787079e1d67a771a68b68e2cfa0ee Mon Sep 17 00:00:00 2001 From: Laurin <60652077+Flexla54@users.noreply.github.com> Date: Thu, 2 Jun 2022 00:09:51 +0200 Subject: [PATCH] patching complications after merging and review suggestions --- src/Controllers/discipline.controller.ts | 90 ++------------- src/Controllers/event.controller.ts | 122 ++++++++------------- src/Controllers/media.controller.ts | 45 ++------ src/Controllers/organisation.controller.ts | 13 +-- src/Controllers/participant.controller.ts | 32 ++---- src/Controllers/role.controller.ts | 87 ++++----------- src/Controllers/role_schema.controller.ts | 50 ++++++++- src/Controllers/team.controller.ts | 34 ++++-- src/Routes/participant.routes.ts | 21 ++-- src/Routes/role.routes.ts | 2 + 10 files changed, 186 insertions(+), 310 deletions(-) diff --git a/src/Controllers/discipline.controller.ts b/src/Controllers/discipline.controller.ts index bc85a7e..bef8240 100644 --- a/src/Controllers/discipline.controller.ts +++ b/src/Controllers/discipline.controller.ts @@ -1,7 +1,6 @@ import { Prisma, Organisation, Admin, AdminLevel, Team } from "@prisma/client"; import { PrismaClientKnownRequestError, PrismaClientUnknownRequestError } from "@prisma/client/runtime"; import { Request, Response } from "express"; -import { z } from "zod"; import prisma from "../lib/prisma"; import ForwardableError from "../Middleware/error/ForwardableError"; import NotFoundError from "../Middleware/error/NotFoundError"; @@ -17,11 +16,12 @@ import { require("express-async-errors"); - const InitialDisciplineBody = z.object({ name: z.string().min(1), minTeamSize: z.number(), maxTeamSize: z.number(), + briefDescription: z.string(), + fullDescription: z.string(), }); const disciplineRefiner = [ @@ -29,7 +29,9 @@ const disciplineRefiner = [ { message: "The minTeamSize must be smaller or equal to the maxTeamSize" }, ] as const; -const DisciplineBody = InitialDisciplineBody.refine(...disciplineRefiner); +const DisciplineBody = InitialDisciplineBody.partial({ briefDescription: true, fullDescription: true }).refine( + ...disciplineRefiner +); const updateDisciplineBody = InitialDisciplineBody.partial().refine(...disciplineRefiner); const basicDiscipline = { @@ -132,84 +134,6 @@ export const getDiscipline = async (req: Request, res: }); }; -export const updateDiscipline = async (req: Request<{ pid: string }>, res: Response) => { - if (req.auth?.permission_level !== "ELEVATED") { - res.status(403).json(createInsufficientPermissionsError()); - } - - const { pid } = req.params; - - const result = UpdateDisciplineBody.safeParse(req.body); - - if (result.success === false) { - return res.status(400).json( - generateInvalidBodyError({ - name: DataType.STRING, - minTeamSize: DataType.NUMBER, - maxTeamSize: DataType.NUMBER, - briefDescription: DataType.STRING, - ["fullDescription?"]: DataType.STRING, - }) - ); - } - - const body = result.data; - - try { - const discipline = await prisma.discipline.update({ - where: { pid }, - data: { - name: body.name, - minTeamSize: body.minTeamSize, - maxTeamSize: body.maxTeamSize, - briefDescription: body.briefDescription, - fullDescription: body.fullDescription, - }, - select: { - pid: true, - name: true, - minTeamSize: true, - maxTeamSize: true, - briefDescription: true, - fullDescription: true, - }, - }); - - if (!discipline) { - throw new NotFoundError("discipline", pid); - } - - res.status(200).json({ - type: "success", - payload: { - discipline, - }, - }); - } catch (e) { - if (e instanceof Prisma.PrismaClientKnownRequestError) { - return res.status(500).json({ - type: "error", - payload: { - message: `Internal Server error occured. Try again later`, - }, - }); - } - if (e instanceof Prisma.PrismaClientUnknownRequestError) { - return res.status(500).json({ - type: "error", - payload: { - message: "Unknown error occurred with your request. Check if your parameters are correct", - schema: { - eventId: DataType.UUID, - }, - }, - }); - } - - throw e; - } -}; - interface CreateDisciplineBody { name?: string; minTeamSize?: number; @@ -272,6 +196,8 @@ export const updateDiscipline = async (req: Request<{ pid: string }>, res: Respo name: DataType.STRING, minTeamSize: DataType.NUMBER, maxTeamSize: DataType.NUMBER, + briefDescription: DataType.STRING, + ["fullDescription?"]: DataType.STRING, }, result.error ) @@ -288,6 +214,8 @@ export const updateDiscipline = async (req: Request<{ pid: string }>, res: Respo name: body.name, minTeamSize: body.minTeamSize, maxTeamSize: body.maxTeamSize, + briefDescription: body.briefDescription, + fullDescription: body.fullDescription, }, select: basicDiscipline, }); diff --git a/src/Controllers/event.controller.ts b/src/Controllers/event.controller.ts index 1e558c7..3dfcd84 100644 --- a/src/Controllers/event.controller.ts +++ b/src/Controllers/event.controller.ts @@ -8,9 +8,13 @@ import { createInsufficientPermissionsError, DataType, generateError, generateIn require("express-async-errors"); +export const dateSchema = z.preprocess((arg) => { + if (typeof arg == "string" || arg instanceof Date) return new Date(arg); +}, z.date()); + const EventBody = z.object({ - name: z.string(), - date: z.string(), + name: z.string().min(1), + date: dateSchema, briefDescription: z.string(), fullDescription: z.string(), }); @@ -21,27 +25,32 @@ const CreateEventBody = EventBody.partial({ fullDescription: true, }); -export const getAllEvents = async (req: Request, res: Response) => { - const events = await prisma.event.findMany({ +const basicEvent = { + pid: true, + name: true, + date: true, + briefDescription: true, + fullDescription: true, +} as const; + +const detailedEvent = { + pid: true, + name: true, + date: true, + briefDescription: true, + fullDescription: true, + visual: { select: { pid: true, description: true } }, + disciplines: { select: { pid: true, name: true, - date: true, - briefDescription: true, - fullDescription: true, - visual: { select: { pid: true, description: true } }, - disciplines: { - select: { - pid: true, - name: true, - }}, - organisations: { - select: { - pid: true, - name: true, - } - } }, + }, +} as const; + +export const getAllEvents = async (req: Request, res: Response) => { + const events = await prisma.event.findMany({ + select: detailedEvent, }); res.status(200).json({ @@ -68,27 +77,7 @@ export const getEvent = async (req: Request, res: Response) => { where: { pid: eventId, }, - select: { - pid: true, - name: true, - date: true, - briefDescription: true, - fullDescription: true, - visual: { select: { pid: true, description: true } }, - disciplines: { - select: { - pid: true, - name: true, - briefDescription: true, - fullDescription: true, - }}, - organisations: { - select: { - pid: true, - name: true, - } - } - }, + select: detailedEvent, }); if (!event) { @@ -134,19 +123,20 @@ export const addEvent = async (req: Request, res: Response) => { const result = CreateEventBody.safeParse(req.body); - if(result.success === false){ + if (result.success === false) { return res.status(400).json( - generateInvalidBodyError({ - name: DataType.STRING, - date: DataType.DATETIME, - briefDescription: DataType.STRING, - ["fullDescription?"]: DataType.STRING, - }) + generateInvalidBodyError( + { + name: DataType.STRING, + date: DataType.DATETIME, + briefDescription: DataType.STRING, + ["fullDescription?"]: DataType.STRING, + }, + result.error + ) ); } - //TODO: Check if date is valid - const event = await prisma.event.create({ data: { name: req.body.name, @@ -154,13 +144,7 @@ export const addEvent = async (req: Request, res: Response) => { briefDescription: req.body.briefDescription, fullDescription: req.body.fullDescription, }, - select: { - pid: true, - name: true, - date: true, - briefDescription: true, - fullDescription: true, - }, + select: basicEvent, }); res.status(201).json({ @@ -214,10 +198,6 @@ export const updateEvent = async (req: Request<{ pid: string }>, res: Response) }, }); - if (!event) { - throw new NotFoundError("event", pid); - } - res.status(200).json({ type: "success", payload: { @@ -225,24 +205,8 @@ export const updateEvent = async (req: Request<{ pid: string }>, res: Response) }, }); } catch (e) { - if (e instanceof Prisma.PrismaClientKnownRequestError) { - return res.status(500).json({ - type: "error", - payload: { - message: `Internal Server error occured. Try again later`, - }, - }); - } - if (e instanceof Prisma.PrismaClientUnknownRequestError) { - return res.status(500).json({ - type: "error", - payload: { - message: "Unknown error occurred with your request. Check if your parameters are correct", - schema: { - eventId: DataType.UUID, - }, - }, - }); + if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === "P2025") { + throw new NotFoundError("discipline", pid); } throw e; @@ -267,7 +231,7 @@ export const deleteEvent = async (req: Request, res: Res return res.status(204).end(); } catch (e) { if (e instanceof PrismaClientKnownRequestError && e.code === "P2025") { - return res.status(404).json(generateError(`The event with the ID ${pid} could not be found`)); + throw new NotFoundError("discipline", pid); } throw e; diff --git a/src/Controllers/media.controller.ts b/src/Controllers/media.controller.ts index 22802a7..11a2cc7 100644 --- a/src/Controllers/media.controller.ts +++ b/src/Controllers/media.controller.ts @@ -1,4 +1,4 @@ -import { NextFunction, Request, response, Response } from "express"; +import { Request, Response } from "express"; import fs from "fs"; import isSvg from "is-svg"; import { fromBuffer as fileTypeFromBuffer } from "file-type"; @@ -8,12 +8,8 @@ import prisma from "../lib/prisma"; import NotFoundError from "../Middleware/error/NotFoundError"; import { PrismaClientKnownRequestError } from "@prisma/client/runtime"; import { generateInvalidBodyError, DataType } from "./common"; -import { type } from "os"; import { unlink } from "fs/promises"; import ForwardableError from "../Middleware/error/ForwardableError"; -import SchemaError from "../Middleware/error/SchemaError"; -import { z } from "zod"; -import { updateEvent } from "./event.controller"; require("express-async-errors"); @@ -101,7 +97,6 @@ export const uploadImage = async (req: Request, res: Response) => { const fileName = file.md5 + (fileIsSvg ? ".svg" : "." + fileType?.ext); try { - //generate record const media = await prisma.media.create({ data: { pid: fileName, @@ -192,12 +187,7 @@ export const deleteMedia = async (req: Request, res: Response) => { } }; -//TODO: maybe create a function that adds the tableToUpdate based on path -// and call it before calling (un)linkMedia - -export const linkMedia = async ( - req: Request<{ pid: string }, {}, { mediaPid: string }>, - res: Response) => { +export const linkMedia = async (req: Request<{ pid: string }, {}, { mediaPid: string }>, res: Response) => { if (req.auth?.permission_level != "ELEVATED") { res.status(403).json(createInsufficientPermissionsError()); } @@ -206,7 +196,7 @@ export const linkMedia = async ( const { mediaPid } = req.body; const tableToUpdate = req.originalUrl.split("/"); - if(typeof mediaPid !== "string") { + if (typeof mediaPid !== "string") { return res.status(400).json( generateInvalidBodyError({ mediaPid: DataType.UUID, @@ -231,9 +221,7 @@ export const linkMedia = async ( }); }; -export const unlinkMedia = async ( - req: Request<{ pid: string, mediaPid: string }>, - res: Response) => { +export const unlinkMedia = async (req: Request<{ pid: string; mediaPid: string }>, res: Response) => { if (req.auth?.permission_level != "ELEVATED") { res.status(403).json(createInsufficientPermissionsError()); } @@ -249,29 +237,20 @@ export const unlinkMedia = async ( return res.status(204).end(); } catch (e) { if (e instanceof PrismaClientKnownRequestError && e.code === "P2025") { - console.log("not found error"); throw new NotFoundError(tableToUpdate[2], pid); } - if (e instanceof Prisma.PrismaClientUnknownRequestError) { - return res.status(500).json({ - type: "error", - payload: { - message: "Unknown error occurred with your request. Check if your parameters are correct", - schema: { - eventId: DataType.UUID, - }, - }, - }); - } throw e; } }; -function getPrismaUpdateFKT( tableToUpdate: string ): Function { - switch(tableToUpdate) { - case "events": return prisma.event.update; - case "disciplines": return prisma.discipline.update; - default: return prisma.roleSchema.update; +function getPrismaUpdateFKT(tableToUpdate: string): Function { + switch (tableToUpdate) { + case "events": + return prisma.event.update; + case "disciplines": + return prisma.discipline.update; + default: + return prisma.roleSchema.update; } } diff --git a/src/Controllers/organisation.controller.ts b/src/Controllers/organisation.controller.ts index 146594e..6007947 100644 --- a/src/Controllers/organisation.controller.ts +++ b/src/Controllers/organisation.controller.ts @@ -12,6 +12,7 @@ import { } from "./common"; import { PrismaClientKnownRequestError } from "@prisma/client/runtime"; import { Prisma } from "@prisma/client"; +import NotFoundError from "../Middleware/error/NotFoundError"; function validateOranisationName(name: string) { return name.length > 0; @@ -187,16 +188,12 @@ export const updateOrganisation = async ( }, }); } catch (e) { - if (e instanceof PrismaClientKnownRequestError) { - if (e.code === "P2025") { - return res.status(404).json(generateError(`The organisation with the ID ${pid} could not be found`)); - } - } else if (e instanceof PrismaClientUnknownRequestError) { - return res.status(400).send(generateError("Unkonwn error occured. This could be due to malformed IDs")); + if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === "P2025") { + throw new NotFoundError("discipline", pid); } - } - return res.status(500).json(genericError); + throw e; + } }; interface DeleteOrganisationQueryParams { diff --git a/src/Controllers/participant.controller.ts b/src/Controllers/participant.controller.ts index cfc6e59..5c7a338 100644 --- a/src/Controllers/participant.controller.ts +++ b/src/Controllers/participant.controller.ts @@ -11,7 +11,7 @@ import { import { Job, Prisma } from "@prisma/client"; import { PrismaClientKnownRequestError } from "@prisma/client/runtime"; import NotFoundError from "../Middleware/error/NotFoundError"; -import { requireResponsibleForGroup } from "../Middleware/auth/auth"; +import { requireConfiguredAuthentication, requireResponsibleForGroup } from "../Middleware/auth/auth"; //TODO: add TeamleaderAuthentification @@ -44,8 +44,8 @@ const returnedParticipant = { }, } as const; -// REVIEW: Location of this endpoints (/groups, /teams, /participants, ...?) -export const createParticipant = async (req: Request<{ pid: string }>, res: Response) => { +// at: POST api/teams/:teamPid/participant/ +export const createParticipant = async (req: Request<{ teamPid: string }>, res: Response) => { const result = ParticipantBody.safeParse(req.body); if (result.success === false) { @@ -61,17 +61,7 @@ export const createParticipant = async (req: Request<{ pid: string }>, res: Resp ); } const body = result.data; - const { pid } = req.params; - - /* - if (!req.auth?.isAuthenticated || req.teamleader?.team != pid) { - return res.status(500).json(AUTH_ERROR); - } - if (req.teamleader?.team != pid) { - return res.status(500).json(AUTH_ERROR); - } - requireResponsibleForGroup(req.auth, req.body.groupId); - */ + const { teamPid } = req.params; try { const participant = await prisma.participant.create({ @@ -80,7 +70,7 @@ export const createParticipant = async (req: Request<{ pid: string }>, res: Resp lastName: body.lastName, relevance: "MEMBER", group: { connect: { pid: body.groupId } }, - team: { connect: { pid } }, + team: { connect: { pid: teamPid } }, }, select: returnedParticipant, }); @@ -93,16 +83,15 @@ export const createParticipant = async (req: Request<{ pid: string }>, res: Resp if (e instanceof PrismaClientKnownRequestError && e.code === "P2025") { return res .status(404) - .json(generateError(`Could not link to team with ID '${pid}, or group with ID ${body.groupId}'`)); + .json(generateError(`Could not link to team with ID '${teamPid}, or group with ID ${body.groupId}'`)); } throw e; } }; +// at: PATCH api/participants/:pid/ export const updateParticipant = async (req: Request<{ pid: string }>, res: Response) => { - //insert TeamleaderAuth - - const result = ParticipantBody.partial().safeParse(req.body); // Should be partial, right? + const result = ParticipantBody.partial().safeParse(req.body); if (result.success === false) { return res.status(400).json( @@ -144,9 +133,8 @@ export const updateParticipant = async (req: Request<{ pid: string }>, res: Resp } }; +// at: DELETE api/participants/:pid/ export const deleteParticipant = async (req: Request<{ pid: string }>, res: Response) => { - //insert TeamleaderAuth - const { pid } = req.params; try { @@ -155,7 +143,7 @@ export const deleteParticipant = async (req: Request<{ pid: string }>, res: Resp return res.status(204).end(); } catch (e) { if (e instanceof PrismaClientKnownRequestError && e.code === "P2025") { - return res.status(404).json(generateError(`The participant with the ID ${pid} could not be found`)); + throw new NotFoundError("participant", pid); } throw e; diff --git a/src/Controllers/role.controller.ts b/src/Controllers/role.controller.ts index feff325..a55fd17 100644 --- a/src/Controllers/role.controller.ts +++ b/src/Controllers/role.controller.ts @@ -8,6 +8,30 @@ import { createInsufficientPermissionsError, DataType, generateInvalidBodyError require("express-async-errors"); +const detailedRole = { + pid: true, + score: true, + schema: { + select: { + pid: true, + name: true, + }, + }, + participant: { + select: { + pid: true, + firstName: true, + lastName: true, + }, + }, + team: { + select: { + pid: true, + name: true, + }, + }, +}; + /** * * @param teamPid: Pid of the team to add the roles to @@ -94,66 +118,3 @@ export async function assignParticipantToRole(req: Request<{ pid: string }>, res }, }); } - -export const updateRoleScore = async (req: Request<{ pid: string }, {}, { score: string }>, res: Response) => { - if (req.auth?.permission_level != "ELEVATED") { - res.status(403).json(createInsufficientPermissionsError()); - } - - const { score } = req.body; - - if (typeof score !== "string") { - res.status(400).json(generateInvalidBodyError({ score: DataType.STRING })); - } - - const { pid } = req.params; - - try { - const role = await prisma.role.update({ - where: { pid }, - data: { score }, - select: { - pid: true, - score: true, - schema: { - select: { - pid: true, - name: true, - }, - }, - participant: { - select: { - pid: true, - firstName: true, - lastName: true, - }, - }, - team: { - select: { - pid: true, - name: true, - }, - }, - }, - }); - - res.status(200).json({ - type: "success", - payload: { - role, - }, - }); - } catch (e) { - if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === "P2025") { - throw new NotFoundError("role", pid); - } - - throw e; - } -}; - -export async function deleteRolesFromTeam(teamPid: string) { - await prisma.role.deleteMany({ - where: { team: { pid: teamPid } }, - }); -} diff --git a/src/Controllers/role_schema.controller.ts b/src/Controllers/role_schema.controller.ts index 8b05caf..f375451 100644 --- a/src/Controllers/role_schema.controller.ts +++ b/src/Controllers/role_schema.controller.ts @@ -20,7 +20,7 @@ const RoleSchemaBody = z.object({ schema: z.string(), }); -const UpdateRoleSchema = RoleSchemaBody.partial(); +const UpdateBody = RoleSchemaBody.partial(); const roleSchema = { pid: true, @@ -133,3 +133,51 @@ export const createRoleSchema = async ( throw e; } }; + +export const UpdateRoleSchema = async (req: Request<{ pid: string }>, res: Response) => { + if (req.auth?.permission_level !== "ELEVATED") { + res.status(403).json(createInsufficientPermissionsError()); + } + + const { pid } = req.params; + + const result = UpdateBody.safeParse(req.body); + + if (result.success === false) { + return res.status(400).json( + generateInvalidBodyError( + { + name: DataType.STRING, + schema: DataType.STRING, + }, + result.error + ) + ); + } + + const body = result.data; + + try { + const schema = await prisma.roleSchema.update({ + where: { pid }, + data: { + name: body.name, + schema: body.schema, + }, + select: roleSchema, + }); + + res.status(200).json({ + type: "success", + payload: { + schema, + }, + }); + } catch (e) { + if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === "P2025") { + throw new NotFoundError("discipline", pid); + } + + throw e; + } +}; diff --git a/src/Controllers/team.controller.ts b/src/Controllers/team.controller.ts index 1f2a5dd..35d6101 100644 --- a/src/Controllers/team.controller.ts +++ b/src/Controllers/team.controller.ts @@ -3,6 +3,8 @@ import prisma from "../lib/prisma"; import { createInsufficientPermissionsError, DataType, generateInvalidBodyError } from "./common"; import { requireLeaderOfTeam } from "../Middleware/auth/teamleaderAuth"; import { z } from "zod"; +import { Prisma } from "@prisma/client"; +import NotFoundError from "../Middleware/error/NotFoundError"; const TeamBody = z.object({ teamName: z.string().min(1), @@ -69,18 +71,26 @@ export const updateTeam = async (req: Request, res: Response) => { return res.status(401).json(createInsufficientPermissionsError("STANDARD")); } - const team = prisma.team.update({ - where: { - pid: body.pid, - }, - data: { - name: body.teamName, - discipline: { connect: { pid: body.disciplineId } }, - leaderEmail: body.leaderEmail, - }, - }); - - res.status(204).json(team); + try { + const team = prisma.team.update({ + where: { + pid: body.pid, + }, + data: { + name: body.teamName, + discipline: { connect: { pid: body.disciplineId } }, + leaderEmail: body.leaderEmail, + }, + }); + + res.status(204).json(team); + } catch (e) { + if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === "P2025") { + throw new NotFoundError("discipline", body.pid); + } + + throw e; + } }; export const deleteTeam = async (req: Request, res: Response) => { diff --git a/src/Routes/participant.routes.ts b/src/Routes/participant.routes.ts index 21937a2..532387a 100644 --- a/src/Routes/participant.routes.ts +++ b/src/Routes/participant.routes.ts @@ -1,29 +1,28 @@ import express from "express"; import teamRouter from "./team.routes"; import { createParticipant, deleteParticipant, updateParticipant } from "../Controllers/participant.controller"; -import { requireAuthentication } from "../Middleware/auth/auth"; -import { requireTeamleaderAuthentication } from "../Middleware/auth/teamleaderAuth"; +import { requireAuthentication, requireConfiguredAuthentication } from "../Middleware/auth/auth"; const router = express.Router(); -teamRouter.post<"/:pid/participant/", { pid: string }>( - "/:pid/participant/", +teamRouter.post<"/:teamPid/participant/", { teamPid: string }>( + "/:teamPid/participant/", requireAuthentication, - requireTeamleaderAuthentication, + requireConfiguredAuthentication({ optional: true, type: { admin: true, teamleader: true } }), createParticipant ); -teamRouter.patch<"/:pid/participant/", { pid: string }>( - "/:pid/participant/", +router.patch<"/:pid/", { pid: string }>( + "/:pid/", requireAuthentication, - requireTeamleaderAuthentication, + requireConfiguredAuthentication({ optional: true, type: { admin: true, teamleader: true } }), updateParticipant ); -teamRouter.delete<"/:pid/participant/", { pid: string }>( - "/:pid/participant/", +router.delete<"/:pid/", { pid: string }>( + "/:pid/", requireAuthentication, - requireTeamleaderAuthentication, + requireConfiguredAuthentication({ optional: true, type: { admin: true, teamleader: true } }), deleteParticipant ); diff --git a/src/Routes/role.routes.ts b/src/Routes/role.routes.ts index 5bbd03e..dff8cc7 100644 --- a/src/Routes/role.routes.ts +++ b/src/Routes/role.routes.ts @@ -7,3 +7,5 @@ const router = Express.Router(); router.get<"team/:teamPid/", { teamPid: string }>("team/:teamPid/", getRolesForTeam); router.put<"/:pid/participant", { pid: string }>("/:pid/participant", assignParticipantToRole); + +export default router;