Skip to content

Commit

Permalink
chat presentation layer completed
Browse files Browse the repository at this point in the history
  • Loading branch information
sinanptm committed Sep 23, 2024
1 parent 243e50d commit e9b393b
Show file tree
Hide file tree
Showing 10 changed files with 102 additions and 26 deletions.
4 changes: 4 additions & 0 deletions server/src/domain/entities/IChat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@ export default interface IChat {
readonly updatedAt?: Date;
readonly doctorName?: string;
readonly patientName?: string;
}

export interface IChatWithNotSeenCount extends IChat{
notSeenMessages?:number;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { PaginatedResult } from "../../../types";
export default interface IMessageRepository {
create(message: IMessage): Promise<void>;
findById(_id: string): Promise<IMessage | null>;
findByChatId(chatId: string, limit: number, offset:number): Promise<PaginatedResult<IMessage>>;
findByChatId(chatId: string, limit: number, offset: number): Promise<PaginatedResult<IMessage>>;
markAsRead(messageId: string): Promise<void>;
getUnreadMessageCountGroupedByChat(receiverId: string): Promise<{ _id: string, count: number }[]>;
}
7 changes: 7 additions & 0 deletions server/src/infrastructure/repositories/MessageRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,11 @@ export default class MessageRepository implements IMessageRepository {
async markAsRead(messageId: string): Promise<void> {
await this.model.findByIdAndUpdate(messageId, { isReceived: true });
}
async getUnreadMessageCountGroupedByChat(receiverId: string): Promise<{ _id: string, count: number }[]> {
return await this.model.aggregate([
{ $match: { receiverId, isReceived: false } },
{ $group: { _id: "$chatId", count: { $sum: 1 } } }
]);
}

}
4 changes: 2 additions & 2 deletions server/src/presentation/controllers/admin/DoctorController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ export default class AdminDoctorController {

async getDoctors(req: Request, res: Response, next: NextFunction) {
try {
let offset = parseInt(req.query.offset as string);
let limit = parseInt(req.query.limit as string);
let offset = +(req.query.offset as string);
let limit = +(req.query.limit as string);
const type = req.query.type as DoctorsFilter;

offset = isNaN(offset) || offset < 0 ? 0 : offset;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ export default class AdminPatientController {

async getPatients(req: Request, res: Response, next: NextFunction) {
try {
const offset = parseInt(req.query.offset as string) || 0;
const limit = parseInt(req.query.limit as string) || 10;
const offset = +(req.query.offset as string) || 0;
const limit = +(req.query.limit as string) || 10;

const patients = await this.adminPatientUseCase.getAll(offset, limit);
res.status(StatusCode.Success).json(patients);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ export default class AppointmentController {
try {
const doctorId = req.doctor?.id;
const status = req.query.status as AppointmentStatus | "undefined";
let offset = parseInt(req.query.offset as string);
let limit = parseInt(req.query.limit as string);
let offset = +(req.query.offset as string);
let limit = +(req.query.limit as string);

offset = isNaN(offset) || offset < 0 ? 0 : offset;
limit = isNaN(limit) || limit < 0 ? 10 : Math.min(limit, 100);
Expand Down Expand Up @@ -81,8 +81,8 @@ export default class AppointmentController {
try {
const patientId = req.patient?.id;
const status = req.query.status as AppointmentStatus | "undefined";
let offset = parseInt(req.query.offset as string);
let limit = parseInt(req.query.limit as string);
let offset = +(req.query.offset as string);
let limit = +(req.query.limit as string);

offset = isNaN(offset) || offset < 0 ? 0 : offset;
limit = isNaN(limit) || limit < 0 ? 10 : Math.min(limit, 100);
Expand Down
37 changes: 33 additions & 4 deletions server/src/presentation/controllers/chat/ChatControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,41 @@ export default class ChatController {
private createChatUseCase: CreateChatUseCase,
private getChatUseCase: GetChatUseCase
) { }
async getChatsOfPatient(req: CustomRequest, res: Response, next: NextFunction) {
try {
const patientId = req.patient?.id;
const chats = await this.getChatUseCase.getAllChatsWithPatientId(patientId!);
res.status(StatusCode.Success).json(chats)
} catch (error) {
next(error)
}
}
async getChatsOfDoctor(req: CustomRequest, res: Response, next: NextFunction) {
try {
const doctorId = req.doctor?.id;
const chats = await this.getChatUseCase.getAllChatsWithDoctorId(doctorId!);
res.status(StatusCode.Success).json(chats)
} catch (error) {
next(error)
}
}
async getMessagesOfChat(req: CustomRequest, res: Response, next: NextFunction) {
try {
const chatId = req.params.chatId;
let limit = +(req.query.limit as string);
limit = isNaN(limit) || limit < 0 ? 10 : Math.min(limit, 100);
const messages = await this.getChatUseCase.getMessagesOfChat(chatId, limit);
res.status(StatusCode.Success).json(messages);
} catch (error) {
next(error)
}
}
async createChatPatient(req: CustomRequest, res: Response, next: NextFunction) {
try {
const patientId = req.patient?.id;
const { doctorId } = req.body;
await this.createChatUseCase.createChat(doctorId, patientId!);
res.status(StatusCode.Success).json({ message: "Chat has created" });
res.status(StatusCode.Created)
} catch (error: any) {
next(error);
}
Expand All @@ -23,7 +52,7 @@ export default class ChatController {
const doctorId = req.doctor?.id;
const { patientId } = req.body;
await this.createChatUseCase.createChat(doctorId!, patientId);
res.status(StatusCode.Success).json({ message: "Chat has created" });
res.status(StatusCode.Created)
} catch (error: any) {
next(error);
}
Expand All @@ -33,7 +62,7 @@ export default class ChatController {
const doctorId = req.doctor?.id;
const { chatId, patientId, message } = req.body;
await this.createChatUseCase.createMessage(chatId, patientId, message, doctorId!);
res.status(StatusCode.Success).json({ message: "Chat has created" });
res.status(StatusCode.Created)
} catch (error: any) {
next(error);
}
Expand All @@ -43,7 +72,7 @@ export default class ChatController {
const doctorId = req.doctor?.id;
const { chatId, patientId, message } = req.body;
await this.createChatUseCase.createMessage(chatId, patientId, message, doctorId!);
res.status(StatusCode.Success).json({ message: "Chat has created" });
res.status(StatusCode.Created)
} catch (error: any) {
next(error);
}
Expand Down
23 changes: 18 additions & 5 deletions server/src/presentation/routers/chat/ChatRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,34 @@ import JWTService from '../../../infrastructure/services/JWTService';
import PatientAuthMiddleware from '../../middlewares/PatientAuthMiddleware';
import DoctorAuthMiddleware from '../../middlewares/DoctorAuthMiddleware';

const router = express.Router()
const router = express.Router();

const validatorService = new JoiService()
const validatorService = new JoiService();
const tokenService = new JWTService();
const chatRepository = new ChatRepository();
const messageRepository = new MessageRepository();
const patientRepository = new PatientRepository();
const doctorRepository = new DoctorRepository();

const createChatUseCase = new CreateChatUseCase(messageRepository, chatRepository, validatorService, patientRepository, doctorRepository);
const getChatUseCase = new GetChatUseCase(messageRepository, chatRepository, validatorService);
const createChatUseCase = new CreateChatUseCase(
messageRepository, chatRepository, validatorService, patientRepository, doctorRepository
);
const getChatUseCase = new GetChatUseCase(
messageRepository, chatRepository, validatorService
);

const chatController = new ChatController(createChatUseCase, getChatUseCase);
const authorizePatient = new PatientAuthMiddleware(tokenService);
const authorizeDoctor = new DoctorAuthMiddleware(tokenService);

router.get('/patient', authorizePatient.exec, chatController.getChatsOfPatient.bind(chatController));
router.post('/patient', authorizePatient.exec, chatController.createChatPatient.bind(chatController));
router.post('/patient/message', authorizePatient.exec, chatController.createMessagePatient.bind(chatController));
router.get('/patient/message/:chatId', authorizePatient.exec, chatController.getMessagesOfChat.bind(chatController));

export default router
router.get('/doctor', authorizeDoctor.exec, chatController.getChatsOfDoctor.bind(chatController));
router.get('/doctor/message/:chatId', authorizeDoctor.exec, chatController.getMessagesOfChat.bind(chatController));
router.post('/doctor', authorizeDoctor.exec, chatController.createChatDoctor.bind(chatController));
router.post('/doctor/message', authorizeDoctor.exec, chatController.createMessageDoctor.bind(chatController));

export default router;
2 changes: 1 addition & 1 deletion server/src/use_case/chat/CreateChatUseCase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@ export default class CreateChatUseCase {
this.validatorService.validateRequiredFields({ chatId, receiverId, message, senderId });
this.validatorService.validateMultipleIds([chatId, receiverId, senderId]);
this.validatorService.validateLength(message, 1);
await this.messageRepository.create({ chatId, message, receiverId, senderId });
await this.messageRepository.create({ chatId, message, receiverId, senderId, isReceived: false });
}
}
36 changes: 29 additions & 7 deletions server/src/use_case/chat/GetChatUseCase.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import IChat from "../../domain/entities/IChat";
import IChat, { IChatWithNotSeenCount } from "../../domain/entities/IChat";
import IMessage from "../../domain/entities/IMessage";
import IChatRepository from "../../domain/interface/repositories/IChatRepository";
import IMessageRepository from "../../domain/interface/repositories/IMessageRepository";
Expand All @@ -12,17 +12,39 @@ export default class GetChatUseCase {
private validatorService: IValidatorService
) { }

async getAllChatsWithPatientId(patientId: string): Promise<IChat[]> {
async getAllChatsWithPatientId(patientId: string): Promise<IChatWithNotSeenCount[] | []> {
this.validatorService.validateIdFormat(patientId);
return await this.chatRepository.findAllChatsForPatient(patientId);
const chats = await this.chatRepository.findAllChatsForPatient(patientId);
return await this.getChatsWithNotSeenMessages(patientId, chats);
}
async getAllChatsWithDoctorId(doctorId: string): Promise<IChat[]> {

async getAllChatsWithDoctorId(doctorId: string): Promise<IChatWithNotSeenCount[] | []> {
this.validatorService.validateIdFormat(doctorId);
return await this.chatRepository.findAllChatsForDoctor(doctorId);
const chats = await this.chatRepository.findAllChatsForDoctor(doctorId);
return await this.getChatsWithNotSeenMessages(doctorId, chats);
}
async getMessagesOfChat(chatId:string,limit:number):Promise<PaginatedResult<IMessage>>{

async getMessagesOfChat(chatId: string, limit: number): Promise<PaginatedResult<IMessage>> {
this.validatorService.validateIdFormat(chatId);
const offset = 0;
return await this.messageRepository.findByChatId(chatId,limit,offset);
return await this.messageRepository.findByChatId(chatId, limit, offset);
}

private async getChatsWithNotSeenMessages(
receiverId: string,
chats: IChatWithNotSeenCount[]
): Promise<IChatWithNotSeenCount[] | []> {
const unreadMessages = await this.messageRepository.getUnreadMessageCountGroupedByChat(receiverId);

const unreadMessagesMap = unreadMessages.reduce((acc, messageCount) => {
acc[messageCount._id] = messageCount.count;
return acc;
}, {} as { [chatId: string]: number });

const updatedChats = chats.map(chat => ({
...chat,
notSeenMessages: unreadMessagesMap[chat._id!] || 0,
}));
return updatedChats.length ? updatedChats : [];
}
}

1 comment on commit e9b393b

@vercel
Copy link

@vercel vercel bot commented on e9b393b Sep 23, 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.