Skip to content

Commit

Permalink
Merge pull request #237 from DentallApp/patch-12
Browse files Browse the repository at this point in the history
feat: Add support for Fluent Validations
  • Loading branch information
MrDave1999 committed Mar 2, 2024
2 parents 8dfbeef + b3e77b1 commit e94e6bb
Show file tree
Hide file tree
Showing 65 changed files with 1,191 additions and 225 deletions.
7 changes: 5 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@
<PackageVersion Include="Scrutor" Version="4.2.2" />
<PackageVersion Include="SendGrid" Version="9.28.1" />
<PackageVersion Include="SendGrid.Extensions.DependencyInjection" Version="1.0.1" />
<PackageVersion Include="SimpleResults" Version="2.3.1" />
<PackageVersion Include="SimpleResults.AspNetCore" Version="2.3.1" />
<PackageVersion Include="FluentValidation" Version="11.9.0" />
<PackageVersion Include="FluentValidation.DependencyInjectionExtensions" Version="11.9.0" />
<PackageVersion Include="SimpleResults" Version="2.4.0" />
<PackageVersion Include="SimpleResults.AspNetCore" Version="2.4.0" />
<PackageVersion Include="SimpleResults.FluentValidation" Version="2.4.0" />
<PackageVersion Include="DotEnv.Core" Version="3.0.0" />
<PackageVersion Include="CopyPluginsToPublishDirectory" Version="1.0.0" />
<PackageVersion Include="CPlugin.Net" Version="1.0.0" />
Expand Down
31 changes: 30 additions & 1 deletion src/Core/Appointments/UseCases/CancelAppointments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,30 @@ public class Appointment
public IEnumerable<Appointment> Appointments { get; init; }
}

public class CancelAppointmentsValidator : AbstractValidator<CancelAppointmentsRequest>
{
public CancelAppointmentsValidator()
{
RuleFor(request => request.Reason).NotEmpty();
RuleFor(request => request.Appointments).NotEmpty();
RuleForEach(request => request.Appointments)
.ChildRules(validator =>
{
validator
.RuleFor(appointment => appointment.AppointmentId)
.GreaterThan(0);
validator
.RuleFor(appointment => appointment.PatientName)
.NotEmpty();
validator
.RuleFor(appointment => appointment.PatientCellPhone)
.NotEmpty();
});
}
}

/// <summary>
/// Represents the appointments that cannot be canceled.
/// </summary>
Expand All @@ -27,10 +51,15 @@ public class CancelAppointmentsUseCase(
AppSettings settings,
IAppointmentRepository appointmentRepository,
IInstantMessaging instantMessaging,
IDateTimeService dateTimeService)
IDateTimeService dateTimeService,
CancelAppointmentsValidator validator)
{
public async Task<Result<CancelAppointmentsResponse>> ExecuteAsync(ClaimsPrincipal currentEmployee, CancelAppointmentsRequest request)
{
var result = validator.Validate(request);
if(result.IsFailed())
return result.Invalid();

// Stores appointments whose stipulated date and time have not passed.
var appointmentsCanBeCancelled = request.Appointments
.Where(appointment => (appointment.AppointmentDate + appointment.StartHour) > dateTimeService.Now);
Expand Down
20 changes: 19 additions & 1 deletion src/Core/Appointments/UseCases/Create.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
namespace DentallApp.Core.Appointments.UseCases;

public class CreateAppointmentValidator : AbstractValidator<CreateAppointmentRequest>
{
public CreateAppointmentValidator()
{
RuleFor(request => request.UserId).GreaterThan(0);
RuleFor(request => request.PersonId).GreaterThan(0);
RuleFor(request => request.DentistId).GreaterThan(0);
RuleFor(request => request.GeneralTreatmentId).GreaterThan(0);
RuleFor(request => request.OfficeId).GreaterThan(0);
RuleFor(request => request.StartHour).LessThan(request => request.EndHour);
}
}

public class CreateAppointmentUseCase(
DbContext context,
IDateTimeService dateTimeService,
SendAppointmentInformationUseCase sendInformationUseCase) : ICreateAppointmentUseCase
SendAppointmentInformationUseCase sendInformationUseCase,
CreateAppointmentValidator validator) : ICreateAppointmentUseCase
{
public async Task<Result<CreatedId>> ExecuteAsync(CreateAppointmentRequest request)
{
var result = validator.Validate(request);
if (result.IsFailed())
return result.Invalid();

// Checks if the date and time of the appointment is not available.
bool isNotAvailable = await context.Set<Appointment>()
.Where(appointment =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
namespace DentallApp.Core.Appointments.UseCases.GetAvailableHours;

public class GetAvailableHoursUseCase(IAvailabilityQueries queries, IDateTimeService dateTimeService) : IGetAvailableHoursUseCase
public class GetAvailableHoursValidator : AbstractValidator<AvailableTimeRangeRequest>
{
public GetAvailableHoursValidator()
{
RuleFor(request => request.OfficeId).GreaterThan(0);
RuleFor(request => request.DentistId).GreaterThan(0);
RuleFor(request => request.DentalServiceId).GreaterThan(0);
}
}

public class GetAvailableHoursUseCase(
IAvailabilityQueries queries,
IDateTimeService dateTimeService,
GetAvailableHoursValidator validator) : IGetAvailableHoursUseCase
{
public async Task<ListedResult<AvailableTimeRangeResponse>> ExecuteAsync(AvailableTimeRangeRequest request)
{
var result = validator.Validate(request);
if (result.IsFailed())
return result.Invalid();

int officeId = request.OfficeId;
int dentistId = request.DentistId;
int serviceId = request.DentalServiceId;
Expand Down
20 changes: 19 additions & 1 deletion src/Core/Appointments/UseCases/GetByDateRange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,18 @@ public class GetAppointmentsByDateRangeRequest
public int StatusId { get; init; }
}

public class GetAppointmentsByDateRangeValidator
: AbstractValidator<GetAppointmentsByDateRangeRequest>
{
public GetAppointmentsByDateRangeValidator()
{
RuleFor(request => request.From).LessThanOrEqualTo(request => request.To);
RuleFor(request => request.OfficeId).GreaterThanOrEqualTo(0);
RuleFor(request => request.DentistId).GreaterThanOrEqualTo(0);
RuleFor(request => request.StatusId).GreaterThanOrEqualTo(0);
}
}

public class GetAppointmentsByDateRangeResponse
{
public int AppointmentId { get; init; }
Expand All @@ -29,12 +41,18 @@ public class GetAppointmentsByDateRangeResponse
public string OfficeName { get; init; }
}

public class GetAppointmentsByDateRangeUseCase(DbContext context)
public class GetAppointmentsByDateRangeUseCase(
DbContext context,
GetAppointmentsByDateRangeValidator validator)
{
public async Task<ListedResult<GetAppointmentsByDateRangeResponse>> ExecuteAsync(
ClaimsPrincipal currentEmployee,
GetAppointmentsByDateRangeRequest request)
{
var result = validator.Validate(request);
if (result.IsFailed())
return result.Invalid();

if (currentEmployee.IsOnlyDentist() && currentEmployee.GetEmployeeId() != request.DentistId)
return Result.Forbidden(Messages.CanOnlyAccessYourOwnAppointments);

Expand Down
17 changes: 16 additions & 1 deletion src/Core/Appointments/UseCases/Update.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,25 @@ public class UpdateAppointmentRequest
public int StatusId { get; init; }
}

public class UpdateAppointmentUseCase(DbContext context, IDateTimeService dateTimeService)
public class UpdateAppointmentValidator : AbstractValidator<UpdateAppointmentRequest>
{
public UpdateAppointmentValidator()
{
RuleFor(request => request.StatusId).GreaterThan(0);
}
}

public class UpdateAppointmentUseCase(
DbContext context,
IDateTimeService dateTimeService,
UpdateAppointmentValidator validator)
{
public async Task<Result> ExecuteAsync(int id, ClaimsPrincipal currentEmployee, UpdateAppointmentRequest request)
{
var result = validator.Validate(request);
if (result.IsFailed())
return result.Invalid();

var appointment = await context.Set<Appointment>()
.Where(appointment => appointment.Id == id)
.FirstOrDefaultAsync();
Expand Down
1 change: 1 addition & 0 deletions src/Core/DentallApp.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<PackageReference Include="CopySqlFilesToOutputDirectory" />
<PackageReference Include="YeSql.Net" />
<PackageReference Include="SimpleResults.AspNetCore" />
<PackageReference Include="SimpleResults.FluentValidation" />
<PackageReference Include="linq2db.EntityFrameworkCore" />
<PackageReference Include="Dapper" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers">
Expand Down
23 changes: 21 additions & 2 deletions src/Core/Dependents/UseCases/Create.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class CreateDependentRequest
public string LastNames { get; init; }
public string CellPhone { get; init; }
public DateTime? DateBirth { get; init; }
public int? GenderId { get; init; }
public int GenderId { get; init; }
public string Email { get; init; }
public int KinshipId { get; init; }

Expand All @@ -33,10 +33,29 @@ public Dependent MapToDependent(int userId)
}
}

public class CreateDependentUseCase(DbContext context)
public class CreateDependentValidator : AbstractValidator<CreateDependentRequest>
{
public CreateDependentValidator()
{
RuleFor(request => request.Document).NotEmpty();
RuleFor(request => request.Names).NotEmpty();
RuleFor(request => request.LastNames).NotEmpty();
RuleFor(request => request.CellPhone).NotEmpty();
RuleFor(request => request.DateBirth).NotEmpty();
RuleFor(request => request.Email).EmailAddress();
RuleFor(request => request.GenderId).GreaterThan(0);
RuleFor(request => request.KinshipId).GreaterThan(0);
}
}

public class CreateDependentUseCase(DbContext context, CreateDependentValidator validator)
{
public async Task<Result<CreatedId>> ExecuteAsync(int userId, CreateDependentRequest request)
{
var result = validator.Validate(request);
if (result.IsFailed())
return result.Invalid();

var dependent = request.MapToDependent(userId);
context.Add(dependent);
await context.SaveChangesAsync();
Expand Down
20 changes: 19 additions & 1 deletion src/Core/Dependents/UseCases/Update.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,28 @@ public void MapToDependent(Dependent dependent)
}
}

public class UpdateDependentUseCase(DbContext context)
public class UpdateDependentValidator : AbstractValidator<UpdateDependentRequest>
{
public UpdateDependentValidator()
{
RuleFor(request => request.Names).NotEmpty();
RuleFor(request => request.LastNames).NotEmpty();
RuleFor(request => request.CellPhone).NotEmpty();
RuleFor(request => request.DateBirth).NotEmpty();
RuleFor(request => request.GenderId).GreaterThan(0);
RuleFor(request => request.KinshipId).GreaterThan(0);
RuleFor(request => request.Email).EmailAddress();
}
}

public class UpdateDependentUseCase(DbContext context, UpdateDependentValidator validator)
{
public async Task<Result> ExecuteAsync(int dependentId, int userId, UpdateDependentRequest request)
{
var result = validator.Validate(request);
if (result.IsFailed())
return result.Invalid();

var dependent = await context.Set<Dependent>()
.Include(dependent => dependent.Person)
.Where(dependent => dependent.Id == dependentId)
Expand Down
19 changes: 18 additions & 1 deletion src/Core/EmployeeSchedules/UseCases/Create.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,27 @@ public class CreateEmployeeScheduleRequest
};
}

public class CreateEmployeeScheduleUseCase(DbContext context)
public class CreateEmployeeScheduleValidator : AbstractValidator<CreateEmployeeScheduleRequest>
{
public CreateEmployeeScheduleValidator()
{
RuleFor(request => request.EmployeeId).GreaterThan(0);
RuleFor(request => request.WeekDayId).InclusiveBetween(1, 7);
RuleFor(request => request.MorningStartHour).NotEmpty();
RuleFor(request => request.MorningEndHour).NotEmpty();
RuleFor(request => request.AfternoonStartHour).NotEmpty();
RuleFor(request => request.AfternoonEndHour).NotEmpty();
}
}

public class CreateEmployeeScheduleUseCase(DbContext context, CreateEmployeeScheduleValidator validator)
{
public async Task<Result<CreatedId>> ExecuteAsync(CreateEmployeeScheduleRequest request)
{
var result = validator.Validate(request);
if (result.IsFailed())
return result.Invalid();

var employeeSchedule = request.MapToEmployeeSchedule();
context.Add(employeeSchedule);
await context.SaveChangesAsync();
Expand Down
18 changes: 17 additions & 1 deletion src/Core/EmployeeSchedules/UseCases/Update.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,26 @@ public void MapToEmployeeSchedule(EmployeeSchedule schedule)
}
}

public class UpdateEmployeeScheduleUseCase(DbContext context)
public class UpdateEmployeeScheduleValidator : AbstractValidator<UpdateEmployeeScheduleRequest>
{
public UpdateEmployeeScheduleValidator()
{
RuleFor(request => request.WeekDayId).InclusiveBetween(1, 7);
RuleFor(request => request.MorningStartHour).NotEmpty();
RuleFor(request => request.MorningEndHour).NotEmpty();
RuleFor(request => request.AfternoonStartHour).NotEmpty();
RuleFor(request => request.AfternoonEndHour).NotEmpty();
}
}

public class UpdateEmployeeScheduleUseCase(DbContext context, UpdateEmployeeScheduleValidator validator)
{
public async Task<Result> ExecuteAsync(int id, UpdateEmployeeScheduleRequest request)
{
var result = validator.Validate(request);
if (result.IsFailed())
return result.Invalid();

var employeeSchedule = await context.Set<EmployeeSchedule>()
.Where(employeeSchedule => employeeSchedule.Id == id)
.IgnoreQueryFilters()
Expand Down
14 changes: 13 additions & 1 deletion src/Core/FavoriteDentists/UseCases/Create.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,22 @@ public class CreateFavoriteDentistRequest
public int DentistId { get; init; }
}

public class CreateFavoriteDentistUseCase(DbContext context)
public class CreateFavoriteDentistValidator : AbstractValidator<CreateFavoriteDentistRequest>
{
public CreateFavoriteDentistValidator()
{
RuleFor(request => request.DentistId).GreaterThan(0);
}
}

public class CreateFavoriteDentistUseCase(DbContext context, CreateFavoriteDentistValidator validator)
{
public async Task<Result<CreatedId>> ExecuteAsync(int userId, CreateFavoriteDentistRequest request)
{
var result = validator.Validate(request);
if (result.IsFailed())
return result.Invalid();

var favoriteDentist = new FavoriteDentist
{
UserId = userId,
Expand Down
24 changes: 21 additions & 3 deletions src/Core/GeneralTreatments/UseCases/Create.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ public class CreateGeneralTreatmentRequest
{
public string Name { get; init; }
public string Description { get; init; }
[Required]
[Image]
public IFormFile Image { get; init; }
public int Duration { get; init; }

Expand All @@ -18,12 +16,32 @@ public class CreateGeneralTreatmentRequest
};
}

public class CreateGeneralTreatmentUseCase(DbContext context, AppSettings settings)
public class CreateGeneralTreatmentValidator : AbstractValidator<CreateGeneralTreatmentRequest>
{
public CreateGeneralTreatmentValidator(IFileTypeValidator fileTypeValidator)
{
RuleFor(request => request.Name).NotEmpty();
RuleFor(request => request.Description).NotEmpty();
RuleFor(request => request.Image)
.NotEmpty()
.MustBeValidImage(fileTypeValidator);
RuleFor(request => request.Duration).GreaterThan(0);
}
}

public class CreateGeneralTreatmentUseCase(
DbContext context,
AppSettings settings,
CreateGeneralTreatmentValidator validator)
{
private readonly string _basePath = settings.DentalServicesImagesPath;

public async Task<Result<CreatedId>> ExecuteAsync(CreateGeneralTreatmentRequest request)
{
var result = validator.Validate(request);
if (result.IsFailed())
return result.Invalid();

var generalTreatment = request.MapToGeneralTreatment();
context.Add(generalTreatment);
await request.Image.WriteAsync(Path.Combine(_basePath, generalTreatment.ImageUrl));
Expand Down
Loading

0 comments on commit e94e6bb

Please sign in to comment.