IdentifiedCommand replaced with DuplicateCommandBehavior
This commit is contained in:
parent
6d9a9e0cbe
commit
f60d161ce7
@ -0,0 +1,37 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||||
|
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Ordering.API.Infrastructure.Behaviors
|
||||||
|
{
|
||||||
|
public class DuplicateCommandBehavior<TCommand, TResponse> : IPipelineBehavior<TCommand, TResponse>
|
||||||
|
where TCommand : ICommand
|
||||||
|
{
|
||||||
|
private readonly IMediator _mediator;
|
||||||
|
private readonly IRequestManager _requestManager;
|
||||||
|
|
||||||
|
public DuplicateCommandBehavior(IMediator mediator, IRequestManager requestManager)
|
||||||
|
{
|
||||||
|
_mediator = mediator;
|
||||||
|
_requestManager = requestManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<TResponse> IPipelineBehavior<TCommand, TResponse>.Handle(TCommand command, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
|
||||||
|
{
|
||||||
|
var alreadyExists = await _requestManager.ExistAsync(command.CommandId);
|
||||||
|
if (alreadyExists)
|
||||||
|
{
|
||||||
|
var duplicateCommand = new DuplicateCommandResponse<TCommand, TResponse>.DuplicateCommand<TCommand>(command);
|
||||||
|
return await _mediator.Send(duplicateCommand, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _requestManager.CreateRequestForCommandAsync<TCommand>(command.CommandId);
|
||||||
|
|
||||||
|
var response = await next();
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,11 +4,13 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||||
|
|
||||||
namespace Ordering.API.Application.Commands
|
namespace Ordering.API.Application.Commands
|
||||||
{
|
{
|
||||||
public class CancelOrderCommand : IRequest<bool>
|
public class CancelOrderCommand : ICommand, IRequest<bool>
|
||||||
{
|
{
|
||||||
|
public Guid CommandId { get; set; }
|
||||||
|
|
||||||
[DataMember]
|
[DataMember]
|
||||||
public int OrderNumber { get; private set; }
|
public int OrderNumber { get; private set; }
|
||||||
|
@ -38,15 +38,12 @@ namespace Ordering.API.Application.Commands
|
|||||||
|
|
||||||
|
|
||||||
// Use for Idempotency in Command process
|
// Use for Idempotency in Command process
|
||||||
public class CancelOrderIdentifiedCommandHandler : IdentifiedCommandHandler<CancelOrderCommand, bool>
|
public class CancelOrderDuplicateCommand : DuplicateCommandResponse<CancelOrderCommand, bool>
|
||||||
{
|
{
|
||||||
public CancelOrderIdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager)
|
protected override Task<bool> CreateResponseForDuplicateCommand(CancelOrderCommand command)
|
||||||
{
|
{
|
||||||
}
|
// Ignore duplicate requests for processing order.
|
||||||
|
return Task.FromResult(true);
|
||||||
protected override bool CreateResultForDuplicateRequest()
|
|
||||||
{
|
|
||||||
return true; // Ignore duplicate requests for processing order.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,9 +18,12 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
|
|||||||
// https://msdn.microsoft.com/en-us/library/bb383979.aspx
|
// https://msdn.microsoft.com/en-us/library/bb383979.aspx
|
||||||
|
|
||||||
[DataContract]
|
[DataContract]
|
||||||
public class CreateOrderCommand
|
public class CreateOrderCommand : ICommand, IRequest<bool>
|
||||||
: IRequest<bool>
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public Guid CommandId { get; private set; }
|
||||||
|
|
||||||
[DataMember]
|
[DataMember]
|
||||||
private readonly List<OrderItemDTO> _orderItems;
|
private readonly List<OrderItemDTO> _orderItems;
|
||||||
|
|
||||||
@ -68,10 +71,11 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
|
|||||||
_orderItems = new List<OrderItemDTO>();
|
_orderItems = new List<OrderItemDTO>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public CreateOrderCommand(List<BasketItem> basketItems, string userId, string userName, string city, string street, string state, string country, string zipcode,
|
public CreateOrderCommand(Guid id, List<BasketItem> basketItems, string userId, string userName, string city, string street, string state, string country, string zipcode,
|
||||||
string cardNumber, string cardHolderName, DateTime cardExpiration,
|
string cardNumber, string cardHolderName, DateTime cardExpiration,
|
||||||
string cardSecurityNumber, int cardTypeId) : this()
|
string cardSecurityNumber, int cardTypeId) : this()
|
||||||
{
|
{
|
||||||
|
CommandId = id;
|
||||||
_orderItems = basketItems.ToOrderItemsDTO().ToList();
|
_orderItems = basketItems.ToOrderItemsDTO().ToList();
|
||||||
UserId = userId;
|
UserId = userId;
|
||||||
UserName = userName;
|
UserName = userName;
|
||||||
|
@ -47,15 +47,12 @@
|
|||||||
|
|
||||||
|
|
||||||
// Use for Idempotency in Command process
|
// Use for Idempotency in Command process
|
||||||
public class CreateOrderIdentifiedCommandHandler : IdentifiedCommandHandler<CreateOrderCommand, bool>
|
public class CreateOrderDuplicateCommand : DuplicateCommandResponse<CreateOrderCommand, bool>
|
||||||
{
|
{
|
||||||
public CreateOrderIdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager)
|
protected override Task<bool> CreateResponseForDuplicateCommand(CreateOrderCommand command)
|
||||||
{
|
{
|
||||||
}
|
// Ignore duplicate requests for creating order.
|
||||||
|
return Task.FromResult(true);
|
||||||
protected override bool CreateResultForDuplicateRequest()
|
|
||||||
{
|
|
||||||
return true; // Ignore duplicate requests for creating order.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,15 +10,17 @@ using static Microsoft.eShopOnContainers.Services.Ordering.API.Application.Comma
|
|||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
|
||||||
{
|
{
|
||||||
public class CreateOrderDraftCommand : IRequest<OrderDraftDTO>
|
public class CreateOrderDraftCommand : ICommand, IRequest<OrderDraftDTO>
|
||||||
{
|
{
|
||||||
|
public Guid CommandId { get; }
|
||||||
|
|
||||||
public string BuyerId { get; private set; }
|
public string BuyerId { get; private set; }
|
||||||
|
|
||||||
public IEnumerable<BasketItem> Items { get; private set; }
|
public IEnumerable<BasketItem> Items { get; private set; }
|
||||||
|
|
||||||
public CreateOrderDraftCommand(string buyerId, IEnumerable<BasketItem> items)
|
public CreateOrderDraftCommand(Guid id, string buyerId, IEnumerable<BasketItem> items)
|
||||||
{
|
{
|
||||||
|
CommandId = id;
|
||||||
BuyerId = buyerId;
|
BuyerId = buyerId;
|
||||||
Items = items;
|
Items = items;
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using static Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands.CreateOrderCommand;
|
|
||||||
|
|
||||||
// Regular CommandHandler
|
// Regular CommandHandler
|
||||||
public class CreateOrderDraftCommandHandler
|
public class CreateOrderDraftCommandHandler
|
||||||
@ -44,14 +43,14 @@
|
|||||||
|
|
||||||
public class OrderDraftDTO
|
public class OrderDraftDTO
|
||||||
{
|
{
|
||||||
public IEnumerable<OrderItemDTO> OrderItems { get; set; }
|
public IEnumerable<CreateOrderCommand.OrderItemDTO> OrderItems { get; set; }
|
||||||
public decimal Total { get; set; }
|
public decimal Total { get; set; }
|
||||||
|
|
||||||
public static OrderDraftDTO FromOrder(Order order)
|
public static OrderDraftDTO FromOrder(Order order)
|
||||||
{
|
{
|
||||||
return new OrderDraftDTO()
|
return new OrderDraftDTO()
|
||||||
{
|
{
|
||||||
OrderItems = order.OrderItems.Select(oi => new OrderItemDTO
|
OrderItems = order.OrderItems.Select(oi => new CreateOrderCommand.OrderItemDTO
|
||||||
{
|
{
|
||||||
Discount = oi.GetCurrentDiscount(),
|
Discount = oi.GetCurrentDiscount(),
|
||||||
ProductId = oi.ProductId,
|
ProductId = oi.ProductId,
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
|
||||||
|
{
|
||||||
|
public abstract class DuplicateCommandResponse<TCommand, TResponse> : IRequestHandler<DuplicateCommandResponse<TCommand, TResponse>.DuplicateCommand<TCommand>, TResponse>
|
||||||
|
{
|
||||||
|
public class DuplicateCommand<TCommand> : IRequest<TResponse>
|
||||||
|
{
|
||||||
|
public DuplicateCommand(TCommand command)
|
||||||
|
{
|
||||||
|
Command = command;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TCommand Command { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates the result value to return if a previous request was found
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected abstract Task<TResponse> CreateResponseForDuplicateCommand(TCommand command);
|
||||||
|
|
||||||
|
Task<TResponse> IRequestHandler<DuplicateCommand<TCommand>, TResponse>.Handle(DuplicateCommand<TCommand> request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return CreateResponseForDuplicateCommand(request.Command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
|
||||||
|
{
|
||||||
|
public interface ICommand
|
||||||
|
{
|
||||||
|
Guid CommandId { get; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,17 +0,0 @@
|
|||||||
using MediatR;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
|
|
||||||
{
|
|
||||||
public class IdentifiedCommand<T, R> : IRequest<R>
|
|
||||||
where T : IRequest<R>
|
|
||||||
{
|
|
||||||
public T Command { get; }
|
|
||||||
public Guid Id { get; }
|
|
||||||
public IdentifiedCommand(T command, Guid id)
|
|
||||||
{
|
|
||||||
Command = command;
|
|
||||||
Id = id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
using MediatR;
|
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Provides a base implementation for handling duplicate request and ensuring idempotent updates, in the cases where
|
|
||||||
/// a requestid sent by client is used to detect duplicate requests.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">Type of the command handler that performs the operation if request is not duplicated</typeparam>
|
|
||||||
/// <typeparam name="R">Return value of the inner command handler</typeparam>
|
|
||||||
public class IdentifiedCommandHandler<T, R> : IRequestHandler<IdentifiedCommand<T, R>, R>
|
|
||||||
where T : IRequest<R>
|
|
||||||
{
|
|
||||||
private readonly IMediator _mediator;
|
|
||||||
private readonly IRequestManager _requestManager;
|
|
||||||
|
|
||||||
public IdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager)
|
|
||||||
{
|
|
||||||
_mediator = mediator;
|
|
||||||
_requestManager = requestManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates the result value to return if a previous request was found
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
protected virtual R CreateResultForDuplicateRequest()
|
|
||||||
{
|
|
||||||
return default(R);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This method handles the command. It just ensures that no other request exists with the same ID, and if this is the case
|
|
||||||
/// just enqueues the original inner command.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="message">IdentifiedCommand which contains both original command & request ID</param>
|
|
||||||
/// <returns>Return value of inner command or default value if request same ID was found</returns>
|
|
||||||
public async Task<R> Handle(IdentifiedCommand<T, R> message, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var alreadyExists = await _requestManager.ExistAsync(message.Id);
|
|
||||||
if (alreadyExists)
|
|
||||||
{
|
|
||||||
return CreateResultForDuplicateRequest();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await _requestManager.CreateRequestForCommandAsync<T>(message.Id);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Send the embeded business command to mediator so it runs its related CommandHandler
|
|
||||||
var result = await _mediator.Send(message.Command);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return default(R);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,11 +4,13 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||||
|
|
||||||
namespace Ordering.API.Application.Commands
|
namespace Ordering.API.Application.Commands
|
||||||
{
|
{
|
||||||
public class ShipOrderCommand : IRequest<bool>
|
public class ShipOrderCommand : ICommand, IRequest<bool>
|
||||||
{
|
{
|
||||||
|
public Guid CommandId { get; set; }
|
||||||
|
|
||||||
[DataMember]
|
[DataMember]
|
||||||
public int OrderNumber { get; private set; }
|
public int OrderNumber { get; private set; }
|
||||||
|
@ -38,15 +38,12 @@ namespace Ordering.API.Application.Commands
|
|||||||
|
|
||||||
|
|
||||||
// Use for Idempotency in Command process
|
// Use for Idempotency in Command process
|
||||||
public class ShipOrderIdentifiedCommandHandler : IdentifiedCommandHandler<ShipOrderCommand, bool>
|
public class ShipOrderDuplicateCommand : DuplicateCommandResponse<ShipOrderCommand, bool>
|
||||||
{
|
{
|
||||||
public ShipOrderIdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager)
|
protected override Task<bool> CreateResponseForDuplicateCommand(ShipOrderCommand command)
|
||||||
{
|
{
|
||||||
}
|
// Ignore duplicate requests for creating order.
|
||||||
|
return Task.FromResult(true);
|
||||||
protected override bool CreateResultForDuplicateRequest()
|
|
||||||
{
|
|
||||||
return true; // Ignore duplicate requests for processing order.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,13 +41,13 @@ namespace Ordering.API.Application.IntegrationEvents.EventHandling
|
|||||||
|
|
||||||
if (eventMsg.RequestId != Guid.Empty)
|
if (eventMsg.RequestId != Guid.Empty)
|
||||||
{
|
{
|
||||||
var createOrderCommand = new CreateOrderCommand(eventMsg.Basket.Items, eventMsg.UserId, eventMsg.UserName, eventMsg.City, eventMsg.Street,
|
var createOrderCommand = new CreateOrderCommand(eventMsg.RequestId, eventMsg.Basket.Items, eventMsg.UserId, eventMsg.UserName, eventMsg.City, eventMsg.Street,
|
||||||
eventMsg.State, eventMsg.Country, eventMsg.ZipCode,
|
eventMsg.State, eventMsg.Country, eventMsg.ZipCode,
|
||||||
eventMsg.CardNumber, eventMsg.CardHolderName, eventMsg.CardExpiration,
|
eventMsg.CardNumber, eventMsg.CardHolderName, eventMsg.CardExpiration,
|
||||||
eventMsg.CardSecurityNumber, eventMsg.CardTypeId);
|
eventMsg.CardSecurityNumber, eventMsg.CardTypeId);
|
||||||
|
|
||||||
var requestCreateOrder = new IdentifiedCommand<CreateOrderCommand, bool>(createOrderCommand, eventMsg.RequestId);
|
//var requestCreateOrder = new IdentifiedCommand<CreateOrderCommand, bool>(createOrderCommand, eventMsg.RequestId);
|
||||||
result = await _mediator.Send(requestCreateOrder);
|
result = await _mediator.Send(createOrderCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.CreateLogger(nameof(UserCheckoutAcceptedIntegrationEventHandler))
|
_logger.CreateLogger(nameof(UserCheckoutAcceptedIntegrationEventHandler))
|
||||||
|
@ -11,6 +11,7 @@ namespace Ordering.API.Application.Validations
|
|||||||
{
|
{
|
||||||
public CancelOrderCommandValidator()
|
public CancelOrderCommandValidator()
|
||||||
{
|
{
|
||||||
|
RuleFor(command => command.CommandId).NotEmpty();
|
||||||
RuleFor(order => order.OrderNumber).NotEmpty().WithMessage("No orderId found");
|
RuleFor(order => order.OrderNumber).NotEmpty().WithMessage("No orderId found");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ namespace Ordering.API.Application.Validations
|
|||||||
{
|
{
|
||||||
public CreateOrderCommandValidator()
|
public CreateOrderCommandValidator()
|
||||||
{
|
{
|
||||||
|
RuleFor(command => command.CommandId).NotEmpty();
|
||||||
RuleFor(command => command.City).NotEmpty();
|
RuleFor(command => command.City).NotEmpty();
|
||||||
RuleFor(command => command.Street).NotEmpty();
|
RuleFor(command => command.Street).NotEmpty();
|
||||||
RuleFor(command => command.State).NotEmpty();
|
RuleFor(command => command.State).NotEmpty();
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
using FluentValidation;
|
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
|
||||||
|
|
||||||
namespace Ordering.API.Application.Validations
|
|
||||||
{
|
|
||||||
public class IdentifiedCommandValidator : AbstractValidator<IdentifiedCommand<CreateOrderCommand,bool>>
|
|
||||||
{
|
|
||||||
public IdentifiedCommandValidator()
|
|
||||||
{
|
|
||||||
RuleFor(command => command.Id).NotEmpty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -11,6 +11,7 @@ namespace Ordering.API.Application.Validations
|
|||||||
{
|
{
|
||||||
public ShipOrderCommandValidator()
|
public ShipOrderCommandValidator()
|
||||||
{
|
{
|
||||||
|
RuleFor(command => command.CommandId).NotEmpty();
|
||||||
RuleFor(order => order.OrderNumber).NotEmpty().WithMessage("No orderId found");
|
RuleFor(order => order.OrderNumber).NotEmpty().WithMessage("No orderId found");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,8 +38,9 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
|
|||||||
bool commandResult = false;
|
bool commandResult = false;
|
||||||
if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty)
|
if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty)
|
||||||
{
|
{
|
||||||
var requestCancelOrder = new IdentifiedCommand<CancelOrderCommand, bool>(command, guid);
|
command.CommandId = guid;
|
||||||
commandResult = await _mediator.Send(requestCancelOrder);
|
//var requestCancelOrder = new IdentifiedCommand<CancelOrderCommand, bool>(command, guid);
|
||||||
|
commandResult = await _mediator.Send(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
return commandResult ? (IActionResult)Ok() : (IActionResult)BadRequest();
|
return commandResult ? (IActionResult)Ok() : (IActionResult)BadRequest();
|
||||||
@ -55,8 +56,9 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
|
|||||||
bool commandResult = false;
|
bool commandResult = false;
|
||||||
if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty)
|
if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty)
|
||||||
{
|
{
|
||||||
var requestShipOrder = new IdentifiedCommand<ShipOrderCommand, bool>(command, guid);
|
command.CommandId = guid;
|
||||||
commandResult = await _mediator.Send(requestShipOrder);
|
//var requestShipOrder = new IdentifiedCommand<ShipOrderCommand, bool>(command, guid);
|
||||||
|
commandResult = await _mediator.Send(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
return commandResult ? (IActionResult)Ok() : (IActionResult)BadRequest();
|
return commandResult ? (IActionResult)Ok() : (IActionResult)BadRequest();
|
||||||
|
@ -42,6 +42,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Autof
|
|||||||
|
|
||||||
builder.RegisterGeneric(typeof(LoggingBehavior<,>)).As(typeof(IPipelineBehavior<,>));
|
builder.RegisterGeneric(typeof(LoggingBehavior<,>)).As(typeof(IPipelineBehavior<,>));
|
||||||
builder.RegisterGeneric(typeof(ValidatorBehavior<,>)).As(typeof(IPipelineBehavior<,>));
|
builder.RegisterGeneric(typeof(ValidatorBehavior<,>)).As(typeof(IPipelineBehavior<,>));
|
||||||
|
builder.RegisterGeneric(typeof(DuplicateCommandBehavior<,>)).As(typeof(IPipelineBehavior<,>));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,91 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace UnitTest.Ordering.Application
|
|
||||||
{
|
|
||||||
using global::Ordering.API.Application.Models;
|
|
||||||
using MediatR;
|
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
|
|
||||||
using Moq;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Xunit;
|
|
||||||
public class IdentifiedCommandHandlerTest
|
|
||||||
{
|
|
||||||
private readonly Mock<IRequestManager> _requestManager;
|
|
||||||
private readonly Mock<IMediator> _mediator;
|
|
||||||
|
|
||||||
public IdentifiedCommandHandlerTest()
|
|
||||||
{
|
|
||||||
_requestManager = new Mock<IRequestManager>();
|
|
||||||
_mediator = new Mock<IMediator>();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task Handler_sends_command_when_order_no_exists()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var fakeGuid = Guid.NewGuid();
|
|
||||||
var fakeOrderCmd = new IdentifiedCommand<CreateOrderCommand, bool>(FakeOrderRequest(), fakeGuid);
|
|
||||||
|
|
||||||
_requestManager.Setup(x => x.ExistAsync(It.IsAny<Guid>()))
|
|
||||||
.Returns(Task.FromResult(false));
|
|
||||||
|
|
||||||
_mediator.Setup(x => x.Send(It.IsAny<IRequest<bool>>(),default(System.Threading.CancellationToken)))
|
|
||||||
.Returns(Task.FromResult(true));
|
|
||||||
|
|
||||||
//Act
|
|
||||||
var handler = new IdentifiedCommandHandler<CreateOrderCommand, bool>(_mediator.Object, _requestManager.Object);
|
|
||||||
var cltToken = new System.Threading.CancellationToken();
|
|
||||||
var result = await handler.Handle(fakeOrderCmd, cltToken);
|
|
||||||
|
|
||||||
//Assert
|
|
||||||
Assert.True(result);
|
|
||||||
_mediator.Verify(x => x.Send(It.IsAny<IRequest<bool>>(), default(System.Threading.CancellationToken)), Times.Once());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task Handler_sends_no_command_when_order_already_exists()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var fakeGuid = Guid.NewGuid();
|
|
||||||
var fakeOrderCmd = new IdentifiedCommand<CreateOrderCommand, bool>(FakeOrderRequest(), fakeGuid);
|
|
||||||
|
|
||||||
_requestManager.Setup(x => x.ExistAsync(It.IsAny<Guid>()))
|
|
||||||
.Returns(Task.FromResult(true));
|
|
||||||
|
|
||||||
_mediator.Setup(x => x.Send(It.IsAny<IRequest<bool>>(), default(System.Threading.CancellationToken)))
|
|
||||||
.Returns(Task.FromResult(true));
|
|
||||||
|
|
||||||
//Act
|
|
||||||
var handler = new IdentifiedCommandHandler<CreateOrderCommand, bool>(_mediator.Object, _requestManager.Object);
|
|
||||||
var cltToken = new System.Threading.CancellationToken();
|
|
||||||
var result = await handler.Handle(fakeOrderCmd, cltToken);
|
|
||||||
|
|
||||||
//Assert
|
|
||||||
Assert.False(result);
|
|
||||||
_mediator.Verify(x => x.Send(It.IsAny<IRequest<bool>>(), default(System.Threading.CancellationToken)), Times.Never());
|
|
||||||
}
|
|
||||||
|
|
||||||
private CreateOrderCommand FakeOrderRequest(Dictionary<string, object> args = null)
|
|
||||||
{
|
|
||||||
return new CreateOrderCommand(
|
|
||||||
new List<BasketItem>(),
|
|
||||||
userId: args != null && args.ContainsKey("userId") ? (string)args["userId"] : null,
|
|
||||||
userName: args != null && args.ContainsKey("userName") ? (string)args["userName"] : null,
|
|
||||||
city: args != null && args.ContainsKey("city") ? (string)args["city"] : null,
|
|
||||||
street: args != null && args.ContainsKey("street") ? (string)args["street"] : null,
|
|
||||||
state: args != null && args.ContainsKey("state") ? (string)args["state"] : null,
|
|
||||||
country: args != null && args.ContainsKey("country") ? (string)args["country"] : null,
|
|
||||||
zipcode: args != null && args.ContainsKey("zipcode") ? (string)args["zipcode"] : null,
|
|
||||||
cardNumber: args != null && args.ContainsKey("cardNumber") ? (string)args["cardNumber"] : "1234",
|
|
||||||
cardExpiration: args != null && args.ContainsKey("cardExpiration") ? (DateTime)args["cardExpiration"] : DateTime.MinValue,
|
|
||||||
cardSecurityNumber: args != null && args.ContainsKey("cardSecurityNumber") ? (string)args["cardSecurityNumber"] : "123",
|
|
||||||
cardHolderName: args != null && args.ContainsKey("cardHolderName") ? (string)args["cardHolderName"] : "XXX",
|
|
||||||
cardTypeId: args != null && args.ContainsKey("cardTypeId") ? (int)args["cardTypeId"] : 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -76,6 +76,7 @@ namespace UnitTest.Ordering.Application
|
|||||||
private CreateOrderCommand FakeOrderRequestWithBuyer(Dictionary<string, object> args = null)
|
private CreateOrderCommand FakeOrderRequestWithBuyer(Dictionary<string, object> args = null)
|
||||||
{
|
{
|
||||||
return new CreateOrderCommand(
|
return new CreateOrderCommand(
|
||||||
|
Guid.NewGuid(),
|
||||||
new List<BasketItem>(),
|
new List<BasketItem>(),
|
||||||
userId: args != null && args.ContainsKey("userId") ? (string)args["userId"] : null,
|
userId: args != null && args.ContainsKey("userId") ? (string)args["userId"] : null,
|
||||||
userName: args != null && args.ContainsKey("userName") ? (string)args["userName"] : null,
|
userName: args != null && args.ContainsKey("userName") ? (string)args["userName"] : null,
|
||||||
|
@ -31,7 +31,7 @@ namespace UnitTest.Ordering.Application
|
|||||||
public async Task Create_order_with_requestId_success()
|
public async Task Create_order_with_requestId_success()
|
||||||
{
|
{
|
||||||
//Arrange
|
//Arrange
|
||||||
_mediatorMock.Setup(x => x.Send(It.IsAny<IdentifiedCommand<CancelOrderCommand, bool>>(), default(CancellationToken)))
|
_mediatorMock.Setup(x => x.Send(It.IsAny<CancelOrderCommand>(), default(CancellationToken)))
|
||||||
.Returns(Task.FromResult(true));
|
.Returns(Task.FromResult(true));
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
@ -47,7 +47,7 @@ namespace UnitTest.Ordering.Application
|
|||||||
public async Task Cancel_order_bad_request()
|
public async Task Cancel_order_bad_request()
|
||||||
{
|
{
|
||||||
//Arrange
|
//Arrange
|
||||||
_mediatorMock.Setup(x => x.Send(It.IsAny<IdentifiedCommand<CancelOrderCommand, bool>>(), default(CancellationToken)))
|
_mediatorMock.Setup(x => x.Send(It.IsAny<CancelOrderCommand>(), default(CancellationToken)))
|
||||||
.Returns(Task.FromResult(true));
|
.Returns(Task.FromResult(true));
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
@ -62,7 +62,7 @@ namespace UnitTest.Ordering.Application
|
|||||||
public async Task Ship_order_with_requestId_success()
|
public async Task Ship_order_with_requestId_success()
|
||||||
{
|
{
|
||||||
//Arrange
|
//Arrange
|
||||||
_mediatorMock.Setup(x => x.Send(It.IsAny<IdentifiedCommand<ShipOrderCommand, bool>>(), default(System.Threading.CancellationToken)))
|
_mediatorMock.Setup(x => x.Send(It.IsAny<ShipOrderCommand>(), default(System.Threading.CancellationToken)))
|
||||||
.Returns(Task.FromResult(true));
|
.Returns(Task.FromResult(true));
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
@ -78,7 +78,7 @@ namespace UnitTest.Ordering.Application
|
|||||||
public async Task Ship_order_bad_request()
|
public async Task Ship_order_bad_request()
|
||||||
{
|
{
|
||||||
//Arrange
|
//Arrange
|
||||||
_mediatorMock.Setup(x => x.Send(It.IsAny<IdentifiedCommand<CreateOrderCommand, bool>>(), default(System.Threading.CancellationToken)))
|
_mediatorMock.Setup(x => x.Send(It.IsAny<CreateOrderCommand>(), default(System.Threading.CancellationToken)))
|
||||||
.Returns(Task.FromResult(true));
|
.Returns(Task.FromResult(true));
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
|
Loading…
x
Reference in New Issue
Block a user