Skip to content

Commit

Permalink
feat: Create validators using Fluent Validation API
Browse files Browse the repository at this point in the history
  • Loading branch information
MrDave1999 committed Mar 2, 2024
1 parent 7878f9c commit b3e77b1
Show file tree
Hide file tree
Showing 11 changed files with 291 additions and 55 deletions.
44 changes: 41 additions & 3 deletions src/Core/ProformaInvoice/DownloadProformaInvoice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,52 @@ public class DentalTreatment
};
}

public class DownloadProformaInvoiceValidator
: AbstractValidator<DownloadProformaInvoiceRequest>
{
public DownloadProformaInvoiceValidator()
{
RuleFor(request => request.FullName).NotEmpty();
RuleFor(request => request.Document).NotEmpty();
RuleFor(request => request.TotalPrice).GreaterThan(0);
RuleFor(request => request.DentalTreatments).NotEmpty();
RuleForEach(request => request.DentalTreatments)
.ChildRules(validator =>
{
validator
.RuleFor(treatment => treatment.GeneralTreatmentName)
.NotEmpty();
validator
.RuleFor(treatment => treatment.SpecificTreatmentName)
.NotEmpty();
validator
.RuleFor(treatment => treatment.Price)
.GreaterThan(0);
});
}
}

public class DownloadProformaInvoiceUseCase(
IHtmlTemplateLoader htmlTemplateLoader,
IHtmlConverter htmlConverter)
IHtmlConverter htmlConverter,
DownloadProformaInvoiceValidator validator)
{
public async Task<byte[]> DownloadAsPdfAsync(DownloadProformaInvoiceRequest request)
public async Task<Result<ByteArrayFileContent>> DownloadAsPdfAsync(DownloadProformaInvoiceRequest request)
{
var result = validator.Validate(request);
if (result.IsFailed())
return result.Invalid();

var html = await htmlTemplateLoader
.LoadAsync("./Templates/ProformaInvoice.html", request.MapToObject());
return htmlConverter.ConvertToPdf(html, new MemoryStream());

byte[] fileContents = htmlConverter.ConvertToPdf(html, new MemoryStream());
return Result.File(new ByteArrayFileContent(fileContents)
{
ContentType = MediaTypeNames.Application.Pdf,
FileName = "ProformaInvoice.pdf"
});
}
}
7 changes: 4 additions & 3 deletions src/Core/ProformaInvoice/ProformaInvoiceController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[Route("proforma-invoice")]
[ApiController]
public class ProformaInvoiceController : ControllerBase
public class ProformaInvoiceController
{
[AuthorizeByRole(RoleName.BasicUser)]
[Route("pdf")]
Expand All @@ -11,7 +11,8 @@ public async Task<ActionResult> DownloadAsPdf(
[FromBody]DownloadProformaInvoiceRequest request,
DownloadProformaInvoiceUseCase useCase)
{
var contents = await useCase.DownloadAsPdfAsync(request);
return File(contents, "application/pdf", "ProformaReporte.pdf");
return (await useCase.DownloadAsPdfAsync(request))
.ToActionResult()
.Result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ public class ReportMostRequestedServicesController : ControllerBase
/// Obtiene el reporte de los servicios dentales más solicitados.
/// </summary>
[HttpPost("report/most-requested/services")]
public async Task<ActionResult<IEnumerable<GetMostRequestedServicesResponse>>> Get(
public async Task<ListedResult<GetMostRequestedServicesResponse>> Get(
[FromBody]GetMostRequestedServicesRequest request,
GetMostRequestedServicesUseCase useCase)
{
if (User.IsAdmin() && User.IsNotInOffice(request.OfficeId))
return Forbid();
return Result.Forbidden();

return Ok(await useCase.ExecuteAsync(request));
return await useCase.ExecuteAsync(request);
}

/// <summary>
Expand All @@ -26,7 +26,8 @@ public async Task<ActionResult> DownloadAsPdf(
[FromBody]DownloadDentalServicesReportRequest request,
DownloadDentalServicesReportUseCase useCase)
{
var contents = await useCase.DownloadAsPdfAsync(request);
return File(contents, "application/pdf", "Reporte sobre los servicios mas solicitados.pdf");
return (await useCase.DownloadAsPdfAsync(request))
.ToActionResult()
.Result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,68 @@

public class DownloadDentalServicesReportRequest
{
public string From { get; init; }
public string To { get; init; }
public class Service
{
public string DentalServiceName { get; init; }
public int TotalAppointmentsAssisted { get; init; }
}

public DateTime From { get; init; }
public DateTime To { get; init; }
public string OfficeName { get; init; }
public IEnumerable<GetMostRequestedServicesResponse> Services { get; init; }
public IEnumerable<Service> Services { get; init; }

public object MapToObject() => new
{
From,
To,
From = From.GetDateWithStandardFormat(),
To = To.GetDateWithStandardFormat(),
OfficeName,
Services
};
}

public class DownloadDentalServicesReportValidator
: AbstractValidator<DownloadDentalServicesReportRequest>
{
public DownloadDentalServicesReportValidator()
{
RuleFor(request => request.From).LessThanOrEqualTo(request => request.To);
RuleFor(request => request.OfficeName).NotEmpty();
RuleFor(request => request.Services).NotEmpty();
RuleForEach(request => request.Services)
.ChildRules(validator =>
{
validator
.RuleFor(service => service.DentalServiceName)
.NotEmpty();
validator
.RuleFor(service => service.TotalAppointmentsAssisted)
.GreaterThanOrEqualTo(0);
});
}
}

public class DownloadDentalServicesReportUseCase(
IHtmlTemplateLoader htmlTemplateLoader,
IHtmlConverter htmlConverter)
IHtmlConverter htmlConverter,
DownloadDentalServicesReportValidator validator)
{
public async Task<byte[]> DownloadAsPdfAsync(DownloadDentalServicesReportRequest request)
public async Task<Result<ByteArrayFileContent>> DownloadAsPdfAsync(
DownloadDentalServicesReportRequest request)
{
var result = validator.Validate(request);
if (result.IsFailed())
return result.Invalid();

var html = await htmlTemplateLoader
.LoadAsync("./Templates/ReportDentalServices.html", request.MapToObject());
return htmlConverter.ConvertToPdf(html, new MemoryStream());

byte[] fileContents = htmlConverter.ConvertToPdf(html, new MemoryStream());
return Result.File(new ByteArrayFileContent(fileContents)
{
ContentType = MediaTypeNames.Application.Pdf,
FileName = "DentalServices.pdf"
});
}
}
22 changes: 19 additions & 3 deletions src/Core/Reports/UseCases/GetMostRequestedServices/ExecuteQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,32 @@ public class GetMostRequestedServicesRequest
public int OfficeId { get; init; }
}

public class GetMostRequestedServicesValidator : AbstractValidator<GetMostRequestedServicesRequest>
{
public GetMostRequestedServicesValidator()
{
RuleFor(request => request.From).LessThanOrEqualTo(request => request.To);
RuleFor(request => request.OfficeId).GreaterThanOrEqualTo(0);
}
}

public class GetMostRequestedServicesResponse
{
public string DentalServiceName { get; init; }
public int TotalAppointmentsAssisted { get; init; }
}

public class GetMostRequestedServicesUseCase(DbContext context)
public class GetMostRequestedServicesUseCase(
DbContext context,
GetMostRequestedServicesValidator validator)
{
public async Task<IEnumerable<GetMostRequestedServicesResponse>> ExecuteAsync(GetMostRequestedServicesRequest request)
public async Task<ListedResult<GetMostRequestedServicesResponse>> ExecuteAsync(
GetMostRequestedServicesRequest request)
{
var result = validator.Validate(request);
if (result.IsFailed())
return result.Invalid();

var appointments = await context.Set<Appointment>()
.Where(appointment =>
appointment.AppointmentStatusId == (int)AppointmentStatus.Predefined.Assisted &&
Expand All @@ -37,6 +53,6 @@ public async Task<IEnumerable<GetMostRequestedServicesResponse>> ExecuteAsync(Ge
.AsNoTracking()
.ToListAsync();

return appointments;
return Result.ObtainedResources(appointments);
}
}
11 changes: 6 additions & 5 deletions src/Core/Reports/UseCases/GetTotalAppointments/ApiController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ public class ReportTotalAppointmentsController : ControllerBase
/// Obtiene el reporte sobre el total de citas asistidas, no asistidas y canceladas.
/// </summary>
[HttpPost("report/appointment")]
public async Task<ActionResult<GetTotalAppointmentsResponse>> Get(
public async Task<Result<GetTotalAppointmentsResponse>> Get(
[FromBody]GetTotalAppointmentsRequest request,
GetTotalAppointmentsUseCase useCase)
{
if (User.IsAdmin() && User.IsNotInOffice(request.OfficeId))
return Forbid();
return Result.Forbidden();

return Ok(await useCase.ExecuteAsync(request));
return await useCase.ExecuteAsync(request);
}

/// <summary>
Expand All @@ -26,7 +26,8 @@ public async Task<ActionResult> DownloadAsPdf(
[FromBody]DownloadTotalAppointmentsReportRequest request,
DownloadTotalAppointmentsReportUseCase useCase)
{
var contents = await useCase.DownloadAsPdfAsync(request);
return File(contents, "application/pdf", "Reporte sobre el total de citas.pdf");
return (await useCase.DownloadAsPdfAsync(request))
.ToActionResult()
.Result;
}
}
72 changes: 64 additions & 8 deletions src/Core/Reports/UseCases/GetTotalAppointments/DownloadReport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,86 @@

public class DownloadTotalAppointmentsReportRequest
{
public string From { get; init; }
public string To { get; init; }
public class TotalAppointments
{
public int Total { get; init; }
public int TotalAppointmentsAssisted { get; init; }
public int TotalAppointmentsNotAssisted { get; init; }
public int TotalAppointmentsCancelledByPatient { get; init; }
public int TotalAppointmentsCancelledByEmployee { get; init; }
}

public DateTime From { get; init; }
public DateTime To { get; init; }
public string OfficeName { get; init; }
public string DentistName { get; init; }
public GetTotalAppointmentsResponse Totals { get; init; }
public TotalAppointments Totals { get; init; }

public object MapToObject() => new
{
From,
To,
From = From.GetDateWithStandardFormat(),
To = To.GetDateWithStandardFormat(),
OfficeName,
DentistName,
Totals
};
}

public class DownloadTotalAppointmentsReportValidator
: AbstractValidator<DownloadTotalAppointmentsReportRequest>
{
public DownloadTotalAppointmentsReportValidator()
{
RuleFor(request => request.From).LessThanOrEqualTo(request => request.To);
RuleFor(request => request.OfficeName).NotEmpty();
RuleFor(request => request.DentistName).NotEmpty();
RuleFor(request => request.Totals)
.NotEmpty()
.ChildRules(validator =>
{
validator
.RuleFor(appointment => appointment.Total)
.GreaterThanOrEqualTo(0);
validator
.RuleFor(appointment => appointment.TotalAppointmentsAssisted)
.GreaterThanOrEqualTo(0);
validator
.RuleFor(appointment => appointment.TotalAppointmentsNotAssisted)
.GreaterThanOrEqualTo(0);
validator
.RuleFor(appointment => appointment.TotalAppointmentsCancelledByPatient)
.GreaterThanOrEqualTo(0);
validator
.RuleFor(appointment => appointment.TotalAppointmentsCancelledByEmployee)
.GreaterThanOrEqualTo(0);
});
}
}

public class DownloadTotalAppointmentsReportUseCase(
IHtmlTemplateLoader htmlTemplateLoader,
IHtmlConverter htmlConverter)
IHtmlConverter htmlConverter,
DownloadTotalAppointmentsReportValidator validator)
{
public async Task<byte[]> DownloadAsPdfAsync(DownloadTotalAppointmentsReportRequest request)
public async Task<Result<ByteArrayFileContent>> DownloadAsPdfAsync(
DownloadTotalAppointmentsReportRequest request)
{
var result = validator.Validate(request);
if (result.IsFailed())
return result.Invalid();

var html = await htmlTemplateLoader
.LoadAsync("./Templates/ReportAppointment.html", request.MapToObject());
return htmlConverter.ConvertToPdf(html, new MemoryStream());

byte[] fileContents = htmlConverter.ConvertToPdf(html, new MemoryStream());
return Result.File(new ByteArrayFileContent(fileContents)
{
ContentType = MediaTypeNames.Application.Pdf,
FileName = "TotalAppointments.pdf"
});
}
}
26 changes: 22 additions & 4 deletions src/Core/Reports/UseCases/GetTotalAppointments/ExecuteQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ public class GetTotalAppointmentsRequest
public int DentistId { get; init; }
}

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

public class GetTotalAppointmentsResponse
{
public int Total { get; init; }
Expand All @@ -17,12 +27,19 @@ public class GetTotalAppointmentsResponse
public int TotalAppointmentsCancelledByEmployee { get; init; }
}

public class GetTotalAppointmentsUseCase(IDbConnection dbConnection, ISqlCollection sqlCollection)
public class GetTotalAppointmentsUseCase(
IDbConnection dbConnection,
ISqlCollection sqlCollection,
GetTotalAppointmentsValidator validator)
{
public async Task<GetTotalAppointmentsResponse> ExecuteAsync(GetTotalAppointmentsRequest request)
public async Task<Result<GetTotalAppointmentsResponse>> ExecuteAsync(GetTotalAppointmentsRequest request)
{
var result = validator.Validate(request);
if (result.IsFailed())
return result.Invalid();

string sql = sqlCollection["GetTotalAppointments"];
var result = await dbConnection.QueryAsync<GetTotalAppointmentsResponse>(sql, new
var totalAppointments = await dbConnection.QueryFirstAsync<GetTotalAppointmentsResponse>(sql, new
{
AppointmentStatus.Predefined.Assisted,
AppointmentStatus.Predefined.NotAssisted,
Expand All @@ -33,6 +50,7 @@ public async Task<GetTotalAppointmentsResponse> ExecuteAsync(GetTotalAppointment
request.OfficeId,
request.DentistId
});
return result.First();

return Result.ObtainedResource(totalAppointments);
}
}
Loading

0 comments on commit b3e77b1

Please sign in to comment.