diff --git a/src/Controllers/common.ts b/src/Controllers/common.ts index a9a0caf..88fc839 100644 --- a/src/Controllers/common.ts +++ b/src/Controllers/common.ts @@ -64,6 +64,13 @@ export const genericError = { }, }; +export const NAME_ERROR = { + type: "error", + payload: { + message: "The name has to be at least 1 character long", + }, +}; + export function validateName(name: string) { return name.length > 0; } @@ -106,12 +113,7 @@ export async function handleCreateByName( } if (!validateName(name)) { - return res.status(400).json({ - type: "error", - payload: { - message: "The name has to be at least 1 character long", - }, - }); + return res.status(400).json(NAME_ERROR); } // Check if link object exsits diff --git a/src/Controllers/discipline.controller.ts b/src/Controllers/discipline.controller.ts index 8a927a1..fbe4a72 100644 --- a/src/Controllers/discipline.controller.ts +++ b/src/Controllers/discipline.controller.ts @@ -1,7 +1,19 @@ import { Prisma, Organisation, Admin, AdminLevel, Team } from "@prisma/client"; +import { PrismaClientKnownRequestError } from "@prisma/client/runtime"; import { Request, Response } from "express"; import prisma from "../lib/prisma"; import ForwardableError from "../Middleware/error/ForwardableError"; +import NotFoundError from "../Middleware/error/NotFoundError"; +import { + createInsufficientPermissionsError, + DataType, + generateError, + generateInvalidBodyError, + NAME_ERROR, + validateName, +} from "./common"; + +require("express-async-errors"); const basicDiscipline = { pid: true, @@ -74,7 +86,7 @@ export const getDiscipline = async (req: Request, res: }); if (!discipline) { - throw new ForwardableError(404, `The discipline with ID ${pid} could not be found`); + throw new NotFoundError("discipline", pid); } return res.status(200).json({ @@ -100,3 +112,53 @@ export const getDiscipline = async (req: Request, res: }, }); }; + +interface CreateDisciplineBody { + name?: string; + minTeamSize?: number; + maxTeamSize?: number; +} + +// require: auth(ELEVATED) +// at: POST /event/:eventPid/discipliens +export const createDiscipline = async (req: Request<{ eventPid: string }, {}, CreateDisciplineBody>, res: Response) => { + if (req.auth?.permission_level !== "ELEVATED") { + return res.status(403).json(createInsufficientPermissionsError()); + } + + const { name, minTeamSize, maxTeamSize } = req.body; + + if (typeof name !== "string" || typeof minTeamSize !== "number" || typeof maxTeamSize !== "number") { + return res.status(400).json( + generateInvalidBodyError({ + name: DataType.STRING, + minTeamSize: DataType.NUMBER, + maxTeamSize: DataType.NUMBER, + }) + ); + } + + if (!validateName(name)) { + return res.status(400).json(NAME_ERROR); + } + + try { + const discipline = await prisma.discipline.create({ + data: { name, minTeamSize, maxTeamSize, event: { connect: { pid: req.params.eventPid } } }, + select: { + pid: true, + name: true, + minTeamSize: true, + maxTeamSize: true, + event: { select: { pid: true, name: true } }, + }, + }); + + return res.status(201).json({ type: "success", payload: { discipline } }); + } catch (e) { + if (e instanceof PrismaClientKnownRequestError && e.code === "P2025") { + return res.status(404).json(generateError(`Could not link to event with ID '${req.params.eventPid}'`)); + } + throw e; + } +}; diff --git a/src/Middleware/error/ForwardableError.ts b/src/Middleware/error/ForwardableError.ts index 04bc5e1..bbae485 100644 --- a/src/Middleware/error/ForwardableError.ts +++ b/src/Middleware/error/ForwardableError.ts @@ -1,6 +1,7 @@ export default class ForwardableError extends Error { // If in different context private readonly __id = "CUSTOM_ERROR"; + protected readonly __oid?: string; public readonly status: number; diff --git a/src/Middleware/error/NotFoundError.ts b/src/Middleware/error/NotFoundError.ts new file mode 100644 index 0000000..382441f --- /dev/null +++ b/src/Middleware/error/NotFoundError.ts @@ -0,0 +1,13 @@ +import ForwardableError from "./ForwardableError"; + +export default class NotFoundError extends ForwardableError { + protected __oid = "NOT_FOUND_ERROR"; + + constructor(resource?: string, pid?: string) { + super(404, `The requested ${resource ?? "resource"}${pid ? ` with PID '${pid}'` : ""} could not be found!`); + } + + static isNotFoundError(err: any): err is NotFoundError { + return err.__oid === "NOT_FOUND_ERROR"; + } +} diff --git a/src/Routes/discipline.routes.ts b/src/Routes/discipline.routes.ts index ef158cd..3ae7ad1 100644 --- a/src/Routes/discipline.routes.ts +++ b/src/Routes/discipline.routes.ts @@ -1,9 +1,13 @@ import express from "express"; -import { getAllDisciplines, getDiscipline } from "../Controllers/discipline.controller"; +import eventRouter from "./event.routes"; +import { createDiscipline, getAllDisciplines, getDiscipline } from "../Controllers/discipline.controller"; +import { requireAuthentication } from "../Middleware/auth/auth"; const router = express.Router(); router.get("/", getAllDisciplines); // TODO: Optional auth router.get("/:pid", getDiscipline); +eventRouter.post("/:eventPid/disciplines", requireAuthentication, createDiscipline); + export default router;