Add controller for roleSchemas (unfinished)

+ Refine error messages for role schemas
+ Add role schema routes
+ Add RESULT_SCHEMA DataType
+ Add SchemaError.isSchemaError() type guard
This commit is contained in:
Stephan
2022-05-01 18:18:48 +02:00
parent 72ee918f00
commit 8384a3eebb
6 changed files with 154 additions and 2 deletions
+1
View File
@@ -10,6 +10,7 @@ export enum DataType {
PERMISSION_LEVEL = "'ELEVATED' | 'STANDARD'",
DATETIME = "ISOstring",
UUID = "string",
RESULT_SCHEMA = "result_schema",
}
interface Body {
+125
View File
@@ -0,0 +1,125 @@
import { Prisma } from "@prisma/client";
import { PrismaClientKnownRequestError } from "@prisma/client/runtime";
import { Request, Response } from "express";
import prisma from "../lib/prisma";
import { DurationSchemaT, parseSchema, PointSchemaT } from "../lib/result_schema";
import NotFoundError from "../Middleware/error/NotFoundError";
import SchemaError from "../Middleware/error/SchemaError";
import {
createInsufficientPermissionsError,
DataType,
generateInvalidBodyError,
NAME_ERROR,
validateName,
} from "./common";
const roleSchema = {
name: true,
schema: true,
discipline: { select: { pid: true, name: true } },
visual: { select: { pid: true, location: true } },
} as const;
export const _getAllRoleSchemas = async (res: Response, disciplinePid: string | undefined) => {
const schemas = await prisma.roleSchema.findMany({
where: { discipline: { pid: disciplinePid } },
select: roleSchema,
});
return res.status(200).json({
type: "success",
payload: {
roleSchemas: schemas,
},
});
};
interface GetAllRoleSchemasSearchParams {
disciplinePid?: string;
}
export const getAllRoleSchemas = async (req: Request<{}, {}, {}, GetAllRoleSchemasSearchParams>, res: Response) => {
return _getAllRoleSchemas(res, req.query.disciplinePid);
};
interface GetAllRoleSchemasWithParamQueryParams {
organisationPid: string;
}
export const getAllRoleSchemasWithParam = async (
req: Request<GetAllRoleSchemasWithParamQueryParams>,
res: Response
) => {
return _getAllRoleSchemas(res, req.params.organisationPid);
};
interface GetRoleSchemaQueryParams {
pid: string;
}
export const getRoleSchema = async (req: Request<GetRoleSchemaQueryParams>, res: Response) => {
const { pid } = req.params;
const schema = await prisma.roleSchema.findUnique({ where: { pid }, select: roleSchema });
if (!schema) {
throw new NotFoundError("roleSchema", pid);
}
return res.status(200).json({
type: "success",
payload: {
roleSchema: {
...schema,
discipline: {
...schema.discipline,
_links: [{ rel: "self", type: "GET", href: `/api/disciplines/${schema.discipline.pid}` }],
},
},
},
});
};
interface CreateRoleSchemaBody {
name?: string;
schema?: any;
}
// at: POST /discipline/:disciplinePid/role-schemas
// requires: auth(ELEVATED)
export const createRoleSchema = async (
req: Request<{ disciplinePid: string }, {}, CreateRoleSchemaBody>,
res: Response
) => {
if (req.auth?.permission_level !== "ELEVATED") {
return res.status(403).json(createInsufficientPermissionsError());
}
const { name, schema: resultSchema } = req.body;
if (typeof name !== "string") {
return res.status(400).json(generateInvalidBodyError({ name: DataType.STRING, schema: DataType.RESULT_SCHEMA }));
}
if (!validateName(name)) {
return res.status(400).json(NAME_ERROR);
}
// Validate the result schema (Errors should be handled by the default error handler)
const validatedSchema = parseSchema(resultSchema);
try {
const schema = await prisma.roleSchema.create({
data: { name, schema: validatedSchema, discipline: { connect: { pid: req.params.disciplinePid } } },
select: roleSchema,
});
return res.status(201).json({ type: "success", payload: { roleSchema: schema } });
} catch (e) {
if (e instanceof PrismaClientKnownRequestError && e.code === "P2025") {
throw new NotFoundError("discipline", req.params.disciplinePid);
}
throw e;
}
};
+4
View File
@@ -6,4 +6,8 @@ export default class SchemaError extends ForwardableError {
constructor(message: string) {
super(400, message);
}
public static isSchemaError(err: any): err is SchemaError {
return err.__oid === "SCHEMA_ERROR";
}
}
+19
View File
@@ -0,0 +1,19 @@
import express from "express";
import disciplineRouter from "./discipline.routes";
import {
createRoleSchema,
getAllRoleSchemas,
getAllRoleSchemasWithParam,
getRoleSchema,
} from "../Controllers/role_schema.controller";
import { requireAuthentication } from "../Middleware/auth/auth";
const router = express.Router();
router.get("/", getAllRoleSchemas);
router.get("/:pid", getRoleSchema);
disciplineRouter.get("/:disciplinePid/role-schemas", getAllRoleSchemasWithParam);
disciplineRouter.post("/:disciplinePid/role-schemas", requireAuthentication, createRoleSchema);
export default router;
+3
View File
@@ -7,6 +7,7 @@ import adminRouter from "./Routes/admin.routes";
import organisationRouter from "./Routes/organisation.routes";
import groupRouter from "./Routes/group.routes";
import disciplineRouter from "./Routes/discipline.routes";
import roleSchemaRouter from "./Routes/role_schema.routes";
import defaultErrorHandler from "./Middleware/error/handler";
import logger from "./Middleware/error/logger";
import debugLogger from "./Middleware/debug/logger";
@@ -56,6 +57,8 @@ async function main() {
app.use("/api/disciplines", disciplineRouter);
app.use("/api/role-schemas", roleSchemaRouter);
// Error handling
app.use(defaultErrorHandler); // Not working
+2 -2
View File
@@ -52,7 +52,7 @@ export function parseSchema(schema: any): DurationSchemaT | PointSchemaT {
const issue = e.issues.at(0);
if (issue) {
throw new SchemaError(`${issue.path}: ${issue.message}`);
throw new SchemaError(`Error with the schema: ${issue.path ? `${issue.path}:` : ""} ${issue.message}`);
}
throw new SchemaError("Unknown error occured while validating the schema");
@@ -61,5 +61,5 @@ export function parseSchema(schema: any): DurationSchemaT | PointSchemaT {
throw e;
}
throw new SchemaError("type must be either 'duration' or 'points'");
throw new SchemaError("Error with the schema: type must be either 'duration' or 'points'");
}