Skip to content

Commit

Permalink
Validation example (#133) (#134)
Browse files Browse the repository at this point in the history
* Updated all dependencies. Removed deprecated dependencies.

* Removing deprecated identity dependency.

* Removing codebloat unit testing projects.

* Added validation example

* Revised examples to include new validation abstractions/implementation.
  • Loading branch information
jasonmwebb-lv authored May 8, 2024
1 parent 2394f43 commit 6ee1d82
Show file tree
Hide file tree
Showing 52 changed files with 271 additions and 331 deletions.
2 changes: 1 addition & 1 deletion Build/Build.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ protected override void OnBuildInitialized()
{
Log.Information("Generating NuGet packages for projects in solution");
int commitNum = 0;
string NuGetVersionCustom = "2.0.0.877";
string NuGetVersionCustom = "2.0.0.878";


//if it's not a tagged release - append the commit number to the package version
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.2">
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\Src\RCommon.ApplicationServices\RCommon.ApplicationServices.csproj" />
<ProjectReference Include="..\..\..\Src\RCommon.EfCore\RCommon.EFCore.csproj" />
<ProjectReference Include="..\..\..\Src\RCommon.Emailing\RCommon.Emailing.csproj" />
<ProjectReference Include="..\..\..\Src\RCommon.FluentValidation\RCommon.FluentValidation.csproj" />
<ProjectReference Include="..\..\..\Src\RCommon.Mediatr\RCommon.MediatR.csproj" />
<ProjectReference Include="..\..\..\Src\RCommon.SendGrid\RCommon.SendGrid.csproj" />
<ProjectReference Include="..\HR.LeaveManagement.Application\HR.LeaveManagement.Application.csproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ private Task HandleExceptionAsync(HttpContext context, Exception exception)
case BadRequestException badRequestException:
statusCode = HttpStatusCode.BadRequest;
break;
case ValidationException validationException:
case RCommon.ApplicationServices.Validation.ValidationException validationException:
statusCode = HttpStatusCode.BadRequest;
result = JsonConvert.SerializeObject(validationException.Errors);
break;
Expand Down
8 changes: 7 additions & 1 deletion Examples/CleanWithCQRS/HR.LeaveManagement.API/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
using HR.LeaveManagement.Application.Features.LeaveTypes.Requests.Queries;
using HR.LeaveManagement.Application.Features.LeaveTypes.Handlers.Queries;
using HR.LeaveManagement.Application.DTOs.LeaveType;
using RCommon.ApplicationServices;
using RCommon.FluentValidation;

var builder = WebApplication.CreateBuilder(args);

Expand Down Expand Up @@ -56,7 +58,7 @@
mediator.AddRequest<GetLeaveAllocationDetailRequest, LeaveAllocationDto, GetLeaveAllocationDetailRequestHandler>();
mediator.AddRequest<GetLeaveAllocationListRequest, List<LeaveAllocationDto>, GetLeaveAllocationListRequestHandler>();
mediator.AddRequest<CreateLeaveAllocationCommand, BaseCommandResponse, CreateLeaveAllocationCommandHandler>();
mediator.AddRequest<DeleteLeaveAllocationCommand, DeleteLeaveAllocationCommandHandler>();
mediator.AddRequest<DeleteLeaveAllocationCommand, DeleteLeaveAllocationCommandHandler>();
mediator.AddRequest<UpdateLeaveAllocationCommand, UpdateLeaveAllocationCommandHandler>();
mediator.AddRequest<CreateLeaveRequestCommand, BaseCommandResponse, CreateLeaveRequestCommandHandler>();
mediator.AddRequest<DeleteLeaveRequestCommand, DeleteLeaveRequestCommandHandler>();
Expand Down Expand Up @@ -96,6 +98,10 @@
options.AutoCompleteScope = true;
options.DefaultIsolation = IsolationLevel.ReadCommitted;
});
})
.WithValidation<FluentValidationBuilder>(validation =>
{
validation.AddValidatorsFromAssemblyContaining(typeof(ApplicationServicesRegistration));
});

// Add services to the container.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

<ItemGroup>
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Bogus" Version="35.5.0" />
<PackageReference Include="Bogus" Version="35.5.1" />
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using HR.LeaveManagement.Domain;
using Moq;
using NUnit.Framework;
using RCommon.ApplicationServices.Validation;
using RCommon.Persistence;
using RCommon.Persistence.Crud;
using Shouldly;
Expand Down Expand Up @@ -42,9 +43,10 @@ public CreateLeaveTypeCommandHandlerTests()

var testData = new List<LeaveType>();
var mock = new Mock<IGraphRepository<LeaveType>>();
var validationMock = new Mock<IValidationService>();
mock.Setup(x => x.AddAsync(TestDataActions.CreateLeaveTypeStub(), CancellationToken.None))
.Returns(() => Task.FromResult(new BaseCommandResponse()));
_handler = new CreateLeaveTypeCommandHandler(_mapper, mock.Object);
_handler = new CreateLeaveTypeCommandHandler(_mapper, mock.Object, validationMock.Object);

_leaveTypeDto = new CreateLeaveTypeDto
{
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using RCommon.Persistence;
using HR.LeaveManagement.Domain.Specifications;
using RCommon.Persistence.Crud;
using RCommon.ApplicationServices.Validation;

namespace HR.LeaveManagement.Application.Features.LeaveAllocations.Handlers.Commands
{
Expand All @@ -25,25 +26,27 @@ public class CreateLeaveAllocationCommandHandler : IAppRequestHandler<CreateLeav
private readonly IGraphRepository<LeaveAllocation> _leaveAllocationRepository;
private readonly IUserService _userService;
private readonly IMapper _mapper;
private readonly IValidationService _validationService;

public CreateLeaveAllocationCommandHandler(IGraphRepository<LeaveType> leaveTypeRepository,
IGraphRepository<LeaveAllocation> leaveAllocationRepository,
IUserService userService,
IMapper mapper)
IMapper mapper,
IValidationService validationService)
{
this._leaveTypeRepository = leaveTypeRepository;
this._leaveAllocationRepository = leaveAllocationRepository;
this._leaveAllocationRepository.DataStoreName = DataStoreNamesConst.LeaveManagement;
this._leaveTypeRepository.DataStoreName = DataStoreNamesConst.LeaveManagement;
this._userService = userService;
_mapper = mapper;
_validationService = validationService;
}

public async Task<BaseCommandResponse> HandleAsync(CreateLeaveAllocationCommand request, CancellationToken cancellationToken)
{
var response = new BaseCommandResponse();
var validator = new CreateLeaveAllocationDtoValidator(_leaveTypeRepository);
var validationResult = await validator.ValidateAsync(request.LeaveAllocationDto);
var validationResult = await _validationService.ValidateAsync(request.LeaveAllocationDto);

if (validationResult.IsValid == false)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using System.Threading.Tasks;
using RCommon.Persistence;
using RCommon.Persistence.Crud;
using RCommon.ApplicationServices.Validation;

namespace HR.LeaveManagement.Application.Features.LeaveAllocations.Handlers.Commands
{
Expand All @@ -20,25 +21,27 @@ public class UpdateLeaveAllocationCommandHandler : IAppRequestHandler<UpdateLeav
private readonly IGraphRepository<LeaveAllocation> _leaveAllocationRepository;
private readonly IReadOnlyRepository<LeaveType> _leaveTypeRepository;
private readonly IMapper _mapper;
private readonly IValidationService _validationService;

public UpdateLeaveAllocationCommandHandler(IGraphRepository<LeaveAllocation> leaveAllocationRepository,
IReadOnlyRepository<LeaveType> leaveTypeRepository,
IMapper mapper)
IMapper mapper,
IValidationService validationService)
{
this._leaveAllocationRepository = leaveAllocationRepository;
this._leaveTypeRepository = leaveTypeRepository;
this._leaveAllocationRepository.DataStoreName = DataStoreNamesConst.LeaveManagement;
this._leaveTypeRepository.DataStoreName = DataStoreNamesConst.LeaveManagement;
_mapper = mapper;
_validationService = validationService;
}

public async Task HandleAsync(UpdateLeaveAllocationCommand request, CancellationToken cancellationToken)
{
var validator = new UpdateLeaveAllocationDtoValidator(this._leaveTypeRepository);
var validationResult = await validator.ValidateAsync(request.LeaveAllocationDto);
var validationResult = await _validationService.ValidateAsync(request.LeaveAllocationDto);

if (validationResult.IsValid == false)
throw new ValidationException(validationResult);
throw new ValidationException(validationResult.Errors);

var leaveAllocation = await _leaveAllocationRepository.FindAsync(request.LeaveAllocationDto.Id);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using HR.LeaveManagement.Application.Contracts.Identity;
using HR.LeaveManagement.Domain;
using HR.LeaveManagement.Application.Constants;
using RCommon.Persistence;
using System.Collections;
using RCommon.Persistence.Crud;
using Microsoft.AspNetCore.Http;

namespace HR.LeaveManagement.Application.Features.LeaveAllocations.Handlers.Queries
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
using Microsoft.Extensions.Options;
using RCommon.Emailing.SendGrid;
using RCommon.Persistence.Crud;
using RCommon.ApplicationServices.Validation;

namespace HR.LeaveManagement.Application.Features.LeaveRequests.Handlers.Commands
{
Expand All @@ -32,6 +33,7 @@ public class CreateLeaveRequestCommandHandler : IAppRequestHandler<CreateLeaveRe
private readonly ICurrentUser _currentUser;
private readonly IOptions<SendGridEmailSettings> _emailSettings;
private readonly IMapper _mapper;
private readonly IValidationService _validationService;
private readonly IReadOnlyRepository<LeaveType> _leaveTypeRepository;
private readonly IGraphRepository<LeaveAllocation> _leaveAllocationRepository;
private readonly IGraphRepository<LeaveRequest> _leaveRequestRepository;
Expand All @@ -43,7 +45,8 @@ public CreateLeaveRequestCommandHandler(
IEmailService emailSender,
ICurrentUser currentUser,
IOptions<SendGridEmailSettings> emailSettings,
IMapper mapper)
IMapper mapper,
IValidationService validationService)
{
_leaveTypeRepository = leaveTypeRepository;
_leaveAllocationRepository = leaveAllocationRepository;
Expand All @@ -55,29 +58,27 @@ public CreateLeaveRequestCommandHandler(
this._currentUser = currentUser;
_emailSettings=emailSettings;
_mapper = mapper;
_validationService = validationService;
}

public async Task<BaseCommandResponse> HandleAsync(CreateLeaveRequestCommand request, CancellationToken cancellationToken)
{
var response = new BaseCommandResponse();
var validator = new CreateLeaveRequestDtoValidator(_leaveTypeRepository);
var validationResult = await validator.ValidateAsync(request.LeaveRequestDto);
var validationResult = await _validationService.ValidateAsync(request.LeaveRequestDto);
var userId = _currentUser.FindClaimValue(CustomClaimTypes.Uid);
//_httpContextAccessor.HttpContext.User.Claims.FirstOrDefault(
//q => q.Type == CustomClaimTypes.Uid)?.Value;

var allocation = _leaveAllocationRepository.FirstOrDefault(x=>x.EmployeeId == userId && x.LeaveTypeId == request.LeaveRequestDto.LeaveTypeId);
if(allocation is null)
{
validationResult.Errors.Add(new FluentValidation.Results.ValidationFailure(nameof(request.LeaveRequestDto.LeaveTypeId),
validationResult.Errors.Add(new ValidationFault(nameof(request.LeaveRequestDto.LeaveTypeId),
"You do not have any allocations for this leave type."));
}
else
{
int daysRequested = (int)(request.LeaveRequestDto.EndDate - request.LeaveRequestDto.StartDate).TotalDays;
if (daysRequested > allocation.NumberOfDays)
{
validationResult.Errors.Add(new FluentValidation.Results.ValidationFailure(
validationResult.Errors.Add(new ValidationFault(
nameof(request.LeaveRequestDto.EndDate), "You do not have enough days for this request"));
}
}
Expand All @@ -102,7 +103,6 @@ public async Task<BaseCommandResponse> HandleAsync(CreateLeaveRequestCommand req
try
{
var emailAddress = _currentUser.FindClaimValue(ClaimTypes.Email);
//_httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.Email).Value;

var email = new MailMessage(new MailAddress(this._emailSettings.Value.FromEmailDefault, this._emailSettings.Value.FromNameDefault),
new MailAddress(emailAddress))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using System.Threading.Tasks;
using RCommon.Persistence;
using RCommon.Persistence.Crud;
using RCommon.ApplicationServices.Validation;

namespace HR.LeaveManagement.Application.Features.LeaveRequests.Handlers.Commands
{
Expand All @@ -22,12 +23,14 @@ public class UpdateLeaveRequestCommandHandler : IAppRequestHandler<UpdateLeaveRe
private readonly IReadOnlyRepository<LeaveType> _leaveTypeRepository;
private readonly IGraphRepository<LeaveAllocation> _leaveAllocationRepository;
private readonly IMapper _mapper;
private readonly IValidationService _validationService;

public UpdateLeaveRequestCommandHandler(
IGraphRepository<LeaveRequest> leaveRequestRepository,
IReadOnlyRepository<LeaveType> leaveTypeRepository,
IGraphRepository<LeaveAllocation> leaveAllocationRepository,
IMapper mapper)
IMapper mapper,
IValidationService validationService)
{
this._leaveRequestRepository = leaveRequestRepository;
_leaveTypeRepository = leaveTypeRepository;
Expand All @@ -36,6 +39,7 @@ public UpdateLeaveRequestCommandHandler(
this._leaveTypeRepository.DataStoreName = DataStoreNamesConst.LeaveManagement;
this._leaveRequestRepository.DataStoreName = DataStoreNamesConst.LeaveManagement;
_mapper = mapper;
_validationService = validationService;
}

public async Task HandleAsync(UpdateLeaveRequestCommand request, CancellationToken cancellationToken)
Expand All @@ -47,10 +51,9 @@ public async Task HandleAsync(UpdateLeaveRequestCommand request, CancellationTok

if (request.LeaveRequestDto != null)
{
var validator = new UpdateLeaveRequestDtoValidator(_leaveTypeRepository);
var validationResult = await validator.ValidateAsync(request.LeaveRequestDto);
var validationResult = await _validationService.ValidateAsync(request.LeaveRequestDto);
if (validationResult.IsValid == false)
throw new ValidationException(validationResult);
throw new ValidationException(validationResult.Errors);

_mapper.Map(request.LeaveRequestDto, leaveRequest);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,28 @@
using System.Linq;
using RCommon.Persistence;
using RCommon.Persistence.Crud;
using RCommon.ApplicationServices.Validation;

namespace HR.LeaveManagement.Application.Features.LeaveTypes.Handlers.Commands
{
public class CreateLeaveTypeCommandHandler : IAppRequestHandler<CreateLeaveTypeCommand, BaseCommandResponse>
{
private readonly IMapper _mapper;
private readonly IGraphRepository<LeaveType> _leaveTypeRepository;
public CreateLeaveTypeCommandHandler(IMapper mapper, IGraphRepository<LeaveType> leaveTypeRepository)
private readonly IValidationService _validationService;

public CreateLeaveTypeCommandHandler(IMapper mapper, IGraphRepository<LeaveType> leaveTypeRepository, IValidationService validationService)
{
_mapper = mapper;
_leaveTypeRepository = leaveTypeRepository;
_validationService = validationService;
this._leaveTypeRepository.DataStoreName = DataStoreNamesConst.LeaveManagement;
}

public async Task<BaseCommandResponse> HandleAsync(CreateLeaveTypeCommand request, CancellationToken cancellationToken)
{
var response = new BaseCommandResponse();
var validator = new CreateLeaveTypeDtoValidator();
var validationResult = await validator.ValidateAsync(request.LeaveTypeDto);
var validationResult = await _validationService.ValidateAsync(request.LeaveTypeDto);

if (validationResult.IsValid == false)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,30 @@
using System.Threading.Tasks;
using RCommon.Persistence;
using RCommon.Persistence.Crud;
using RCommon.ApplicationServices.Validation;

namespace HR.LeaveManagement.Application.Features.LeaveTypes.Handlers.Commands
{
public class UpdateLeaveTypeCommandHandler : IAppRequestHandler<UpdateLeaveTypeCommand>
{
private readonly IMapper _mapper;
private readonly IGraphRepository<LeaveType> _leaveTypeRepository;
private readonly IValidationService _validationService;

public UpdateLeaveTypeCommandHandler(IMapper mapper, IGraphRepository<LeaveType> leaveTypeRepository)
public UpdateLeaveTypeCommandHandler(IMapper mapper, IGraphRepository<LeaveType> leaveTypeRepository, IValidationService validationService)
{
_mapper = mapper;
_leaveTypeRepository = leaveTypeRepository;
_validationService = validationService;
this._leaveTypeRepository.DataStoreName = DataStoreNamesConst.LeaveManagement;
}

public async Task HandleAsync(UpdateLeaveTypeCommand request, CancellationToken cancellationToken)
{
var validator = new UpdateLeaveTypeDtoValidator();
var validationResult = await validator.ValidateAsync(request.LeaveTypeDto);
var validationResult = await _validationService.ValidateAsync(request.LeaveTypeDto);

if (validationResult.IsValid == false)
throw new ValidationException(validationResult);
throw new ValidationException(validationResult.Errors);

var leaveType = await _leaveTypeRepository.FindAsync(request.LeaveTypeDto.Id);

Expand Down
Loading

0 comments on commit 6ee1d82

Please sign in to comment.