finalizare 1.0

This commit is contained in:
andrei-mihnea-cerbu
2024-05-21 12:10:53 +03:00
parent f7795f7519
commit 1cc1d34003
11268 changed files with 2102399 additions and 10909 deletions
@@ -1,6 +1,6 @@
namespace Application.Endpoints.Appointments;
public class AppointmentManagementDto
public class AppointmentInformation
{
public Guid DoctorId { get; set; }
public Guid PatientId { get; set; }
@@ -1,165 +0,0 @@
using Application.Services.Database.MongoDB;
using Application.Services.Database.PostgreSQL;
using Core.Entities;
namespace Application.Endpoints.Appointments;
public class AppointmentManagementHandler
{
private readonly IAppointmentsMongoDbService _appointmentsMongoDbService;
private readonly IDoctorRepository _doctorRepository;
private readonly IPatientRepository _patientRepository;
public AppointmentManagementHandler(IAppointmentsMongoDbService appointmentsMongoDbService,
IPatientRepository patientRepository, IDoctorRepository doctorRepository)
{
_appointmentsMongoDbService = appointmentsMongoDbService;
_patientRepository = patientRepository;
_doctorRepository = doctorRepository;
}
public async Task<BaseResponse> HandleCreateAppointment(AppointmentManagementDto dto)
{
var validation = new CreateAppointmentValidator(_appointmentsMongoDbService,
_doctorRepository, _patientRepository);
var validationResult = await validation.ValidateAsync(dto);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
var appointmentId = IdentifierGenerator.GenerateId(dto.DoctorId, dto.PatientId);
var criteria = new List<(string FieldName, string Value)>
{
("_id", appointmentId)
};
var appointments = await _appointmentsMongoDbService.FindAsync<Appointment>(criteria);
if (!appointments.Any())
{
var appointment = new Appointment();
appointment.SetId(appointmentId);
appointment.SetDoctorIid(dto.DoctorId.ToString());
appointment.SetPatientId(dto.PatientId.ToString());
appointment.AddAppointment(dto.Appointment);
await _appointmentsMongoDbService.AddAsync(appointment);
}
else
{
var appointment = appointments[0];
appointment.AddAppointment(dto.Appointment);
await _appointmentsMongoDbService.ModifyAsync("_id", appointmentId, appointment);
}
return new BaseResponse
{
StatusCode = HttpStatusCodes.Created,
Message = "Appointment successfully created.",
Data = null
};
}
public async Task<BaseResponse> HandleDeleteAppointment(AppointmentManagementDto dto)
{
var validation = new DeleteAppointmentValidator(_appointmentsMongoDbService,
_doctorRepository, _patientRepository);
var validationResult = await validation.ValidateAsync(dto);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
var appointmentId = IdentifierGenerator.GenerateId(dto.DoctorId, dto.PatientId);
var criteria = new List<(string FieldName, string Value)>
{
("_id", appointmentId)
};
var appointments = await _appointmentsMongoDbService.FindAsync<Appointment>(criteria);
if (!appointments.Any())
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = "Appointment not found in system.",
Data = null
};
var appointment = appointments[0];
appointment.RemoveAppointment(dto.Appointment);
await _appointmentsMongoDbService.ModifyAsync("_id", appointmentId, appointment);
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Appointment successfully removed.",
Data = null
};
}
public async Task<BaseResponse> HandleGetAppointmentsByPatientId(Guid patientId)
{
var criteria = new List<(string FieldName, string Value)>
{
("PatientId", patientId.ToString())
};
var appointments = await _appointmentsMongoDbService.FindAsync<Appointment>(criteria);
if (appointments.Count == 0)
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = $"Appointments not found in system for patient with id {patientId}.",
Data = null
};
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = $"Appointments for patient with id {patientId} successfully retrieved.",
Data = appointments
};
}
public async Task<BaseResponse> HandleGetAppointmentsByDoctorId(Guid doctorId)
{
var criteria = new List<(string FieldName, string Value)>
{
("DoctorId", doctorId.ToString())
};
var appointments = await _appointmentsMongoDbService.FindAsync<Appointment>(criteria);
if (appointments.Count == 0)
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = $"Appointments not found in system for doctor with id {doctorId}.",
Data = null
};
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = $"Appointments for doctor with id {doctorId} successfully retrieved.",
Data = appointments
};
}
}
@@ -0,0 +1,63 @@
using Application.Services.Database.MongoDB;
using Application.Services.Database.PostgreSQL;
using Domain.Entities;
namespace Application.Endpoints.Appointments.CreateAppointment;
public class CreateAppointmentHandler(
IAppointmentsMongoDbService appointmentsMongoDbService,
IPatientRepository patientRepository,
IDoctorRepository doctorRepository)
{
public async Task<BaseResponse> Handle(AppointmentInformation request, CancellationToken token)
{
var validation = new CreateAppointmentValidator(appointmentsMongoDbService,
doctorRepository, patientRepository);
var validationResult = await validation.ValidateAsync(request, token);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
var appointmentId = IdentifierGenerator.GenerateId(request.DoctorId, request.PatientId);
var criteria = new List<(string FieldName, string Value)>
{
("_id", appointmentId)
};
var appointments = await appointmentsMongoDbService.FindAsync<Appointment>(criteria, token);
if (appointments.Count == 0)
{
var appointment = new Appointment();
appointment.SetId(appointmentId);
appointment.SetDoctorIid(request.DoctorId.ToString());
appointment.SetPatientId(request.PatientId.ToString());
appointment.AddAppointment(request.Appointment);
await appointmentsMongoDbService.AddAsync(appointment, token);
}
else
{
var appointment = appointments[0];
appointment.AddAppointment(request.Appointment);
await appointmentsMongoDbService.ModifyAsync("_id", appointmentId, appointment, token);
}
return new BaseResponse
{
StatusCode = HttpStatusCodes.Created,
Message = "Appointment successfully created.",
Data = null
};
}
}
@@ -2,9 +2,9 @@
using Application.Services.Database.PostgreSQL;
using FluentValidation;
namespace Application.Endpoints.Appointments;
namespace Application.Endpoints.Appointments.CreateAppointment;
public class CreateAppointmentValidator : AbstractValidator<AppointmentManagementDto>
public class CreateAppointmentValidator : AbstractValidator<AppointmentInformation>
{
private readonly IAppointmentsMongoDbService _appointmentsMongoDbService;
private readonly IDoctorRepository _doctorRepository;
@@ -28,7 +28,8 @@ public class CreateAppointmentValidator : AbstractValidator<AppointmentManagemen
.WithErrorCode(HttpStatusCodes.BadRequest.ToString());
RuleFor(x => x)
.MustAsync(IsAppointmentUnique).WithMessage("Appointment already in system.")
.MustAsync(IsAppointmentUnique)
.WithMessage("Appointment already in system.")
.WithErrorCode(HttpStatusCodes.Conflict.ToString());
_appointmentsMongoDbService = appointmentsMongoDbService;
@@ -36,15 +37,15 @@ public class CreateAppointmentValidator : AbstractValidator<AppointmentManagemen
_patientRepository = patientRepository;
}
private async Task<bool> IsDoctorRegistered(Guid id, CancellationToken cancellationToken)
private async Task<bool> IsDoctorRegistered(Guid id, CancellationToken token)
{
var doctor = await _doctorRepository.GetByIdAsync(id);
var doctor = await _doctorRepository.GetByIdAsync(id, token);
return doctor != null;
}
private async Task<bool> IsPatientRegistered(Guid id, CancellationToken cancellationToken)
private async Task<bool> IsPatientRegistered(Guid id, CancellationToken token)
{
var patient = await _patientRepository.GetByIdAsync(id);
var patient = await _patientRepository.GetByIdAsync(id, token);
return patient != null;
}
@@ -59,8 +60,8 @@ public class CreateAppointmentValidator : AbstractValidator<AppointmentManagemen
return true;
}
private async Task<bool> IsAppointmentUnique(AppointmentManagementDto dto, CancellationToken cancellationToken)
private async Task<bool> IsAppointmentUnique(AppointmentInformation request, CancellationToken token)
{
return await _appointmentsMongoDbService.IsAppointmentUnique(dto);
return await _appointmentsMongoDbService.IsAppointmentUnique(request, token);
}
}
@@ -0,0 +1,58 @@
using Application.Services.Database.MongoDB;
using Application.Services.Database.PostgreSQL;
using Domain.Entities;
namespace Application.Endpoints.Appointments.DeleteAppointment;
public class DeleteAppointmentHandler(
IAppointmentsMongoDbService appointmentsMongoDbService,
IPatientRepository patientRepository,
IDoctorRepository doctorRepository)
{
public async Task<BaseResponse> Handle(AppointmentInformation request, CancellationToken token)
{
var validation = new DeleteAppointmentValidator(appointmentsMongoDbService,
doctorRepository, patientRepository);
var validationResult = await validation.ValidateAsync(request, token);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
var appointmentId = IdentifierGenerator.GenerateId(request.DoctorId, request.PatientId);
var criteria = new List<(string FieldName, string Value)>
{
("_id", appointmentId)
};
var appointments = await appointmentsMongoDbService.FindAsync<Appointment>(criteria, token);
if (!appointments.Any())
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = "Appointment not found in system.",
Data = null
};
var appointment = appointments[0];
appointment.RemoveAppointment(request.Appointment);
await appointmentsMongoDbService.ModifyAsync("_id", appointmentId, appointment, token);
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Appointment successfully removed.",
Data = null
};
}
}
@@ -1,10 +1,11 @@
using Application.Services.Database.MongoDB;
using Application.Services.Database.PostgreSQL;
using Domain.Entities;
using FluentValidation;
namespace Application.Endpoints.Appointments;
namespace Application.Endpoints.Appointments.DeleteAppointment;
public class DeleteAppointmentValidator : AbstractValidator<AppointmentManagementDto>
public class DeleteAppointmentValidator : AbstractValidator<AppointmentInformation>
{
private readonly IAppointmentsMongoDbService _appointmentsMongoDbService;
private readonly IDoctorRepository _doctorRepository;
@@ -36,15 +37,15 @@ public class DeleteAppointmentValidator : AbstractValidator<AppointmentManagemen
_patientRepository = patientRepository;
}
private async Task<bool> IsDoctorRegistered(Guid id, CancellationToken cancellationToken)
private async Task<bool> IsDoctorRegistered(Guid id, CancellationToken token)
{
var doctor = await _doctorRepository.GetByIdAsync(id);
var doctor = await _doctorRepository.GetByIdAsync(id, token);
return doctor != null;
}
private async Task<bool> IsPatientRegistered(Guid id, CancellationToken cancellationToken)
private async Task<bool> IsPatientRegistered(Guid id, CancellationToken token)
{
var patient = await _patientRepository.GetByIdAsync(id);
var patient = await _patientRepository.GetByIdAsync(id, token);
return patient != null;
}
@@ -59,8 +60,8 @@ public class DeleteAppointmentValidator : AbstractValidator<AppointmentManagemen
return true;
}
private async Task<bool> DoesAppointmentExists(AppointmentManagementDto dto, CancellationToken cancellationToken)
private async Task<bool> DoesAppointmentExists(AppointmentInformation request, CancellationToken token)
{
return await _appointmentsMongoDbService.DoesAppointmentExists(dto);
return await _appointmentsMongoDbService.DoesAppointmentExists(request, token);
}
}
@@ -0,0 +1,54 @@
using Application.Services.Database.MongoDB;
using Application.Services.Database.PostgreSQL;
using Domain.Entities;
namespace Application.Endpoints.Appointments.QuerriesAppointments;
public class QuerriesAppointmentHandler(IAppointmentsMongoDbService appointmentsMongoDbService)
{
public async Task<BaseResponse> GetByPatientId(Guid patientId, CancellationToken token)
{
var criteria = new List<(string FieldName, string Value)>
{
("PatientId", patientId.ToString())
};
var appointments = await appointmentsMongoDbService.FindAsync<Appointment>(criteria, token);
if (appointments.Count == 0)
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = $"Appointments not found in system.",
Data = null
};
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = $"Appointments successfully retrieved.",
Data = appointments
};
}
public async Task<BaseResponse> GetsByDoctorId(Guid doctorId, CancellationToken token)
{
var criteria = new List<(string FieldName, string Value)>
{
("DoctorId", doctorId.ToString())
};
var appointments = await appointmentsMongoDbService.FindAsync<Appointment>(criteria, token);
if (appointments.Count == 0)
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = $"Appointments not found in system.",
Data = null
};
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = $"Appointments successfully retrieved.",
Data = appointments
};
}
}
@@ -1,45 +0,0 @@
using Application.Services.Database.PostgreSQL;
using Application.Services.HashingAlgorithms;
namespace Application.Endpoints.Authorization.Doctor;
public class DoctorLoginHandler
{
private readonly IDoctorRepository _database;
private readonly IHashingAlgorithms _hashingAlgorithms;
public DoctorLoginHandler(IDoctorRepository database, IHashingAlgorithms hashingAlgorithms)
{
_database = database;
_hashingAlgorithms = hashingAlgorithms;
}
public async Task<BaseResponse> Handle(LoginDto loginDTO)
{
loginDTO.Password = _hashingAlgorithms.SHA256Algorithm(loginDTO.Password);
var validation = new DoctorLoginValidation(_database);
var validationResult = await validation.ValidateAsync(loginDTO);
if (validationResult.IsValid)
{
var doctor = await _database.FindByEmailAsync(loginDTO.Email);
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Authentication successful",
Data = doctor
};
}
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
}
@@ -1,33 +0,0 @@
using Application.Services.Database.PostgreSQL;
using FluentValidation;
namespace Application.Endpoints.Authorization.Doctor;
public class DoctorLoginValidation : AbstractValidator<LoginDto>
{
private readonly IDoctorRepository _doctorRepository;
public DoctorLoginValidation(IDoctorRepository doctorRepository)
{
_doctorRepository = doctorRepository;
RuleFor(x => x.Email)
.NotEmpty().WithMessage("Email is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.EmailAddress().WithMessage("Invalid email format.").WithErrorCode(HttpStatusCodes.BadRequest.ToString());
RuleFor(x => x.Password)
.NotEmpty().WithMessage("Password is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.MinimumLength(8).WithMessage("Password must be at least 8 characters long.")
.WithErrorCode(HttpStatusCodes.BadRequest.ToString());
RuleFor(x => x)
.MustAsync(CredentialsMatch).WithMessage("Invalid credentials")
.WithErrorCode(HttpStatusCodes.Unauthorized.ToString());
}
private async Task<bool> CredentialsMatch(LoginDto dto, CancellationToken cancellationToken)
{
var code = await _doctorRepository.CredentialsMatch(dto.Email, dto.Password);
return code;
}
}
@@ -1,49 +0,0 @@
using Application.Services.Database.PostgreSQL;
using Application.Services.HashingAlgorithms;
namespace Application.Endpoints.Authorization.Doctor;
public class DoctorResetPasswordHandler
{
private readonly IDoctorRepository _doctorRepository;
private readonly IHashingAlgorithms _hashingAlgorithms;
public DoctorResetPasswordHandler(IDoctorRepository doctorRepository, IHashingAlgorithms hashingAlgorithms)
{
_doctorRepository = doctorRepository;
_hashingAlgorithms = hashingAlgorithms;
}
public async Task<BaseResponse> Handle(LoginDto resetDoctorDto)
{
var validation = new DoctorResetPasswordValidation(_doctorRepository, _hashingAlgorithms);
var validationResult = await validation.ValidateAsync(resetDoctorDto);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
var currentDoctor = await _doctorRepository.FindByEmailAsync(resetDoctorDto.Email);
var updatedDoctor = currentDoctor;
updatedDoctor.SetPassword(_hashingAlgorithms.SHA256Algorithm(resetDoctorDto.Password));
await _doctorRepository.UpdateAsync(updatedDoctor);
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Password successfully changed!",
Data = null
};
}
}
@@ -1,45 +0,0 @@
using System.Net;
using Application.Services.Database.PostgreSQL;
using Application.Services.HashingAlgorithms;
using FluentValidation;
namespace Application.Endpoints.Authorization.Doctor;
public class DoctorResetPasswordValidation : AbstractValidator<LoginDto>
{
private readonly IDoctorRepository _doctorRepository;
private readonly IHashingAlgorithms _hashingAlgorithms;
public DoctorResetPasswordValidation(IDoctorRepository doctorRepository, IHashingAlgorithms hashingAlgorithms)
{
_doctorRepository = doctorRepository;
_hashingAlgorithms = hashingAlgorithms;
RuleFor(x => x.Email)
.NotEmpty().WithMessage("Email is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.EmailAddress().WithMessage("Invalid email format.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.MustAsync(BeExistingDoctor).WithMessage("Patient with this email does not exist.")
.WithErrorCode(HttpStatusCodes.NotFound.ToString());
RuleFor(x => x.Password)
.NotEmpty().WithMessage("Password is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.WithErrorCode(HttpStatusCode.BadRequest.ToString());
RuleFor(x => x)
.MustAsync(BeDifferentFromOldPassword).WithMessage("Password can't be as the previous.")
.WithErrorCode(HttpStatusCode.BadRequest.ToString()).WithErrorCode(HttpStatusCodes.BadRequest.ToString());
}
private async Task<bool> BeExistingDoctor(string email, CancellationToken cancellationToken)
{
var doctor = await _doctorRepository.FindByEmailAsync(email);
return doctor != null;
}
private async Task<bool> BeDifferentFromOldPassword(LoginDto dto, CancellationToken cancellationToken)
{
var currentPatient = await _doctorRepository.FindByEmailAsync(dto.Email);
return !_hashingAlgorithms.SHA256Algorithm(dto.Password)
.Equals(currentPatient?.Password, StringComparison.Ordinal);
}
}
@@ -1,7 +0,0 @@
namespace Application.Endpoints.Authorization;
public class LoginDto
{
public string Email;
public string Password;
}
@@ -1,43 +0,0 @@
using Application.Services.Database.PostgreSQL;
using Application.Services.HashingAlgorithms;
namespace Application.Endpoints.Authorization.Patient;
public class PatientLoginHandler
{
private readonly IPatientRepository _patientRepository;
private readonly IHashingAlgorithms _hashingAlgorithms;
public PatientLoginHandler(IPatientRepository patientRepository, IHashingAlgorithms hashingAlgorithms)
{
_patientRepository = patientRepository;
_hashingAlgorithms = hashingAlgorithms;
}
public async Task<BaseResponse> Handle(LoginDto loginDTO)
{
var validation = new PatientLoginValidation(_patientRepository, _hashingAlgorithms);
var validationResult = await validation.ValidateAsync(loginDTO);
if (validationResult.IsValid)
{
var patient = await _patientRepository.FindByEmailAsync(loginDTO.Email);
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Authentication successful",
Data = patient
};
}
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
}
@@ -1,37 +0,0 @@
using Application.Services.Database.PostgreSQL;
using Application.Services.HashingAlgorithms;
using FluentValidation;
namespace Application.Endpoints.Authorization.Patient;
public class PatientLoginValidation : AbstractValidator<LoginDto>
{
private readonly IPatientRepository _patientRepository;
private readonly IHashingAlgorithms _hashingAlgorithms;
public PatientLoginValidation(IPatientRepository patientRepository, IHashingAlgorithms hashingAlgorithms)
{
_patientRepository = patientRepository;
_hashingAlgorithms = hashingAlgorithms;
RuleFor(x => x.Email)
.NotEmpty().WithMessage("Email is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.EmailAddress().WithMessage("Invalid email format.").WithErrorCode(HttpStatusCodes.BadRequest.ToString());
RuleFor(x => x.Password)
.NotEmpty().WithMessage("Password is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.MinimumLength(8).WithMessage("Password must be at least 8 characters long.")
.WithErrorCode(HttpStatusCodes.BadRequest.ToString());
RuleFor(x => x)
.MustAsync(CredentialsMatch).WithMessage("Invalid credentials")
.WithErrorCode(HttpStatusCodes.Unauthorized.ToString());
}
private async Task<bool> CredentialsMatch(LoginDto dto, CancellationToken cancellationToken)
{
var code = await _patientRepository.CredentialsMatch(
dto.Email, _hashingAlgorithms.SHA256Algorithm(dto.Password));
return code;
}
}
@@ -1,49 +0,0 @@
using Application.Services.Database.PostgreSQL;
using Application.Services.HashingAlgorithms;
namespace Application.Endpoints.Authorization.Patient;
public class PatientResetPasswordHandler
{
private readonly IHashingAlgorithms _hashingAlgorithms;
private readonly IPatientRepository _patientRepository;
public PatientResetPasswordHandler(IPatientRepository patientRepository, IHashingAlgorithms hashingAlgorithms)
{
_patientRepository = patientRepository;
_hashingAlgorithms = hashingAlgorithms;
}
public async Task<BaseResponse> Handle(LoginDto patientResetPasswordDto)
{
var validation = new PatientResetPasswordValidation(_patientRepository, _hashingAlgorithms);
var validationResult = await validation.ValidateAsync(patientResetPasswordDto);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
var currentPatient = await _patientRepository.FindByEmailAsync(patientResetPasswordDto.Email);
var updatedPatient = currentPatient;
updatedPatient.SetPassword(_hashingAlgorithms.SHA256Algorithm(patientResetPasswordDto.Password));
await _patientRepository.UpdateAsync(updatedPatient);
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Password successfully changed!",
Data = null
};
}
}
@@ -1,46 +0,0 @@
using System.Net;
using Application.Services.Database.PostgreSQL;
using Application.Services.HashingAlgorithms;
using FluentValidation;
namespace Application.Endpoints.Authorization.Patient;
public class PatientResetPasswordValidation : AbstractValidator<LoginDto>
{
private readonly IPatientRepository _patientRepository;
private readonly IHashingAlgorithms _hashingAlgorithms;
public PatientResetPasswordValidation(IPatientRepository patientRepository,
IHashingAlgorithms hashingAlgorithms)
{
_patientRepository = patientRepository;
_hashingAlgorithms = hashingAlgorithms;
RuleFor(x => x.Email)
.NotEmpty().WithMessage("Email is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.EmailAddress().WithMessage("Invalid email format.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.MustAsync(BeExistingPatient).WithMessage("Patient with this email does not exist.")
.WithErrorCode(HttpStatusCodes.NotFound.ToString());
RuleFor(x => x.Password)
.NotEmpty().WithMessage("Password is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.WithErrorCode(HttpStatusCode.BadRequest.ToString());
RuleFor(x => x)
.MustAsync(BeDifferentFromOldPassword).WithMessage("Password can't be as the previous.")
.WithErrorCode(HttpStatusCode.BadRequest.ToString()).WithErrorCode(HttpStatusCodes.BadRequest.ToString());
}
private async Task<bool> BeExistingPatient(string email, CancellationToken cancellationToken)
{
var patient = await _patientRepository.FindByEmailAsync(email);
return patient != null;
}
private async Task<bool> BeDifferentFromOldPassword(LoginDto dto, CancellationToken cancellationToken)
{
var currentPatient = await _patientRepository.FindByEmailAsync(dto.Email);
return !_hashingAlgorithms.SHA256Algorithm(dto.Password)
.Equals(currentPatient?.Password, StringComparison.Ordinal);
}
}
@@ -0,0 +1,6 @@
namespace Application.Endpoints.Authorization.RefreshToken;
public class RefreshJwtCommand
{
public string Token { get; set; } = string.Empty;
}
@@ -0,0 +1,30 @@
using Application.Services.Jwt;
namespace Application.Endpoints.Authorization.RefreshToken;
public class RefreshJwtHandler(IJwtService jwtService)
{
public async Task<BaseResponse> Handle(RefreshJwtCommand command, CancellationToken cancellationToken)
{
var validator = new RefreshJwtValidator(jwtService);
var validationResult = await validator.ValidateAsync(command, cancellationToken);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
return new BaseResponse
{
StatusCode = HttpStatusCodes.Unauthorized,
Message = firstError?.ErrorMessage
};
}
var newToken = jwtService.RefreshToken(command.Token);
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Token refreshed successfully.",
Data = newToken
};
}
}
@@ -0,0 +1,23 @@
using Application.Services.Jwt;
using FluentValidation;
namespace Application.Endpoints.Authorization.RefreshToken;
public class RefreshJwtValidator : AbstractValidator<RefreshJwtCommand>
{
private readonly IJwtService _jwtService;
public RefreshJwtValidator(IJwtService jwtService)
{
_jwtService = jwtService;
RuleFor(x => x.Token)
.NotEmpty().WithMessage("Token is required.")
.Must(BeAValidToken).WithMessage("Token is invalid or expired.");
}
private bool BeAValidToken(string token)
{
return _jwtService.ValidateJwtToken(token);
}
}
@@ -0,0 +1,7 @@
namespace Application.Endpoints.Authorization.UserLogin;
public class UserLoginCommand
{
public string Email { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
}
@@ -0,0 +1,79 @@
using Application.Services.Database.PostgreSQL;
using Application.Services.HashingAlgorithms;
using Application.Services.Jwt;
using Domain;
namespace Application.Endpoints.Authorization.UserLogin;
public class UserLoginHandler(
IJwtService jwtService,
IHashingAlgorithms hashingAlgorithms,
IDoctorRepository doctorRepository,
IPatientRepository patientRepository,
IAdminRepository adminRepository)
{
public async Task<BaseResponse> Handle(UserLoginCommand request, CancellationToken token)
{
var validator = new UserLoginValidator();
var result = await validator.ValidateAsync(request, token);
if (!result.IsValid)
{
var firstError = result.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
var email = request.Email;
var password = hashingAlgorithms.Sha256Algorithm(request.Password);
if (await doctorRepository.CredentialsMatch(email, password, token))
{
var doctor = await doctorRepository.FindByEmailAsync(email, token);
var authToken = jwtService.GenerateJwtToken(doctor.Id, UserRoles.Doctor, doctor.Name);
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Doctor logged in successfully.",
Data = authToken
};
}
if (await patientRepository.CredentialsMatch(email, password, token))
{
var patient = await patientRepository.FindByEmailAsync(email, token);
var authToken = jwtService.GenerateJwtToken(patient.Id, UserRoles.Patient, patient.Name);
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Patient logged in successfully.",
Data = authToken
};
}
if (await adminRepository.CredentialsMatch(email, password, token))
{
var admin = await adminRepository.FindByEmailAsync(email, token);
var authToken = jwtService.GenerateJwtToken(admin.Id, UserRoles.Admin, admin.Name);
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Admin logged in successfully.",
Data = authToken
};
}
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = "User not registered in our system.",
Data = null
};
}
}
@@ -0,0 +1,18 @@
using FluentValidation;
namespace Application.Endpoints.Authorization.UserLogin;
public class UserLoginValidator : AbstractValidator<UserLoginCommand>
{
public UserLoginValidator()
{
RuleFor(x => x.Email)
.NotEmpty().WithMessage("Email is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.EmailAddress().WithMessage("Invalid email format.").WithErrorCode(HttpStatusCodes.BadRequest.ToString());
RuleFor(x => x.Password)
.NotEmpty().WithMessage("Password is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.MinimumLength(8).WithMessage("Password must be at least 8 characters long.")
.WithErrorCode(HttpStatusCodes.BadRequest.ToString());
}
}
@@ -1,8 +0,0 @@
namespace Application.Endpoints.Authorization;
public class UserLoginModel
{
public string? UserType { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
@@ -0,0 +1,9 @@
namespace Application.Endpoints.Authorization.UserRegister;
public class UserRegisterCommand
{
public string Name { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
public string Role { get; set; } = string.Empty;
}
@@ -0,0 +1,73 @@
using Application.Services.Database.PostgreSQL;
using Application.Services.Email;
using Application.Services.HashingAlgorithms;
using Domain;
using Domain.Entities;
namespace Application.Endpoints.Authorization.UserRegister;
public class UserRegisterHandler(
IEmailService emailService,
IHashingAlgorithms hashingAlgorithms,
IDoctorRepository doctorRepository,
IPatientRepository patientRepository,
IAdminRepository adminRepository)
{
public async Task<BaseResponse> Handle(UserRegisterCommand request, CancellationToken token)
{
var validator = new UserRegisterValidator(
patientRepository, doctorRepository, adminRepository);
var result = await validator.ValidateAsync(request, token);
if (!result.IsValid)
{
var firstError = result.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
switch (request.Role)
{
case UserRoles.Admin:
var admin = new Admin();
admin.SetName(request.Name);
admin.SetEmail(request.Email);
admin.SetPassword(hashingAlgorithms.Sha256Algorithm(request.Password));
await adminRepository.AddAsync(admin, token);
break;
case UserRoles.Doctor:
var doctor = new Doctor();
doctor.SetName(request.Name);
doctor.SetEmail(request.Email);
doctor.SetPassword(hashingAlgorithms.Sha256Algorithm(request.Password));
await doctorRepository.AddAsync(doctor, token);
break;
case UserRoles.Patient:
var patient = new Patient();
patient.SetName(request.Name);
patient.SetEmail(request.Email);
patient.SetPassword(hashingAlgorithms.Sha256Algorithm(request.Password));
await patientRepository.AddAsync(patient, token);
break;
}
var emailBody = emailService.GenerateCredentialsEmailBody(request.Name, request.Email, request.Password);
await emailService.SendEmailAsync(request.Email, emailService.GetSuccessfulRegistrationSubject(), emailBody);
return new BaseResponse
{
StatusCode = HttpStatusCodes.Created,
Message = "User created successfully",
Data = null
};
}
}
@@ -0,0 +1,57 @@
using Application.Services.Database.PostgreSQL;
using Application.Services.HashingAlgorithms;
using Domain;
using FluentValidation;
namespace Application.Endpoints.Authorization.UserRegister;
public class UserRegisterValidator : AbstractValidator<UserRegisterCommand>
{
private readonly IAdminRepository _adminRepository;
private readonly IDoctorRepository _doctorRepository;
private readonly IPatientRepository _patientRepository;
public UserRegisterValidator(IPatientRepository patientRepository,
IDoctorRepository doctorRepository, IAdminRepository adminRepository)
{
_patientRepository = patientRepository;
_doctorRepository = doctorRepository;
_adminRepository = adminRepository;
RuleFor(x => x.Name)
.NotEmpty().WithMessage("Name is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.MaximumLength(30).WithMessage("Maximum name length of 30 characters")
.WithErrorCode(HttpStatusCodes.BadRequest.ToString());
RuleFor(x => x.Email)
.NotEmpty().WithMessage("Email is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.EmailAddress().WithMessage("Invalid email format.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.MustAsync(async (email, token) => await AccountNotRegistered(email, token))
.WithMessage("Account already registered in system.").WithErrorCode(HttpStatusCodes.BadRequest.ToString());
RuleFor(x => x.Password)
.NotEmpty().WithMessage("Password is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.MinimumLength(8).WithMessage("Password must be at least 8 characters long.")
.WithErrorCode(HttpStatusCodes.BadRequest.ToString());
RuleFor(x => x.Role)
.NotEmpty().WithMessage("Role is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.Must(BeAValidRole).WithMessage("Invalid role specified.")
.WithErrorCode(HttpStatusCodes.BadRequest.ToString());
}
private bool BeAValidRole(string role)
{
var validRoles = new[] { UserRoles.Admin, UserRoles.Doctor, UserRoles.Patient };
return validRoles.Contains(role);
}
private async Task<bool> AccountNotRegistered(string email, CancellationToken token)
{
var patient = await _patientRepository.FindByEmailAsync(email, token);
var doctor = await _doctorRepository.FindByEmailAsync(email, token);
var admin = await _adminRepository.FindByEmailAsync(email, token);
return patient == null && doctor == null && admin == null;
}
}
@@ -0,0 +1,7 @@
namespace Application.Endpoints.Authorization.UserResetPassword;
public class UserResetPasswordCommand
{
public string Email { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
}
@@ -0,0 +1,68 @@
using Application.Services.Database.PostgreSQL;
using Application.Services.Email;
using Application.Services.HashingAlgorithms;
namespace Application.Endpoints.Authorization.UserResetPassword;
public class UserResetPasswordHandler(
IEmailService emailService,
IHashingAlgorithms hashingAlgorithms,
IDoctorRepository doctorRepository,
IPatientRepository patientRepository,
IAdminRepository adminRepository)
{
public async Task<BaseResponse> Handle(UserResetPasswordCommand request, CancellationToken token)
{
var validator = new UserResetPasswordValidator(
patientRepository, doctorRepository, adminRepository);
var result = await validator.ValidateAsync(request, token);
if (!result.IsValid)
{
var firstError = result.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
string? name = null;
var patient = await patientRepository.FindByEmailAsync(request.Email, token);
if (patient != null)
{
name = patient.Name;
patient.SetPassword(hashingAlgorithms.Sha256Algorithm(request.Password));
await patientRepository.UpdateAsync(patient, token);
}
var doctor = await doctorRepository.FindByEmailAsync(request.Email, token);
if (doctor != null)
{
name = doctor.Name;
doctor.SetPassword(hashingAlgorithms.Sha256Algorithm(request.Password));
await doctorRepository.UpdateAsync(doctor, token);
}
var admin = await adminRepository.FindByEmailAsync(request.Email, token);
if (admin != null)
{
name = admin.Name;
admin.SetPassword(hashingAlgorithms.Sha256Algorithm(request.Password));
await adminRepository.UpdateAsync(admin, token);
}
var emailBody = emailService.GenerateResetCredentialsEmailBody(name, request.Email, request.Password);
await emailService.SendEmailAsync(request.Email, emailService.GetSuccessfulPasswordResetSubject(), emailBody);
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Password updated successfully.",
Data = null
};
}
}
@@ -0,0 +1,52 @@
using Application.Services.Database.PostgreSQL;
using Domain;
using FluentValidation;
namespace Application.Endpoints.Authorization.UserResetPassword;
public class UserResetPasswordValidator : AbstractValidator<UserResetPasswordCommand>
{
private readonly IAdminRepository _adminRepository;
private readonly IDoctorRepository _doctorRepository;
private readonly IPatientRepository _patientRepository;
public UserResetPasswordValidator(IPatientRepository patientRepository,
IDoctorRepository doctorRepository, IAdminRepository adminRepository)
{
_patientRepository = patientRepository;
_doctorRepository = doctorRepository;
_adminRepository = adminRepository;
RuleFor(x => x.Email)
.NotEmpty().WithMessage("Email is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.EmailAddress().WithMessage("Invalid email format.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.MustAsync(async (email, token) => await AccountRegistered(email, token))
.WithMessage("Account already registered in system.").WithErrorCode(HttpStatusCodes.BadRequest.ToString());
RuleFor(x => x.Password)
.NotEmpty().WithMessage("Password is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.MinimumLength(8).WithMessage("Password must be at least 8 characters long.")
.WithErrorCode(HttpStatusCodes.BadRequest.ToString());
RuleFor(x => x)
.MustAsync(async (request, token) =>
await AccountRegistered(request.Email, token))
.WithMessage("Account already registered in system.").WithErrorCode(HttpStatusCodes.BadRequest.ToString());
}
private bool BeAValidRole(string role)
{
var validRoles = new[] { UserRoles.Admin, UserRoles.Doctor, UserRoles.Patient };
return validRoles.Contains(role);
}
private async Task<bool> AccountRegistered(string email, CancellationToken token)
{
var patient = await _patientRepository.FindByEmailAsync(email, token);
var doctor = await _doctorRepository.FindByEmailAsync(email, token);
var admin = await _adminRepository.FindByEmailAsync(email, token);
return patient != null || doctor != null || admin != null;
}
}
@@ -1,144 +0,0 @@
using Application.Services.Database.MongoDB;
using Application.Services.Database.PostgreSQL;
using Core.Entities;
namespace Application.Endpoints.Chats;
public class ChatHandler
{
private readonly IChatMongoDbService _chatMongoDbService;
private readonly IDoctorRepository _doctorRepository;
private readonly IPatientRepository _patientRepository;
public ChatHandler(IChatMongoDbService chatMongoDbService, IPatientRepository patientRepository,
IDoctorRepository doctorRepository)
{
_chatMongoDbService = chatMongoDbService;
_patientRepository = patientRepository;
_doctorRepository = doctorRepository;
}
public async Task<BaseResponse> HandleSendMessage(SendMessageDto sendMessageDto)
{
var validation = new SendMessageValidator();
var validationResult = await validation.ValidateAsync(sendMessageDto);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
if (!await CheckForUsersExistence(sendMessageDto.Sender, sendMessageDto.Receiver))
return new BaseResponse
{
StatusCode = HttpStatusCodes.BadRequest,
Message = "Users can't be found in the system.",
Data = null
};
var chatId = IdentifierGenerator.GenerateId(sendMessageDto.Sender, sendMessageDto.Receiver);
var criteria = new List<(string, string)>();
criteria.Add(("_id", chatId));
var documents = await _chatMongoDbService.FindAsync<Chat>(criteria);
if (!documents.Any())
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = "Access to medical history not found.",
Data = null
};
var chat = documents[0].Messages;
chat.Add(new Message(sendMessageDto.Sender, sendMessageDto.Message));
var newChat = new Chat();
newChat.SetId(chatId);
newChat.SetMessages(chat);
await _chatMongoDbService.ModifyAsync("_id", chatId, newChat);
return new BaseResponse
{
StatusCode = HttpStatusCodes.NoContent,
Message = null,
Data = null
};
}
public async Task<BaseResponse> HandleGetConversation(GetConversationDto getConversationDto)
{
var validation = new GetConversationValidator();
var validationResult = await validation.ValidateAsync(getConversationDto);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
if (!await CheckForUsersExistence(getConversationDto.IdUser1, getConversationDto.IdUser2))
return new BaseResponse
{
StatusCode = HttpStatusCodes.BadRequest,
Message = "Users can't be found in the system.",
Data = null
};
var chatId = IdentifierGenerator.GenerateId(getConversationDto.IdUser1, getConversationDto.IdUser2);
var criteria = new List<(string, string)>();
criteria.Add(("_id", chatId));
Chat? chat = null;
var documents = await _chatMongoDbService.FindAsync<Chat>(criteria);
if (!documents.Any())
{
chat = new Chat();
chat.SetId(chatId);
await _chatMongoDbService.AddAsync(chat);
}
else
{
chat = documents[0];
}
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Fetching messages.",
Data = chat
};
}
private async Task<bool> CheckForUsersExistence(Guid idUser1, Guid idUser2)
{
var firstCheck = await _patientRepository.GetByIdAsync(idUser1) != null &&
await _doctorRepository.GetByIdAsync(idUser2) != null;
var secondCheck = await _patientRepository.GetByIdAsync(idUser2) != null &&
await _doctorRepository.GetByIdAsync(idUser1) != null;
return firstCheck || secondCheck;
}
}
@@ -0,0 +1,7 @@
namespace Application.Endpoints.Chats.GetChats;
public class GetConversationsCommand
{
public Guid IdUser1 { get; set; } = Guid.Empty;
public Guid IdUser2 { get; set; } = Guid.Empty;
}
@@ -0,0 +1,59 @@
using Application.Services.Database.MongoDB;
using Application.Services.Database.PostgreSQL;
using Domain.Entities;
namespace Application.Endpoints.Chats.GetChats;
public class GetConversationsHandler(
IChatMongoDbService chatMongoDbService,
IPatientRepository patientRepository,
IDoctorRepository doctorRepository)
{
public async Task<BaseResponse> HandleGetConversation(GetConversationsCommand getConversationCommand,
CancellationToken token)
{
var validation = new GetConversationsValidator(doctorRepository, patientRepository);
var validationResult = await validation.ValidateAsync(getConversationCommand, token);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
var chatId = IdentifierGenerator.GenerateId(getConversationCommand.IdUser1, getConversationCommand.IdUser2);
var criteria = new List<(string, string)>();
criteria.Add(("_id", chatId));
Chat? chat = null;
var documents = await chatMongoDbService.FindAsync<Chat>(criteria, token);
if (!documents.Any())
{
chat = new Chat();
chat.SetId(chatId);
await chatMongoDbService.AddAsync(chat, token);
}
else
{
chat = documents[0];
}
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Fetching messages.",
Data = chat
};
}
}
@@ -0,0 +1,37 @@
using Application.Services.Database.PostgreSQL;
using FluentValidation;
namespace Application.Endpoints.Chats.GetChats;
public class GetConversationsValidator : AbstractValidator<GetConversationsCommand>
{
private readonly IPatientRepository _patientRepository;
private readonly IDoctorRepository _doctorRepository;
public GetConversationsValidator(IDoctorRepository doctorRepository, IPatientRepository patientRepository)
{
RuleFor(x => x.IdUser1)
.NotEmpty().WithMessage("IdUser1 is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString());
RuleFor(x => x.IdUser2)
.NotEmpty().WithMessage("IdUser2 is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString());
RuleFor(x => x)
.MustAsync(CheckForUsersExistence).WithMessage("One of the users is not existing.")
.WithErrorCode(HttpStatusCodes.BadRequest.ToString());
_patientRepository = patientRepository;
_doctorRepository = doctorRepository;
}
private async Task<bool> CheckForUsersExistence(GetConversationsCommand request, CancellationToken token)
{
var firstCheck = await _patientRepository.GetByIdAsync(request.IdUser1, token) != null &&
await _doctorRepository.GetByIdAsync(request.IdUser2, token) != null;
var secondCheck = await _patientRepository.GetByIdAsync(request.IdUser2, token) != null &&
await _doctorRepository.GetByIdAsync(request.IdUser1, token) != null;
return firstCheck || secondCheck;
}
}
@@ -1,7 +0,0 @@
namespace Application.Endpoints.Chats;
public class GetConversationDto
{
public Guid IdUser1 { get; set; }
public Guid IdUser2 { get; set; }
}
@@ -1,15 +0,0 @@
using FluentValidation;
namespace Application.Endpoints.Chats;
public class GetConversationValidator : AbstractValidator<GetConversationDto>
{
public GetConversationValidator()
{
RuleFor(x => x.IdUser1)
.NotEmpty().WithMessage("IdUser1 is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString());
RuleFor(x => x.IdUser2)
.NotEmpty().WithMessage("IdUser2 is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString());
}
}
@@ -0,0 +1,8 @@
namespace Application.Endpoints.Chats.SendMessage;
public class SendMessageCommand
{
public Guid Sender { get; set; } = Guid.Empty;
public Guid Receiver { get; set; } = Guid.Empty;
public string Message { get; set; } = string.Empty;
}
@@ -0,0 +1,61 @@
using Application.Services.Database.MongoDB;
using Application.Services.Database.PostgreSQL;
using Domain.Entities;
namespace Application.Endpoints.Chats.SendMessage;
public class SendMessageHandler(
IChatMongoDbService chatMongoDbService,
IPatientRepository patientRepository,
IDoctorRepository doctorRepository)
{
public async Task<BaseResponse> Handle(SendMessageCommand request, CancellationToken token)
{
var validation = new SendMessageValidator(doctorRepository, patientRepository);
var validationResult = await validation.ValidateAsync(request, token);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
var chatId = IdentifierGenerator.GenerateId(request.Sender, request.Receiver);
var criteria = new List<(string, string)>();
criteria.Add(("_id", chatId));
var documents = await chatMongoDbService.FindAsync<Chat>(criteria, token);
if (!documents.Any())
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = "Access to medical history not found.",
Data = null
};
var chat = documents[0].Messages;
chat.Add(new Message(request.Sender, request.Message));
var newChat = new Chat();
newChat.SetId(chatId);
newChat.SetMessages(chat);
await chatMongoDbService.ModifyAsync("_id", chatId, newChat, token);
return new BaseResponse
{
StatusCode = HttpStatusCodes.NoContent,
Message = null,
Data = null
};
}
}
@@ -0,0 +1,40 @@
using Application.Services.Database.PostgreSQL;
using FluentValidation;
namespace Application.Endpoints.Chats.SendMessage;
public class SendMessageValidator : AbstractValidator<SendMessageCommand>
{
private readonly IDoctorRepository _doctorRepository;
private readonly IPatientRepository _patientRepository;
public SendMessageValidator(IDoctorRepository doctorRepository, IPatientRepository patientRepository)
{
RuleFor(x => x.Sender)
.NotEmpty().WithMessage("Sender Id is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString());
RuleFor(x => x.Receiver)
.NotEmpty().WithMessage("Receiver Id is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString());
RuleFor(x => x.Message)
.NotEmpty().WithMessage("Message is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString());
RuleFor(x => x)
.MustAsync(CheckForUsersExistence).WithMessage("One of the users is not existing.")
.WithErrorCode(HttpStatusCodes.BadRequest.ToString());
_patientRepository = patientRepository;
_doctorRepository = doctorRepository;
}
private async Task<bool> CheckForUsersExistence(SendMessageCommand request, CancellationToken token)
{
var firstCheck = await _patientRepository.GetByIdAsync(request.Sender, token) != null &&
await _doctorRepository.GetByIdAsync(request.Receiver, token) != null;
var secondCheck = await _patientRepository.GetByIdAsync(request.Receiver, token) != null &&
await _doctorRepository.GetByIdAsync(request.Sender, token) != null;
return firstCheck || secondCheck;
}
}
@@ -1,8 +0,0 @@
namespace Application.Endpoints.Chats;
public class SendMessageDto
{
public Guid Sender { get; set; }
public Guid Receiver { get; set; }
public string Message { get; set; }
}
@@ -1,18 +0,0 @@
using FluentValidation;
namespace Application.Endpoints.Chats;
public class SendMessageValidator : AbstractValidator<SendMessageDto>
{
public SendMessageValidator()
{
RuleFor(x => x.Sender)
.NotEmpty().WithMessage("Sender Id is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString());
RuleFor(x => x.Receiver)
.NotEmpty().WithMessage("Receiver Id is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString());
RuleFor(x => x.Message)
.NotEmpty().WithMessage("Message is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString());
}
}
@@ -0,0 +1,38 @@
using Application.Services.Database.MongoDB;
using Application.Services.Database.PostgreSQL;
using Domain.Entities;
namespace Application.Endpoints.Doctors.DeleteDoctor;
public class DeleteDoctorHandler(
IDoctorRepository doctorRepository,
IAppointmentsMongoDbService appointmentsMongoDbService)
{
public async Task<BaseResponse> Handle(Guid id, CancellationToken token)
{
var doctor = await doctorRepository.GetByIdAsync(id, token);
if (doctor == null)
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = "Doctor was not found.",
Data = null
};
await doctorRepository.DeleteAsync(doctor, token);
var criteria = new List<(string FieldName, string Value)>
{
("DoctorId", id.ToString())
};
var appointments = await appointmentsMongoDbService.FindAsync<Appointment>(criteria, token);
if (appointments.Count != 0)
await appointmentsMongoDbService.DeleteByIdAsync<Appointment>(appointments[0].Id, token);
return new BaseResponse
{
StatusCode = HttpStatusCodes.NoContent,
Message = null,
Data = null
};
}
}
@@ -0,0 +1,10 @@
namespace Application.Endpoints.Doctors.ModifyDoctor;
public class ModifyDoctorCommand
{
public Guid Id { get; set; } = Guid.Empty;
public string Name { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
}
@@ -0,0 +1,47 @@
using Application.Services.Database.PostgreSQL;
using Application.Services.HashingAlgorithms;
namespace Application.Endpoints.Doctors.ModifyDoctor;
public class ModifyDoctorHandler(
IHashingAlgorithms hashingAlgorithms,
IDoctorRepository doctorRepository,
IPatientRepository patientRepository,
IAdminRepository adminRepository)
{
public async Task<BaseResponse> Handle(ModifyDoctorCommand request, CancellationToken token)
{
var validation = new ModifyDoctorValidator(doctorRepository, patientRepository, adminRepository);
var validationResult = await validation.ValidateAsync(request, token);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
var newDoctor = await doctorRepository.GetByIdAsync(request.Id, token);
newDoctor.SetEmail(request.Email);
newDoctor.SetPassword(hashingAlgorithms.Sha256Algorithm(request.Password));
newDoctor.SetName(request.Name);
newDoctor.SetDescription(request.Description);
await doctorRepository.UpdateAsync(newDoctor, token);
return new BaseResponse
{
StatusCode = HttpStatusCodes.NoContent,
Message = null,
Data = null
};
}
}
@@ -1,25 +1,30 @@
using Application.Services.Database.PostgreSQL;
using FluentValidation;
namespace Application.Endpoints.Doctors.Profile;
namespace Application.Endpoints.Doctors.ModifyDoctor;
public class DoctorProfileValidation : AbstractValidator<DoctorProfileUpdateDto>
public class ModifyDoctorValidator : AbstractValidator<ModifyDoctorCommand>
{
private readonly IDoctorRepository _doctorRepository;
private readonly IPatientRepository _patientRepository;
private readonly IAdminRepository _adminRepository;
public DoctorProfileValidation(IDoctorRepository doctorRepository)
public ModifyDoctorValidator(IDoctorRepository doctorRepository,
IPatientRepository patientRepository, IAdminRepository adminRepository)
{
_doctorRepository = doctorRepository;
_patientRepository = patientRepository;
_adminRepository = adminRepository;
RuleFor(x => x.Id)
.NotEmpty().WithMessage("Id is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.MustAsync(IsDoctorRegistered).WithMessage("Doctor is not registered in system")
.MustAsync(IsDoctorRegistered).WithMessage("Patient is not registered in system")
.WithErrorCode(HttpStatusCodes.NotFound.ToString());
RuleFor(x => x.Email)
.NotEmpty().WithMessage("Email is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.EmailAddress().WithMessage("Invalid email format.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.MustAsync(BeUniqueEmail).WithMessage("Email in use by another doctor.")
.MustAsync(BeUniqueEmail).WithMessage("Email in use by another patient.")
.WithErrorCode(HttpStatusCodes.Conflict.ToString());
RuleFor(x => x.Password)
@@ -31,21 +36,20 @@ public class DoctorProfileValidation : AbstractValidator<DoctorProfileUpdateDto>
.NotEmpty().WithMessage("Name is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.MinimumLength(3).WithMessage("Name must be at least 3 characters long.")
.WithErrorCode(HttpStatusCodes.BadRequest.ToString());
RuleFor(x => x.Description)
.MaximumLength(3000).WithMessage("Description must not exceed 3000 characters.")
.WithErrorCode(HttpStatusCodes.BadRequest.ToString());
}
private async Task<bool> IsDoctorRegistered(Guid id, CancellationToken cancellationToken)
private async Task<bool> IsDoctorRegistered(Guid id, CancellationToken token)
{
var doctor = await _doctorRepository.GetByIdAsync(id);
var doctor = await _doctorRepository.GetByIdAsync(id, token);
return doctor != null;
}
private async Task<bool> BeUniqueEmail(string email, CancellationToken cancellationToken)
private async Task<bool> BeUniqueEmail(string email, CancellationToken token)
{
var doctor = await _doctorRepository.FindByEmailAsync(email);
return doctor == null;
var patient = await _patientRepository.FindByEmailAsync(email, token);
var doctor = await _doctorRepository.FindByEmailAsync(email, token);
var admin = await _adminRepository.FindByEmailAsync(email, token);
return patient == null && admin == null;
}
}
@@ -1,110 +0,0 @@
using Application.Services.Database.PostgreSQL;
using Application.Services.HashingAlgorithms;
namespace Application.Endpoints.Doctors.Profile;
public class DoctorProfileHandler
{
private readonly IDoctorRepository _database;
private readonly IHashingAlgorithms _hashingAlgorithms;
public DoctorProfileHandler(IDoctorRepository database, IHashingAlgorithms hashingAlgorithms)
{
_database = database;
_hashingAlgorithms = hashingAlgorithms;
}
public async Task<BaseResponse> HandleGet(Guid id)
{
var doctor = await _database.GetByIdAsync(id).ConfigureAwait(false);
if (doctor != null)
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = $"Retrieved doctor with id: {id}",
Data = doctor
};
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = $"Doctor with id: {id} not found",
Data = null
};
}
public async Task<BaseResponse> HandleGetAll()
{
var doctors = await _database.GetAllAsync().ConfigureAwait(false);
if (doctors.Any())
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Retrieved doctors",
Data = doctors.ToList()
};
return new BaseResponse
{
StatusCode = HttpStatusCodes.NoContent,
Message = "Doctors not found",
Data = null
};
}
public async Task<BaseResponse> HandleUpdate(DoctorProfileUpdateDto doctorProfileUpdateDto)
{
var validation = new DoctorProfileValidation(_database);
var validationResult = await validation.ValidateAsync(doctorProfileUpdateDto);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
var doctorToUpdate = await _database.GetByIdAsync(doctorProfileUpdateDto.Id);
doctorToUpdate.SetEmail(doctorProfileUpdateDto.Email);
doctorToUpdate.SetPassword(_hashingAlgorithms.SHA256Algorithm(doctorProfileUpdateDto.Password));
doctorToUpdate.SetName(doctorProfileUpdateDto.Name);
doctorToUpdate.SetDescription(doctorProfileUpdateDto.Description);
await _database.UpdateAsync(doctorToUpdate);
return new BaseResponse
{
StatusCode = HttpStatusCodes.NoContent,
Message = null,
Data = null
};
}
public async Task<BaseResponse> HandleDelete(Guid id)
{
var doctorToDelete = await _database.GetByIdAsync(id);
if (doctorToDelete == null)
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = "Doctor was not found.",
Data = null
};
await _database.DeleteAsync(doctorToDelete);
return new BaseResponse
{
StatusCode = HttpStatusCodes.NoContent,
Message = null,
Data = null
};
}
}
@@ -1,10 +0,0 @@
namespace Application.Endpoints.Doctors.Profile;
public class DoctorProfileUpdateDto
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string Description { get; set; }
}
@@ -0,0 +1,36 @@
using Application.Services.Database.PostgreSQL;
namespace Application.Endpoints.Doctors.QuerriesDoctors;
public class QuerriesDoctorsHandler(IDoctorRepository database)
{
public async Task<BaseResponse> HandleGet(Guid id, CancellationToken token)
{
var doctor = await database.GetByIdAsync(id, token);
if (doctor != null)
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = $"Retrieved doctor with id: {id}",
Data = doctor
};
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = $"Doctor with id: {id} not found",
Data = null
};
}
public async Task<BaseResponse> HandleGetAll(CancellationToken token)
{
var doctors = await database.GetAllAsync(token);
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Retrieved doctors",
Data = doctors.ToList()
};
}
}
@@ -1,9 +0,0 @@
namespace Application.Endpoints.Doctors.Registration;
public class DoctorRegistrationDto
{
public string? Name { get; set; }
public string? Email { get; set; }
public string? Password { get; set; }
public string? Description { get; set; }
}
@@ -1,52 +0,0 @@
using Application.Services.Database.PostgreSQL;
using Application.Services.HashingAlgorithms;
using Core.Entities;
namespace Application.Endpoints.Doctors.Registration;
public class DoctorRegistrationHandler
{
private readonly IDoctorRepository _doctorRepository;
private readonly IHashingAlgorithms _hashingAlgorithms;
public DoctorRegistrationHandler(IDoctorRepository doctorRepository, IHashingAlgorithms hashingAlgorithms)
{
_doctorRepository = doctorRepository;
_hashingAlgorithms = hashingAlgorithms;
}
public async Task<BaseResponse> Handle(DoctorRegistrationDto registrationDTO)
{
var validation = new DoctorRegistrationValidation(_doctorRepository);
var validationResult = await validation.ValidateAsync(registrationDTO);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
var doctor = new Doctor();
doctor.SetEmail(registrationDTO.Email);
doctor.SetPassword(_hashingAlgorithms.SHA256Algorithm(registrationDTO.Password));
doctor.SetName(registrationDTO.Name);
doctor.SetDescription(registrationDTO.Description);
await _doctorRepository.AddAsync(doctor);
return new BaseResponse
{
StatusCode = HttpStatusCodes.Created,
Message = "Doctor registered successfully",
Data = null
};
}
}
@@ -1,40 +0,0 @@
using Application.Services.Database.PostgreSQL;
using FluentValidation;
namespace Application.Endpoints.Doctors.Registration;
public class DoctorRegistrationValidation : AbstractValidator<DoctorRegistrationDto>
{
private readonly IDoctorRepository _doctorRepository;
public DoctorRegistrationValidation(IDoctorRepository doctorRepository)
{
_doctorRepository = doctorRepository;
RuleFor(x => x.Email)
.NotEmpty().WithMessage("Email is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.EmailAddress().WithMessage("Invalid email format.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.MustAsync(BeUniqueEmail).WithMessage("Email already exists.")
.WithErrorCode(HttpStatusCodes.Conflict.ToString());
RuleFor(x => x.Password)
.NotEmpty().WithMessage("Password is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.MinimumLength(8).WithMessage("Password must be at least 8 characters long.")
.WithErrorCode(HttpStatusCodes.BadRequest.ToString());
RuleFor(x => x.Name)
.NotEmpty().WithMessage("Name is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.MinimumLength(3).WithMessage("Name must be at least 3 characters long.")
.WithErrorCode(HttpStatusCodes.BadRequest.ToString());
RuleFor(x => x.Description)
.MaximumLength(3000).WithMessage("Description must not exceed 3000 characters.")
.WithErrorCode(HttpStatusCodes.BadRequest.ToString());
}
private async Task<bool> BeUniqueEmail(string email, CancellationToken cancellationToken)
{
var doctor = await _doctorRepository.FindByEmailAsync(email);
return doctor == null;
}
}
@@ -0,0 +1,7 @@
namespace Application.Endpoints.MedicalHistories.CreateMedicalHistory;
public class CreateMedicalHistoryCommand
{
public Guid UserId { get; set; }
public byte[] Content { get; set; } = [];
}
@@ -0,0 +1,53 @@
using Application.Endpoints.MedicalHistories.ManageAuthorization;
using Application.Services.Database.MongoDB;
using Application.Services.Database.PostgreSQL;
using Domain.Entities;
namespace Application.Endpoints.MedicalHistories.CreateMedicalHistory;
public class CreateMedicalHistoryHandler(
IMedicalHistoryRepository medicalHistoryRepository,
IPatientRepository patientRepository,
IMedicalHistoryMongoDbService medicalHistoryMongoDbService)
{
public async Task<BaseResponse> Handle(CreateMedicalHistoryCommand request, CancellationToken token)
{
var validation = new CreateMedicalHistoryValidator(patientRepository);
var validationResult = await validation.ValidateAsync(request, token);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
var medicalHistory = new MedicalHistory();
medicalHistory.SetUserId(request.UserId);
medicalHistory.SetContent(request.Content);
var medicalHistoryId = medicalHistory.Id;
var newMedicalHistory = new MedicalHistoryAuthorisationModel
{
Id = medicalHistoryId.ToString(),
Authorisation = new List<string>()
};
await medicalHistoryMongoDbService.AddAsync(newMedicalHistory, token);
await medicalHistoryRepository.AddAsync(medicalHistory, token);
return new BaseResponse
{
StatusCode = HttpStatusCodes.Created,
Message = "Medical history record registered successfully",
Data = medicalHistory
};
}
}
@@ -1,13 +1,13 @@
using Application.Services.Database.PostgreSQL;
using FluentValidation;
namespace Application.Endpoints.MedicalHistories;
namespace Application.Endpoints.MedicalHistories.CreateMedicalHistory;
public class MedicalHistoryCreateValidation : AbstractValidator<MedicalHistoryCreateDto>
public class CreateMedicalHistoryValidator : AbstractValidator<CreateMedicalHistoryCommand>
{
private readonly IPatientRepository _patientRepository;
public MedicalHistoryCreateValidation(IPatientRepository patientRepository)
public CreateMedicalHistoryValidator(IPatientRepository patientRepository)
{
_patientRepository = patientRepository;
@@ -20,9 +20,9 @@ public class MedicalHistoryCreateValidation : AbstractValidator<MedicalHistoryCr
.NotEmpty().WithMessage("Description is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString());
}
private async Task<bool> BeExistingUser(Guid userId, CancellationToken cancellationToken)
private async Task<bool> BeExistingUser(Guid userId, CancellationToken token)
{
var patient = await _patientRepository.GetByIdAsync(userId);
var patient = await _patientRepository.GetByIdAsync(userId, token);
return patient != null;
}
}
@@ -0,0 +1,33 @@
using Application.Endpoints.MedicalHistories.ManageAuthorization;
using Application.Services.Database.MongoDB;
using Application.Services.Database.PostgreSQL;
namespace Application.Endpoints.MedicalHistories.DeleteMedicalHistory;
public class DeleteMedicalHistoryHandler(
IMedicalHistoryRepository medicalHistoryRepository,
IMedicalHistoryMongoDbService medicalHistoryMongoDbService)
{
public async Task<BaseResponse> Handle(Guid id, CancellationToken token)
{
var medicalRecord = await medicalHistoryRepository.GetByIdAsync(id, token);
if (medicalRecord == null)
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = "Medical record is not in system.",
Data = null
};
await medicalHistoryRepository.DeleteAsync(medicalRecord, token);
await medicalHistoryMongoDbService.DeleteAsync<MedicalHistoryAuthorisationModel>(
"_id", id.ToString(), token);
return new BaseResponse
{
StatusCode = HttpStatusCodes.NoContent,
Message = null,
Data = null
};
}
}
@@ -1,7 +0,0 @@
namespace Application.Endpoints.MedicalHistories.FileManagement;
public class MedicalHistoryCreateDto
{
public Guid UserId { get; set; }
public byte[] Content { get; set; } = [];
}
@@ -1,28 +0,0 @@
using Application.Services.Database.PostgreSQL;
using FluentValidation;
namespace Application.Endpoints.MedicalHistories.FileManagement;
public class MedicalHistoryCreateValidation : AbstractValidator<MedicalHistoryCreateDto>
{
private readonly IPatientRepository _patientRepository;
public MedicalHistoryCreateValidation(IPatientRepository patientRepository)
{
_patientRepository = patientRepository;
RuleFor(x => x.UserId)
.NotEmpty().WithMessage("Patient is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.MustAsync(BeExistingUser).WithMessage("Specified patient doesn't exist.")
.WithErrorCode(HttpStatusCodes.NotFound.ToString());
RuleFor(x => x.Content)
.NotEmpty().WithMessage("Description is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString());
}
private async Task<bool> BeExistingUser(Guid userId, CancellationToken cancellationToken)
{
var patient = await _patientRepository.GetByIdAsync(userId);
return patient != null;
}
}
@@ -1,156 +0,0 @@
using Application.Services.Database.MongoDB;
using Application.Services.Database.PostgreSQL;
using Core.Entities;
namespace Application.Endpoints.MedicalHistories.FileManagement;
public class MedicalHistoryFileManagementHandler
{
private readonly IMedicalHistoryMongoDbService _medicalHistoryMongoDbService;
private readonly IMedicalHistoryRepository _medicalHistoryRepository;
private readonly IPatientRepository _patientRepository;
public MedicalHistoryFileManagementHandler(IMedicalHistoryRepository medicalHistoryRepository,
IPatientRepository patientRepository, IMedicalHistoryMongoDbService medicalHistoryMongoDbService)
{
_medicalHistoryRepository = medicalHistoryRepository;
_patientRepository = patientRepository;
_medicalHistoryMongoDbService = medicalHistoryMongoDbService;
}
public async Task<BaseResponse> HandleGetAll()
{
var documents = await _medicalHistoryRepository.GetAllAsync().ConfigureAwait(false);
if (documents.Any())
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Retrieved medical histories",
Data = documents.ToList()
};
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = "Medical histories not found",
Data = null
};
}
public async Task<BaseResponse> HandleGet(Guid id)
{
var medicalHistory = await _medicalHistoryRepository.GetByIdAsync(id).ConfigureAwait(false);
if (medicalHistory != null)
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Medical history successfully retrieved",
Data = medicalHistory
};
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = "Medical history not found in system.",
Data = null
};
}
public async Task<BaseResponse> HandleCreate(MedicalHistoryCreateDto medicalHistoryCreateDto)
{
var validation = new MedicalHistoryCreateValidation(_patientRepository);
var validationResult = await validation.ValidateAsync(medicalHistoryCreateDto);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
var medicalHistory = new MedicalHistory
{
UserId = medicalHistoryCreateDto.UserId,
Content = medicalHistoryCreateDto.Content
};
var medicalHistoryId = medicalHistory.Id;
var newMedicalHistory = new MedicalHistoryAuthorisationModel
{
Id = medicalHistoryId.ToString(),
Authorisation = new List<string>()
};
await _medicalHistoryMongoDbService.AddAsync(newMedicalHistory);
await _medicalHistoryRepository.AddAsync(medicalHistory);
return new BaseResponse
{
StatusCode = HttpStatusCodes.Created,
Message = "Medical history record registered successfully",
Data = medicalHistory
};
}
public async Task<BaseResponse> HandleUpdate(MedicalHistoryUpdateDto updateDto)
{
var validation = new MedicalHistoryUpdateValidation(_medicalHistoryRepository, _patientRepository);
var validationResult = await validation.ValidateAsync(updateDto);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
var medicalHistoryToUpdate = await _medicalHistoryRepository.GetByIdAsync(updateDto.Id);
medicalHistoryToUpdate.Content = updateDto.Content;
await _medicalHistoryRepository.UpdateAsync(medicalHistoryToUpdate);
return new BaseResponse
{
StatusCode = HttpStatusCodes.NoContent,
Message = "Medical record updated successfully",
Data = null
};
}
public async Task<BaseResponse> HandleDelete(Guid id)
{
var medicalRecord = await _medicalHistoryRepository.GetByIdAsync(id);
if (medicalRecord == null)
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = "Medical record is not in system.",
Data = null
};
await _medicalHistoryMongoDbService.DeleteAsync<MedicalHistoryAuthorisationModel>("_id", id.ToString());
await _medicalHistoryRepository.DeleteAsync(medicalRecord);
//TODO delete from MongoDB
return new BaseResponse
{
StatusCode = HttpStatusCodes.NoContent,
Message = null,
Data = null
};
}
}
@@ -1,7 +0,0 @@
namespace Application.Endpoints.MedicalHistories.FileManagement;
public class MedicalHistoryUpdateDto
{
public Guid Id { get; set; }
public byte[] Content { get; set; } = [];
}
@@ -1,30 +0,0 @@
using Application.Services.Database.PostgreSQL;
using FluentValidation;
namespace Application.Endpoints.MedicalHistories.FileManagement;
public class MedicalHistoryUpdateValidation : AbstractValidator<MedicalHistoryUpdateDto>
{
private readonly IMedicalHistoryRepository _medicalHistoryRepository;
private readonly IPatientRepository _patientRepository;
public MedicalHistoryUpdateValidation(IMedicalHistoryRepository medicalHistoryRepository,
IPatientRepository patientRepository)
{
_medicalHistoryRepository = medicalHistoryRepository;
_patientRepository = patientRepository;
RuleFor(x => x.Id)
.NotEmpty().WithMessage("Id is required")
.MustAsync(BeExistingMedicalHistoryRecord).WithMessage("Medical history record does not exist");
RuleFor(x => x.Content)
.NotEmpty().WithMessage("Description is required.");
}
private async Task<bool> BeExistingMedicalHistoryRecord(Guid guid, CancellationToken token)
{
var record = await _medicalHistoryRepository.GetByIdAsync(guid);
return record != null;
}
}
@@ -0,0 +1,7 @@
namespace Application.Endpoints.MedicalHistories.ManageAuthorization;
public class MedicalHistoryAuthorizationInfo
{
public Guid MedicalRecordId { get; set; } = Guid.Empty;
public Guid DoctorId { get; set; } = Guid.Empty;
}
@@ -1,7 +0,0 @@
namespace Application.Endpoints.MedicalHistories.ManageAuthorization;
public class MedicalHistoryManageAuthorizationDoctorDto
{
public Guid MedicalRecordId { get; set; }
public Guid DoctorId { get; set; }
}
@@ -3,24 +3,16 @@ using Application.Services.Database.PostgreSQL;
namespace Application.Endpoints.MedicalHistories.ManageAuthorization;
public class MedicalHistoryManageAuthorizationHandler
public class MedicalHistoryManageAuthorizationHandler(
IMedicalHistoryRepository medicalHistoryRepository,
IMedicalHistoryMongoDbService medicalHistoryMongoDbService,
IDoctorRepository doctorRepository)
{
private readonly IDoctorRepository _doctorRepository;
private readonly IMedicalHistoryMongoDbService _medicalHistoryMongoDbService;
private readonly IMedicalHistoryRepository _medicalHistoryRepository;
public MedicalHistoryManageAuthorizationHandler(IMedicalHistoryRepository medicalHistoryRepository,
IMedicalHistoryMongoDbService medicalHistoryMongoDbService, IDoctorRepository doctorRepository)
public async Task<BaseResponse> HandleGrantDoctorAccess(
MedicalHistoryAuthorizationInfo request, CancellationToken token)
{
_medicalHistoryRepository = medicalHistoryRepository;
_medicalHistoryMongoDbService = medicalHistoryMongoDbService;
_doctorRepository = doctorRepository;
}
public async Task<BaseResponse> HandleGrantDoctorAccess(MedicalHistoryManageAuthorizationDoctorDto infoDto)
{
var validation = new MedicalHistoryManageAuthorizationValidation(_medicalHistoryRepository, _doctorRepository);
var validationResult = await validation.ValidateAsync(infoDto);
var validation = new MedicalHistoryManageAuthorizationValidator(medicalHistoryRepository, doctorRepository);
var validationResult = await validation.ValidateAsync(request, token);
if (!validationResult.IsValid)
{
@@ -37,9 +29,10 @@ public class MedicalHistoryManageAuthorizationHandler
}
var criteria = new List<(string, string)>();
criteria.Add(("_id", infoDto.MedicalRecordId.ToString()));
criteria.Add(("_id", request.MedicalRecordId.ToString()));
var documents = await _medicalHistoryMongoDbService.FindAsync<MedicalHistoryAuthorisationModel>(criteria);
var documents =
await medicalHistoryMongoDbService.FindAsync<MedicalHistoryAuthorisationModel>(criteria, token);
if (!documents.Any())
return new BaseResponse
{
@@ -49,7 +42,7 @@ public class MedicalHistoryManageAuthorizationHandler
};
var authorisations = documents[0].Authorisation;
if (authorisations.Contains(infoDto.ToString()))
if (authorisations.Contains(request.ToString()))
return new BaseResponse
{
StatusCode = HttpStatusCodes.Conflict,
@@ -57,14 +50,15 @@ public class MedicalHistoryManageAuthorizationHandler
Data = null
};
authorisations.Add(infoDto.DoctorId.ToString());
authorisations.Add(request.DoctorId.ToString());
var authorizationModel = new MedicalHistoryAuthorisationModel
{
Id = infoDto.MedicalRecordId.ToString(),
Id = request.MedicalRecordId.ToString(),
Authorisation = authorisations
};
await _medicalHistoryMongoDbService.ModifyAsync("_id", infoDto.MedicalRecordId.ToString(), authorizationModel);
await medicalHistoryMongoDbService.ModifyAsync(
"_id", request.MedicalRecordId.ToString(), authorizationModel, token);
return new BaseResponse
{
@@ -74,10 +68,11 @@ public class MedicalHistoryManageAuthorizationHandler
};
}
public async Task<BaseResponse> HandleRevokeDoctorAccess(MedicalHistoryManageAuthorizationDoctorDto infoDto)
public async Task<BaseResponse> HandleRevokeDoctorAccess(
MedicalHistoryAuthorizationInfo request, CancellationToken token)
{
var validation = new MedicalHistoryManageAuthorizationValidation(_medicalHistoryRepository, _doctorRepository);
var validationResult = await validation.ValidateAsync(infoDto);
var validation = new MedicalHistoryManageAuthorizationValidator(medicalHistoryRepository, doctorRepository);
var validationResult = await validation.ValidateAsync(request, token);
if (!validationResult.IsValid)
{
@@ -94,9 +89,10 @@ public class MedicalHistoryManageAuthorizationHandler
}
var criteria = new List<(string, string)>();
criteria.Add(("_id", infoDto.MedicalRecordId.ToString()));
criteria.Add(("_id", request.MedicalRecordId.ToString()));
var documents = await _medicalHistoryMongoDbService.FindAsync<MedicalHistoryAuthorisationModel>(criteria);
var documents = await medicalHistoryMongoDbService
.FindAsync<MedicalHistoryAuthorisationModel>(criteria, token);
if (!documents.Any())
return new BaseResponse
{
@@ -106,7 +102,7 @@ public class MedicalHistoryManageAuthorizationHandler
};
var authorisations = documents[0].Authorisation;
if (!authorisations.Contains(infoDto.DoctorId.ToString()))
if (!authorisations.Contains(request.DoctorId.ToString()))
return new BaseResponse
{
StatusCode = HttpStatusCodes.Conflict,
@@ -114,14 +110,15 @@ public class MedicalHistoryManageAuthorizationHandler
Data = null
};
authorisations.Remove(infoDto.DoctorId.ToString());
authorisations.Remove(request.DoctorId.ToString());
var authorizationModel = new MedicalHistoryAuthorisationModel
{
Id = infoDto.MedicalRecordId.ToString(),
Id = request.MedicalRecordId.ToString(),
Authorisation = authorisations
};
await _medicalHistoryMongoDbService.ModifyAsync("_id", infoDto.MedicalRecordId.ToString(), authorizationModel);
await medicalHistoryMongoDbService.ModifyAsync(
"_id", request.MedicalRecordId.ToString(), authorizationModel, token);
return new BaseResponse
{
@@ -130,4 +127,53 @@ public class MedicalHistoryManageAuthorizationHandler
Data = null
};
}
public async Task<BaseResponse> HandleCheckAccessToMedicalHistory(
MedicalHistoryAuthorizationInfo request, CancellationToken token)
{
var validation = new MedicalHistoryManageAuthorizationValidator(medicalHistoryRepository, doctorRepository);
var validationResult = await validation.ValidateAsync(request, token);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
var criteria = new List<(string, string)>();
criteria.Add(("_id", request.MedicalRecordId.ToString()));
var documents = await medicalHistoryMongoDbService
.FindAsync<MedicalHistoryAuthorisationModel>(criteria, token);
if (!documents.Any())
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = "Access to medical history not found.",
Data = null
};
var authorisations = documents[0].Authorisation;
if (!authorisations.Contains(request.DoctorId.ToString()))
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = "Access to medical history is revoked.",
Data = null
};
return new BaseResponse()
{
StatusCode = HttpStatusCodes.OK,
Message = "Access to medical history is granted",
Data = null
};
}
}
@@ -3,12 +3,12 @@ using FluentValidation;
namespace Application.Endpoints.MedicalHistories.ManageAuthorization;
public class MedicalHistoryManageAuthorizationValidation : AbstractValidator<MedicalHistoryManageAuthorizationDoctorDto>
public class MedicalHistoryManageAuthorizationValidator : AbstractValidator<MedicalHistoryAuthorizationInfo>
{
private readonly IDoctorRepository _doctorRepository;
private readonly IMedicalHistoryRepository _medicalHistoryRepository;
public MedicalHistoryManageAuthorizationValidation(IMedicalHistoryRepository medicalHistoryRepository,
public MedicalHistoryManageAuthorizationValidator(IMedicalHistoryRepository medicalHistoryRepository,
IDoctorRepository doctorRepository)
{
RuleFor(x => x.MedicalRecordId)
@@ -27,13 +27,13 @@ public class MedicalHistoryManageAuthorizationValidation : AbstractValidator<Med
private async Task<bool> BeExistingMedicalHistoryRecord(Guid guid, CancellationToken token)
{
var record = await _medicalHistoryRepository.GetByIdAsync(guid);
var record = await _medicalHistoryRepository.GetByIdAsync(guid, token);
return record != null;
}
private async Task<bool> IsDoctorRegistered(Guid id, CancellationToken cancellationToken)
private async Task<bool> IsDoctorRegistered(Guid id, CancellationToken token)
{
var doctor = await _doctorRepository.GetByIdAsync(id);
var doctor = await _doctorRepository.GetByIdAsync(id, token);
return doctor != null;
}
}
@@ -2,6 +2,6 @@
public class MedicalHistoryAuthorisationModel
{
public string Id { get; set; }
public List<string> Authorisation { get; set; } = new();
public string Id { get; set; } = string.Empty;
public List<string> Authorisation { get; set; } = [];
}
@@ -1,7 +0,0 @@
namespace Application.Endpoints.MedicalHistories;
public class MedicalHistoryCreateDto
{
public Guid UserId { get; set; }
public byte[] Content { get; set; } = [];
}
@@ -1,156 +0,0 @@
using Application.Services.Database.MongoDB;
using Application.Services.Database.PostgreSQL;
using Core.Entities;
namespace Application.Endpoints.MedicalHistories;
public class MedicalHistoryHandler
{
private readonly IMedicalHistoryMongoDbService _medicalHistoryMongoDbService;
private readonly IMedicalHistoryRepository _medicalHistoryRepository;
private readonly IPatientRepository _patientRepository;
public MedicalHistoryHandler(IMedicalHistoryRepository medicalHistoryRepository,
IPatientRepository patientRepository, IMedicalHistoryMongoDbService medicalHistoryMongoDbService)
{
_medicalHistoryRepository = medicalHistoryRepository;
_patientRepository = patientRepository;
_medicalHistoryMongoDbService = medicalHistoryMongoDbService;
}
public async Task<BaseResponse> HandleGetAll()
{
var documents = await _medicalHistoryRepository.GetAllAsync().ConfigureAwait(false);
if (documents.Any())
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Retrieved medical histories",
Data = documents.ToList()
};
return new BaseResponse
{
StatusCode = HttpStatusCodes.NoContent,
Message = "Medical histories not found",
Data = null
};
}
public async Task<BaseResponse> HandleGet(Guid id)
{
var medicalHistory = await _medicalHistoryRepository.GetByIdAsync(id).ConfigureAwait(false);
if (medicalHistory != null)
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Medical history successfully retrieved",
Data = medicalHistory
};
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = "Medical history not found in system.",
Data = null
};
}
public async Task<BaseResponse> HandleCreate(MedicalHistoryCreateDto medicalHistoryCreateDto)
{
var validation = new MedicalHistoryCreateValidation(_patientRepository);
var validationResult = await validation.ValidateAsync(medicalHistoryCreateDto);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
var medicalHistory = new MedicalHistory
{
UserId = medicalHistoryCreateDto.UserId,
Content = medicalHistoryCreateDto.Content
};
var medicalHistoryId = medicalHistory.Id;
var newMedicalHistory = new MedicalHistoryAuthorisationModel
{
Id = medicalHistoryId.ToString(),
Authorisation = new List<string>()
};
await _medicalHistoryMongoDbService.AddAsync(newMedicalHistory);
await _medicalHistoryRepository.AddAsync(medicalHistory);
return new BaseResponse
{
StatusCode = HttpStatusCodes.Created,
Message = "Medical history record registered successfully",
Data = medicalHistory
};
}
public async Task<BaseResponse> HandleUpdate(MedicalHistoryUpdateDto updateDto)
{
var validation = new MedicalHistoryUpdateValidation(_medicalHistoryRepository, _patientRepository);
var validationResult = await validation.ValidateAsync(updateDto);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
var medicalHistoryToUpdate = await _medicalHistoryRepository.GetByIdAsync(updateDto.Id);
medicalHistoryToUpdate.Content = updateDto.Content;
await _medicalHistoryRepository.UpdateAsync(medicalHistoryToUpdate);
return new BaseResponse
{
StatusCode = HttpStatusCodes.NoContent,
Message = "Medical record updated successfully",
Data = null
};
}
public async Task<BaseResponse> HandleDelete(Guid id)
{
var medicalRecord = await _medicalHistoryRepository.GetByIdAsync(id);
if (medicalRecord == null)
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = "Medical record is not in system.",
Data = null
};
await _medicalHistoryMongoDbService.DeleteAsync<MedicalHistoryAuthorisationModel>("_id", id.ToString());
await _medicalHistoryRepository.DeleteAsync(medicalRecord);
//TODO delete from MongoDB
return new BaseResponse
{
StatusCode = HttpStatusCodes.NoContent,
Message = null,
Data = null
};
}
}
@@ -1,7 +0,0 @@
namespace Application.Endpoints.MedicalHistories;
public class MedicalHistoryUpdateDto
{
public Guid Id { get; set; }
public byte[] Content { get; set; } = [];
}
@@ -0,0 +1,7 @@
namespace Application.Endpoints.MedicalHistories.ModifyMedicalHistory;
public class ModifyMedicalHistoryCommand
{
public Guid Id { get; set; }
public byte[] Content { get; set; } = [];
}
@@ -0,0 +1,38 @@
using Application.Services.Database.MongoDB;
using Application.Services.Database.PostgreSQL;
namespace Application.Endpoints.MedicalHistories.ModifyMedicalHistory;
public class ModifyMedicalHistoryHandler(IMedicalHistoryRepository medicalHistoryRepository)
{
public async Task<BaseResponse> Handle(ModifyMedicalHistoryCommand request, CancellationToken token)
{
var validation = new ModifyMedicalHistoryValidator(medicalHistoryRepository);
var validationResult = await validation.ValidateAsync(request, token);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
var newMedicalHistory = await medicalHistoryRepository.GetByIdAsync(request.Id, token);
newMedicalHistory.SetContent(request.Content);
await medicalHistoryRepository.UpdateAsync(newMedicalHistory, token);
return new BaseResponse
{
StatusCode = HttpStatusCodes.NoContent,
Message = "Medical record updated successfully",
Data = null
};
}
}
@@ -1,18 +1,15 @@
using Application.Services.Database.PostgreSQL;
using FluentValidation;
namespace Application.Endpoints.MedicalHistories;
namespace Application.Endpoints.MedicalHistories.ModifyMedicalHistory;
public class MedicalHistoryUpdateValidation : AbstractValidator<MedicalHistoryUpdateDto>
public class ModifyMedicalHistoryValidator : AbstractValidator<ModifyMedicalHistoryCommand>
{
private readonly IMedicalHistoryRepository _medicalHistoryRepository;
private readonly IPatientRepository _patientRepository;
public MedicalHistoryUpdateValidation(IMedicalHistoryRepository medicalHistoryRepository,
IPatientRepository patientRepository)
public ModifyMedicalHistoryValidator(IMedicalHistoryRepository medicalHistoryRepository)
{
_medicalHistoryRepository = medicalHistoryRepository;
_patientRepository = patientRepository;
RuleFor(x => x.Id)
.NotEmpty().WithMessage("Id is required")
@@ -22,9 +19,9 @@ public class MedicalHistoryUpdateValidation : AbstractValidator<MedicalHistoryUp
.NotEmpty().WithMessage("Description is required.");
}
private async Task<bool> BeExistingMedicalHistoryRecord(Guid guid, CancellationToken token)
private async Task<bool> BeExistingMedicalHistoryRecord(Guid id, CancellationToken token)
{
var record = await _medicalHistoryRepository.GetByIdAsync(guid);
var record = await _medicalHistoryRepository.GetByIdAsync(id, token);
return record != null;
}
}
@@ -0,0 +1,63 @@
using Application.Services.Database.PostgreSQL;
namespace Application.Endpoints.MedicalHistories.QuerriesMedicalHistories;
public class QuerriesMedicalHistoriesHandler(IMedicalHistoryRepository medicalHistoryRepository)
{
public async Task<BaseResponse> HandleGetAll(CancellationToken token)
{
var documents = await medicalHistoryRepository.GetAllAsync(token);
if (documents.Any())
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Retrieved medical histories",
Data = documents.ToList()
};
return new BaseResponse
{
StatusCode = HttpStatusCodes.NoContent,
Message = "Medical histories not found",
Data = null
};
}
public async Task<BaseResponse> HandleGet(Guid id, CancellationToken token)
{
var medicalHistory = await medicalHistoryRepository.GetByIdAsync(id, token);
if (medicalHistory != null)
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Medical history successfully retrieved",
Data = medicalHistory
};
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = "Medical history not found in system.",
Data = null
};
}
public async Task<BaseResponse> HandleGetByPatientId(Guid patientId, CancellationToken token)
{
var medicalHistory = await medicalHistoryRepository.GetByPatientIdAsync(patientId, token);
if (medicalHistory != null)
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Medical history successfully retrieved",
Data = medicalHistory
};
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = "Medical history not found in system.",
Data = null
};
}
}
@@ -0,0 +1,40 @@
using Application.Endpoints.MedicalHistories;
using Application.Services.Database.MongoDB;
using Application.Services.Database.PostgreSQL;
namespace Application.Endpoints.Patients.DeletePatient;
public class DeletePatientHandler(
IPatientRepository patientRepository,
IMedicalHistoryRepository medicalHistoryRepository,
IMedicalHistoryMongoDbService medicalHistoryMongoDbService)
{
public async Task<BaseResponse> Handle(Guid patientId, CancellationToken token)
{
var patient = await patientRepository.GetByIdAsync(patientId, token);
if (patient == null)
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = "Patient not found.",
Data = null
};
await patientRepository.DeleteAsync(patient, token);
var medicalHistoryList = await medicalHistoryRepository.GetAllAsync(token);
foreach (var med in medicalHistoryList)
if (med.UserId == patientId)
{
await medicalHistoryRepository.DeleteAsync(med, token);
await medicalHistoryMongoDbService.DeleteByIdAsync<MedicalHistoryAuthorisationModel>(med.Id.ToString(), token);
}
return new BaseResponse
{
StatusCode = HttpStatusCodes.NoContent,
Message = null,
Data = null
};
}
}
@@ -0,0 +1,9 @@
namespace Application.Endpoints.Patients.ModifyPatient;
public class ModifyPatientCommand
{
public Guid Id { get; set; } = Guid.Empty;
public string Name { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
}
@@ -0,0 +1,46 @@
using Application.Services.Database.PostgreSQL;
using Application.Services.HashingAlgorithms;
namespace Application.Endpoints.Patients.ModifyPatient;
public class ModifyPatientHandler(
IHashingAlgorithms hashingAlgorithms,
IPatientRepository patientRepository,
IDoctorRepository doctorRepository,
IAdminRepository adminRepository
)
{
public async Task<BaseResponse> Handle(ModifyPatientCommand request, CancellationToken token)
{
var validation = new PatientProfileValidator(patientRepository, doctorRepository, adminRepository);
var validationResult = await validation.ValidateAsync(request, token);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
var newPatient = await patientRepository.GetByIdAsync(request.Id, token);
newPatient.SetName(request.Name);
newPatient.SetEmail(request.Email);
newPatient.SetPassword(hashingAlgorithms.Sha256Algorithm(request.Password));
await patientRepository.UpdateAsync(newPatient, token);
return new BaseResponse
{
StatusCode = HttpStatusCodes.NoContent,
Message = null,
Data = null
};
}
}
@@ -1,15 +1,20 @@
using Application.Services.Database.PostgreSQL;
using FluentValidation;
namespace Application.Endpoints.Patients.Profile;
namespace Application.Endpoints.Patients.ModifyPatient;
public class PatientProfileValidation : AbstractValidator<PatientProfileDto>
public class PatientProfileValidator : AbstractValidator<ModifyPatientCommand>
{
private readonly IPatientRepository _patientRepository;
private readonly IDoctorRepository _doctorRepository;
private readonly IAdminRepository _adminRepository;
public PatientProfileValidation(IPatientRepository patientRepository)
public PatientProfileValidator(IPatientRepository patientRepository,
IDoctorRepository doctorRepository, IAdminRepository adminRepository)
{
_patientRepository = patientRepository;
_doctorRepository = doctorRepository;
_adminRepository = adminRepository;
RuleFor(x => x.Id)
.NotEmpty().WithMessage("Id is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
@@ -33,15 +38,18 @@ public class PatientProfileValidation : AbstractValidator<PatientProfileDto>
.WithErrorCode(HttpStatusCodes.BadRequest.ToString());
}
private async Task<bool> IsPatientRegistered(Guid id, CancellationToken cancellationToken)
private async Task<bool> IsPatientRegistered(Guid id, CancellationToken token)
{
var patient = await _patientRepository.GetByIdAsync(id);
var patient = await _patientRepository.GetByIdAsync(id, token);
return patient != null;
}
private async Task<bool> BeUniqueEmail(string email, CancellationToken cancellationToken)
private async Task<bool> BeUniqueEmail(string email, CancellationToken token)
{
var patient = await _patientRepository.FindByEmailAsync(email);
return patient == null;
var patient = await _patientRepository.FindByEmailAsync(email, token);
var doctor = await _doctorRepository.FindByEmailAsync(email, token);
var admin = await _adminRepository.FindByEmailAsync(email, token);
return doctor == null && admin == null;
}
}
@@ -1,9 +0,0 @@
namespace Application.Endpoints.Patients.Profile;
public class PatientProfileDto
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
@@ -1,110 +0,0 @@
using Application.Services.Database.PostgreSQL;
using Application.Services.HashingAlgorithms;
namespace Application.Endpoints.Patients.Profile;
public class PatientProfileHandler
{
private readonly IHashingAlgorithms _hashingAlgorithms;
private readonly IPatientRepository _patientRepository;
public PatientProfileHandler(IPatientRepository patientRepository, IHashingAlgorithms hashingAlgorithms)
{
_patientRepository = patientRepository;
_hashingAlgorithms = hashingAlgorithms;
}
public async Task<BaseResponse> HandleGet(Guid id)
{
var patient = await _patientRepository.GetByIdAsync(id).ConfigureAwait(false);
if (patient != null)
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = $"Retrieved patient with id: {id}",
Data = patient
};
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = $"Patient with id: {id} not found",
Data = null
};
}
public async Task<BaseResponse> HandleGetAll()
{
var patients = await _patientRepository.GetAllAsync().ConfigureAwait(false);
if (patients.Any())
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Retrieved patients",
Data = patients.ToList()
};
return new BaseResponse
{
StatusCode = HttpStatusCodes.NoContent,
Message = "Patients not found",
Data = null
};
}
public async Task<BaseResponse> HandleUpdate(PatientProfileDto updateDto)
{
var validation = new PatientProfileValidation(_patientRepository);
var validationResult = await validation.ValidateAsync(updateDto);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
var patientToUpdate = await _patientRepository.GetByIdAsync(updateDto.Id);
patientToUpdate.SetEmail(updateDto.Email);
patientToUpdate.SetPassword(_hashingAlgorithms.SHA256Algorithm(updateDto.Password));
patientToUpdate.SetName(updateDto.Name);
await _patientRepository.UpdateAsync(patientToUpdate);
return new BaseResponse
{
StatusCode = HttpStatusCodes.NoContent,
Message = null,
Data = null
};
}
public async Task<BaseResponse> HandleDelete(Guid id)
{
var patientToDelete = await _patientRepository.GetByIdAsync(id);
if (patientToDelete == null)
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = "Patient not found.",
Data = null
};
await _patientRepository.DeleteAsync(patientToDelete);
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = null,
Data = null
};
}
}
@@ -0,0 +1,36 @@
using Application.Services.Database.PostgreSQL;
namespace Application.Endpoints.Patients.QuerriesPatients;
public class QuerriesPatientsHandle(IPatientRepository patientRepository)
{
public async Task<BaseResponse> HandleGet(Guid id, CancellationToken token)
{
var patient = await patientRepository.GetByIdAsync(id, token);
if (patient != null)
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = $"Retrieved patient with id: {id}",
Data = patient
};
return new BaseResponse
{
StatusCode = HttpStatusCodes.NotFound,
Message = $"Patient with id: {id} not found",
Data = null
};
}
public async Task<BaseResponse> HandleGetAll(CancellationToken token)
{
var patients = await patientRepository.GetAllAsync(token);
return new BaseResponse
{
StatusCode = HttpStatusCodes.OK,
Message = "Retrieved patients",
Data = patients.ToList()
};
}
}
@@ -1,8 +0,0 @@
namespace Application.Endpoints.Patients.Registration;
public class PatientRegistrationDto
{
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
@@ -1,51 +0,0 @@
using Application.Services.Database.PostgreSQL;
using Application.Services.HashingAlgorithms;
using Core.Entities;
namespace Application.Endpoints.Patients.Registration;
public class PatientRegistrationHandler
{
private readonly IHashingAlgorithms _hashingAlgorithms;
private readonly IPatientRepository _patientRepository;
public PatientRegistrationHandler(IPatientRepository patientRepository, IHashingAlgorithms hashingAlgorithms)
{
_patientRepository = patientRepository;
_hashingAlgorithms = hashingAlgorithms;
}
public async Task<BaseResponse> Handle(PatientRegistrationDto registrationDTO)
{
var validation = new PatientRegistrationValidation(_patientRepository);
var validationResult = await validation.ValidateAsync(registrationDTO);
if (!validationResult.IsValid)
{
var firstError = validationResult.Errors.FirstOrDefault();
var errorCode = int.TryParse(firstError.ErrorCode, out var code) ? code : -1;
var errorMessage = firstError.ErrorMessage;
return new BaseResponse
{
StatusCode = errorCode,
Message = errorMessage,
Data = null
};
}
var patient = new Patient();
patient.SetEmail(registrationDTO.Email);
patient.SetName(registrationDTO.Name);
patient.SetPassword(_hashingAlgorithms.SHA256Algorithm(registrationDTO.Password));
await _patientRepository.AddAsync(patient);
return new BaseResponse
{
StatusCode = HttpStatusCodes.Created,
Message = "Patient registered successfully",
Data = patient // Be careful with sending sensitive data like Passwords
};
}
}
@@ -1,37 +0,0 @@
using Application.Services.Database.PostgreSQL;
using FluentValidation;
namespace Application.Endpoints.Patients.Registration;
public class PatientRegistrationValidation : AbstractValidator<PatientRegistrationDto>
{
private readonly IPatientRepository _patientRepository;
public PatientRegistrationValidation(IPatientRepository patientRepository)
{
_patientRepository = patientRepository;
RuleFor(x => x.Email)
.NotEmpty().WithMessage("Email is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.EmailAddress().WithMessage("Invalid email format.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.MustAsync(BeUniqueEmail).WithMessage("Email already exists.")
.WithErrorCode(HttpStatusCodes.Conflict.ToString());
RuleFor(x => x.Password)
.NotEmpty().WithMessage("Password is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.MinimumLength(8).WithMessage("Password must be at least 8 characters long.")
.WithErrorCode(HttpStatusCodes.BadRequest.ToString());
RuleFor(x => x.Name)
.NotEmpty().WithMessage("Name is required.").WithErrorCode(HttpStatusCodes.BadRequest.ToString())
.MinimumLength(3).WithMessage("Name must be at least 3 characters long.")
.WithErrorCode(HttpStatusCodes.BadRequest.ToString());
}
private async Task<bool> BeUniqueEmail(string email, CancellationToken cancellationToken)
{
var pacient = await _patientRepository.FindByEmailAsync(email);
return pacient == null;
}
}
@@ -1,55 +1,97 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using Newtonsoft.Json;
namespace Application.Endpoints.SicknessPrediction;
public class PythonScriptRunner
{
public async Task<BaseResponse> RunPythonScript(string scriptPath, string args)
public static async Task<BaseResponse> RunPythonScript(string scriptPath, string args)
{
var response = new BaseResponse();
using (Process process = new Process())
var solutionRoot = PathHelper.GetSolutionRoot();
var pythonExecutablePath = Path.Combine(solutionRoot, "venv", "Scripts", "python.exe");
using var process = new Process();
process.StartInfo = new ProcessStartInfo(pythonExecutablePath, $"\"{scriptPath}\" \"{args}\"")
{
process.StartInfo = new ProcessStartInfo("python", $"\"{scriptPath}\" \"{args}\"")
{
RedirectStandardOutput = true,
RedirectStandardError = true, // Redirect standard error to capture any errors.
UseShellExecute = false,
CreateNoWindow = true,
};
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
try
{
process.Start();
// Read output and error streams.
var output = await process.StandardOutput.ReadToEndAsync();
var error = await process.StandardError.ReadToEndAsync();
process.WaitForExit();
try
{
process.Start();
var output = await process.StandardOutput.ReadToEndAsync();
var error = await process.StandardError.ReadToEndAsync();
await process.WaitForExitAsync();
if (process.ExitCode == 0) // Assuming exit code 0 as a success.
if (process.ExitCode == 0) // Assuming exit code 0 as a success.
try
{
Console.WriteLine(output);
response.StatusCode = HttpStatusCodes.OK;
response.Message = "Success";
response.Data = output;
var response = JsonConvert.DeserializeObject<BaseResponsePython>(output);
return new BaseResponse()
{
StatusCode = response.StatusCode,
Message = response.Message,
Data = response.Data
};
}
else
catch (JsonException ex)
{
Console.WriteLine(error);
response.StatusCode = HttpStatusCodes.InternalServerError;
response.Message = error; // Using the error output as the message if not successful.
response.Data = null;
Console.WriteLine($"JSON Error: {ex.Message}");
return new BaseResponse { StatusCode = 500, Message = "Error parsing JSON response." };
}
}
catch (Exception ex)
{
response.StatusCode = HttpStatusCodes.InternalServerError;
response.Message = $"An error occurred while executing the Python script: {ex.Message}";
response.Data = null;
}
return response;
Console.WriteLine(error);
return new BaseResponse { StatusCode = 500, Message = error };
}
catch (Exception ex)
{
Console.WriteLine($"Execution Error: {ex.Message}");
return new BaseResponse
{ StatusCode = 500, Message = $"An error occurred while executing the Python script: {ex.Message}" };
}
}
}
public static class PathHelper
{
public static string GetSolutionRoot()
{
var currentDir = Directory.GetCurrentDirectory();
var solutionRoot = Directory.GetParent(currentDir)?.FullName;
if (solutionRoot == null)
throw new InvalidOperationException("Failed to find the solution root directory.");
return solutionRoot;
}
public static string GetPythonExecutablePath()
{
var solutionRoot = GetSolutionRoot();
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return Path.Combine(solutionRoot, "venv", "Scripts", "python.exe");
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
return Path.Combine(solutionRoot, "venv", "bin", "python");
throw new InvalidOperationException("Unsupported operating system.");
}
}
public class BaseResponsePython
{
public int StatusCode { get; set; }
public string Message { get; set; } = string.Empty;
public List<PredictionData> Data { get; set; } = []; // Correct initialization and make it a property
}
public class PredictionData // Removed abstract if no inheritance is required
{
public string Disease { get; set; } = string.Empty;
public double Probability { get; set; }
}
@@ -0,0 +1,6 @@
namespace Application.Endpoints.SicknessPrediction;
public class SicknessPredictionCommand
{
public string Text { get; set; } = string.Empty;
}
@@ -1,6 +0,0 @@
namespace Application.Endpoints.SicknessPrediction;
public class SicknessPredictionDto
{
public string Text { get; set; }
}
@@ -2,10 +2,10 @@
public class SicknessPredictionHandler
{
public async Task<BaseResponse> GetPrediction(SicknessPredictionDto dto)
public static async Task<BaseResponse> GetPrediction(SicknessPredictionCommand command)
{
var validation = new SicknessPredictionValidator();
var validationResult = await validation.ValidateAsync(dto);
var validationResult = await validation.ValidateAsync(command);
if (!validationResult.IsValid)
{
@@ -20,13 +20,14 @@ public class SicknessPredictionHandler
Data = null
};
}
var currentDirectory = Directory.GetCurrentDirectory(); // gets the current working directory
var applicationDirectory = Path.Combine(Directory.GetParent(currentDirectory)?.FullName, "Application");
var scriptPath = Path.Combine(applicationDirectory, "Endpoints","SicknessPrediction", "SicknessPredictionScript.py");
var scriptPath = Path.Combine(applicationDirectory, "Endpoints", "SicknessPrediction",
"SicknessPredictionScript.py");
var scriptRunner = new PythonScriptRunner();
var result = await scriptRunner.RunPythonScript(scriptPath, dto.Text);
var result = await PythonScriptRunner.RunPythonScript(scriptPath, command.Text);
return result;
}
@@ -46,7 +46,6 @@ class DiseasePredictor:
self.model.fit(self.X_train, self.y_train) # X_train is a DataFrame with feature names
logging.info(f"Training accuracy: {self.model.score(self.X_test, self.y_test):.2f}")
def extract_features(self, text):
logging.info("Extracting features from text")
@@ -118,7 +117,7 @@ class DiseasePredictor:
if count == 3:
break
# Return the JSON object
return json.dumps(json_output, indent=4)
return json.dumps(json_output)
# Example usage:
@@ -127,11 +126,11 @@ if __name__ == "__main__":
if len(sys.argv) != 2:
logging.error("Incorrect number of arguments provided.")
json_output = {
"StatusCode": 400,
"Message": "Incorrect number of arguments provided.",
"Data": []
}
print(json.dumps(json_output, indent=4))
"StatusCode": 400,
"Message": "Incorrect number of arguments provided.",
"Data": []
}
print(json.dumps(json_output))
sys.exit(1)
logging.basicConfig(filename='disease_prediction.log', level=logging.INFO,
@@ -2,7 +2,7 @@
namespace Application.Endpoints.SicknessPrediction;
public class SicknessPredictionValidator : AbstractValidator<SicknessPredictionDto>
public class SicknessPredictionValidator : AbstractValidator<SicknessPredictionCommand>
{
public SicknessPredictionValidator()
{