From 7c6fd898367e0a18954a39e7924853956dd82086 Mon Sep 17 00:00:00 2001 From: Sinan Date: Sat, 14 Sep 2024 15:44:56 +0530 Subject: [PATCH] appointment presentation added --- server/package.json | 1 + server/src/domain/entities/IAppointment.ts | 1 - .../interface/repositories/ISlotRepository.ts | 1 - .../database/AppointmentModel.ts | 4 --- .../repositories/SlotRepository.ts | 7 ++--- .../appointment/AppointmentControllers.ts | 27 +++++++++++++++++++ .../routers/appointment/AppointmentRoutes.ts | 26 ++++++++++++++++++ server/src/presentation/routers/index.ts | 9 +++---- .../validators/AppointmentValidator.ts | 22 +++++++++++++++ .../appointment/AppointmentUseCase.ts | 14 +++------- 10 files changed, 86 insertions(+), 26 deletions(-) create mode 100644 server/src/presentation/controllers/appointment/AppointmentControllers.ts create mode 100644 server/src/presentation/routers/appointment/AppointmentRoutes.ts create mode 100644 server/src/presentation/validators/AppointmentValidator.ts diff --git a/server/package.json b/server/package.json index dd72c026..98606f3a 100644 --- a/server/package.json +++ b/server/package.json @@ -24,6 +24,7 @@ "dotenv": "^16.4.5", "express": "^4.19.2", "helmet": "^7.1.0", + "joi": "^17.13.3", "jsonwebtoken": "^9.0.2", "mongoose": "^8.5.3", "multer": "^1.4.5-lts.1", diff --git a/server/src/domain/entities/IAppointment.ts b/server/src/domain/entities/IAppointment.ts index 56627627..e0e478f7 100644 --- a/server/src/domain/entities/IAppointment.ts +++ b/server/src/domain/entities/IAppointment.ts @@ -19,6 +19,5 @@ export default interface IAppointment { readonly appointmentDate?: string; readonly reason?: string; readonly notes?: string; - readonly startTime?:string; status?: AppointmentStatus; } diff --git a/server/src/domain/interface/repositories/ISlotRepository.ts b/server/src/domain/interface/repositories/ISlotRepository.ts index efe4b830..7633f2f3 100644 --- a/server/src/domain/interface/repositories/ISlotRepository.ts +++ b/server/src/domain/interface/repositories/ISlotRepository.ts @@ -7,5 +7,4 @@ export default interface ISlotRepository { findMany(doctorId: string): Promise; findManyByDay(doctorId: string, day: Days): Promise; findById(slotId: string): Promise; - findByDoctorIdStartTimeAndDay(slotId: string, doctorId: string, startTime: string, day:Days): Promise; } \ No newline at end of file diff --git a/server/src/infrastructure/database/AppointmentModel.ts b/server/src/infrastructure/database/AppointmentModel.ts index aea54a99..469848b5 100644 --- a/server/src/infrastructure/database/AppointmentModel.ts +++ b/server/src/infrastructure/database/AppointmentModel.ts @@ -21,10 +21,6 @@ const appointmentSchema = new Schema( required: true, index: true, }, - startTime: { - type:String, - required:true - }, appointmentType: { type: String, enum: Object.values(AppointmentType), diff --git a/server/src/infrastructure/repositories/SlotRepository.ts b/server/src/infrastructure/repositories/SlotRepository.ts index b46c8575..0e3d80a7 100644 --- a/server/src/infrastructure/repositories/SlotRepository.ts +++ b/server/src/infrastructure/repositories/SlotRepository.ts @@ -26,11 +26,8 @@ export default class SlotRepository implements ISlotRepository { return await this.model.find({ doctorId, day }) } async findById(slotId: string): Promise { + if(!isValidObjectId(slotId)) throw new Error("Invalid Object Id") return await this.model.findById(slotId) } - async findByDoctorIdStartTimeAndDay(slotId: string, doctorId: string, startTime: string, day: Days): Promise { - if (!isValidObjectId(doctorId)) throw new Error("Invalid Object Id"); - if (!isValidObjectId(slotId)) throw new Error("Invalid Object Id"); - return await this.model.findOne({ _id: slotId, doctorId, startTime, day }); - } + } diff --git a/server/src/presentation/controllers/appointment/AppointmentControllers.ts b/server/src/presentation/controllers/appointment/AppointmentControllers.ts new file mode 100644 index 00000000..404af48c --- /dev/null +++ b/server/src/presentation/controllers/appointment/AppointmentControllers.ts @@ -0,0 +1,27 @@ +import { NextFunction, Response } from "express"; +import { CustomRequest, StatusCode } from "../../../types"; +import AppointmentUseCase from "../../../use_case/appointment/AppointmentUseCase"; +import AppointmentValidator from "../../validators/AppointmentValidator"; + +export default class AppointmentController { + constructor( + private appointmentUseCase: AppointmentUseCase, + private appointmentValidator: AppointmentValidator + ) { } + + async create(req: CustomRequest, res: Response, next: NextFunction) { + try { + const { error } = this.appointmentValidator.validate(req.body.appointment); + if (error) { + return res.status(StatusCode.BadRequest).json({ message: error.details[0].message }); + } + const { appointment } = req.body; + const patientId = req.patient?.id; + await this.appointmentUseCase.create(appointment, patientId!); + res.status(StatusCode.Success).json({ message: "Appointment created successfully" }); + } catch (error) { + next(error); + } + } + +} diff --git a/server/src/presentation/routers/appointment/AppointmentRoutes.ts b/server/src/presentation/routers/appointment/AppointmentRoutes.ts new file mode 100644 index 00000000..4b320dbe --- /dev/null +++ b/server/src/presentation/routers/appointment/AppointmentRoutes.ts @@ -0,0 +1,26 @@ +import express from 'express' +import AppointmentRepository from '../../../infrastructure/repositories/AppointmentRepository'; +import SlotRepository from '../../../infrastructure/repositories/SlotRepository'; +import AppointmentUseCase from '../../../use_case/appointment/AppointmentUseCase'; +import AppointmentController from '../../controllers/appointment/AppointmentControllers'; +import AppointmentValidator from '../../validators/AppointmentValidator'; +import PatientAuthMiddleware from '../../middlewares/PatientAuthMiddleware'; +import JWTService from '../../../infrastructure/services/JWTService'; + +const router = express.Router(); + + +const appointmentRepository = new AppointmentRepository(); +const slotRepository = new SlotRepository(); +const tokenService = new JWTService() + +const appointmentUseCase = new AppointmentUseCase(appointmentRepository, slotRepository); + +const appointmentValidator = new AppointmentValidator() +const appointmentController = new AppointmentController(appointmentUseCase, appointmentValidator); + +const authorizePatient = new PatientAuthMiddleware(tokenService); + +router.post('/', authorizePatient.exec.bind(authorizePatient), appointmentController.create.bind(appointmentController)); + +export default router; \ No newline at end of file diff --git a/server/src/presentation/routers/index.ts b/server/src/presentation/routers/index.ts index 60f6abd9..e629c513 100644 --- a/server/src/presentation/routers/index.ts +++ b/server/src/presentation/routers/index.ts @@ -12,6 +12,7 @@ import slotRoutes from "./slots/SlotsRoutes"; import UnauthenticatedUseCases from "../../use_case/UnauthenticatedUseCases"; import UnauthenticatedControllers from "../controllers/UnauthenticatedControllers"; import DoctorRepository from "../../infrastructure/repositories/DoctorRepository"; +import appointmentRoutes from "./appointment/AppointmentRoutes"; const app = express(); const tokenService = new TokenService(); @@ -25,17 +26,15 @@ const unauthenticatedController = new UnauthenticatedControllers(unauthenticated const errorHandler = new ErrorHandler(); +app.get('/doctors', unauthenticatedController.getDoctors.bind(unauthenticatedController)) app.use("/patient/auth", patientAuthentication); app.use("/patient", authorizePatient.exec.bind(authorizePatient), protectedRoutes); - app.use("/admin/auth", adminAuthentication); app.use("/admin", authorizeAdmin.exec.bind(authorizeAdmin), protectedAdminRoutes); - app.use("/doctor/auth", doctorAuthentication); +app.use('/slots', slotRoutes); +app.use('/appointment', appointmentRoutes) -app.use('/slots',slotRoutes); - -app.get('/doctors', unauthenticatedController.getDoctors.bind(unauthenticatedController)) app.use(errorHandler.exec.bind(errorHandler)); diff --git a/server/src/presentation/validators/AppointmentValidator.ts b/server/src/presentation/validators/AppointmentValidator.ts new file mode 100644 index 00000000..6d923c2f --- /dev/null +++ b/server/src/presentation/validators/AppointmentValidator.ts @@ -0,0 +1,22 @@ +import Joi from "joi"; +import IAppointment, { AppointmentType } from "../../domain/entities/IAppointment"; + +export default class AppointmentValidator { + private schema: Joi.ObjectSchema; + + constructor() { + this.schema = Joi.object({ + doctorId: Joi.string().required(), + patientId: Joi.string().required(), + slotId: Joi.string().required(), + appointmentType: Joi.string().valid(...Object.values(AppointmentType)).required(), + appointmentDate: Joi.string().isoDate().required(), + reason: Joi.string().optional(), + notes: Joi.string().optional(), + }); + } + + validate(appointment: IAppointment) { + return this.schema.validate(appointment); + } +} diff --git a/server/src/use_case/appointment/AppointmentUseCase.ts b/server/src/use_case/appointment/AppointmentUseCase.ts index 19ae8dcf..f2de93f6 100644 --- a/server/src/use_case/appointment/AppointmentUseCase.ts +++ b/server/src/use_case/appointment/AppointmentUseCase.ts @@ -1,5 +1,4 @@ import IAppointment, { AppointmentStatus } from "../../domain/entities/IAppointment"; -import { Days } from "../../domain/entities/ISlot"; import IAppointmentRepository from "../../domain/interface/repositories/IAppointmentRepository"; import ISlotRepository from "../../domain/interface/repositories/ISlotRepository"; @@ -9,16 +8,11 @@ export default class AppointmentUseCase { private slotRepository: ISlotRepository ) { } - async create({ _id, startTime, doctorId, appointmentDate, ...appointment }: IAppointment, patientId: string): Promise { - const day = this.getDayFromDate(appointmentDate!) - const slot = await this.slotRepository.findByDoctorIdStartTimeAndDay(_id!, doctorId!, startTime!, day); + async create({ slotId, ...appointment }: IAppointment, patientId: string): Promise { + const slot = await this.slotRepository.findById(slotId!); if (!slot) throw new Error("Slot Not Found"); - await this.appointRepository.create({ ...appointment, _id, startTime, doctorId, patientId, status: AppointmentStatus.PENDING }); + await this.appointRepository.create({ ...appointment, slotId, patientId, status: AppointmentStatus.PENDING }); } - private getDayFromDate(date: string): Days { - const dayOfWeek = new Date(date).getUTCDay(); - const dayNames = Object.values(Days); - return dayNames[dayOfWeek] as Days; - } + } \ No newline at end of file