From abf4fc5fe7898b6324b4db6407c48ea89f9ec79f Mon Sep 17 00:00:00 2001 From: hsn Date: Sat, 8 Jul 2023 12:44:47 +0300 Subject: [PATCH 01/11] CompleteOrderAsyn action is added. --- .../Commands/CompleteOrderCommand.cs | 16 +++++++ .../Commands/CompleteOrderCommandHandler.cs | 48 +++++++++++++++++++ .../Controllers/OrdersController.cs | 30 ++++++++++++ .../AggregatesModel/OrderAggregate/Order.cs | 14 ++++++ .../OrderAggregate/OrderStatus.cs | 3 +- .../Events/OrderCompletedDomainEvent.cs | 12 +++++ 6 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 src/Services/Ordering/Ordering.API/Application/Commands/CompleteOrderCommand.cs create mode 100644 src/Services/Ordering/Ordering.API/Application/Commands/CompleteOrderCommandHandler.cs create mode 100644 src/Services/Ordering/Ordering.Domain/Events/OrderCompletedDomainEvent.cs diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/CompleteOrderCommand.cs b/src/Services/Ordering/Ordering.API/Application/Commands/CompleteOrderCommand.cs new file mode 100644 index 000000000..e8ec80253 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Application/Commands/CompleteOrderCommand.cs @@ -0,0 +1,16 @@ +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; + +public class CompleteOrderCommand : IRequest +{ + + [DataMember] + public int OrderNumber { get; set; } + public CompleteOrderCommand() + { + + } + public CompleteOrderCommand(int orderNumber) + { + OrderNumber = orderNumber; + } +} diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/CompleteOrderCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/CompleteOrderCommandHandler.cs new file mode 100644 index 000000000..0f0a5da18 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Application/Commands/CompleteOrderCommandHandler.cs @@ -0,0 +1,48 @@ +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; + +// Regular CommandHandler +public class CompleteOrderCommandHandler : IRequestHandler +{ + private readonly IOrderRepository _orderRepository; + + public CompleteOrderCommandHandler(IOrderRepository orderRepository) + { + _orderRepository = orderRepository; + } + + /// + /// Handler which processes the command when + /// customer executes complete order from app + /// + /// + /// + public async Task Handle(CompleteOrderCommand command, CancellationToken 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 CompleteOrderIdentifiedCommandHandler : IdentifiedCommandHandler +{ + public CompleteOrderIdentifiedCommandHandler( + IMediator mediator, + IRequestManager requestManager, + ILogger> logger) + : base(mediator, requestManager, logger) + { + } + + protected override bool CreateResultForDuplicateRequest() + { + return true; // Ignore duplicate requests for processing order. + } +} diff --git a/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs b/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs index df7572bb7..e6843591c 100644 --- a/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs +++ b/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs @@ -55,6 +55,36 @@ public class OrdersController : ControllerBase return Ok(); } + [Route("complete")] + [HttpPut] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public async Task CompleteOrderAsync([FromBody] CompleteOrderCommand command, [FromHeader(Name = "x-requestid")] string requestId) + { + bool commandResult = false; + + if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty) + { + var requestCompleteOrder = new IdentifiedCommand(command, guid); + + _logger.LogInformation( + "Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", + requestCompleteOrder.GetGenericTypeName(), + nameof(requestCompleteOrder.Command.OrderNumber), + requestCompleteOrder.Command.OrderNumber, + requestCompleteOrder); + + commandResult = await _mediator.Send(requestCompleteOrder); + } + + if (!commandResult) + { + return BadRequest(); + } + + return Ok(); + } + [Route("ship")] [HttpPut] [ProducesResponseType(StatusCodes.Status200OK)] diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs index 8e452c42f..2670f5e49 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs @@ -156,6 +156,20 @@ public class Order AddDomainEvent(new OrderCancelledDomainEvent(this)); } + public void SetCompletedStatus() + { + // make sure it is shipped and paid before completing + if (_orderStatusId == OrderStatus.Paid.Id || + _orderStatusId == OrderStatus.Shipped.Id) + { + StatusChangeException(OrderStatus.Completed); + } + + _orderStatusId = OrderStatus.Completed.Id; + _description = $"The order is completed."; + AddDomainEvent(new OrderCompletedDomainEvent(this)); // a postponed way to raise domain events + } + public void SetCancelledStatusWhenStockIsRejected(IEnumerable orderStockRejectedItems) { if (_orderStatusId == OrderStatus.AwaitingValidation.Id) diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderStatus.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderStatus.cs index 8c3cc50fb..91657d6e6 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderStatus.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderStatus.cs @@ -11,6 +11,7 @@ public class OrderStatus public static OrderStatus Paid = new OrderStatus(4, nameof(Paid).ToLowerInvariant()); public static OrderStatus Shipped = new OrderStatus(5, nameof(Shipped).ToLowerInvariant()); public static OrderStatus Cancelled = new OrderStatus(6, nameof(Cancelled).ToLowerInvariant()); + public static OrderStatus Completed = new OrderStatus(7, nameof(Completed).ToLowerInvariant()); public OrderStatus(int id, string name) : base(id, name) @@ -18,7 +19,7 @@ public class OrderStatus } public static IEnumerable List() => - new[] { Submitted, AwaitingValidation, StockConfirmed, Paid, Shipped, Cancelled }; + new[] { Submitted, AwaitingValidation, StockConfirmed, Paid, Shipped, Cancelled, Completed }; public static OrderStatus FromName(string name) { diff --git a/src/Services/Ordering/Ordering.Domain/Events/OrderCompletedDomainEvent.cs b/src/Services/Ordering/Ordering.Domain/Events/OrderCompletedDomainEvent.cs new file mode 100644 index 000000000..bf7255c6f --- /dev/null +++ b/src/Services/Ordering/Ordering.Domain/Events/OrderCompletedDomainEvent.cs @@ -0,0 +1,12 @@ +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Events; + +public class OrderCompletedDomainEvent : INotification +{ + public Order Order { get; } + + public OrderCompletedDomainEvent(Order order) + { + Order = order; + } +} + From ba8bbe71dfeed651d0a0572ca58c8a77acf543e2 Mon Sep 17 00:00:00 2001 From: hsn Date: Sat, 8 Jul 2023 12:45:38 +0300 Subject: [PATCH 02/11] Units tests are added for completeOrderAsync --- .../Application/OrdersWebApiTest.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/Services/Ordering/Ordering.UnitTests/Application/OrdersWebApiTest.cs b/src/Services/Ordering/Ordering.UnitTests/Application/OrdersWebApiTest.cs index 0235c94b6..5a0948e7a 100644 --- a/src/Services/Ordering/Ordering.UnitTests/Application/OrdersWebApiTest.cs +++ b/src/Services/Ordering/Ordering.UnitTests/Application/OrdersWebApiTest.cs @@ -48,6 +48,37 @@ public class OrdersWebApiTest Assert.Equal((int)System.Net.HttpStatusCode.BadRequest, actionResult.StatusCode); } + [Fact] + public async Task Complete_order_with_requestId_success() + { + //Arrange + _mediatorMock.Setup(x => x.Send(It.IsAny>(), default)) + .Returns(Task.FromResult(true)); + + //Act + var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object); + var actionResult = await orderController.CompleteOrderAsync(new CompleteOrderCommand(1), Guid.NewGuid().ToString()) as OkResult; + + //Assert + Assert.Equal((int)System.Net.HttpStatusCode.OK, actionResult.StatusCode); + + } + + [Fact] + public async Task Complete_order_bad_request() + { + //Arrange + _mediatorMock.Setup(x => x.Send(It.IsAny>(), default)) + .Returns(Task.FromResult(true)); + + //Act + var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object); + var actionResult = await orderController.CompleteOrderAsync(new CompleteOrderCommand(1), string.Empty) as BadRequestResult; + + //Assert + Assert.Equal((int)System.Net.HttpStatusCode.BadRequest, actionResult.StatusCode); + } + [Fact] public async Task Ship_order_with_requestId_success() { From 979d4ff01d21727da5a31f097b8d26f8c35788d7 Mon Sep 17 00:00:00 2001 From: hsn Date: Sat, 8 Jul 2023 12:46:39 +0300 Subject: [PATCH 03/11] Functional test is added that partially tests CompleteOrderAsync action --- .../OrderingScenarioBase.cs | 1 + .../OrderingScenarios.cs | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/Services/Ordering/Ordering.FunctionalTests/OrderingScenarioBase.cs b/src/Services/Ordering/Ordering.FunctionalTests/OrderingScenarioBase.cs index f3fba40e3..422fd1b3e 100644 --- a/src/Services/Ordering/Ordering.FunctionalTests/OrderingScenarioBase.cs +++ b/src/Services/Ordering/Ordering.FunctionalTests/OrderingScenarioBase.cs @@ -50,6 +50,7 @@ public class OrderingScenarioBase { public static string CancelOrder = "api/v1/orders/cancel"; public static string ShipOrder = "api/v1/orders/ship"; + public static string CompleteOrder = "api/v1/orders/complete"; } private class AuthStartupFilter : IStartupFilter diff --git a/src/Services/Ordering/Ordering.FunctionalTests/OrderingScenarios.cs b/src/Services/Ordering/Ordering.FunctionalTests/OrderingScenarios.cs index 85dd4ab3d..cb21cb400 100644 --- a/src/Services/Ordering/Ordering.FunctionalTests/OrderingScenarios.cs +++ b/src/Services/Ordering/Ordering.FunctionalTests/OrderingScenarios.cs @@ -19,6 +19,20 @@ namespace Ordering.FunctionalTests response.EnsureSuccessStatusCode(); } + [Fact] + public async Task Complete_order_no_order_created_bad_request_response() + { + using var server = CreateServer(); + var content = new StringContent(BuildOrder(), UTF8Encoding.UTF8, "application/json") + { + Headers = { { "x-requestid", Guid.NewGuid().ToString() } } + }; + var response = await server.CreateClient() + .PutAsync(Put.CompleteOrder, content); + + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + } + [Fact] public async Task Cancel_order_no_order_created_bad_request_response() { @@ -29,8 +43,7 @@ namespace Ordering.FunctionalTests }; var response = await server.CreateClient() .PutAsync(Put.CancelOrder, content); - - var s = await response.Content.ReadAsStringAsync(); + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); } From fe8528d85503f0d08d7f3f708eda68a81b7fb70b Mon Sep 17 00:00:00 2001 From: hsn Date: Sat, 8 Jul 2023 13:09:48 +0300 Subject: [PATCH 04/11] CompleteOrderValidations are added --- .../Validations/CompleteOrderCommandValidator.cs | 11 +++++++++++ src/Services/Ordering/Ordering.API/Program.cs | 1 + 2 files changed, 12 insertions(+) create mode 100644 src/Services/Ordering/Ordering.API/Application/Validations/CompleteOrderCommandValidator.cs diff --git a/src/Services/Ordering/Ordering.API/Application/Validations/CompleteOrderCommandValidator.cs b/src/Services/Ordering/Ordering.API/Application/Validations/CompleteOrderCommandValidator.cs new file mode 100644 index 000000000..31ba48d68 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Application/Validations/CompleteOrderCommandValidator.cs @@ -0,0 +1,11 @@ +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Validations; + +public class CompleteOrderCommandValidator : AbstractValidator +{ + public CompleteOrderCommandValidator(ILogger logger) + { + RuleFor(order => order.OrderNumber).NotEmpty().WithMessage("No orderId found"); + + logger.LogTrace("INSTANCE CREATED - {ClassName}", GetType().Name); + } +} diff --git a/src/Services/Ordering/Ordering.API/Program.cs b/src/Services/Ordering/Ordering.API/Program.cs index 56d76de33..cab0e0b95 100644 --- a/src/Services/Ordering/Ordering.API/Program.cs +++ b/src/Services/Ordering/Ordering.API/Program.cs @@ -23,6 +23,7 @@ services.AddMediatR(cfg => // Register the command validators for the validator behavior (validators based on FluentValidation library) services.AddSingleton, CancelOrderCommandValidator>(); +services.AddSingleton, CompleteOrderCommandValidator>(); services.AddSingleton, CreateOrderCommandValidator>(); services.AddSingleton>, IdentifiedCommandValidator>(); services.AddSingleton, ShipOrderCommandValidator>(); From 48caa9bb2c8838dc4f4ecdff9dbf9d14c22b321e Mon Sep 17 00:00:00 2001 From: hsn Date: Mon, 10 Jul 2023 00:01:03 +0300 Subject: [PATCH 05/11] Integration event is added. --- .../OrderCompletedIntegrationEventHandler.cs | 40 +++++++++++++++++++ .../Events/OrderCompletedIntegrationEvent.cs | 10 +++++ src/Services/Ordering/Ordering.API/Program.cs | 2 + 3 files changed, 52 insertions(+) create mode 100644 src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderCompletedIntegrationEventHandler.cs create mode 100644 src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderCompletedIntegrationEvent.cs diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderCompletedIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderCompletedIntegrationEventHandler.cs new file mode 100644 index 000000000..dcc3f0c43 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderCompletedIntegrationEventHandler.cs @@ -0,0 +1,40 @@ +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.EventHandling; + +public class OrderCompletedIntegrationEventHandler : IIntegrationEventHandler +{ + private readonly IMediator _mediator; + private readonly ILogger _logger; + + public OrderCompletedIntegrationEventHandler( + IMediator mediator, + ILogger logger) + { + _mediator = mediator; + _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); + } + + /// + /// Event handler which confirms order is completed. + /// + /// + /// + /// + public async Task Handle(OrderCompletedIntegrationEvent @event) + { + using (_logger.BeginScope(new List> { new ("IntegrationEventContext", @event.Id) })) + { + _logger.LogInformation("Handling integration event: {IntegrationEventId} - ({@IntegrationEvent})", @event.Id, @event); + + var command = new SetAwaitingValidationOrderStatusCommand(@event.OrderId); + + _logger.LogInformation( + "Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", + command.GetGenericTypeName(), + nameof(command.OrderNumber), + command.OrderNumber, + command); + + await _mediator.Send(command); + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderCompletedIntegrationEvent.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderCompletedIntegrationEvent.cs new file mode 100644 index 000000000..555cb1ee4 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderCompletedIntegrationEvent.cs @@ -0,0 +1,10 @@ +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; + +public record OrderCompletedIntegrationEvent : IntegrationEvent +{ + public int OrderId { get; } + + public OrderCompletedIntegrationEvent(int orderId) => + OrderId = orderId; +} + diff --git a/src/Services/Ordering/Ordering.API/Program.cs b/src/Services/Ordering/Ordering.API/Program.cs index cab0e0b95..e3bd21f2a 100644 --- a/src/Services/Ordering/Ordering.API/Program.cs +++ b/src/Services/Ordering/Ordering.API/Program.cs @@ -35,6 +35,7 @@ services.AddScoped(); // Add integration event handlers. services.AddTransient, GracePeriodConfirmedIntegrationEventHandler>(); +services.AddTransient, OrderCompletedIntegrationEventHandler>(); services.AddTransient, OrderPaymentFailedIntegrationEventHandler>(); services.AddTransient, OrderPaymentSucceededIntegrationEventHandler>(); services.AddTransient, OrderStockConfirmedIntegrationEventHandler>(); @@ -52,6 +53,7 @@ var eventBus = app.Services.GetRequiredService(); eventBus.Subscribe>(); eventBus.Subscribe>(); +eventBus.Subscribe>(); eventBus.Subscribe>(); eventBus.Subscribe>(); eventBus.Subscribe>(); From 76c45d128a54069b97e1f3aacdb205a65d3c1c29 Mon Sep 17 00:00:00 2001 From: hsn Date: Mon, 10 Jul 2023 00:14:30 +0300 Subject: [PATCH 06/11] OrderCompletedDomainEventHandler is added. --- .../OrderCompletedDomainEventHandler.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCompletedDomainEventHandler.cs diff --git a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCompletedDomainEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCompletedDomainEventHandler.cs new file mode 100644 index 000000000..3625c6c88 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCompletedDomainEventHandler.cs @@ -0,0 +1,33 @@ +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers; + +public partial class OrderCompletedDomainEventHandler + : INotificationHandler +{ + private readonly IOrderRepository _orderRepository; + private readonly IBuyerRepository _buyerRepository; + private readonly ILogger _logger; + private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; + + public OrderCompletedDomainEventHandler( + IOrderRepository orderRepository, + ILogger 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; + } + + public async Task Handle(OrderCompletedDomainEvent domainEvent, CancellationToken cancellationToken) + { + OrderingApiTrace.LogOrderStatusUpdated(_logger, domainEvent.Order.Id, nameof(OrderStatus.Cancelled), OrderStatus.Cancelled.Id); + + var order = await _orderRepository.GetAsync(domainEvent.Order.Id); + var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); + + var integrationEvent = new OrderStatusChangedToCancelledIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name); + await _orderingIntegrationEventService.AddAndSaveEventAsync(integrationEvent); + } +} From f70035641f0ec5c3e49ace8950ba84e01d6d5400 Mon Sep 17 00:00:00 2001 From: hsn Date: Tue, 11 Jul 2023 08:09:49 +0300 Subject: [PATCH 07/11] IntegrationEvent is completed --- .../OrderCompletedDomainEventHandler.cs | 2 +- ...derStatusChangedToCompletedIntegrationEvent.cs | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToCompletedIntegrationEvent.cs diff --git a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCompletedDomainEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCompletedDomainEventHandler.cs index 3625c6c88..04cd44fdc 100644 --- a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCompletedDomainEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCompletedDomainEventHandler.cs @@ -27,7 +27,7 @@ public partial class OrderCompletedDomainEventHandler var order = await _orderRepository.GetAsync(domainEvent.Order.Id); var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); - var integrationEvent = new OrderStatusChangedToCancelledIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name); + var integrationEvent = new OrderStatusChangedToCompletedIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name); await _orderingIntegrationEventService.AddAndSaveEventAsync(integrationEvent); } } diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToCompletedIntegrationEvent.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToCompletedIntegrationEvent.cs new file mode 100644 index 000000000..f68f941ec --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToCompletedIntegrationEvent.cs @@ -0,0 +1,15 @@ +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 OrderStatusChangedToCompletedIntegrationEvent(int orderId, string orderStatus, string buyerName) + { + OrderId = orderId; + OrderStatus = orderStatus; + BuyerName = buyerName; + } +} From bb04842fe1a4f85f3b983451b1b9e908fdcedfc4 Mon Sep 17 00:00:00 2001 From: hsn Date: Tue, 11 Jul 2023 08:32:11 +0300 Subject: [PATCH 08/11] SignalHub OrderCompleted is added --- ...angedToCompletedIntegrationEventHandler.cs | 30 +++++++++++++++++++ ...tatusChangedToCompletedIntegrationEvent.cs | 16 ++++++++++ .../Ordering/Ordering.SignalrHub/Program.cs | 2 ++ 3 files changed, 48 insertions(+) create mode 100644 src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToCompletedIntegrationEventHandler.cs create mode 100644 src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToCompletedIntegrationEvent.cs diff --git a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToCompletedIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToCompletedIntegrationEventHandler.cs new file mode 100644 index 000000000..7824a26c3 --- /dev/null +++ b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToCompletedIntegrationEventHandler.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; + +namespace Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents.EventHandling; + +public class OrderStatusChangedToCompletedIntegrationEventHandler : IIntegrationEventHandler +{ + private readonly IHubContext _hubContext; + private readonly ILogger _logger; + + public OrderStatusChangedToCompletedIntegrationEventHandler( + IHubContext hubContext, + ILogger logger) + { + _hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + + public async Task Handle(OrderStatusChangedToCompletedIntegrationEvent @event) + { + using (_logger.BeginScope(new List> { new ("IntegrationEventContext", @event.Id) })) + { + _logger.LogInformation("Handling integration event: {IntegrationEventId} - ({@IntegrationEvent})", @event.Id, @event); + + await _hubContext.Clients + .Group(@event.BuyerName) + .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); + } + } +} diff --git a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToCompletedIntegrationEvent.cs b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToCompletedIntegrationEvent.cs new file mode 100644 index 000000000..b8113bc69 --- /dev/null +++ b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToCompletedIntegrationEvent.cs @@ -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; + } +} + diff --git a/src/Services/Ordering/Ordering.SignalrHub/Program.cs b/src/Services/Ordering/Ordering.SignalrHub/Program.cs index 1b0abd099..bfdbadd83 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/Program.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/Program.cs @@ -6,6 +6,7 @@ builder.Services.AddSignalR(builder.Configuration); builder.Services.AddSingleton, OrderStatusChangedToAwaitingValidationIntegrationEventHandler>(); builder.Services.AddSingleton, OrderStatusChangedToCancelledIntegrationEventHandler>(); +builder.Services.AddSingleton, OrderStatusChangedToCompletedIntegrationEventHandler>(); builder.Services.AddSingleton, OrderStatusChangedToPaidIntegrationEventHandler>(); builder.Services.AddSingleton, OrderStatusChangedToShippedIntegrationEventHandler>(); builder.Services.AddSingleton, OrderStatusChangedToStockConfirmedIntegrationEventHandler>(); @@ -24,6 +25,7 @@ eventBus.Subscribe(); eventBus.Subscribe(); eventBus.Subscribe(); +eventBus.Subscribe(); eventBus.Subscribe(); await app.RunAsync(); From 0cc909b660f0927e1b03bb2e67c0702d8cc72140 Mon Sep 17 00:00:00 2001 From: hsn Date: Tue, 11 Jul 2023 09:22:41 +0300 Subject: [PATCH 09/11] Wrongly located Integration event is removed. --- .../OrderCompletedIntegrationEventHandler.cs | 40 ------------------- .../Events/OrderCompletedIntegrationEvent.cs | 10 ----- src/Services/Ordering/Ordering.API/Program.cs | 2 - 3 files changed, 52 deletions(-) delete mode 100644 src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderCompletedIntegrationEventHandler.cs delete mode 100644 src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderCompletedIntegrationEvent.cs diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderCompletedIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderCompletedIntegrationEventHandler.cs deleted file mode 100644 index dcc3f0c43..000000000 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderCompletedIntegrationEventHandler.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.EventHandling; - -public class OrderCompletedIntegrationEventHandler : IIntegrationEventHandler -{ - private readonly IMediator _mediator; - private readonly ILogger _logger; - - public OrderCompletedIntegrationEventHandler( - IMediator mediator, - ILogger logger) - { - _mediator = mediator; - _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); - } - - /// - /// Event handler which confirms order is completed. - /// - /// - /// - /// - public async Task Handle(OrderCompletedIntegrationEvent @event) - { - using (_logger.BeginScope(new List> { new ("IntegrationEventContext", @event.Id) })) - { - _logger.LogInformation("Handling integration event: {IntegrationEventId} - ({@IntegrationEvent})", @event.Id, @event); - - var command = new SetAwaitingValidationOrderStatusCommand(@event.OrderId); - - _logger.LogInformation( - "Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", - command.GetGenericTypeName(), - nameof(command.OrderNumber), - command.OrderNumber, - command); - - await _mediator.Send(command); - } - } -} diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderCompletedIntegrationEvent.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderCompletedIntegrationEvent.cs deleted file mode 100644 index 555cb1ee4..000000000 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderCompletedIntegrationEvent.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; - -public record OrderCompletedIntegrationEvent : IntegrationEvent -{ - public int OrderId { get; } - - public OrderCompletedIntegrationEvent(int orderId) => - OrderId = orderId; -} - diff --git a/src/Services/Ordering/Ordering.API/Program.cs b/src/Services/Ordering/Ordering.API/Program.cs index e3bd21f2a..cab0e0b95 100644 --- a/src/Services/Ordering/Ordering.API/Program.cs +++ b/src/Services/Ordering/Ordering.API/Program.cs @@ -35,7 +35,6 @@ services.AddScoped(); // Add integration event handlers. services.AddTransient, GracePeriodConfirmedIntegrationEventHandler>(); -services.AddTransient, OrderCompletedIntegrationEventHandler>(); services.AddTransient, OrderPaymentFailedIntegrationEventHandler>(); services.AddTransient, OrderPaymentSucceededIntegrationEventHandler>(); services.AddTransient, OrderStockConfirmedIntegrationEventHandler>(); @@ -53,7 +52,6 @@ var eventBus = app.Services.GetRequiredService(); eventBus.Subscribe>(); eventBus.Subscribe>(); -eventBus.Subscribe>(); eventBus.Subscribe>(); eventBus.Subscribe>(); eventBus.Subscribe>(); From 51c9e0dce64c3af9a73bf5218ea58c3a4cf77b78 Mon Sep 17 00:00:00 2001 From: hsn Date: Wed, 12 Jul 2023 08:22:06 +0300 Subject: [PATCH 10/11] SetCompletedStatus method logical mistake is reverted. --- .../Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs index 2670f5e49..3a86bc1cf 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs @@ -158,9 +158,8 @@ public class Order public void SetCompletedStatus() { - // make sure it is shipped and paid before completing - if (_orderStatusId == OrderStatus.Paid.Id || - _orderStatusId == OrderStatus.Shipped.Id) + // make sure it is shipped before completing + if (_orderStatusId == OrderStatus.Shipped.Id) { StatusChangeException(OrderStatus.Completed); } From ddd99393692f5b30f723ced3adf904073da5b8da Mon Sep 17 00:00:00 2001 From: hsn Date: Wed, 12 Jul 2023 08:22:38 +0300 Subject: [PATCH 11/11] Configurations are added for Docker Compose --- src/docker-compose.override.yml | 2 +- src/eShopOnContainers-ServicesAndWebApps.sln | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/docker-compose.override.yml b/src/docker-compose.override.yml index 086804125..5f00bc8bb 100644 --- a/src/docker-compose.override.yml +++ b/src/docker-compose.override.yml @@ -260,7 +260,7 @@ services: webspa: environment: - - ASPNETCORE_ENVIRONMENT=Production + - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=http://0.0.0.0:80 - IdentityUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105 - PurchaseUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5121 diff --git a/src/eShopOnContainers-ServicesAndWebApps.sln b/src/eShopOnContainers-ServicesAndWebApps.sln index d73ec39c4..4fa1ce317 100644 --- a/src/eShopOnContainers-ServicesAndWebApps.sln +++ b/src/eShopOnContainers-ServicesAndWebApps.sln @@ -443,8 +443,8 @@ Global {F16E3C6A-1C94-4EAB-BE91-099618060B68}.AppStore|x64.Build.0 = Release|Any CPU {F16E3C6A-1C94-4EAB-BE91-099618060B68}.AppStore|x86.ActiveCfg = Release|Any CPU {F16E3C6A-1C94-4EAB-BE91-099618060B68}.AppStore|x86.Build.0 = Release|Any CPU - {F16E3C6A-1C94-4EAB-BE91-099618060B68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F16E3C6A-1C94-4EAB-BE91-099618060B68}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F16E3C6A-1C94-4EAB-BE91-099618060B68}.Debug|Any CPU.ActiveCfg = Release|Any CPU + {F16E3C6A-1C94-4EAB-BE91-099618060B68}.Debug|Any CPU.Build.0 = Release|Any CPU {F16E3C6A-1C94-4EAB-BE91-099618060B68}.Debug|ARM.ActiveCfg = Debug|Any CPU {F16E3C6A-1C94-4EAB-BE91-099618060B68}.Debug|ARM.Build.0 = Debug|Any CPU {F16E3C6A-1C94-4EAB-BE91-099618060B68}.Debug|iPhone.ActiveCfg = Debug|Any CPU