Skip to content

Commit

Permalink
Error Handling Changed ❤
Browse files Browse the repository at this point in the history
  • Loading branch information
sinanptm committed Sep 15, 2024
1 parent 3612e13 commit 40ded24
Show file tree
Hide file tree
Showing 13 changed files with 91 additions and 128 deletions.
2 changes: 1 addition & 1 deletion server/src/domain/entities/ValidationError.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { StatusCode } from "../../types";

export class ValidationError extends Error {
export default class ValidationError extends Error {
public statusCode: StatusCode
constructor(message: string, statusCode: StatusCode) {
super(message);
Expand Down
2 changes: 1 addition & 1 deletion server/src/infrastructure/services/JoiService.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Joi from "joi";
import { StatusCode } from '../../types';
import IValidatorService from "../../domain/interface/services/IValidatorService";
import { ValidationError } from "../../domain/entities/ValidationError";
import ValidationError from "../../domain/entities/ValidationError";

export default class JoiService implements IValidatorService {

Expand Down
1 change: 0 additions & 1 deletion server/src/infrastructure/services/S3StorageService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { S3Client, PutObjectCommand, DeleteObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { Upload } from "@aws-sdk/lib-storage";
import dotenv from "dotenv";
import ICloudStorageService from "../../domain/interface/services/ICloudStorageService";
dotenv.config();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ export default class AppointmentController {
await this.appointmentUseCase.create(appointment, patientId!);
res.status(StatusCode.Success).json({ message: "Appointment created successfully" });
} catch (error: any) {
if (error.message === 'Slot already booked') {
return res.status(StatusCode.Conflict).json({ message: error.message })
}
next(error);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ export default class AuthPatientController {
email: patientDetails?.email,
});
} catch (error: any) {
if (error.message === "Patient has no Password") {
return res.status(StatusCode.Unauthorized).json({ message: "Please use other login methods" });
}
next(error);
}
}
Expand Down
31 changes: 3 additions & 28 deletions server/src/presentation/middlewares/ErrorHandler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Request, Response, NextFunction } from "express";
import { StatusCode } from "../../types/index";
import logger from "../../utils/logger";
import { ValidationError } from "../../domain/entities/ValidationError";
import ValidationError from "../../domain/entities/ValidationError";

export default class ErrorHandler {
exec(err: any, req: Request, res: Response, next: NextFunction) {
Expand All @@ -16,7 +16,7 @@ export default class ErrorHandler {

const statusCode = err.statusCode || StatusCode.InternalServerError;
const message = err.message || "Internal Server Error";

if (err instanceof ValidationError) {
return res.status(statusCode).json({ message });
}
Expand All @@ -30,39 +30,14 @@ export default class ErrorHandler {
}

if (
message.includes("Unauthorized") ||
message.includes("Invalid Credentials") ||
message.includes("Invalid Otp")
) {
logger.warn(`Unauthorized access attempt: ${message}`);
return res.status(StatusCode.Unauthorized).json({ message });
} else if (message.includes("Patient is Blocked") || message.includes("Not Verified")) {
logger.warn(`Blocked patient access attempt`);
return res.status(StatusCode.Forbidden).json({ message });
} else if (message.includes("Doctor is Blocked")) {
logger.warn(`Blocked Doctor access attempt`);
return res.status(StatusCode.Forbidden).json({ message });
} else if (message.includes("Not Found")) {
logger.warn(`Not found`);
return res.status(StatusCode.NotFound).json({ message });
} else if (
message.includes("getaddrinfo ENOTFOUND smtp.gmail.com") ||
message.includes("queryA ETIMEOUT smtp.gmail.com")
) {
logger.error("Email service issue encountered.");
return res.status(StatusCode.InternalServerError).json({
message: "We are Having Issue with Email Service",
});
} else if (message.includes("Email Already Exists")) {
logger.warn("Conflict: Email already exists.");
return res.status(StatusCode.Conflict).json({
message: "Email Already Exists!",
});
} else if (message.includes("Invalid Object Id")) {
return res.status(StatusCode.UnprocessableEntity).json({ message });
} else if (message.includes("Invalid Filter")) {
return res.status(StatusCode.BadRequest).json({ message })
}
}

res.status(statusCode).json({
message,
Expand Down
22 changes: 11 additions & 11 deletions server/src/use_case/admin/AuthenticationUseCase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import IOtpRepository from "../../domain/interface/repositories/IOtpRepository";
import IEmailService from "../../domain/interface/services/IEmailService";
import ITokenService from "../../domain/interface/services/ITokenService";
import { IPasswordServiceRepository } from "../../domain/interface/services/IPasswordServiceRepository";
import { UserRole } from "../../types";
import { StatusCode, UserRole } from "../../types";
import IValidatorService from "../../domain/interface/services/IValidatorService";
import ValidationError from "../../domain/entities/ValidationError";

export default class AuthenticationUseCase {
constructor(
private adminRepository: IDoctorRepository,
Expand All @@ -16,12 +18,12 @@ export default class AuthenticationUseCase {
) { }

async login(email: string, password: string): Promise<void> {
this.validatorService.validateRequiredFields({email,password})
this.validatorService.validateRequiredFields({ email, password })
this.validatorService.validateEmailFormat(email);
const doctor = await this.adminRepository.findByEmailWithCredentials(email);
if (!doctor) throw new Error("Invalid Credentials");
if (doctor?.role !== "admin") throw new Error("Invalid Credentials");
if (!(await this.passwordService.compare(password, doctor.password!))) throw new Error("Invalid Credentials");
if (!doctor) throw new ValidationError("Invalid Credentials", StatusCode.Unauthorized);
if (doctor?.role !== "admin") throw new ValidationError("Invalid Credentials", StatusCode.Unauthorized);
if (!(await this.passwordService.compare(password, doctor.password!))) throw new ValidationError("Invalid Credentials", StatusCode.Unauthorized);

let otp = parseInt(this.generateOTP(6), 10);
while (otp.toString().length !== 6) {
Expand All @@ -41,10 +43,10 @@ export default class AuthenticationUseCase {
async validateOtp(email: string, otp: number): Promise<{ accessToken: string; refreshToken: string }> {
this.validatorService.validateEmailFormat(email)
const requestedOtp = await this.otpRepository.findOne(otp, email);
if (!requestedOtp) throw new Error("Invalid Credentials");
if (!requestedOtp) throw new ValidationError("Invalid Credentials", StatusCode.Unauthorized);

const admin = await this.adminRepository.findByEmailWithCredentials(email);
if (!admin) throw new Error("Not Found");
if (!admin) throw new ValidationError("Not Found", StatusCode.NotFound);

const accessToken = this.tokenService.createAccessToken(email, admin._id!, UserRole.Admin);
const refreshToken = this.tokenService.createRefreshToken(email, admin._id!);
Expand All @@ -59,7 +61,7 @@ export default class AuthenticationUseCase {
async resendOtp(email: string) {
this.validatorService.validateEmailFormat(email)
const admin = await this.adminRepository.findByEmail(email);
if (!admin) throw new Error("Not Found");
if (!admin) throw new ValidationError("Not Found", StatusCode.NotFound);

let otp = parseInt(this.generateOTP(6), 10);
while (otp.toString().length !== 6) {
Expand All @@ -80,9 +82,7 @@ export default class AuthenticationUseCase {
const { email } = this.tokenService.verifyRefreshToken(token);

const admin = await this.adminRepository.findByEmail(email);
if (!admin) {
throw new Error("Unauthorized");
}
if (!admin) throw new ValidationError("Unauthorized", StatusCode.Unauthorized);
const accessToken = this.tokenService.createAccessToken(admin.email!, admin._id!, UserRole.Admin);

return { accessToken };
Expand Down
5 changes: 3 additions & 2 deletions server/src/use_case/admin/DoctorUseCase.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import IDoctor from "../../domain/entities/IDoctor";
import ValidationError from "../../domain/entities/ValidationError";
import IDoctorRepository from "../../domain/interface/repositories/IDoctorRepository";
import IEmailService from "../../domain/interface/services/IEmailService";
import IValidatorService from "../../domain/interface/services/IValidatorService";
import { DoctorsFilter, PaginatedResult } from "../../types";
import { DoctorsFilter, PaginatedResult, StatusCode } from "../../types";

export default class AdminDoctorUseCase {
constructor(
Expand All @@ -27,7 +28,7 @@ export default class AdminDoctorUseCase {

async update(doctor: IDoctor): Promise<void> {
const updatedDoctor = await this.doctorRepository.update(doctor);
if (!updatedDoctor) throw new Error("Not Found");
if (!updatedDoctor) throw new ValidationError("Not Found",StatusCode.NotFound);
if (updatedDoctor?.isVerified! && doctor.isVerified) {
await this.emailService.sendMail({
email: updatedDoctor.email!,
Expand Down
2 changes: 1 addition & 1 deletion server/src/use_case/appointment/AppointmentUseCase.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import IAppointment, { AppointmentStatus, AppointmentType } from "../../domain/entities/IAppointment";
import { ValidationError } from "../../domain/entities/ValidationError";
import ValidationError from "../../domain/entities/ValidationError";
import IAppointmentRepository from "../../domain/interface/repositories/IAppointmentRepository";
import ISlotRepository from "../../domain/interface/repositories/ISlotRepository";
import IValidatorService from "../../domain/interface/services/IValidatorService";
Expand Down
35 changes: 18 additions & 17 deletions server/src/use_case/doctor/AuthenticationUseCase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import ICloudStorageService from "../../domain/interface/services/ICloudStorageS
import IEmailService from "../../domain/interface/services/IEmailService";
import ITokenService from "../../domain/interface/services/ITokenService";
import { IPasswordServiceRepository } from "../../domain/interface/services/IPasswordServiceRepository";
import { UserRole } from "../../types";
import { StatusCode, UserRole } from "../../types";
import IValidatorService from "../../domain/interface/services/IValidatorService";
import ValidationError from "../../domain/entities/ValidationError";

export default class AuthenticationUseCase {
constructor(
Expand All @@ -23,10 +24,10 @@ export default class AuthenticationUseCase {
this.validatorService.validateEmailFormat(email)
this.validatorService.validatePassword(password)
const doctor = await this.doctorRepository.findByEmailWithCredentials(email);
if (!doctor) throw new Error("Not Found");
if (doctor.isBlocked) throw new Error("Doctor is Blocked");
if (doctor.role !== "doctor") throw new Error("Invalid Credentials");
if (!(await this.passwordService.compare(password, doctor.password!))) throw new Error("Invalid Credentials");
if (!doctor) throw new ValidationError("Not Found", StatusCode.NotFound);
if (doctor.isBlocked) throw new ValidationError("Doctor is Blocked", StatusCode.Forbidden);
if (doctor.role !== "doctor") throw new ValidationError("Invalid Credentials", StatusCode.Unauthorized);
if (!(await this.passwordService.compare(password, doctor.password!))) throw new ValidationError("Invalid Credentials", StatusCode.Unauthorized);
if (!doctor.isVerified) throw new Error("Not Verified");

let otp = +this.generateOTP(6);
Expand All @@ -47,10 +48,10 @@ export default class AuthenticationUseCase {
async validateOtp(email: string, otp: number): Promise<{ accessToken: string; refreshToken: string }> {
this.validatorService.validateEmailFormat(email)
const isOtp = await this.otpRepository.findOne(otp, email);
if (!isOtp) throw Error("Invalid Credentials");
if (!isOtp) throw new ValidationError("Invalid Credentials", StatusCode.Unauthorized);

const doctor = await this.doctorRepository.findByEmailWithCredentials(email)!;
if (!doctor) throw new Error("Unauthorized");
if (!doctor) throw new ValidationError("Unauthorized", StatusCode.Unauthorized);

const refreshToken = this.tokenService.createRefreshToken(doctor?.email!, doctor?._id!);
const accessToken = this.tokenService.createAccessToken(doctor?.email!, doctor?._id!, UserRole.Doctor);
Expand All @@ -67,7 +68,7 @@ export default class AuthenticationUseCase {
async resendOtp(email: string) {
this.validatorService.validateEmailFormat(email)
const doctor = await this.doctorRepository.findByEmail(email);
if (!doctor) throw new Error("Invalid Credentials");
if (!doctor) throw new ValidationError("Invalid Credentials", StatusCode.Unauthorized);

let otp = +this.generateOTP(6);
while (otp.toString().length !== 6) {
Expand All @@ -87,8 +88,8 @@ export default class AuthenticationUseCase {
async sendForgotPasswordMail(email: string): Promise<void> {
this.validatorService.validateEmailFormat(email)
const doctor = await this.doctorRepository.findByEmail(email);
if (!doctor) throw new Error("Invalid Credentials");
if (doctor.isBlocked) throw new Error("Doctor is Blocked");
if (!doctor) throw new ValidationError("Invalid Credentials", StatusCode.Unauthorized);
if (doctor.isBlocked) throw new ValidationError("Doctor is Blocked", StatusCode.Forbidden);
await this.emailService.sendMail({
email,
name: doctor.name!,
Expand All @@ -102,8 +103,8 @@ export default class AuthenticationUseCase {
this.validatorService.validateEmailFormat(email)
this.validatorService.validatePassword(password)
const doctor = await this.doctorRepository.findByEmail(email);
if (!doctor) throw new Error("Invalid Credentials");
if (doctor.isBlocked) throw new Error("Doctor is Blocked");
if (!doctor) throw new ValidationError("Invalid Credentials", StatusCode.Unauthorized);
if (doctor.isBlocked) throw new ValidationError("Doctor is Blocked", StatusCode.Forbidden);

doctor.password = await this.passwordService.hash(password);
await this.doctorRepository.update(doctor);
Expand All @@ -122,7 +123,7 @@ export default class AuthenticationUseCase {
async getPreSignedUrl(id: string): Promise<{ url: string; key: string }> {
this.validatorService.validateIdFormat(id);
const doctor = await this.doctorRepository.findByID(id);
if (!doctor) throw new Error("Not Found");
if (!doctor) throw new ValidationError("Not Found", StatusCode.NotFound);
const key = `profile-images/${id}-${Date.now()}`;
const url = await this.cloudService.generatePreSignedUrl(process.env.S3_BUCKET_NAME!, key, 30);
return { url, key };
Expand All @@ -131,8 +132,8 @@ export default class AuthenticationUseCase {
async updateProfileImage(key: string, id: string): Promise<void> {
this.validatorService.validateIdFormat(id)
const doctor = await this.doctorRepository.findByID(id);
if (!doctor) throw new Error("Not Found");
if (doctor.isBlocked) throw new Error("Doctor is Blocked");
if (!doctor) throw new ValidationError("Not Found", StatusCode.NotFound);
if (doctor.isBlocked) throw new ValidationError("Doctor is Blocked", StatusCode.Forbidden);

if (doctor.image) {
await this.cloudService.deleteFile(process.env.S3_BUCKET_NAME!, doctor.image.split("amazonaws.com/").pop()!);
Expand All @@ -145,9 +146,9 @@ export default class AuthenticationUseCase {
async refresh(token: string): Promise<{ accessToken: string }> {
const { id } = this.tokenService.verifyRefreshToken(token);
const doctor = await this.doctorRepository.findByID(id);
if (!doctor) throw new Error("Unauthorized");
if (!doctor) throw new ValidationError("Unauthorized", StatusCode.Unauthorized);

if (doctor.isBlocked) throw new Error("Doctor is Blocked");
if (doctor.isBlocked) throw new ValidationError("Doctor is Blocked", StatusCode.Forbidden);

const accessToken = this.tokenService.createAccessToken(doctor.email!, doctor._id!, UserRole.Doctor);

Expand Down
Loading

1 comment on commit 40ded24

@vercel
Copy link

@vercel vercel bot commented on 40ded24 Sep 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.