@ -0,0 +1,13 @@ | |||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | |||||
public class SetCompletedOrderStatusCommand : IRequest<bool> | |||||
{ | |||||
[DataMember] | |||||
public int OrderNumber { get; private set; } | |||||
public SetCompletedOrderStatusCommand(int orderNumber) | |||||
{ | |||||
OrderNumber = orderNumber; | |||||
} | |||||
} |
@ -0,0 +1,51 @@ | |||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | |||||
// Regular CommandHandler | |||||
public class SetCompletedOrderStatusCommandHandler : IRequestHandler<SetCompletedOrderStatusCommand, bool> | |||||
{ | |||||
private readonly IOrderRepository _orderRepository; | |||||
public SetCompletedOrderStatusCommandHandler(IOrderRepository orderRepository) | |||||
{ | |||||
_orderRepository = orderRepository; | |||||
} | |||||
/// <summary> | |||||
/// Handler which processes the command when | |||||
/// Completed the order | |||||
/// </summary> | |||||
/// <param name="command"></param> | |||||
/// <returns></returns> | |||||
public async Task<bool> Handle(SetCompletedOrderStatusCommand command, CancellationToken cancellationToken) | |||||
{ | |||||
// Simulate a work time for validating the completed | |||||
await Task.Delay(10000, cancellationToken); | |||||
var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); | |||||
if (orderToUpdate == null) | |||||
{ | |||||
return false; | |||||
} | |||||
orderToUpdate.SetCompletedStatus(); | |||||
return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); | |||||
} | |||||
} | |||||
// Use for Idempotency in Command process | |||||
public class SetCompletedIdentifiedOrderStatusCommandHandler : IdentifiedCommandHandler<SetCompletedOrderStatusCommand, bool> | |||||
{ | |||||
public SetCompletedIdentifiedOrderStatusCommandHandler( | |||||
IMediator mediator, | |||||
IRequestManager requestManager, | |||||
ILogger<IdentifiedCommandHandler<SetCompletedOrderStatusCommand, bool>> logger) | |||||
: base(mediator, requestManager, logger) | |||||
{ | |||||
} | |||||
protected override bool CreateResultForDuplicateRequest() | |||||
{ | |||||
return true; // Ignore duplicate requests for processing order. | |||||
} | |||||
} |
@ -0,0 +1,44 @@ | |||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderCompleted; | |||||
public class OrderStatusChangedToCompletedDomainEventHandler | |||||
: INotificationHandler<OrderStatusChangedToCompletedDomainEvent> | |||||
{ | |||||
private readonly IOrderRepository _orderRepository; | |||||
private readonly ILoggerFactory _logger; | |||||
private readonly IBuyerRepository _buyerRepository; | |||||
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; | |||||
public OrderStatusChangedToCompletedDomainEventHandler( | |||||
IOrderRepository orderRepository, ILoggerFactory logger, | |||||
IBuyerRepository buyerRepository, | |||||
IOrderingIntegrationEventService orderingIntegrationEventService | |||||
) | |||||
{ | |||||
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); | |||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | |||||
_buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); | |||||
_orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentNullException(nameof(orderingIntegrationEventService)); | |||||
} | |||||
public async Task Handle(OrderStatusChangedToCompletedDomainEvent orderStatusChangedToCompletedDomainEvent, CancellationToken cancellationToken) | |||||
{ | |||||
_logger.CreateLogger<OrderStatusChangedToCompletedDomainEventHandler>() | |||||
.LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})", | |||||
orderStatusChangedToCompletedDomainEvent.OrderId, nameof(OrderStatus.Complete), OrderStatus.Complete.Id); | |||||
var order = await _orderRepository.GetAsync(orderStatusChangedToCompletedDomainEvent.OrderId); | |||||
var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); | |||||
var orderStockList = orderStatusChangedToCompletedDomainEvent.OrderItems | |||||
.Select(orderItem => new OrderStockItem(orderItem.ProductId, orderItem.GetUnits())); | |||||
var orderStatusChangedToCompletedIntegrationEvent = new OrderStatusChangedToCompletedIntegrationEvent( | |||||
orderStatusChangedToCompletedDomainEvent.OrderId, | |||||
order.OrderStatus.Name, | |||||
buyer.Name, | |||||
orderStockList); | |||||
await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedToCompletedIntegrationEvent); | |||||
} | |||||
} |
@ -0,0 +1,35 @@ | |||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.EventHandling; | |||||
public class OrderCompletedFailedIntegrationEventHandler : | |||||
IIntegrationEventHandler<OrderCompletedFailedIntegrationEvent> | |||||
{ | |||||
private readonly IMediator _mediator; | |||||
private readonly ILogger<OrderCompletedFailedIntegrationEventHandler> _logger; | |||||
public OrderCompletedFailedIntegrationEventHandler( | |||||
IMediator mediator, | |||||
ILogger<OrderCompletedFailedIntegrationEventHandler> logger) | |||||
{ | |||||
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); | |||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | |||||
} | |||||
public async Task Handle(OrderCompletedFailedIntegrationEvent @event) | |||||
{ | |||||
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) | |||||
{ | |||||
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); | |||||
var command = new CancelOrderCommand(@event.OrderId); | |||||
_logger.LogInformation( | |||||
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", | |||||
command.GetGenericTypeName(), | |||||
nameof(command.OrderNumber), | |||||
command.OrderNumber, | |||||
command); | |||||
await _mediator.Send(command); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,35 @@ | |||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.EventHandling; | |||||
public class OrderCompletedSucceededIntegrationEventHandler : | |||||
IIntegrationEventHandler<OrderCompletedSucceededIntegrationEvent> | |||||
{ | |||||
private readonly IMediator _mediator; | |||||
private readonly ILogger<OrderCompletedSucceededIntegrationEventHandler> _logger; | |||||
public OrderCompletedSucceededIntegrationEventHandler( | |||||
IMediator mediator, | |||||
ILogger<OrderCompletedSucceededIntegrationEventHandler> logger) | |||||
{ | |||||
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); | |||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | |||||
} | |||||
public async Task Handle(OrderCompletedSucceededIntegrationEvent @event) | |||||
{ | |||||
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) | |||||
{ | |||||
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); | |||||
var command = new SetCompletedOrderStatusCommand(@event.OrderId); | |||||
_logger.LogInformation( | |||||
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", | |||||
command.GetGenericTypeName(), | |||||
nameof(command.OrderNumber), | |||||
command.OrderNumber, | |||||
command); | |||||
await _mediator.Send(command); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,8 @@ | |||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; | |||||
public record OrderCompletedFailedIntegrationEvent : IntegrationEvent | |||||
{ | |||||
public int OrderId { get; } | |||||
public OrderCompletedFailedIntegrationEvent(int orderId) => OrderId = orderId; | |||||
} |
@ -0,0 +1,8 @@ | |||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; | |||||
public record OrderCompletedSucceededIntegrationEvent : IntegrationEvent | |||||
{ | |||||
public int OrderId { get; } | |||||
public OrderCompletedSucceededIntegrationEvent(int orderId) => OrderId = orderId; | |||||
} |
@ -0,0 +1,21 @@ | |||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; | |||||
public record OrderStatusChangedToCompletedIntegrationEvent : IntegrationEvent | |||||
{ | |||||
public int OrderId { get; } | |||||
public string OrderStatus { get; } | |||||
public string BuyerName { get; } | |||||
public IEnumerable<OrderStockItem> OrderStockItems { get; } | |||||
public OrderStatusChangedToCompletedIntegrationEvent(int orderId, | |||||
string orderStatus, | |||||
string buyerName, | |||||
IEnumerable<OrderStockItem> orderStockItems) | |||||
{ | |||||
OrderId = orderId; | |||||
OrderStockItems = orderStockItems; | |||||
OrderStatus = orderStatus; | |||||
BuyerName = buyerName; | |||||
} | |||||
} | |||||
@ -0,0 +1,11 @@ | |||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Validations; | |||||
public class CompletedOrderCommandValidator : AbstractValidator<SetCompletedOrderStatusCommand> | |||||
{ | |||||
public CompletedOrderCommandValidator(ILogger<CompletedOrderCommandValidator> logger) | |||||
{ | |||||
RuleFor(order => order.OrderNumber).NotEmpty().WithMessage("No orderId found"); | |||||
logger.LogTrace("----- INSTANCE CREATED - {ClassName}", GetType().Name); | |||||
} | |||||
} |
@ -0,0 +1,18 @@ | |||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Events; | |||||
/// <summary> | |||||
/// Event used when the order is completed | |||||
/// </summary> | |||||
public class OrderStatusChangedToCompletedDomainEvent | |||||
: INotification | |||||
{ | |||||
public int OrderId { get; } | |||||
public IEnumerable<OrderItem> OrderItems { get; } | |||||
public OrderStatusChangedToCompletedDomainEvent(int orderId, | |||||
IEnumerable<OrderItem> orderItems) | |||||
{ | |||||
OrderId = orderId; | |||||
OrderItems = orderItems; | |||||
} | |||||
} |
@ -0,0 +1,28 @@ | |||||
namespace Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents.EventHandling; | |||||
public class OrderStatusChangedToCompletedIntegrationEventHandler : IIntegrationEventHandler<OrderStatusChangedToCompletedIntegrationEvent> | |||||
{ | |||||
private readonly IHubContext<NotificationsHub> _hubContext; | |||||
private readonly ILogger<OrderStatusChangedToCompletedIntegrationEventHandler> _logger; | |||||
public OrderStatusChangedToCompletedIntegrationEventHandler( | |||||
IHubContext<NotificationsHub> hubContext, | |||||
ILogger<OrderStatusChangedToCompletedIntegrationEventHandler> logger) | |||||
{ | |||||
_hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); | |||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | |||||
} | |||||
public async Task Handle(OrderStatusChangedToCompletedIntegrationEvent @event) | |||||
{ | |||||
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) | |||||
{ | |||||
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); | |||||
await _hubContext.Clients | |||||
.Group(@event.BuyerName) | |||||
.SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,16 @@ | |||||
namespace Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents.Events; | |||||
public record OrderStatusChangedToCompletedIntegrationEvent : IntegrationEvent | |||||
{ | |||||
public int OrderId { get; } | |||||
public string OrderStatus { get; } | |||||
public string BuyerName { get; } | |||||
public OrderStatusChangedToCompletedIntegrationEvent(int orderId, | |||||
string orderStatus, string buyerName) | |||||
{ | |||||
OrderId = orderId; | |||||
OrderStatus = orderStatus; | |||||
BuyerName = buyerName; | |||||
} | |||||
} |