diff --git a/backend/typescript/middlewares/validators/activityValidators.ts b/backend/typescript/middlewares/validators/activityValidators.ts index 8ac5d09..973ae29 100644 --- a/backend/typescript/middlewares/validators/activityValidators.ts +++ b/backend/typescript/middlewares/validators/activityValidators.ts @@ -1,5 +1,5 @@ import { Request, Response, NextFunction } from "express"; -import { getApiValidationError, validateDate, validatePrimitive, } from "./util"; +import { getApiValidationError, validateDate, validatePrimitive } from "./util"; /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable-next-line import/prefer-default-export */ @@ -9,11 +9,12 @@ export const activityRequestDtoValidator = async ( next: NextFunction, ) => { const { body } = req; - console.log(body) - if (body.userId !== undefined && + if ( + body.userId !== undefined && body.userId !== null && - !validatePrimitive(body.userId, "integer")) { + !validatePrimitive(body.userId, "integer") + ) { return res.status(400).send(getApiValidationError("userId", "integer")); } @@ -22,30 +23,42 @@ export const activityRequestDtoValidator = async ( } if (!validatePrimitive(body.activityTypeId, "integer")) { - return res.status(400).send(getApiValidationError("activityTypeId", "integer")); + return res + .status(400) + .send(getApiValidationError("activityTypeId", "integer")); } - if (body.scheduledStartTime !== undefined && + if ( + body.scheduledStartTime !== undefined && body.scheduledStartTime !== null && - !validateDate(body.scheduledStartTime)) { - return res.status(400).send(getApiValidationError("scheduledStartTime", "Date")); + !validateDate(body.scheduledStartTime) + ) { + return res + .status(400) + .send(getApiValidationError("scheduledStartTime", "Date")); } - if (body.startTime !== undefined && + if ( + body.startTime !== undefined && body.startTime !== null && - !validateDate(body.startTime)) { + !validateDate(body.startTime) + ) { return res.status(400).send(getApiValidationError("startTime", "Date")); } - if (body.endTime !== undefined && + if ( + body.endTime !== undefined && body.endTime !== null && - !validateDate(body.endTime)) { + !validateDate(body.endTime) + ) { return res.status(400).send(getApiValidationError("endTime", "Date")); } - if (body.notes !== undefined && + if ( + body.notes !== undefined && body.notes !== null && - !validatePrimitive(body.notes, "string")) { + !validatePrimitive(body.notes, "string") + ) { return res.status(400).send(getApiValidationError("notes", "string")); } @@ -58,47 +71,64 @@ export const activityUpdateDtoValidator = async ( next: NextFunction, ) => { const { body } = req; - console.log(body) - if (body.userId !== undefined && + if ( + body.userId !== undefined && body.userId !== null && - !validatePrimitive(body.userId, "integer")) { + !validatePrimitive(body.userId, "integer") + ) { return res.status(400).send(getApiValidationError("userId", "integer")); } - if (body.petId !== undefined && + if ( + body.petId !== undefined && body.petId !== null && - !validatePrimitive(body.petId, "integer")) { + !validatePrimitive(body.petId, "integer") + ) { return res.status(400).send(getApiValidationError("petId", "integer")); } - if (body.activityTypeId !== undefined && + if ( + body.activityTypeId !== undefined && body.activityTypeId !== null && - !validatePrimitive(body.activityTypeId, "integer")) { - return res.status(400).send(getApiValidationError("activityTypeId", "integer")); + !validatePrimitive(body.activityTypeId, "integer") + ) { + return res + .status(400) + .send(getApiValidationError("activityTypeId", "integer")); } - if (body.scheduledStartTime !== undefined && + if ( + body.scheduledStartTime !== undefined && body.scheduledStartTime !== null && - !validateDate(body.scheduledStartTime)) { - return res.status(400).send(getApiValidationError("scheduledStartTime", "Date")); + !validateDate(body.scheduledStartTime) + ) { + return res + .status(400) + .send(getApiValidationError("scheduledStartTime", "Date")); } - if (body.startTime !== undefined && + if ( + body.startTime !== undefined && body.startTime !== null && - !validateDate(body.startTime)) { + !validateDate(body.startTime) + ) { return res.status(400).send(getApiValidationError("startTime", "Date")); } - if (body.endTime !== undefined && + if ( + body.endTime !== undefined && body.endTime !== null && - !validateDate(body.endTime)) { + !validateDate(body.endTime) + ) { return res.status(400).send(getApiValidationError("endTime", "Date")); } - if (body.notes !== undefined && + if ( + body.notes !== undefined && body.notes !== null && - !validatePrimitive(body.notes, "string")) { + !validatePrimitive(body.notes, "string") + ) { return res.status(400).send(getApiValidationError("notes", "string")); } diff --git a/backend/typescript/middlewares/validators/util.ts b/backend/typescript/middlewares/validators/util.ts index 65db88d..3c4f4e9 100644 --- a/backend/typescript/middlewares/validators/util.ts +++ b/backend/typescript/middlewares/validators/util.ts @@ -1,4 +1,11 @@ -type Type = "string" | "integer" | "boolean" | "decimal" | "PetStatus" | "Sex" | "Date"; +type Type = + | "string" + | "integer" + | "boolean" + | "decimal" + | "PetStatus" + | "Sex" + | "Date"; const allowableContentTypes = new Set([ "text/plain", @@ -51,7 +58,7 @@ export const validateFileType = (mimetype: string): boolean => { }; export const validateDate = (value: any): boolean => { - return value instanceof Date && !isNaN(value.getTime()); + return !Number.isNaN(Date.parse(value)); }; export const getApiValidationError = ( diff --git a/backend/typescript/models/activity.model.ts b/backend/typescript/models/activity.model.ts index d7fd55b..05f20bf 100644 --- a/backend/typescript/models/activity.model.ts +++ b/backend/typescript/models/activity.model.ts @@ -10,9 +10,9 @@ import User from "./user.model"; import Pet from "./pet.model"; import ActivityType from "./activityType.model"; -@Table({ +@Table({ tableName: "activities", - timestamps: true, + timestamps: true, createdAt: "created_at", updatedAt: "updated_at", }) diff --git a/backend/typescript/rest/activityRoutes.ts b/backend/typescript/rest/activityRoutes.ts index 4366265..319bd5f 100644 --- a/backend/typescript/rest/activityRoutes.ts +++ b/backend/typescript/rest/activityRoutes.ts @@ -1,123 +1,131 @@ import { Router } from "express"; -import { getAccessToken, isAuthorizedByRole } from "../middlewares/auth"; -import { activityRequestDtoValidator, activityUpdateDtoValidator } from "../middlewares/validators/activityValidators"; +import { isAuthorizedByRole } from "../middlewares/auth"; +import { + activityRequestDtoValidator, + activityUpdateDtoValidator, +} from "../middlewares/validators/activityValidators"; import ActivityService from "../services/implementations/activityService"; import { - ActivityResponseDTO, - IActivityService, + ActivityResponseDTO, + IActivityService, } from "../services/interfaces/activityService"; -import { getErrorMessage, INTERNAL_SERVER_ERROR_MESSAGE, NotFoundError } from "../utilities/errorUtils"; +import { getErrorMessage, NotFoundError } from "../utilities/errorUtils"; import { sendResponseByMimeType } from "../utilities/responseUtil"; import { Role } from "../types"; const activityRouter: Router = Router(); activityRouter.use( - isAuthorizedByRole(new Set([Role.ADMINISTRATOR, Role.ANIMAL_BEHAVIOURIST, Role.STAFF, Role.VOLUNTEER])), - ); + isAuthorizedByRole( + new Set([ + Role.ADMINISTRATOR, + Role.ANIMAL_BEHAVIOURIST, + Role.STAFF, + Role.VOLUNTEER, + ]), + ), +); const activityService: IActivityService = new ActivityService(); /* Get all Activities */ activityRouter.get("/", async (req, res) => { - const contentType = req.headers["content-type"]; - try { - const activities = await activityService.getActivities(); - await sendResponseByMimeType(res, 200, contentType, activities); - } catch (e: unknown) { - await sendResponseByMimeType(res, 500, contentType, [ - { - error: INTERNAL_SERVER_ERROR_MESSAGE, - }, - ]); - } + const contentType = req.headers["content-type"]; + try { + const activities = await activityService.getActivities(); + await sendResponseByMimeType( + res, + 200, + contentType, + activities, + ); + } catch (e: unknown) { + await sendResponseByMimeType(res, 500, contentType, [ + { error: getErrorMessage(e) }, + ]); + } }); /* Get Activity by id */ activityRouter.get("/:id", async (req, res) => { - const { id } = req.params; - try { - const activity = await activityService.getActivity(id); - res.status(200).json(activity); - } catch (error: unknown) { - if (error instanceof NotFoundError) { - res.status(404).send(getErrorMessage(error)); - } else { - res.status(500).send(INTERNAL_SERVER_ERROR_MESSAGE); - } + const { id } = req.params; + try { + const activity = await activityService.getActivity(id); + res.status(200).json(activity); + } catch (e: unknown) { + if (e instanceof NotFoundError) { + res.status(404).send(getErrorMessage(e)); + } else { + res.status(500).send(getErrorMessage(e)); } + } }); /* Create Activity */ activityRouter.post( - "/", - isAuthorizedByRole(new Set([Role.ANIMAL_BEHAVIOURIST, Role.ADMINISTRATOR])), - activityRequestDtoValidator, - async (req, res) => { - const accessToken = getAccessToken(req); - if (!accessToken) { - res.status(404).json({ error: "Access token not found" }); - return; - } - - try { - - const { body } = req; - const newActivity = await activityService.createActivity({ - userId: body.userId, - petId: body.petId, - activityTypeId: body.activityTypeId, - scheduledStartTime: body.scheduledStartTime, - startTime: body.startTime, - endTime: body.endTime, - notes: body.notes, - }); - res.status(201).json(newActivity); - } catch (e: unknown) { - res.status(500).send(INTERNAL_SERVER_ERROR_MESSAGE); - } - }, + "/", + isAuthorizedByRole(new Set([Role.ANIMAL_BEHAVIOURIST, Role.ADMINISTRATOR])), + activityRequestDtoValidator, + async (req, res) => { + try { + const { body } = req; + const newActivity = await activityService.createActivity({ + userId: body.userId, + petId: body.petId, + activityTypeId: body.activityTypeId, + scheduledStartTime: body.scheduledStartTime, + startTime: body.startTime, + endTime: body.endTime, + notes: body.notes, + }); + res.status(201).json(newActivity); + } catch (error: unknown) { + if (error instanceof NotFoundError) { + res.status(404).send(getErrorMessage(error)); + } else { + res.status(500).send(getErrorMessage(error)); + } + } + }, ); - - /* Update Activity by id */ activityRouter.patch( - "/:id", - isAuthorizedByRole(new Set([Role.ANIMAL_BEHAVIOURIST, Role.ADMINISTRATOR])), - activityUpdateDtoValidator, - async (req, res) => { - const { id } = req.params; - try { - const { body } = req; - const Activity = await activityService.updateActivity(id, { - userId: body.userId, - petId: body.petId, - activityTypeId: body.activityTypeId, - scheduledStartTime: body.scheduledStartTime, - startTime: body.startTime, - endTime: body.endTime, - notes: body.notes, - }); - res.status(200).json(Activity); - } catch (e: unknown) { - res.status(500).send(getErrorMessage(e)); - } - }, + "/:id", + isAuthorizedByRole(new Set([Role.ANIMAL_BEHAVIOURIST, Role.ADMINISTRATOR])), + activityUpdateDtoValidator, + async (req, res) => { + const { id } = req.params; + try { + const { body } = req; + const Activity = await activityService.updateActivity(id, { + userId: body.userId, + petId: body.petId, + activityTypeId: body.activityTypeId, + scheduledStartTime: body.scheduledStartTime, + startTime: body.startTime, + endTime: body.endTime, + notes: body.notes, + }); + res.status(200).json(Activity); + } catch (e: unknown) { + res.status(500).send(getErrorMessage(e)); + } + }, ); /* Delete Activity by id */ activityRouter.delete( - "/:id", - isAuthorizedByRole(new Set([Role.ANIMAL_BEHAVIOURIST, Role.ADMINISTRATOR])), - async (req, res) => { - console.log("Request body:", req.body); // Log incoming request body - const { id } = req.params; + "/:id", + isAuthorizedByRole(new Set([Role.ANIMAL_BEHAVIOURIST, Role.ADMINISTRATOR])), + async (req, res) => { + const { id } = req.params; - try { - const deletedId = await activityService.deleteActivity(id); - res.status(200).json({ id: deletedId }); - } catch (e: unknown) { - res.status(500).send(getErrorMessage(e)); - } - }); + try { + const deletedId = await activityService.deleteActivity(id); + res.status(200).json({ id: deletedId }); + } catch (e: unknown) { + res.status(500).send(getErrorMessage(e)); + } + }, +); export default activityRouter; diff --git a/backend/typescript/server.ts b/backend/typescript/server.ts index db7f257..fefb8f1 100644 --- a/backend/typescript/server.ts +++ b/backend/typescript/server.ts @@ -16,7 +16,6 @@ import simpleEntityRouter from "./rest/simpleEntityRoutes"; import userRouter from "./rest/userRoutes"; import activtyRouter from "./rest/activityRoutes"; - const CORS_ALLOW_LIST = [ "http://localhost:3000", "https://uw-blueprint-starter-code.firebaseapp.com", diff --git a/backend/typescript/services/implementations/activityService.ts b/backend/typescript/services/implementations/activityService.ts index fa3ab7a..bd143c9 100644 --- a/backend/typescript/services/implementations/activityService.ts +++ b/backend/typescript/services/implementations/activityService.ts @@ -19,7 +19,9 @@ class ActivityService implements IActivityService { throw new NotFoundError(`Activity id ${id} not found`); } } catch (error: unknown) { - Logger.error(`Failed to get activity. Reason = ${getErrorMessage(error)}`); + Logger.error( + `Failed to get activity. Reason = ${getErrorMessage(error)}`, + ); throw error; } @@ -37,7 +39,9 @@ class ActivityService implements IActivityService { async getActivities(): Promise { try { - const activities: Array = await PgActivity.findAll({ raw: true }); + const activities: Array = await PgActivity.findAll({ + raw: true, + }); return activities.map((activity) => ({ id: activity.id, userId: activity.user_id, diff --git a/backend/typescript/services/implementations/petService.ts b/backend/typescript/services/implementations/petService.ts index de72f51..becb112 100644 --- a/backend/typescript/services/implementations/petService.ts +++ b/backend/typescript/services/implementations/petService.ts @@ -41,7 +41,7 @@ class PetService implements IPetService { photo: pet.photo, careInfo: { id: pet.petCareInfo?.id, - safetyInfo: pet.petCareInfo?. ?? null, + safetyInfo: pet.petCareInfo?.safety_info ?? null, medicalInfo: pet.petCareInfo?.medical_info ?? null, managementInfo: pet.petCareInfo?.management_info ?? null, }, diff --git a/backend/typescript/services/interfaces/activityService.ts b/backend/typescript/services/interfaces/activityService.ts index dee8733..99d28c4 100644 --- a/backend/typescript/services/interfaces/activityService.ts +++ b/backend/typescript/services/interfaces/activityService.ts @@ -8,58 +8,59 @@ export interface ActivityRequestDTO { notes?: string; } - - export interface ActivityResponseDTO { - id: number; - userId?: number; - petId: number; - activityTypeId: number; - scheduledStartTime?: Date; - startTime?: Date; - endTime?: Date; - notes?: string; - } - - export interface IActivityService { - /** - * retrieve the Activity with the given id - * @param id Activity id - * @returns requested Activity - * @throws Error if retrieval fails - */ - getActivity(id: string): Promise; - - /** - * retrieve all Activities - * @param - * @returns returns array of Activities - * @throws Error if retrieval fails - */ - getActivities(): Promise>; - - /** - * create a Activity with the fields given in the DTO, return created Activity - * @param Activity new Activity to be created - * @returns the created Activity - * @throws Error if creation fails - */ - createActivity(Activity: ActivityRequestDTO): Promise; - - /** - * update the Activity with the given id with fields in the DTO, return updated Activity - * @param id Activity id - * @param Activity Updated Activity - * @returns the updated Activity - * @throws Error if update fails - */ - updateActivity(id: string, Activity: ActivityRequestDTO): Promise; - - /** - * delete the Activity with the given id - * @param id Activity id - * @returns id of the Activity deleted - * @throws Error if deletion fails - */ - deleteActivity(id: string): Promise; - } - \ No newline at end of file +export interface ActivityResponseDTO { + id: number; + userId?: number; + petId: number; + activityTypeId: number; + scheduledStartTime?: Date; + startTime?: Date; + endTime?: Date; + notes?: string; +} + +export interface IActivityService { + /** + * retrieve the Activity with the given id + * @param id Activity id + * @returns requested Activity + * @throws Error if retrieval fails + */ + getActivity(id: string): Promise; + + /** + * retrieve all Activities + * @param + * @returns returns array of Activities + * @throws Error if retrieval fails + */ + getActivities(): Promise>; + + /** + * create a Activity with the fields given in the DTO, return created Activity + * @param Activity new Activity to be created + * @returns the created Activity + * @throws Error if creation fails + */ + createActivity(Activity: ActivityRequestDTO): Promise; + + /** + * update the Activity with the given id with fields in the DTO, return updated Activity + * @param id Activity id + * @param Activity Updated Activity + * @returns the updated Activity + * @throws Error if update fails + */ + updateActivity( + id: string, + Activity: ActivityRequestDTO, + ): Promise; + + /** + * delete the Activity with the given id + * @param id Activity id + * @returns id of the Activity deleted + * @throws Error if deletion fails + */ + deleteActivity(id: string): Promise; +}