using MediatR; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; using Ordering.API.Application.Commands; using Ordering.API.Application.IntegrationCommands.Commands; using Ordering.API.Application.IntegrationEvents; using Ordering.Domain.Exceptions; using System.Threading.Tasks; namespace Ordering.API.Application.Sagas { /// /// Saga for handling the place order process /// and which is started once the basket.api has /// successfully processed the items ordered. /// Saga provides a period of grace to give the customer /// the opportunity to cancel the order before proceeding /// with the validations. /// public class OrderProcessSaga : OrderSaga, IIntegrationEventHandler, IAsyncRequestHandler, IAsyncRequestHandler { public OrderProcessSaga( OrderingContext orderingContext) : base(orderingContext) { } /// /// Command handler which confirms that the grace period /// has been completed and order has not been cancelled. /// If so, the process continues for validation. /// /// /// Integration command message which is sent by a saga /// scheduler which provides the sagas that its grace /// period has completed. /// /// public async Task Handle(ConfirmGracePeriodCommandMsg @event) { var orderSaga = FindSagaById(@event.OrderId); CheckValidSagaId(orderSaga); if (orderSaga.OrderStatus != OrderStatus.Cancelled) { orderSaga.SetAwaitingValidationStatus(); await SaveChangesAsync(); } } /// /// Handler which processes the command when /// customer executes cancel order from app /// /// /// public async Task Handle(CancelOrderCommand command) { var result = false; var orderSaga = FindSagaById(command.OrderNumber); CheckValidSagaId(orderSaga); // Not possible to cancel order when // it has already been shipped if (orderSaga.GetOrderStatusId() != OrderStatus.Cancelled.Id || orderSaga.GetOrderStatusId() != OrderStatus.Shipped.Id) { orderSaga.SetCancelStatus(); result = await SaveChangesAsync(); } return result; } /// /// Handler which processes the command when /// administrator executes ship order from app /// /// /// public async Task Handle(ShipOrderCommand command) { var result = false; var orderSaga = FindSagaById(command.OrderNumber); CheckValidSagaId(orderSaga); // Only ship order when // its status is paid if (orderSaga.GetOrderStatusId() == OrderStatus.Paid.Id) { orderSaga.SetShippedStatus(); result = await SaveChangesAsync(); } return result; } private void CheckValidSagaId(Order orderSaga) { if (orderSaga is null) { throw new OrderingDomainException("Not able to process order saga event. Reason: no valid orderId"); } } #region CommandHandlerIdentifiers public class CancelOrderCommandIdentifiedHandler : IdentifierCommandHandler { public CancelOrderCommandIdentifiedHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager) { } protected override bool CreateResultForDuplicateRequest() { return true; // Ignore duplicate requests for processing order. } } public class ShipOrderCommandIdentifiedHandler : IdentifierCommandHandler { public ShipOrderCommandIdentifiedHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager) { } protected override bool CreateResultForDuplicateRequest() { return true; // Ignore duplicate requests for processing order. } } #endregion } }