Browse Source

Added decorator for validation in commands

Added Validation for orders and identified commands
pull/133/head
Ramón Tomás 7 years ago
parent
commit
94c7fd31e1
5 changed files with 124 additions and 5 deletions
  1. +45
    -0
      src/Services/Ordering/Ordering.API/Application/Decorators/ValidatorDecorator.cs
  2. +39
    -0
      src/Services/Ordering/Ordering.API/Application/Validations/CreateOrderCommandValidator.cs
  3. +17
    -0
      src/Services/Ordering/Ordering.API/Application/Validations/IdentifierCommandValidator.cs
  4. +21
    -5
      src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs
  5. +2
    -0
      src/Services/Ordering/Ordering.API/Ordering.API.csproj

+ 45
- 0
src/Services/Ordering/Ordering.API/Application/Decorators/ValidatorDecorator.cs View File

@ -0,0 +1,45 @@
using FluentValidation;
using MediatR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Ordering.API.Application.Decorators
{
public class ValidatorDecorator<TRequest, TResponse>
: IAsyncRequestHandler<TRequest, TResponse>
where TRequest : IAsyncRequest<TResponse>
{
private readonly IAsyncRequestHandler<TRequest, TResponse> _inner;
private readonly IValidator<TRequest>[] _validators;
public ValidatorDecorator(
IAsyncRequestHandler<TRequest, TResponse> inner,
IValidator<TRequest>[] validators)
{
_inner = inner;
_validators = validators;
}
public async Task<TResponse> Handle(TRequest message)
{
var failures = _validators
.Select(v => v.Validate(message))
.SelectMany(result => result.Errors)
.Where(error => error != null)
.ToList();
if (failures.Any())
{
throw new ValidationException(
$"Command Validation Errors for type {typeof(TRequest).Name}", failures);
}
var response = await _inner.Handle(message);
return response;
}
}
}

+ 39
- 0
src/Services/Ordering/Ordering.API/Application/Validations/CreateOrderCommandValidator.cs View File

@ -0,0 +1,39 @@
using FluentValidation;
using MediatR;
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using static Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands.CreateOrderCommand;
namespace Ordering.API.Application.Validations
{
public class CreateOrderCommandValidator : AbstractValidator<CreateOrderCommand>
{
public CreateOrderCommandValidator()
{
RuleFor(order => order.City).NotEmpty();
RuleFor(order => order.Street).NotEmpty();
RuleFor(order => order.State).NotEmpty();
RuleFor(order => order.Country).NotEmpty();
RuleFor(order => order.ZipCode).NotEmpty();
RuleFor(order => order.CardNumber).NotEmpty().Length(12, 19);
RuleFor(order => order.CardHolderName).NotEmpty();
RuleFor(order => order.CardExpiration).NotEmpty().Must(BeValidExpirationDate).WithMessage("Please specify a valid card expiration date");
RuleFor(order => order.CardSecurityNumber).NotEmpty().Length(3);
RuleFor(order => order.CardTypeId).NotEmpty();
RuleFor(order => order.OrderItems).Must(ContainOrderItems).WithMessage("No order items found");
}
private bool BeValidExpirationDate(DateTime dateTime)
{
return dateTime >= DateTime.UtcNow;
}
private bool ContainOrderItems(IEnumerable<OrderItemDTO> orderItems)
{
return orderItems.Any();
}
}
}

+ 17
- 0
src/Services/Ordering/Ordering.API/Application/Validations/IdentifierCommandValidator.cs View File

@ -0,0 +1,17 @@
using FluentValidation;
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Ordering.API.Application.Validations
{
public class IdentifierCommandValidator : AbstractValidator<IdentifiedCommand<CreateOrderCommand,bool>>
{
public IdentifierCommandValidator()
{
RuleFor(customer => customer.Id).NotEmpty();
}
}
}

+ 21
- 5
src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs View File

@ -1,9 +1,12 @@
using Autofac;
using Autofac.Core;
using FluentValidation;
using MediatR;
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Decorators;
using Ordering.API.Application.Decorators;
using Ordering.API.Application.DomainEventHandlers.OrderStartedEvent;
using Ordering.API.Application.Validations;
using Ordering.Domain.Events;
using System.Collections.Generic;
using System.Linq;
@ -24,11 +27,17 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Autof
.Where(i => i.IsClosedTypeOf(typeof(IAsyncRequestHandler<,>)))
.Select(i => new KeyedService("IAsyncRequestHandler", i)));
// Register all the Domain Event Handler classes (they implement IAsyncNotificationHandler<>) in assembly holding the Domain Events
// Register all the event classes (they implement IAsyncNotificationHandler) in assembly holding the Commands
builder.RegisterAssemblyTypes(typeof(ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler).GetTypeInfo().Assembly)
.As(o => o.GetInterfaces()
.Where(i => i.IsClosedTypeOf(typeof(IAsyncNotificationHandler<>)))
.Select(i => new KeyedService("IAsyncNotificationHandler", i)));
builder
.RegisterAssemblyTypes(typeof(ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler).GetTypeInfo().Assembly)
.Where(t => t.IsClosedTypeOf(typeof(IAsyncNotificationHandler<>)))
.AsImplementedInterfaces();
.RegisterAssemblyTypes(typeof(CreateOrderCommandValidator).GetTypeInfo().Assembly)
.Where(t => t.IsClosedTypeOf(typeof(IValidator<>)))
.AsImplementedInterfaces();
builder.Register<SingleInstanceFactory>(context =>
{
@ -44,9 +53,16 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Autof
return t => (IEnumerable<object>)componentContext.Resolve(typeof(IEnumerable<>).MakeGenericType(t));
});
builder.RegisterGenericDecorator(typeof(LogDecorator<,>),
typeof(IAsyncRequestHandler<,>),
"IAsyncRequestHandler");
"IAsyncRequestHandler")
.Keyed("handlerDecorator", typeof(IAsyncRequestHandler<,>));
builder.RegisterGenericDecorator(typeof(ValidatorDecorator<,>),
typeof(IAsyncRequestHandler<,>),
fromKey: "handlerDecorator");
}
}
}

+ 2
- 0
src/Services/Ordering/Ordering.API/Ordering.API.csproj View File

@ -28,6 +28,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="FluentValidation.AspNetCore" Version="6.4.0" />
<PackageReference Include="FluentValidation.MVC6" Version="6.4.0" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="1.1.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.0" />


Loading…
Cancel
Save