diff --git a/backend/Application/Endpoints/BaseResponse.cs b/backend/Application/Endpoints/BaseResponse.cs index 8e00d30..3af49b2 100644 --- a/backend/Application/Endpoints/BaseResponse.cs +++ b/backend/Application/Endpoints/BaseResponse.cs @@ -1,9 +1,8 @@ -namespace Application.Endpoints +namespace Application.Endpoints; + +public class BaseResponse { - public class BaseResponse - { - public bool Success { get; set; } - public string? Message { get; set; } - public object? Data { get; set; } - } + public bool Success { get; set; } + public string? Message { get; set; } + public object? Data { get; set; } } diff --git a/backend/Application/Endpoints/Doctors/Login/DoctorLoginDto.cs b/backend/Application/Endpoints/Doctors/Login/DoctorLoginDto.cs index fd73c05..c3bdb6b 100644 --- a/backend/Application/Endpoints/Doctors/Login/DoctorLoginDto.cs +++ b/backend/Application/Endpoints/Doctors/Login/DoctorLoginDto.cs @@ -1,8 +1,7 @@ -namespace Application.Endpoints.Doctors.Login +namespace Application.Endpoints.Doctors.Login; + +public class DoctorLoginDTO { - public class DoctorLoginDTO - { - public string? Email { get; set; } - public string? Password { get; set; } - } + public string? Email { get; set; } + public string? Password { get; set; } } diff --git a/backend/Application/Endpoints/Doctors/Login/DoctorLoginHandler.cs b/backend/Application/Endpoints/Doctors/Login/DoctorLoginHandler.cs index 5efec83..fa3ad37 100644 --- a/backend/Application/Endpoints/Doctors/Login/DoctorLoginHandler.cs +++ b/backend/Application/Endpoints/Doctors/Login/DoctorLoginHandler.cs @@ -1,38 +1,37 @@ using Application.Services.Database; -namespace Application.Endpoints.Doctors.Login +namespace Application.Endpoints.Doctors.Login; + +public class DoctorLoginHandler { - public class DoctorLoginHandler + private readonly IDoctorRepository _database; + + public DoctorLoginHandler(IDoctorRepository database) { - private readonly IDoctorRepository _database; + _database = database; + } - public DoctorLoginHandler(IDoctorRepository database) + public async Task Handle(DoctorLoginDTO loginDTO) + { + var validation = new DoctorLoginValidation(_database); + var validationResult = await validation.ValidateAsync(loginDTO); + + if (!validationResult.IsValid) { - _database = database; - } - - public async Task Handle(DoctorLoginDTO loginDTO) - { - var validation = new DoctorLoginValidation(_database); - var validationResult = await validation.ValidateAsync(loginDTO); - - if (!validationResult.IsValid) - { - var errorMessage = validationResult.Errors.FirstOrDefault()?.ErrorMessage; - return new BaseResponse - { - Success = false, - Message = errorMessage, - Data = null - }; - } - + var errorMessage = validationResult.Errors.FirstOrDefault()?.ErrorMessage; return new BaseResponse { - Success = true, - Message = "Authentication successful", + Success = false, + Message = errorMessage, Data = null }; } + + return new BaseResponse + { + Success = true, + Message = "Authentication successful", + Data = null + }; } } diff --git a/backend/Application/Endpoints/Doctors/Login/DoctorLoginValidation.cs b/backend/Application/Endpoints/Doctors/Login/DoctorLoginValidation.cs index 23ce514..592eb30 100644 --- a/backend/Application/Endpoints/Doctors/Login/DoctorLoginValidation.cs +++ b/backend/Application/Endpoints/Doctors/Login/DoctorLoginValidation.cs @@ -1,29 +1,29 @@ using Application.Services.Database; using FluentValidation; -namespace Application.Endpoints.Doctors.Login +namespace Application.Endpoints.Doctors.Login; + +public class DoctorLoginValidation : AbstractValidator { - public class DoctorLoginValidation : AbstractValidator + private readonly IDoctorRepository _doctorRepository; + + public DoctorLoginValidation(IDoctorRepository doctorRepository) { - private readonly IDoctorRepository _doctorRepository; + _doctorRepository = doctorRepository; - public DoctorLoginValidation(IDoctorRepository doctorRepository) - { - _doctorRepository = doctorRepository; + RuleFor(x => x.Email) + .NotEmpty().WithMessage("Email is required.") + .EmailAddress().WithMessage("Invalid email format.") + .MustAsync(BeExistingDoctor).WithMessage("Doctor with this email does not exist."); - RuleFor(x => x.Email) - .NotEmpty().WithMessage("Email is required.") - .EmailAddress().WithMessage("Invalid email format.") - .MustAsync(BeExistingDoctor).WithMessage("Doctor with this email does not exist."); + RuleFor(x => x.Password) + .NotEmpty().WithMessage("Password is required.") + .MinimumLength(8).WithMessage("Password must be at least 8 characters long."); + } - RuleFor(x => x.Password) - .NotEmpty().WithMessage("Password is required.") - .MinimumLength(8).WithMessage("Password must be at least 8 characters long."); - } - - private async Task BeExistingDoctor(string email, CancellationToken cancellationToken) - { - return await _doctorRepository.IsDoctorExisting(email); - } + private async Task BeExistingDoctor(string email, CancellationToken cancellationToken) + { + var doctor = await _doctorRepository.FindByEmailAsync(email); + return doctor != null; } } diff --git a/backend/Application/Endpoints/Doctors/Profile/DoctorProfileDto.cs b/backend/Application/Endpoints/Doctors/Profile/DoctorProfileDto.cs new file mode 100644 index 0000000..0127ca2 --- /dev/null +++ b/backend/Application/Endpoints/Doctors/Profile/DoctorProfileDto.cs @@ -0,0 +1,9 @@ +namespace Application.Endpoints.Doctors.Profile; + +public class DoctorProfileDTO +{ + public string Name { get; set; } + public string Email { get; set; } + public string Password { get; set; } + public string Description { get; set; } +} diff --git a/backend/Application/Endpoints/Doctors/Profile/DoctorProfileHandler.cs b/backend/Application/Endpoints/Doctors/Profile/DoctorProfileHandler.cs new file mode 100644 index 0000000..957b04a --- /dev/null +++ b/backend/Application/Endpoints/Doctors/Profile/DoctorProfileHandler.cs @@ -0,0 +1,121 @@ +using Application.Services.Database; + +namespace Application.Endpoints.Doctors.Profile; + +public class DoctorProfileHandler +{ + private readonly IDoctorRepository _doctorRepository; + + public DoctorProfileHandler(IDoctorRepository doctorRepository) + { + _doctorRepository = doctorRepository; + } + + public async Task HandleGet(Guid id) + { + var doctor = await _doctorRepository.GetByIdAsync(id).ConfigureAwait(false); + if (doctor != null) + { + return new BaseResponse + { + Success = true, + Message = $"Retrieved doctor with id: {id}", + Data = doctor + }; + } + + return new BaseResponse + { + Success = false, + Message = $"Doctor with id: {id} not found", + Data = null + }; + } + + public async Task HandleGetAll() + { + var doctors = await _doctorRepository.GetAllAsync().ConfigureAwait(false); + if (doctors.Any()) + { + return new BaseResponse + { + Success = true, + Message = "Retrieved doctors", + Data = doctors.ToList() + }; + } + + return new BaseResponse + { + Success = false, + Message = "Doctors not found", + Data = null + }; + } + + public async Task HandleUpdate(Guid id, DoctorProfileDTO updateDto) + { + var validation = new DoctorProfileValidation(_doctorRepository); + var validationResult = await validation.ValidateAsync(updateDto); + + if (!validationResult.IsValid) + { + var errorMessage = validationResult.Errors.Select(e => e.ErrorMessage).ToList(); + return new BaseResponse + { + Success = false, + Message = string.Join(", ", errorMessage), + Data = null + }; + } + + var doctorToUpdate = await _doctorRepository.GetByIdAsync(id); + + if (doctorToUpdate == null) + { + return new BaseResponse + { + Success = false, + Message = "Doctor not found for given Id", + Data = null + }; + } + + doctorToUpdate.Email = updateDto.Email; + doctorToUpdate.Password = updateDto.Password; + doctorToUpdate.Name = updateDto.Name; + doctorToUpdate.Description = updateDto.Description; + + await _doctorRepository.UpdateAsync(doctorToUpdate); + + return new BaseResponse + { + Success = true, + Message = "Doctor updated successfully", + Data = doctorToUpdate + }; + } + + public async Task HandleDelete(Guid id) + { + var doctorToDelete = await _doctorRepository.GetByIdAsync(id); + if (doctorToDelete == null) + { + return new BaseResponse + { + Success = false, + Message = $"Doctor with id: {id} does not exist", + Data = null + }; + } + + await _doctorRepository.DeleteAsync(doctorToDelete); + + return new BaseResponse + { + Success = true, + Message = $"Doctor with id: {id} was succesfully deleted", + Data = doctorToDelete + }; + } +} diff --git a/backend/Application/Endpoints/Doctors/Profile/DoctorProfileValidation.cs b/backend/Application/Endpoints/Doctors/Profile/DoctorProfileValidation.cs new file mode 100644 index 0000000..879b3fd --- /dev/null +++ b/backend/Application/Endpoints/Doctors/Profile/DoctorProfileValidation.cs @@ -0,0 +1,40 @@ +using Application.Services.Database; +using FluentValidation; + +namespace Application.Endpoints.Doctors.Profile; + +public class DoctorProfileValidation : AbstractValidator +{ + private readonly IDoctorRepository _doctorRepository; + + public DoctorProfileValidation(IDoctorRepository doctorRepository) + { + _doctorRepository = doctorRepository; + + RuleFor(x => x.Email) + .NotEmpty().WithMessage("Email is required.") + .EmailAddress().WithMessage("Invalid email format.") + .MustAsync(BeUniqueEmail).WithMessage("Email in use by another doctor."); + + RuleFor(x => x.Password) + .NotEmpty().WithMessage("Password is required.") + .MinimumLength(8).WithMessage("Password must be at least 8 characters long."); + + RuleFor(x => x.Name) + .NotEmpty().WithMessage("Name is required.") + .MinimumLength(3).WithMessage("Name must be at least 3 characters long."); + + RuleFor(x => x.Description) + .MaximumLength(3000).WithMessage("Description must not exceed 3000 characters."); + } + + private async Task BeUniqueEmail(string email, CancellationToken cancellationToken) + { + var doctor = await _doctorRepository.FindByEmailAsync(email); + if (doctor != null) + { + return doctor.Email.Equals(email, StringComparison.OrdinalIgnoreCase); + } + return doctor == null; + } +} diff --git a/backend/Application/Endpoints/Doctors/Registration/DoctorRegistrationDto.cs b/backend/Application/Endpoints/Doctors/Registration/DoctorRegistrationDto.cs new file mode 100644 index 0000000..e24f2b4 --- /dev/null +++ b/backend/Application/Endpoints/Doctors/Registration/DoctorRegistrationDto.cs @@ -0,0 +1,9 @@ +namespace Application.Endpoints.Doctors.Login; + +public class DoctorRegistrationDto +{ + public string Name { get; set; } + public string Email { get; set; } + public string Password { get; set; } + public string Description { get; set; } +} diff --git a/backend/Application/Endpoints/Doctors/Registration/DoctorRegistrationHandler.cs b/backend/Application/Endpoints/Doctors/Registration/DoctorRegistrationHandler.cs new file mode 100644 index 0000000..7768af3 --- /dev/null +++ b/backend/Application/Endpoints/Doctors/Registration/DoctorRegistrationHandler.cs @@ -0,0 +1,49 @@ +using Application.Endpoints.Doctors.Login; +using Application.Services.Database; +using Core.Entities; + +namespace Application.Endpoints.Doctors.Registration; + +public class DoctorRegistrationHandler +{ + private readonly IDoctorRepository _doctorRepository; + + public DoctorRegistrationHandler(IDoctorRepository doctorRepository) + { + _doctorRepository = doctorRepository; + } + + public async Task Handle(DoctorRegistrationDto registrationDTO) + { + var validation = new DoctorRegistrationValidation(_doctorRepository); + var validationResult = await validation.ValidateAsync(registrationDTO); + + if (!validationResult.IsValid) + { + var errorMessage = validationResult.Errors.Select(e => e.ErrorMessage).ToList(); + return new BaseResponse + { + Success = false, + Message = string.Join(", ", errorMessage), + Data = null + }; + } + + var doctor = new Doctor + { + Email = registrationDTO.Email, + Password = registrationDTO.Password, + Name = registrationDTO.Name, + Description = registrationDTO.Description + }; + + await _doctorRepository.AddAsync(doctor); + + return new BaseResponse + { + Success = true, + Message = "Doctor registered successfully", + Data = doctor // Be careful with sending sensitive data like Passwords + }; + } +} diff --git a/backend/Application/Endpoints/Doctors/Registration/DoctorRegistrationValidation.cs b/backend/Application/Endpoints/Doctors/Registration/DoctorRegistrationValidation.cs new file mode 100644 index 0000000..1bc3b43 --- /dev/null +++ b/backend/Application/Endpoints/Doctors/Registration/DoctorRegistrationValidation.cs @@ -0,0 +1,37 @@ +using Application.Endpoints.Doctors.Login; +using Application.Services.Database; +using FluentValidation; + +namespace Application.Endpoints.Doctors.Registration; + +public class DoctorRegistrationValidation : AbstractValidator +{ + private readonly IDoctorRepository _doctorRepository; + + public DoctorRegistrationValidation(IDoctorRepository doctorRepository) + { + _doctorRepository = doctorRepository; + + RuleFor(x => x.Email) + .NotEmpty().WithMessage("Email is required.") + .EmailAddress().WithMessage("Invalid email format.") + .MustAsync(BeUniqueEmail).WithMessage("Email already exists."); + + RuleFor(x => x.Password) + .NotEmpty().WithMessage("Password is required.") + .MinimumLength(8).WithMessage("Password must be at least 8 characters long."); + + RuleFor(x => x.Name) + .NotEmpty().WithMessage("Name is required.") + .MinimumLength(3).WithMessage("Name must be at least 3 characters long."); + + RuleFor(x => x.Description) + .MaximumLength(3000).WithMessage("Description must not exceed 3000 characters."); + } + + private async Task BeUniqueEmail(string email, CancellationToken cancellationToken) + { + var doctor = await _doctorRepository.FindByEmailAsync(email); + return doctor == null; + } +} diff --git a/backend/Application/Endpoints/Doctors/ResetPassword/DoctorResetPasswordHandler.cs b/backend/Application/Endpoints/Doctors/ResetPassword/DoctorResetPasswordHandler.cs new file mode 100644 index 0000000..498a407 --- /dev/null +++ b/backend/Application/Endpoints/Doctors/ResetPassword/DoctorResetPasswordHandler.cs @@ -0,0 +1,58 @@ +using Application.Endpoints.Doctors.Login; +using Application.Services.Database; + +namespace Application.Endpoints.Doctors.ResetPassword; + +public class DoctorResetPasswordHandler +{ + private readonly IDoctorRepository _doctorRepository; + + public DoctorResetPasswordHandler(IDoctorRepository doctorRepository) + { + _doctorRepository = doctorRepository; + } + + public async Task Handle(DoctorLoginDTO resetDoctorDto) + { + var validation = new DoctorResetPasswordValidation(_doctorRepository); + var validationResult = await validation.ValidateAsync(resetDoctorDto); + + if (!validationResult.IsValid) + { + var errorMessage = validationResult.Errors.Select(e => e.ErrorMessage).ToList(); + return new BaseResponse + { + Success = false, + Message = string.Join(", ", errorMessage), + Data = null + }; + } + + var currentDoctor = await _doctorRepository.FindByEmailAsync(resetDoctorDto.Email); + + var updatedDoctor = currentDoctor; + + updatedDoctor.Password = resetDoctorDto.Password; + + await _doctorRepository.UpdateAsync(updatedDoctor); + + updatedDoctor = await _doctorRepository.GetByIdAsync(currentDoctor.Id); + + if (updatedDoctor.Password != resetDoctorDto.Password) + { + return new BaseResponse + { + Success = false, + Message = $"Failed to update passwor for doctor {updatedDoctor.Name}", + Data = null + }; + } + + return new BaseResponse + { + Success = true, + Message = $"Password of doctor {updatedDoctor.Name} has been reset succesfully", + Data = updatedDoctor.Email + }; + } +} diff --git a/backend/Application/Endpoints/Doctors/ResetPassword/DoctorResetPasswordValidation.cs b/backend/Application/Endpoints/Doctors/ResetPassword/DoctorResetPasswordValidation.cs new file mode 100644 index 0000000..8398eb1 --- /dev/null +++ b/backend/Application/Endpoints/Doctors/ResetPassword/DoctorResetPasswordValidation.cs @@ -0,0 +1,37 @@ +using Application.Services.Database; +using FluentValidation; + +namespace Application.Endpoints.Doctors.Login; + +public class DoctorResetPasswordValidation : AbstractValidator +{ + private readonly IDoctorRepository _doctorRepository; + + public DoctorResetPasswordValidation(IDoctorRepository doctorRepository) + { + _doctorRepository = doctorRepository; + + RuleFor(x => x.Email) + .NotEmpty().WithMessage("Email is required.") + .EmailAddress().WithMessage("Invalid email format.") + .MustAsync(BeExistingDoctor).WithMessage("Doctor with this email does not exist."); + + RuleFor(x => x.Password) + .NotEmpty().WithMessage("Password is required.") + .MinimumLength(8).WithMessage("Password must be at least 8 characters long.") + .MustAsync((dto, password, context, cancellationToken) => BeDifferentFromOldPassword(dto.Email, password, cancellationToken)) + .WithMessage("New password cannot be the same as old password."); + } + + private async Task BeExistingDoctor(string email, CancellationToken cancellationToken) + { + var doctor = await _doctorRepository.FindByEmailAsync(email); + return doctor != null; + } + + private async Task BeDifferentFromOldPassword(string email, string newPassword, CancellationToken cancellationToken) + { + var currentDoctor = await _doctorRepository.FindByEmailAsync(email); + return !newPassword.Equals(currentDoctor?.Password, StringComparison.Ordinal); + } +} diff --git a/backend/Application/Endpoints/MedicalHistories/MedicalHistoryCreateValidation.cs b/backend/Application/Endpoints/MedicalHistories/MedicalHistoryCreateValidation.cs new file mode 100644 index 0000000..ff0f5a3 --- /dev/null +++ b/backend/Application/Endpoints/MedicalHistories/MedicalHistoryCreateValidation.cs @@ -0,0 +1,27 @@ +using Application.Services.Database; +using FluentValidation; + +namespace Application.Endpoints.MedicalHistories; + +public class MedicalHistoryCreateValidation : AbstractValidator +{ + private readonly IPacientRepository _pacientRepository; + + public MedicalHistoryCreateValidation(IPacientRepository pacientRepository) + { + _pacientRepository = pacientRepository; + + RuleFor(x => x.UserId) + .NotEmpty().WithMessage("Pacient is required.") + .MustAsync(BeExistingUser).WithMessage("Specified pacient id doesn't exist."); + + RuleFor(x => x.Description) + .NotEmpty().WithMessage("Description is required."); + } + + private async Task BeExistingUser(Guid userId, CancellationToken cancellationToken) + { + var pacient = await _pacientRepository.GetByIdAsync(userId); + return pacient != null; + } +} diff --git a/backend/Application/Endpoints/MedicalHistories/MedicalHistoryDto.cs b/backend/Application/Endpoints/MedicalHistories/MedicalHistoryDto.cs new file mode 100644 index 0000000..86c5ab2 --- /dev/null +++ b/backend/Application/Endpoints/MedicalHistories/MedicalHistoryDto.cs @@ -0,0 +1,7 @@ +namespace Application.Endpoints.MedicalHistories; + +public class MedicalHistoryDTO +{ + public Guid UserId { get; set; } + public byte[] Description { get; set; } = []; +} diff --git a/backend/Application/Endpoints/MedicalHistories/MedicalHistoryHandler.cs b/backend/Application/Endpoints/MedicalHistories/MedicalHistoryHandler.cs new file mode 100644 index 0000000..fb665e9 --- /dev/null +++ b/backend/Application/Endpoints/MedicalHistories/MedicalHistoryHandler.cs @@ -0,0 +1,109 @@ +using Application.Services.Database; +using Core.Entities; + +namespace Application.Endpoints.MedicalHistories; + +public class MedicalHistoryHandler +{ + private readonly IMedicalHistoryRepository _medicalHistoryRepository; + private readonly IPacientRepository _pacientRepository; + + public MedicalHistoryHandler(IMedicalHistoryRepository medicalHistoryRepository, IPacientRepository pacientRepository) + { + _medicalHistoryRepository = medicalHistoryRepository; + _pacientRepository = pacientRepository; + } + public async Task HandleGet(Guid id) + { + var medicalHistory = await _medicalHistoryRepository.GetByIdAsync(id).ConfigureAwait(false); + if (medicalHistory != null) + { + return new BaseResponse + { + Success = true, + Message = $"Retrieved Medical History with id: {id}", + Data = medicalHistory + }; + } + + return new BaseResponse + { + Success = false, + Message = $"Medical History with id: {id} not found", + Data = null + }; + } + + public async Task HandleCreate(Guid userId, byte[] description) + { + var validation = new MedicalHistoryCreateValidation(_pacientRepository); + var validationResult = await validation.ValidateAsync(new MedicalHistoryDTO { UserId = userId, Description = description}); + + if (!validationResult.IsValid) + { + var errorMessage = validationResult.Errors.Select(e => e.ErrorMessage).ToList(); + return new BaseResponse + { + Success = false, + Message = string.Join(", ", errorMessage), + Data = null + }; + } + + var medicalHistory = new MedicalHistory + { + UserId = userId, + Description = description + }; + + await _medicalHistoryRepository.AddAsync(medicalHistory); + + return new BaseResponse + { + Success = true, + Message = "Medical history record registered successfully", + Data = medicalHistory + }; + } + + public async Task HandleUpdate(Guid id, MedicalHistoryDTO updateDto) + { + var validation = new MedicalHistoryUpdateValidation(_medicalHistoryRepository, _pacientRepository); + var validationResult = await validation.ValidateAsync(new MedicalHistoryUpdateDTO { Id = id, UserId = updateDto.UserId, Description = updateDto.Description}); + + if (!validationResult.IsValid) + { + var errorMessage = validationResult.Errors.Select(e => e.ErrorMessage).ToList(); + return new BaseResponse + { + Success = false, + Message = string.Join(", ", errorMessage), + Data = null + }; + } + + var medicalHistoryToUpdate = await _medicalHistoryRepository.GetByIdAsync(id); + + if (medicalHistoryToUpdate == null) + { + return new BaseResponse + { + Success = false, + Message = "Medical history not found for given Id", + Data = null + }; + } + + medicalHistoryToUpdate.UserId = updateDto.UserId; + medicalHistoryToUpdate.Description = updateDto.Description; + + await _medicalHistoryRepository.UpdateAsync(medicalHistoryToUpdate); + + return new BaseResponse + { + Success = true, + Message = "Pacient updated successfully", + Data = medicalHistoryToUpdate + }; + } +} diff --git a/backend/Application/Endpoints/MedicalHistories/MedicalHistoryUpdateDto.cs b/backend/Application/Endpoints/MedicalHistories/MedicalHistoryUpdateDto.cs new file mode 100644 index 0000000..aaee541 --- /dev/null +++ b/backend/Application/Endpoints/MedicalHistories/MedicalHistoryUpdateDto.cs @@ -0,0 +1,8 @@ +namespace Application.Endpoints.MedicalHistories; + +public class MedicalHistoryUpdateDTO +{ + public Guid Id { get; set; } + public Guid UserId { get; set; } + public byte[] Description { get; set; } = []; +} diff --git a/backend/Application/Endpoints/MedicalHistories/MedicalHistoryUpdateValidation.cs b/backend/Application/Endpoints/MedicalHistories/MedicalHistoryUpdateValidation.cs new file mode 100644 index 0000000..d02fe41 --- /dev/null +++ b/backend/Application/Endpoints/MedicalHistories/MedicalHistoryUpdateValidation.cs @@ -0,0 +1,39 @@ +using Application.Services.Database; +using FluentValidation; + +namespace Application.Endpoints.MedicalHistories; + +public class MedicalHistoryUpdateValidation : AbstractValidator +{ + private readonly IMedicalHistoryRepository _medicalHistoryRepository; + private readonly IPacientRepository _pacientRepository; + + public MedicalHistoryUpdateValidation(IMedicalHistoryRepository medicalHistoryRepository, IPacientRepository pacientRepository) + { + _medicalHistoryRepository = medicalHistoryRepository; + _pacientRepository = pacientRepository; + + RuleFor(x => x.Id) + .NotEmpty().WithMessage("Id is required") + .MustAsync(BeExistingMedicalHistoryRecord).WithMessage("Medical hostory record does not exist"); + + RuleFor(x => x.UserId) + .NotEmpty().WithMessage("Pacient is required.") + .MustAsync(BeExistingUser).WithMessage("Specified pacient id doesn't exist."); + + RuleFor(x => x.Description) + .NotEmpty().WithMessage("Description is required."); + } + + private async Task BeExistingMedicalHistoryRecord(Guid guid, CancellationToken token) + { + var record = await _medicalHistoryRepository.GetByIdAsync(guid); + return record != null; + } + + private async Task BeExistingUser(Guid userId, CancellationToken cancellationToken) + { + var pacient = await _pacientRepository.GetByIdAsync(userId); + return pacient != null; + } +} diff --git a/backend/Application/Endpoints/Pacients/Login/PacientLoginDto.cs b/backend/Application/Endpoints/Pacients/Login/PacientLoginDto.cs new file mode 100644 index 0000000..04b6600 --- /dev/null +++ b/backend/Application/Endpoints/Pacients/Login/PacientLoginDto.cs @@ -0,0 +1,7 @@ +namespace Application.Endpoints.Pacients.Login; + +public class PacientLoginDTO +{ + public string? Email { get; set; } + public string? Password { get; set; } +} diff --git a/backend/Application/Endpoints/Pacients/Login/PacientLoginHandler.cs b/backend/Application/Endpoints/Pacients/Login/PacientLoginHandler.cs new file mode 100644 index 0000000..9d5f98d --- /dev/null +++ b/backend/Application/Endpoints/Pacients/Login/PacientLoginHandler.cs @@ -0,0 +1,37 @@ +using Application.Services.Database; + +namespace Application.Endpoints.Pacients.Login; + +public class PacientLoginHandler +{ + private readonly IPacientRepository _database; + + public PacientLoginHandler(IPacientRepository database) + { + _database = database; + } + + public async Task Handle(PacientLoginDTO loginDTO) + { + var validation = new PacientLoginValidation(_database); + var validationResult = await validation.ValidateAsync(loginDTO); + + if (!validationResult.IsValid) + { + var errorMessage = validationResult.Errors.FirstOrDefault()?.ErrorMessage; + return new BaseResponse + { + Success = false, + Message = errorMessage, + Data = null + }; + } + + return new BaseResponse + { + Success = true, + Message = "Authentication successful", + Data = null + }; + } +} diff --git a/backend/Application/Endpoints/Pacients/Login/PacientLoginValidation.cs b/backend/Application/Endpoints/Pacients/Login/PacientLoginValidation.cs new file mode 100644 index 0000000..960a0e5 --- /dev/null +++ b/backend/Application/Endpoints/Pacients/Login/PacientLoginValidation.cs @@ -0,0 +1,29 @@ +using Application.Services.Database; +using FluentValidation; + +namespace Application.Endpoints.Pacients.Login; + +public class PacientLoginValidation : AbstractValidator +{ + private readonly IPacientRepository _pacientRepository; + + public PacientLoginValidation(IPacientRepository pacientRepository) + { + _pacientRepository = pacientRepository; + + RuleFor(x => x.Email) + .NotEmpty().WithMessage("Email is required.") + .EmailAddress().WithMessage("Invalid email format.") + .MustAsync(BeExistingPacient).WithMessage("Pacient with this email does not exist."); + + RuleFor(x => x.Password) + .NotEmpty().WithMessage("Password is required.") + .MinimumLength(8).WithMessage("Password must be at least 8 characters long."); + } + + private async Task BeExistingPacient(string email, CancellationToken cancellationToken) + { + var pacient = await _pacientRepository.FindByEmailAsync(email); + return pacient != null; + } +} diff --git a/backend/Application/Endpoints/Pacients/Profile/PacientProfileDto.cs b/backend/Application/Endpoints/Pacients/Profile/PacientProfileDto.cs new file mode 100644 index 0000000..6a7f877 --- /dev/null +++ b/backend/Application/Endpoints/Pacients/Profile/PacientProfileDto.cs @@ -0,0 +1,9 @@ +namespace Application.Endpoints.Pacients.Profile; + +public class PacientProfileDTO +{ + public string Name { get; set; } + public string Email { get; set; } + public string Password { get; set; } + public string Description { get; set; } +} diff --git a/backend/Application/Endpoints/Pacients/Profile/PacientProfileHandler.cs b/backend/Application/Endpoints/Pacients/Profile/PacientProfileHandler.cs new file mode 100644 index 0000000..415d8aa --- /dev/null +++ b/backend/Application/Endpoints/Pacients/Profile/PacientProfileHandler.cs @@ -0,0 +1,120 @@ +using Application.Services.Database; + +namespace Application.Endpoints.Pacients.Profile; + +public class PacientProfileHandler +{ + private readonly IPacientRepository _pacientRepository; + + public PacientProfileHandler(IPacientRepository pacientRepository) + { + _pacientRepository = pacientRepository; + } + + public async Task HandleGet(Guid id) + { + var pacient = await _pacientRepository.GetByIdAsync(id).ConfigureAwait(false); + if (pacient != null) + { + return new BaseResponse + { + Success = true, + Message = $"Retrieved pacient with id: {id}", + Data = pacient + }; + } + + return new BaseResponse + { + Success = false, + Message = $"Pacient with id: {id} not found", + Data = null + }; + } + + public async Task HandleGetAll() + { + var pacients = await _pacientRepository.GetAllAsync().ConfigureAwait(false); + if (pacients.Any()) + { + return new BaseResponse + { + Success = true, + Message = "Retrieved pacients", + Data = pacients.ToList() + }; + } + + return new BaseResponse + { + Success = false, + Message = "Pacients not found", + Data = null + }; + } + + public async Task HandleUpdate(Guid id, PacientProfileDTO updateDto) + { + var validation = new PacientProfileValidation(_pacientRepository); + var validationResult = await validation.ValidateAsync(updateDto); + + if (!validationResult.IsValid) + { + var errorMessage = validationResult.Errors.Select(e => e.ErrorMessage).ToList(); + return new BaseResponse + { + Success = false, + Message = string.Join(", ", errorMessage), + Data = null + }; + } + + var pacientToUpdate = await _pacientRepository.GetByIdAsync(id); + + if (pacientToUpdate == null) + { + return new BaseResponse + { + Success = false, + Message = "Pacient not found for given Id", + Data = null + }; + } + + pacientToUpdate.Email = updateDto.Email; + pacientToUpdate.Password = updateDto.Password; + pacientToUpdate.Name = updateDto.Name; + + await _pacientRepository.UpdateAsync(pacientToUpdate); + + return new BaseResponse + { + Success = true, + Message = "Pacient updated successfully", + Data = pacientToUpdate + }; + } + + public async Task HandleDelete(Guid id) + { + var pacientToDelete = await _pacientRepository.GetByIdAsync(id); + if (pacientToDelete == null) + { + return new BaseResponse + { + Success = false, + Message = $"Pacient with id: {id} does not exist", + Data = null + }; + } + + await _pacientRepository.DeleteAsync(pacientToDelete); + + return new BaseResponse + { + Success = true, + Message = $"Pacient with id: {id} was succesfully deleted", + Data = pacientToDelete + }; + } +} diff --git a/backend/Application/Endpoints/Pacients/Profile/PacientProfileValidation.cs b/backend/Application/Endpoints/Pacients/Profile/PacientProfileValidation.cs new file mode 100644 index 0000000..43c2209 --- /dev/null +++ b/backend/Application/Endpoints/Pacients/Profile/PacientProfileValidation.cs @@ -0,0 +1,40 @@ +using Application.Services.Database; +using FluentValidation; + +namespace Application.Endpoints.Pacients.Profile; + +public class PacientProfileValidation : AbstractValidator +{ + private readonly IPacientRepository _pacientRepository; + + public PacientProfileValidation(IPacientRepository pacientRepository) + { + _pacientRepository = pacientRepository; + + RuleFor(x => x.Email) + .NotEmpty().WithMessage("Email is required.") + .EmailAddress().WithMessage("Invalid email format.") + .MustAsync(BeUniqueEmail).WithMessage("Email in use by another pacient."); + + RuleFor(x => x.Password) + .NotEmpty().WithMessage("Password is required.") + .MinimumLength(8).WithMessage("Password must be at least 8 characters long."); + + RuleFor(x => x.Name) + .NotEmpty().WithMessage("Name is required.") + .MinimumLength(3).WithMessage("Name must be at least 3 characters long."); + + RuleFor(x => x.Description) + .MaximumLength(3000).WithMessage("Description must not exceed 3000 characters."); + } + + private async Task BeUniqueEmail(string email, CancellationToken cancellationToken) + { + var pacient = await _pacientRepository.FindByEmailAsync(email); + if (pacient != null) + { + return pacient.Email.Equals(email, StringComparison.OrdinalIgnoreCase); + } + return pacient == null; + } +} diff --git a/backend/Application/Endpoints/Pacients/Registration/PacientRegistrationDto.cs b/backend/Application/Endpoints/Pacients/Registration/PacientRegistrationDto.cs new file mode 100644 index 0000000..b9fee04 --- /dev/null +++ b/backend/Application/Endpoints/Pacients/Registration/PacientRegistrationDto.cs @@ -0,0 +1,8 @@ +namespace Application.Endpoints.Pacients.Login; + +public class PacientRegistrationDto +{ + public string Name { get; set; } + public string Email { get; set; } + public string Password { get; set; } +} diff --git a/backend/Application/Endpoints/Pacients/Registration/PacientRegistrationHandler.cs b/backend/Application/Endpoints/Pacients/Registration/PacientRegistrationHandler.cs new file mode 100644 index 0000000..54d7202 --- /dev/null +++ b/backend/Application/Endpoints/Pacients/Registration/PacientRegistrationHandler.cs @@ -0,0 +1,48 @@ +using Application.Endpoints.Pacients.Login; +using Application.Services.Database; +using Core.Entities; + +namespace Application.Endpoints.Pacients.Registration; + +public class PacientRegistrationHandler +{ + private readonly IPacientRepository _pacientRepository; + + public PacientRegistrationHandler(IPacientRepository pacientRepository) + { + _pacientRepository = pacientRepository; + } + + public async Task Handle(PacientRegistrationDto registrationDTO) + { + var validation = new PacientRegistrationValidation(_pacientRepository); + var validationResult = await validation.ValidateAsync(registrationDTO); + + if (!validationResult.IsValid) + { + var errorMessage = validationResult.Errors.Select(e => e.ErrorMessage).ToList(); + return new BaseResponse + { + Success = false, + Message = string.Join(", ", errorMessage), + Data = null + }; + } + + var pacient = new Pacient + { + Email = registrationDTO.Email, + Password = registrationDTO.Password, + Name = registrationDTO.Name + }; + + await _pacientRepository.AddAsync(pacient); + + return new BaseResponse + { + Success = true, + Message = "Pacient registered successfully", + Data = pacient // Be careful with sending sensitive data like Passwords + }; + } +} diff --git a/backend/Application/Endpoints/Pacients/Registration/PacientRegistrationValidation.cs b/backend/Application/Endpoints/Pacients/Registration/PacientRegistrationValidation.cs new file mode 100644 index 0000000..86bb3ac --- /dev/null +++ b/backend/Application/Endpoints/Pacients/Registration/PacientRegistrationValidation.cs @@ -0,0 +1,34 @@ +using Application.Endpoints.Pacients.Login; +using Application.Services.Database; +using FluentValidation; + +namespace Application.Endpoints.Pacients.Registration; + +public class PacientRegistrationValidation : AbstractValidator +{ + private readonly IPacientRepository _pacientRepository; + + public PacientRegistrationValidation(IPacientRepository pacientRepository) + { + _pacientRepository = pacientRepository; + + RuleFor(x => x.Email) + .NotEmpty().WithMessage("Email is required.") + .EmailAddress().WithMessage("Invalid email format.") + .MustAsync(BeUniqueEmail).WithMessage("Email already exists."); + + RuleFor(x => x.Password) + .NotEmpty().WithMessage("Password is required.") + .MinimumLength(8).WithMessage("Password must be at least 8 characters long."); + + RuleFor(x => x.Name) + .NotEmpty().WithMessage("Name is required.") + .MinimumLength(3).WithMessage("Name must be at least 3 characters long."); + } + + private async Task BeUniqueEmail(string email, CancellationToken cancellationToken) + { + var pacient = await _pacientRepository.FindByEmailAsync(email); + return pacient == null; + } +} diff --git a/backend/Application/Endpoints/Pacients/ResetPassword/PacientResetPasswordHandler.cs b/backend/Application/Endpoints/Pacients/ResetPassword/PacientResetPasswordHandler.cs new file mode 100644 index 0000000..5619604 --- /dev/null +++ b/backend/Application/Endpoints/Pacients/ResetPassword/PacientResetPasswordHandler.cs @@ -0,0 +1,58 @@ +using Application.Endpoints.Pacients.Login; +using Application.Services.Database; + +namespace Application.Endpoints.Pacients.ResetPassword; + +public class PacientResetPasswordHandler +{ + private readonly IPacientRepository _pacientRepository; + + public PacientResetPasswordHandler(IPacientRepository pacientRepository) + { + _pacientRepository = pacientRepository; + } + + public async Task Handle(PacientLoginDTO resetPacientDto) + { + var validation = new PacientResetPasswordValidation(_pacientRepository); + var validationResult = await validation.ValidateAsync(resetPacientDto); + + if (!validationResult.IsValid) + { + var errorMessage = validationResult.Errors.Select(e => e.ErrorMessage).ToList(); + return new BaseResponse + { + Success = false, + Message = string.Join(", ", errorMessage), + Data = null + }; + } + + var currentPacient = await _pacientRepository.FindByEmailAsync(resetPacientDto.Email); + + var updatedPacient = currentPacient; + + updatedPacient.Password = resetPacientDto.Password; + + await _pacientRepository.UpdateAsync(updatedPacient); + + updatedPacient = await _pacientRepository.GetByIdAsync(currentPacient.Id); + + if (updatedPacient.Password != resetPacientDto.Password) + { + return new BaseResponse + { + Success = false, + Message = $"Failed to update passwor for pacient {updatedPacient.Name}", + Data = null + }; + } + + return new BaseResponse + { + Success = true, + Message = $"Password of pacient {updatedPacient.Name} has been reset succesfully", + Data = updatedPacient.Email + }; + } +} diff --git a/backend/Application/Endpoints/Pacients/ResetPassword/PacientResetPasswordValidation.cs b/backend/Application/Endpoints/Pacients/ResetPassword/PacientResetPasswordValidation.cs new file mode 100644 index 0000000..bbecf60 --- /dev/null +++ b/backend/Application/Endpoints/Pacients/ResetPassword/PacientResetPasswordValidation.cs @@ -0,0 +1,37 @@ +using Application.Services.Database; +using FluentValidation; + +namespace Application.Endpoints.Pacients.Login; + +public class PacientResetPasswordValidation : AbstractValidator +{ + private readonly IPacientRepository _pacientRepository; + + public PacientResetPasswordValidation(IPacientRepository pacientRepository) + { + _pacientRepository = pacientRepository; + + RuleFor(x => x.Email) + .NotEmpty().WithMessage("Email is required.") + .EmailAddress().WithMessage("Invalid email format.") + .MustAsync(BeExistingPacient).WithMessage("Pacient with this email does not exist."); + + RuleFor(x => x.Password) + .NotEmpty().WithMessage("Password is required.") + .MinimumLength(8).WithMessage("Password must be at least 8 characters long.") + .MustAsync((dto, password, context, cancellationToken) => BeDifferentFromOldPassword(dto.Email, password, cancellationToken)) + .WithMessage("New password cannot be the same as old password."); + } + + private async Task BeExistingPacient(string email, CancellationToken cancellationToken) + { + var pacient = await _pacientRepository.FindByEmailAsync(email); + return pacient != null; + } + + private async Task BeDifferentFromOldPassword(string email, string newPassword, CancellationToken cancellationToken) + { + var currentPacient = await _pacientRepository.FindByEmailAsync(email); + return !newPassword.Equals(currentPacient?.Password, StringComparison.Ordinal); + } +} diff --git a/backend/Application/Services/Database/IConversation.cs b/backend/Application/Services/Database/IConversation.cs index ccc1f86..41e4fae 100644 --- a/backend/Application/Services/Database/IConversation.cs +++ b/backend/Application/Services/Database/IConversation.cs @@ -1,6 +1,5 @@ -namespace Application.Services.Database +namespace Application.Services.Database; + +public interface IConversationRepository { - public interface IConversationRepository - { - } } diff --git a/backend/Application/Services/Database/IDoctors.cs b/backend/Application/Services/Database/IDoctors.cs index 7b61476..b9ae00d 100644 --- a/backend/Application/Services/Database/IDoctors.cs +++ b/backend/Application/Services/Database/IDoctors.cs @@ -1,7 +1,18 @@ -namespace Application.Services.Database +using Core.Entities; + +namespace Application.Services.Database; + +public interface IDoctorRepository { - public interface IDoctorRepository - { - Task IsDoctorExisting(string email); - } + Task AddAsync(Doctor doctor); + + Task GetByIdAsync(Guid id); + + Task FindByEmailAsync(string email); + + Task UpdateAsync(Doctor doctor); + + Task DeleteAsync(Doctor doctor); + + Task> GetAllAsync(); } \ No newline at end of file diff --git a/backend/Application/Services/Database/IMedicalHistory.cs b/backend/Application/Services/Database/IMedicalHistory.cs index 7cd4fd7..9b931b8 100644 --- a/backend/Application/Services/Database/IMedicalHistory.cs +++ b/backend/Application/Services/Database/IMedicalHistory.cs @@ -1,6 +1,15 @@ -namespace Application.Services.Database +using Core.Entities; + +namespace Application.Services.Database; + +public interface IMedicalHistoryRepository { - public interface IMedicalHistoryRepository - { - } + Task GetByIdAsync(Guid id); + + Task GetByUserIdAsync(Guid userId); + + Task AddAsync(MedicalHistory medicalHistory); + + Task UpdateAsync(MedicalHistory medicalHistory); + } diff --git a/backend/Application/Services/Database/IPatients.cs b/backend/Application/Services/Database/IPatients.cs index 894b842..5162989 100644 --- a/backend/Application/Services/Database/IPatients.cs +++ b/backend/Application/Services/Database/IPatients.cs @@ -1,6 +1,18 @@ -namespace Application.Services.Database +using Core.Entities; + +namespace Application.Services.Database; + +public interface IPacientRepository { - public interface IPacientRepository - { - } + Task AddAsync(Pacient pacient); + + Task GetByIdAsync(Guid id); + + Task FindByEmailAsync(string email); + + Task UpdateAsync(Pacient doctor); + + Task DeleteAsync(Pacient doctor); + + Task> GetAllAsync(); } diff --git a/backend/Core/Entities/Chat.cs b/backend/Core/Entities/Chat.cs index c7556f7..06e3528 100644 --- a/backend/Core/Entities/Chat.cs +++ b/backend/Core/Entities/Chat.cs @@ -1,25 +1,24 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; -namespace Core.Entities +namespace Core.Entities; + +public class Chat { - public class Chat - { - [BsonId] - [BsonRepresentation(BsonType.String)] - public Guid Id { get; set; } + [BsonId] + [BsonRepresentation(BsonType.String)] + public Guid Id { get; set; } - public Guid PatientId { get; set; } - public Guid DoctorId { get; set; } + public Guid PatientId { get; set; } + public Guid DoctorId { get; set; } - public List Messages { get; set; } = new List(); - } + public List Messages { get; set; } = new List(); +} - public class Message - { - [BsonRepresentation(BsonType.String)] - public Guid UserId { get; set; } +public class Message +{ + [BsonRepresentation(BsonType.String)] + public Guid UserId { get; set; } - public string Content { get; set; } - } + public string Content { get; set; } } diff --git a/backend/Core/Entities/Doctor.cs b/backend/Core/Entities/Doctor.cs index 72a80a8..e987ff8 100644 --- a/backend/Core/Entities/Doctor.cs +++ b/backend/Core/Entities/Doctor.cs @@ -1,24 +1,18 @@ using System.ComponentModel.DataAnnotations; -namespace Core.Entities +namespace Core.Entities; + +public class Doctor { - public class Doctor + public Doctor() { - public Doctor() - { - Id = Guid.NewGuid(); - } - - [Key] - public Guid Id { get; private set; } - public string? Name { get; private set; } - public string? Email { get; private set; } - public string? Password { get; private set; } - public string? Description { get; private set; } - - public void SetName(string name) { Name = name; } - public void SetEmail(string email) { Email = email; } - public void SetPassword(string password) { Password = password; } - public void SetDescription (string description) { Description = description; } + Id = Guid.NewGuid(); } + + [Key] + public Guid Id { get; private set; } + public string? Name { get; set; } + public string? Email { get; set; } + public string? Password { get; set; } + public string? Description { get; set; } } diff --git a/backend/Core/Entities/MedicalHistory.cs b/backend/Core/Entities/MedicalHistory.cs index 678f82d..9d4946d 100644 --- a/backend/Core/Entities/MedicalHistory.cs +++ b/backend/Core/Entities/MedicalHistory.cs @@ -1,20 +1,16 @@ using System.ComponentModel.DataAnnotations; -namespace Core.Entities +namespace Core.Entities; + +public class MedicalHistory { - public class MedicalHistory + public MedicalHistory() { - public MedicalHistory() - { - Id = Guid.NewGuid(); - } - - [Key] - public Guid Id { get; private set; } - public Guid UserId { get; private set; } - public byte[] Description { get; private set; } = []; - - public void SetUserId(Guid userId) { UserId = userId; } - public void SetDescription(byte[] description) { Description = description; } + Id = Guid.NewGuid(); } + + [Key] + public Guid Id { get; private set; } + public Guid UserId { get; set; } + public byte[] Description { get; set; } = []; } diff --git a/backend/Core/Entities/Pacient.cs b/backend/Core/Entities/Pacient.cs index f2f121d..8dbb814 100644 --- a/backend/Core/Entities/Pacient.cs +++ b/backend/Core/Entities/Pacient.cs @@ -1,22 +1,17 @@ using System.ComponentModel.DataAnnotations; -namespace Core.Entities +namespace Core.Entities; + +public class Pacient { - public class Pacient + public Pacient() { - public Pacient() - { - Id = Guid.NewGuid(); - } - - [Key] - public Guid Id { get; private set; } - public string? Name { get; private set; } - public string? Email { get; private set; } - public string? Password { get; private set; } - - public void SetName(string name) { Name = name; } - public void SetEmail(string email) { Email = email; } - public void SetPassword(string password) { Password = password; } + Id = Guid.NewGuid(); } + + [Key] + public Guid Id { get; private set; } + public string? Name { get; set; } + public string? Email { get; set; } + public string? Password { get; set; } } diff --git a/backend/Infrastructure/DesignTimeDbContextFactory.cs b/backend/Infrastructure/DesignTimeDbContextFactory.cs index ef86c21..729c6cc 100644 --- a/backend/Infrastructure/DesignTimeDbContextFactory.cs +++ b/backend/Infrastructure/DesignTimeDbContextFactory.cs @@ -3,26 +3,25 @@ using Microsoft.EntityFrameworkCore.Design; using Microsoft.Extensions.Configuration; -namespace Infrastructure.Data +namespace Infrastructure.Data; + +public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory { - public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory + public HealthcareManagerDatabase CreateDbContext(string[] args) { - public HealthcareManagerDatabase CreateDbContext(string[] args) - { - // Adjust the path to point to the API project directory - var basePath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), @"..\API")); + // Adjust the path to point to the API project directory + var basePath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), @"..\API")); - IConfigurationRoot configuration = new ConfigurationBuilder() - .SetBasePath(basePath) - .AddJsonFile("appsettings.json") - .Build(); + IConfigurationRoot configuration = new ConfigurationBuilder() + .SetBasePath(basePath) + .AddJsonFile("appsettings.json") + .Build(); - var builder = new DbContextOptionsBuilder(); - var connectionString = configuration.GetConnectionString("HealthcareManagerDatabase"); + var builder = new DbContextOptionsBuilder(); + var connectionString = configuration.GetConnectionString("HealthcareManagerDatabase"); - builder.UseNpgsql(connectionString); // Make sure this matches your database provider + builder.UseNpgsql(connectionString); // Make sure this matches your database provider - return new HealthcareManagerDatabase(builder.Options); - } + return new HealthcareManagerDatabase(builder.Options); } } diff --git a/backend/Infrastructure/HealthcareManagerDatabase.cs b/backend/Infrastructure/HealthcareManagerDatabase.cs index 7598577..8ccf072 100644 --- a/backend/Infrastructure/HealthcareManagerDatabase.cs +++ b/backend/Infrastructure/HealthcareManagerDatabase.cs @@ -1,19 +1,18 @@ using Core.Entities; using Microsoft.EntityFrameworkCore; -namespace Infrastructure.Data +namespace Infrastructure.Data; + +public class HealthcareManagerDatabase : DbContext { - public class HealthcareManagerDatabase : DbContext + public HealthcareManagerDatabase(DbContextOptions options) : base(options) { } + + public DbSet Pacients { get; set; } + public DbSet MedicalHistories { get; set; } + public DbSet Doctors { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) { - public HealthcareManagerDatabase(DbContextOptions options) : base(options) { } - - public DbSet Pacients { get; set; } - public DbSet MedicalHistories { get; set; } - public DbSet Doctors { get; set; } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - } + base.OnModelCreating(modelBuilder); } } diff --git a/backend/Infrastructure/Infrastructure.csproj b/backend/Infrastructure/Infrastructure.csproj index 6c83559..90944bb 100644 --- a/backend/Infrastructure/Infrastructure.csproj +++ b/backend/Infrastructure/Infrastructure.csproj @@ -12,6 +12,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/backend/Infrastructure/InfrastructureDI.cs b/backend/Infrastructure/InfrastructureDI.cs index 031e915..c29feb6 100644 --- a/backend/Infrastructure/InfrastructureDI.cs +++ b/backend/Infrastructure/InfrastructureDI.cs @@ -7,27 +7,26 @@ using Infrastructure.Services.MongoDB; using Application.Services.Database; using Infrastructure.Services.PostgreSQL; -namespace Infrastructure +namespace Infrastructure; + +public static class DependencyInjection { - public static class DependencyInjection + public static IServiceCollection AddInfrastructureServices(this IServiceCollection services, IConfiguration configuration) { - public static IServiceCollection AddInfrastructureServices(this IServiceCollection services, IConfiguration configuration) - { - services.AddDbContext(options => - options.UseNpgsql(configuration.GetConnectionString("HealthcareManagerDatabase"))); + services.AddDbContext(options => + options.UseNpgsql(configuration.GetConnectionString("HealthcareManagerDatabase"))); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - var mongoDbConnection = configuration.GetConnectionString("MongoDBDatabase"); + var mongoDbConnection = configuration.GetConnectionString("MongoDBDatabase"); - services.AddSingleton(serviceProvider => new MongoDbService(mongoDbConnection)); + services.AddSingleton(serviceProvider => new MongoDbService(mongoDbConnection)); - // services.AddScoped(); + // services.AddScoped(); - return services; - } + return services; } } diff --git a/backend/Infrastructure/Services/PostgreSQL/BasePostgreSQLRepository.cs b/backend/Infrastructure/Services/PostgreSQL/BasePostgreSQLRepository.cs index e80c2a3..fdd169f 100644 --- a/backend/Infrastructure/Services/PostgreSQL/BasePostgreSQLRepository.cs +++ b/backend/Infrastructure/Services/PostgreSQL/BasePostgreSQLRepository.cs @@ -1,44 +1,43 @@ using Infrastructure.Data; using Microsoft.EntityFrameworkCore; -namespace Infrastructure.Services.PostgreSQL +namespace Infrastructure.Services.PostgreSQL; + +public class BasePostgreSQLRepository where T : class { - public class BasePostgreSQLRepository where T : class + protected readonly HealthcareManagerDatabase _context; + + public BasePostgreSQLRepository(HealthcareManagerDatabase context) { - protected readonly HealthcareManagerDatabase _context; + _context = context; + } - public BasePostgreSQLRepository(HealthcareManagerDatabase context) - { - _context = context; - } + public async Task GetByIdAsync(Guid id) + { + return await _context.Set().FindAsync(id); + } - public async Task GetByIdAsync(Guid id) - { - return await _context.Set().FindAsync(id); - } + public async Task> GetAllAsync() + { + return await _context.Set().ToListAsync(); + } - public async Task> ListAllAsync() - { - return await _context.Set().ToListAsync(); - } + public async Task AddAsync(T entity) + { + await _context.Set().AddAsync(entity); + await _context.SaveChangesAsync(); + } - public async Task AddAsync(T entity) - { - await _context.Set().AddAsync(entity); - await _context.SaveChangesAsync(); - } + public async Task UpdateAsync(T entity) + { + _context.Set().Attach(entity); + _context.Entry(entity).State = EntityState.Modified; + await _context.SaveChangesAsync(); + } - public async Task Update(T entity) - { - _context.Set().Attach(entity); - _context.Entry(entity).State = EntityState.Modified; - await _context.SaveChangesAsync(); - } - - public async Task Delete(T entity) - { - _context.Set().Remove(entity); - await _context.SaveChangesAsync(); - } + public async Task DeleteAsync(T entity) + { + _context.Set().Remove(entity); + await _context.SaveChangesAsync(); } } diff --git a/backend/Infrastructure/Services/PostgreSQL/DoctorRepository.cs b/backend/Infrastructure/Services/PostgreSQL/DoctorRepository.cs index dbf8451..3d85cf7 100644 --- a/backend/Infrastructure/Services/PostgreSQL/DoctorRepository.cs +++ b/backend/Infrastructure/Services/PostgreSQL/DoctorRepository.cs @@ -3,18 +3,10 @@ using Core.Entities; using Infrastructure.Data; using Microsoft.EntityFrameworkCore; -namespace Infrastructure.Services.PostgreSQL +namespace Infrastructure.Services.PostgreSQL; + +public class DoctorRepository(HealthcareManagerDatabase context) : BasePostgreSQLRepository(context), IDoctorRepository { - public class DoctorRepository : BasePostgreSQLRepository, IDoctorRepository - { - public DoctorRepository(HealthcareManagerDatabase context) : base(context) - { - } - - public async Task IsDoctorExisting(string email) - { - return await _context.Doctors.AnyAsync(d => d.Email == email); - } - - } + public async Task FindByEmailAsync(string email) + => await _context.Doctors.FirstOrDefaultAsync(d => d.Email == email); } diff --git a/backend/Infrastructure/Services/PostgreSQL/MedicalHistoryRepository.cs b/backend/Infrastructure/Services/PostgreSQL/MedicalHistoryRepository.cs index 891915a..44833f1 100644 --- a/backend/Infrastructure/Services/PostgreSQL/MedicalHistoryRepository.cs +++ b/backend/Infrastructure/Services/PostgreSQL/MedicalHistoryRepository.cs @@ -1,14 +1,17 @@ using Application.Services.Database; using Core.Entities; using Infrastructure.Data; +using Microsoft.EntityFrameworkCore; -namespace Infrastructure.Services.PostgreSQL +namespace Infrastructure.Services.PostgreSQL; + +public class MedicalHistoryRepository : BasePostgreSQLRepository, IMedicalHistoryRepository { - public class MedicalHistoryRepository : BasePostgreSQLRepository, IMedicalHistoryRepository + public MedicalHistoryRepository(HealthcareManagerDatabase context) : base(context) { - public MedicalHistoryRepository(HealthcareManagerDatabase context) : base(context) - { - } - } + + public async Task GetByUserIdAsync(Guid userId) + => await _context.MedicalHistories.FirstOrDefaultAsync(d => d.UserId == userId); + } diff --git a/backend/Infrastructure/Services/PostgreSQL/PacientRepository.cs b/backend/Infrastructure/Services/PostgreSQL/PacientRepository.cs index b70915c..65ba268 100644 --- a/backend/Infrastructure/Services/PostgreSQL/PacientRepository.cs +++ b/backend/Infrastructure/Services/PostgreSQL/PacientRepository.cs @@ -1,14 +1,12 @@ using Application.Services.Database; using Core.Entities; using Infrastructure.Data; +using Microsoft.EntityFrameworkCore; -namespace Infrastructure.Services.PostgreSQL +namespace Infrastructure.Services.PostgreSQL; + +public class PacientRepository(HealthcareManagerDatabase context) : BasePostgreSQLRepository(context), IPacientRepository { - public class PacientRepository : BasePostgreSQLRepository, IPacientRepository - { - public PacientRepository(HealthcareManagerDatabase context) : base(context) - { - } - - } + public async Task FindByEmailAsync(string email) + => await _context.Pacients.FirstOrDefaultAsync(d => d.Email == email); }