@ -0,0 +1,46 @@ | |||
using MediatR; | |||
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | |||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | |||
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
namespace Ordering.API.Application.Commands | |||
{ | |||
public class CancelOrderCommandIdentifiedHandler : IdentifierCommandHandler<CancelOrderCommand, bool> | |||
{ | |||
public CancelOrderCommandIdentifiedHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager) | |||
{ | |||
} | |||
protected override bool CreateResultForDuplicateRequest() | |||
{ | |||
return true; // Ignore duplicate requests for processing order. | |||
} | |||
} | |||
public class CancelOrderCommandHandler : IAsyncRequestHandler<CancelOrderCommand, bool> | |||
{ | |||
private readonly IOrderRepository _orderRepository; | |||
public CancelOrderCommandHandler(IOrderRepository orderRepository) | |||
{ | |||
_orderRepository = orderRepository; | |||
} | |||
/// <summary> | |||
/// Handler which processes the command when | |||
/// customer executes cancel order from app | |||
/// </summary> | |||
/// <param name="command"></param> | |||
/// <returns></returns> | |||
public async Task<bool> Handle(CancelOrderCommand command) | |||
{ | |||
var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); | |||
orderToUpdate.SetCancelledStatus(); | |||
return await _orderRepository.UnitOfWork.SaveEntitiesAsync(); | |||
} | |||
} | |||
} |
@ -0,0 +1,43 @@ | |||
using MediatR; | |||
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | |||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | |||
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; | |||
using System.Threading.Tasks; | |||
namespace Ordering.API.Application.Commands | |||
{ | |||
public class ShipOrderCommandIdentifiedHandler : IdentifierCommandHandler<ShipOrderCommand, bool> | |||
{ | |||
public ShipOrderCommandIdentifiedHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager) | |||
{ | |||
} | |||
protected override bool CreateResultForDuplicateRequest() | |||
{ | |||
return true; // Ignore duplicate requests for processing order. | |||
} | |||
} | |||
public class ShipOrderCommandHandler : IAsyncRequestHandler<ShipOrderCommand, bool> | |||
{ | |||
private readonly IOrderRepository _orderRepository; | |||
public ShipOrderCommandHandler(IOrderRepository orderRepository) | |||
{ | |||
_orderRepository = orderRepository; | |||
} | |||
/// <summary> | |||
/// Handler which processes the command when | |||
/// administrator executes ship order from app | |||
/// </summary> | |||
/// <param name="command"></param> | |||
/// <returns></returns> | |||
public async Task<bool> Handle(ShipOrderCommand command) | |||
{ | |||
var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); | |||
orderToUpdate.SetShippedStatus(); | |||
return await _orderRepository.UnitOfWork.SaveEntitiesAsync(); | |||
} | |||
} | |||
} |
@ -0,0 +1,32 @@ | |||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | |||
using Ordering.API.Application.IntegrationEvents.Events; | |||
using System.Threading.Tasks; | |||
namespace Ordering.API.Application.IntegrationEvents.EventHandling | |||
{ | |||
public class GracePeriodConfirmedIntegrationEventHandler : IIntegrationEventHandler<GracePeriodConfirmedIntegrationEvent> | |||
{ | |||
private readonly IOrderRepository _orderRepository; | |||
public GracePeriodConfirmedIntegrationEventHandler(IOrderRepository orderRepository) | |||
{ | |||
_orderRepository = orderRepository; | |||
} | |||
/// <summary> | |||
/// Event handler which confirms that the grace period | |||
/// has been completed and order will not initially be cancelled. | |||
/// Therefore, the order process continues for validation. | |||
/// </summary> | |||
/// <param name="event"> | |||
/// </param> | |||
/// <returns></returns> | |||
public async Task Handle(GracePeriodConfirmedIntegrationEvent @event) | |||
{ | |||
var orderToUpdate = await _orderRepository.GetAsync(@event.OrderId); | |||
orderToUpdate.SetAwaitingValidationStatus(); | |||
await _orderRepository.UnitOfWork.SaveEntitiesAsync(); | |||
} | |||
} | |||
} |
@ -1,121 +0,0 @@ | |||
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.IntegrationEvents.Events; | |||
using Ordering.Domain.Exceptions; | |||
using System.Threading.Tasks; | |||
namespace Ordering.API.Application.Sagas | |||
{ | |||
/// <summary> | |||
/// 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. | |||
/// </summary> | |||
public class OrderProcessSaga : OrderSaga, | |||
IIntegrationEventHandler<GracePeriodConfirmedIntegrationEvent>, | |||
IAsyncRequestHandler<CancelOrderCommand, bool>, | |||
IAsyncRequestHandler<ShipOrderCommand, bool> | |||
{ | |||
public OrderProcessSaga( | |||
OrderingContext orderingContext) | |||
: base(orderingContext) | |||
{ | |||
} | |||
/// <summary> | |||
/// Event handler which confirms that the grace period | |||
/// has been completed and order will not initially be cancelled. | |||
/// Therefore, the order process continues for validation. | |||
/// </summary> | |||
/// <param name="event"> | |||
/// Integration event message which is sent by a saga scheduler | |||
/// telling us that the saga's grace period has completed. | |||
/// </param> | |||
/// <returns></returns> | |||
public async Task Handle(GracePeriodConfirmedIntegrationEvent @event) | |||
{ | |||
var orderSaga = FindSagaById(@event.OrderId); | |||
CheckValidSagaId(orderSaga); | |||
orderSaga.SetAwaitingValidationStatus(); | |||
await SaveChangesAsync(); | |||
} | |||
/// <summary> | |||
/// Handler which processes the command when | |||
/// customer executes cancel order from app | |||
/// </summary> | |||
/// <param name="command"></param> | |||
/// <returns></returns> | |||
public async Task<bool> Handle(CancelOrderCommand command) | |||
{ | |||
var result = false; | |||
var orderSaga = FindSagaById(command.OrderNumber); | |||
CheckValidSagaId(orderSaga); | |||
orderSaga.SetCancelledStatus(); | |||
result = await SaveChangesAsync(); | |||
return result; | |||
} | |||
/// <summary> | |||
/// Handler which processes the command when | |||
/// administrator executes ship order from app | |||
/// </summary> | |||
/// <param name="command"></param> | |||
/// <returns></returns> | |||
public async Task<bool> Handle(ShipOrderCommand command) | |||
{ | |||
var result = false; | |||
var orderSaga = FindSagaById(command.OrderNumber); | |||
CheckValidSagaId(orderSaga); | |||
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"); | |||
} | |||
} | |||
public class CancelOrderCommandIdentifiedHandler : IdentifierCommandHandler<CancelOrderCommand, bool> | |||
{ | |||
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<ShipOrderCommand, bool> | |||
{ | |||
public ShipOrderCommandIdentifiedHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager) | |||
{ | |||
} | |||
protected override bool CreateResultForDuplicateRequest() | |||
{ | |||
return true; // Ignore duplicate requests for processing order. | |||
} | |||
} | |||
} | |||
} |
@ -1,32 +0,0 @@ | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
using Microsoft.EntityFrameworkCore; | |||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | |||
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; | |||
namespace Ordering.API.Application.Sagas | |||
{ | |||
public abstract class OrderSaga : Saga<Order> | |||
{ | |||
private OrderingContext _orderingContext; | |||
public OrderSaga(OrderingContext orderingContext) : base(orderingContext) | |||
{ | |||
_orderingContext = orderingContext; | |||
} | |||
public override Order FindSagaById(int id) | |||
{ | |||
return _orderingContext.Orders | |||
.Include(c => c.OrderStatus) | |||
.Include(c => c.OrderItems) | |||
.Include(c => c.Address) | |||
.Single(c => c.Id == id); | |||
} | |||
public override async Task<bool> SaveChangesAsync() | |||
{ | |||
return await _orderingContext.SaveEntitiesAsync(); | |||
} | |||
} | |||
} |
@ -1,21 +0,0 @@ | |||
using Microsoft.EntityFrameworkCore; | |||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
namespace Ordering.API.Application.Sagas | |||
{ | |||
public abstract class Saga<TEntity> where TEntity : Entity | |||
{ | |||
private readonly DbContext _dbContext; | |||
public Saga(DbContext dbContext) | |||
{ | |||
_dbContext = dbContext; | |||
} | |||
public abstract TEntity FindSagaById(int id); | |||
public abstract Task<bool> SaveChangesAsync(); | |||
} | |||
} |