diff --git a/src/Services/Ordering/Ordering.API/Application/Decorators/ValidatorDecorator.cs b/src/Services/Ordering/Ordering.API/Application/Decorators/ValidatorDecorator.cs new file mode 100644 index 000000000..5bdff8330 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Application/Decorators/ValidatorDecorator.cs @@ -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 + : IAsyncRequestHandler + where TRequest : IAsyncRequest + { + private readonly IAsyncRequestHandler _inner; + private readonly IValidator[] _validators; + + + public ValidatorDecorator( + IAsyncRequestHandler inner, + IValidator[] validators) + { + _inner = inner; + _validators = validators; + } + + public async Task 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; + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Application/Validations/CreateOrderCommandValidator.cs b/src/Services/Ordering/Ordering.API/Application/Validations/CreateOrderCommandValidator.cs new file mode 100644 index 000000000..449f95df5 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Application/Validations/CreateOrderCommandValidator.cs @@ -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 + { + 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 orderItems) + { + return orderItems.Any(); + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Application/Validations/IdentifierCommandValidator.cs b/src/Services/Ordering/Ordering.API/Application/Validations/IdentifierCommandValidator.cs new file mode 100644 index 000000000..44b374ee6 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Application/Validations/IdentifierCommandValidator.cs @@ -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> + { + public IdentifierCommandValidator() + { + RuleFor(customer => customer.Id).NotEmpty(); + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs b/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs index a6864e0ef..e741644f3 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs @@ -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(context => { @@ -44,9 +53,16 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Autof return t => (IEnumerable)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"); } } } diff --git a/src/Services/Ordering/Ordering.API/Ordering.API.csproj b/src/Services/Ordering/Ordering.API/Ordering.API.csproj index d0102c19f..d7dd2bc1a 100644 --- a/src/Services/Ordering/Ordering.API/Ordering.API.csproj +++ b/src/Services/Ordering/Ordering.API/Ordering.API.csproj @@ -28,6 +28,8 @@ + +