Skip to content

Commit

Permalink
Merge branch 'main' into S24/jerry/adding-user-roles
Browse files Browse the repository at this point in the history
  • Loading branch information
jerry-cheng5 authored Jul 3, 2024
2 parents f2121d8 + f69d19c commit 681781d
Show file tree
Hide file tree
Showing 7 changed files with 294 additions and 1 deletion.
18 changes: 18 additions & 0 deletions backend/typescript/middlewares/validators/behaviourValidators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Request, Response, NextFunction } from "express";
import { getApiValidationError, validatePrimitive } from "./util";

/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/* eslint-disable-next-line import/prefer-default-export */
export const behaviourRequestDtoValidators = async (
req: Request,
res: Response,
next: NextFunction,
) => {
const { body } = req;
if (!validatePrimitive(body.behaviourName, "string")) {
return res
.status(400)
.send(getApiValidationError("behaviourName", "string"));
}
return next();
};
2 changes: 1 addition & 1 deletion backend/typescript/models/behaviour.model.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Column, Model, Table } from "sequelize-typescript";

@Table({ tableName: "behaviours" })
@Table({ timestamps: false, tableName: "behaviours" })
export default class Behaviour extends Model {
@Column
behaviour_name!: string;
Expand Down
97 changes: 97 additions & 0 deletions backend/typescript/rest/behaviourRoutes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { Router } from "express";
import { behaviourRequestDtoValidators } from "../middlewares/validators/behaviourValidators";
import BehaviourService from "../services/implementations/behaviourService";
import {
BehaviourResponseDTO,
IBehaviourService,
} from "../services/interfaces/behaviourService";
import { getErrorMessage, NotFoundError } from "../utilities/errorUtils";
import { sendResponseByMimeType } from "../utilities/responseUtil";

const behaviourRouter: Router = Router();
const behaviourService: IBehaviourService = new BehaviourService();

/* Create Behaviour */
behaviourRouter.post("/", behaviourRequestDtoValidators, async (req, res) => {
try {
const { body } = req;
const newBehaviour = await behaviourService.createBehaviour({
behaviourName: body.behaviourName,
});
res.status(201).json(newBehaviour);
} catch (e: unknown) {
res.status(500).send("Internal server error occured.");
}
});

/* Get all Behaviours */
behaviourRouter.get("/", async (req, res) => {
const contentType = req.headers["content-type"];
try {
const behaviours = await behaviourService.getBehaviours();
await sendResponseByMimeType<BehaviourResponseDTO>(
res,
200,
contentType,
behaviours,
);
} catch (e: unknown) {
await sendResponseByMimeType(res, 500, contentType, [
{
error: "Internal server error occured.",
},
]);
}
});

/* Get Behaviour by id */
behaviourRouter.get("/:id", async (req, res) => {
const { id } = req.params;

try {
const behaviour = await behaviourService.getBehaviour(id);
res.status(200).json(behaviour);
} catch (e: unknown) {
if (e instanceof NotFoundError) {
res.status(404).send(getErrorMessage(e));
} else {
res.status(500).send("Internal server error occured.");
}
}
});

/* Update Behaviour by id */
behaviourRouter.put("/:id", behaviourRequestDtoValidators, async (req, res) => {
const { id } = req.params;
try {
const { body } = req;
const behaviour = await behaviourService.updateBehaviour(id, {
behaviourName: body.behaviourName,
});
res.status(200).json(behaviour);
} catch (e: unknown) {
if (e instanceof NotFoundError) {
res.status(404).send(getErrorMessage(e));
} else {
res.status(500).send("Internal server error occured.");
}
}
});

/* Delete Behaviour by id */
behaviourRouter.delete("/:id", async (req, res) => {
const { id } = req.params;

try {
const deletedId = await behaviourService.deleteBehaviour(id);
res.status(200).json({ id: deletedId });
} catch (e: unknown) {
if (e instanceof NotFoundError) {
res.status(404).send(getErrorMessage(e));
} else {
res.status(500).send("Internal server error occured.");
}
}
});

export default behaviourRouter;
2 changes: 2 additions & 0 deletions backend/typescript/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import YAML from "yamljs";

import { sequelize } from "./models";
import authRouter from "./rest/authRoutes";
import behaviourRouter from "./rest/behaviourRoutes";
import entityRouter from "./rest/entityRoutes";
import simpleEntityRouter from "./rest/simpleEntityRoutes";
import userRouter from "./rest/userRoutes";
Expand All @@ -32,6 +33,7 @@ app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.use("/auth", authRouter);
app.use("/behaviours", behaviourRouter);
app.use("/entities", entityRouter);
app.use("/simple-entities", simpleEntityRouter);
app.use("/users", userRouter);
Expand Down
119 changes: 119 additions & 0 deletions backend/typescript/services/implementations/behaviourService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import PgBehaviour from "../../models/behaviour.model";
import {
IBehaviourService,
BehaviourRequestDTO,
BehaviourResponseDTO,
} from "../interfaces/behaviourService";
import { getErrorMessage, NotFoundError } from "../../utilities/errorUtils";
import logger from "../../utilities/logger";

const Logger = logger(__filename);

class BehaviourService implements IBehaviourService {
/* eslint-disable class-methods-use-this */
async getBehaviour(id: string): Promise<BehaviourResponseDTO> {
let behaviour: PgBehaviour | null;
try {
behaviour = await PgBehaviour.findByPk(id, { raw: true });
if (!behaviour) {
throw new NotFoundError(`Behaviour id ${id} not found`);
}
} catch (error: unknown) {
Logger.error(
`Failed to get behaviour. Reason = ${getErrorMessage(error)}`,
);
throw error;
}

return {
id: behaviour.id,
behaviourName: behaviour.behaviour_name,
};
}

async getBehaviours(): Promise<BehaviourResponseDTO[]> {
try {
const behaviours: Array<PgBehaviour> = await PgBehaviour.findAll({
raw: true,
});
return behaviours.map((behaviour) => ({
id: behaviour.id,
behaviourName: behaviour.behaviour_name,
}));
} catch (error: unknown) {
Logger.error(
`Failed to get behaviours. Reason = ${getErrorMessage(error)}`,
);
throw error;
}
}

async createBehaviour(
behaviour: BehaviourRequestDTO,
): Promise<BehaviourResponseDTO> {
let newBehaviour: PgBehaviour | null;
try {
newBehaviour = await PgBehaviour.create({
behaviour_name: behaviour.behaviourName,
});
} catch (error: unknown) {
Logger.error(
`Failed to create behaviour. Reason = ${getErrorMessage(error)}`,
);
throw error;
}
return {
id: newBehaviour?.id,
behaviourName: newBehaviour?.behaviour_name,
};
}

async updateBehaviour(
id: string,
behaviour: BehaviourRequestDTO,
): Promise<BehaviourResponseDTO | null> {
let resultingBehaviour: PgBehaviour | null;
let updateResult: [number, PgBehaviour[]] | null;
try {
updateResult = await PgBehaviour.update(
{
behaviour_name: behaviour.behaviourName,
},
{ where: { id }, returning: true },
);

if (!updateResult[0]) {
throw new NotFoundError(`Behaviour id ${id} not found`);
}
[, [resultingBehaviour]] = updateResult;
} catch (error: unknown) {
Logger.error(
`Failed to update behaviour. Reason = ${getErrorMessage(error)}`,
);
throw error;
}
return {
id: resultingBehaviour?.id,
behaviourName: resultingBehaviour?.behaviour_name,
};
}

async deleteBehaviour(id: string): Promise<string> {
try {
const deleteResult: number | null = await PgBehaviour.destroy({
where: { id },
});
if (!deleteResult) {
throw new NotFoundError(`Behaviour id ${id} not found`);
}
return id;
} catch (error: unknown) {
Logger.error(
`Failed to delete behaviour. Reason = ${getErrorMessage(error)}`,
);
throw error;
}
}
}

export default BehaviourService;
56 changes: 56 additions & 0 deletions backend/typescript/services/interfaces/behaviourService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
export interface BehaviourRequestDTO {
behaviourName: string;
}

export interface BehaviourResponseDTO {
id: number;
behaviourName: string;
}

export interface IBehaviourService {
/**
* retrieve the Behaviour with the given id
* @param id Behaviour id
* @returns requested Behaviour
* @throws Error if retrieval fails
*/
getBehaviour(id: string): Promise<BehaviourResponseDTO>;

/**
* retrieve all Behaviours
* @param
* @returns returns array of Behaviours
* @throws Error if retrieval fails
*/
getBehaviours(): Promise<BehaviourResponseDTO[]>;

/**
* create a Behaviour with the fields given in the DTO, return created Behaviour
* @param behaviour new Behaviour
* @returns the created Behaviour
* @throws Error if creation fails
*/
createBehaviour(
behaviour: BehaviourRequestDTO,
): Promise<BehaviourResponseDTO>;

/**
* update the Behaviour with the given id with fields in the DTO, return updated Behaviour
* @param id Behaviour id
* @param behaviour Updated Behaviour
* @returns the updated Behaviour
* @throws Error if update fails
*/
updateBehaviour(
id: string,
behaviour: BehaviourRequestDTO,
): Promise<BehaviourResponseDTO | null>;

/**
* delete the Behaviour with the given id
* @param id Behaviour id
* @returns id of the Behaviour deleted
* @throws Error if deletion fails
*/
deleteBehaviour(id: string): Promise<string>;
}
1 change: 1 addition & 0 deletions backend/typescript/utilities/errorUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ export class NotFoundError extends Error {
this.name = "NotFoundError";
}
}

export const INTERNAL_SERVER_ERROR_MESSAGE = "Internal server error occured.";

0 comments on commit 681781d

Please sign in to comment.