Skip to content
This repository was archived by the owner on Nov 17, 2023. It is now read-only.

Added complete order service #2170

Open
wants to merge 4 commits into
base: dev
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers;

[Route("api/v1/[controller]")]
[Authorize]

[ApiController]
public class OrderController : ControllerBase
{
@@ -33,4 +33,15 @@ public async Task<ActionResult<OrderData>> GetOrderDraftAsync(string basketId)

return await _orderingService.GetOrderDraftAsync(basket);
}
[Route("complete")]
[HttpPut]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<CompleteData>> CompleteOrderAsync(string orderId)
{
if (string.IsNullOrWhiteSpace(orderId))
{
return BadRequest("Need a valid orderId");
}
return await _orderingService.CompleteOrderAsync(orderId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models
{
public class CompleteData
{
public string CompleteStatus { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models
{
public class CompleteRequest
{
public string OrderId { get; set; }
}
}
2 changes: 1 addition & 1 deletion src/ApiGateways/Web.Bff.Shopping/aggregator/Program.cs
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@

builder.Services.AddReverseProxy(builder.Configuration);
builder.Services.AddControllers();

builder.Services.AddHttpClient<OrderingService>();
builder.Services.AddHealthChecks(builder.Configuration);
builder.Services.AddCors(options =>
{
Original file line number Diff line number Diff line change
@@ -3,4 +3,5 @@
public interface IOrderingService
{
Task<OrderData> GetOrderDraftAsync(BasketData basketData);
Task<CompleteData> CompleteOrderAsync(string orderId);
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services;
using Newtonsoft.Json;
using System.Text;

namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services;

public class OrderingService : IOrderingService
{
private readonly GrpcOrdering.OrderingGrpc.OrderingGrpcClient _orderingGrpcClient;
private readonly ILogger<OrderingService> _logger;
private readonly HttpClient _httpClient;

public OrderingService(GrpcOrdering.OrderingGrpc.OrderingGrpcClient orderingGrpcClient, ILogger<OrderingService> logger)
public OrderingService(GrpcOrdering.OrderingGrpc.OrderingGrpcClient orderingGrpcClient, ILogger<OrderingService> logger, HttpClient httpClient)
{
_orderingGrpcClient = orderingGrpcClient;
_logger = logger;
_httpClient = httpClient;
}

public async Task<OrderData> GetOrderDraftAsync(BasketData basketData)
@@ -68,5 +73,28 @@ private GrpcOrdering.CreateOrderDraftCommand MapToOrderDraftCommand(BasketData b

return command;
}
/// <summary>
/// CompleteOrderAsync is the endpoint that will be called to indicate that the order is complete.
/// </summary>
/// <param name="orderId">Order number</param>
/// <returns></returns>
public async Task<CompleteData> CompleteOrderAsync(string orderId)
{
// TODO Grpc OrderingGrpc bağlantı servisinde hata alınmaktadır.Bu nedenle httpclient sınıfı kullanılmıştır.
#region OrderingGrpc
CompleteData completeData = new CompleteData();
_logger.LogDebug("CompleteOrderAsync method called with orderId={@orderId}", orderId);

var request = new GrpcOrdering.CompleteOrderCommand
{
OrderId = orderId
};
var response = await _orderingGrpcClient.CompleteOrderAsync(request);

_logger.LogDebug("gRPC CompleteOrder response: {@response}", response);
completeData.CompleteStatus = response.CompleteStatus;
return completeData;
#endregion
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
{
public class CompleteOrderCommand : IRequest<CompleteOrderDTO>
{
public int OrderId { get; set; }
public CompleteOrderCommand(int orderNumber)
{
OrderId = orderNumber;
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
{
public class CompleteOrderCommandHandler : IRequestHandler<CompleteOrderCommand, CompleteOrderDTO>
{
private readonly IOrderRepository _orderRepository; // Varsayılan bir repo

private readonly IMediator _mediator; // Integration event'leri fırlatmak için

public CompleteOrderCommandHandler(IOrderRepository orderRepository, IMediator mediator)
{
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
}

public async Task<CompleteOrderDTO> Handle(CompleteOrderCommand request, CancellationToken cancellationToken)
{

//var order = await _orderRepository.GetOrderAsync(request.OrderId);
CompleteOrderDTO completeStatus = new CompleteOrderDTO();
var order = await _orderRepository.GetAsync(request.OrderId);
if (order == null)
{
completeStatus.CompleteStatus = "Incompleted";
return completeStatus;
}
order.CompleteOrder(); // The status of the order was set to "Complete".

_orderRepository.Update(order);
await _orderRepository.UnitOfWork.SaveChangesAsync(cancellationToken);

await _mediator.Publish(new OrderCompletedIntegrationEvent(order.Id)); //When the process is completed, an integration event is thrown.
completeStatus.CompleteStatus = "Completed";
return completeStatus;
}
}
public class CompleteOrderDTO
{
public string CompleteStatus { get; set; }

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using Ordering.Domain.Events;

namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers
{
public class OrderCompletedDomainEventHandler : INotificationHandler<OrderCompletedDomainEvent>
{
private readonly IOrderRepository _orderRepository;
private readonly IBuyerRepository _buyerRepository;
private readonly ILogger _logger;
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;

public OrderCompletedDomainEventHandler(
IOrderRepository orderRepository,
ILogger<OrderCompletedDomainEventHandler> 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(OrderCompletedDomainEvent domainEvent, CancellationToken cancellationToken)
{
OrderingApiTrace.LogOrderStatusUpdated(_logger, domainEvent.Order.Id, nameof(OrderStatus.Completed), OrderStatus.Completed.Id);

var order = await _orderRepository.GetAsync(domainEvent.Order.Id);
var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString());

var integrationEvent = new OrderCompletedIntegrationEvent(order.Id);
await _orderingIntegrationEventService.AddAndSaveEventAsync(integrationEvent);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events
{
public record OrderCompletedIntegrationEvent : IntegrationEvent
{
public int OrderId { get; }

public OrderCompletedIntegrationEvent(int orderId)
{
OrderId = orderId;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Validations
{
public class CompleteOrderCommandValidator : AbstractValidator<CompleteOrderCommand>
{
public CompleteOrderCommandValidator(ILogger<CompleteOrderCommandValidator> logger)
{
RuleFor(command => command.OrderId)
.GreaterThan(0)
.WithMessage("OrderId should be a positive number.");

logger.LogTrace("INSTANCE CREATED - {ClassName}", GetType().Name);
}
}
}
18 changes: 18 additions & 0 deletions src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs
Original file line number Diff line number Diff line change
@@ -138,4 +138,22 @@ public async Task<ActionResult<OrderDraftDTO>> CreateOrderDraftFromBasketDataAsy

return await _mediator.Send(createOrderDraftCommand);
}

[Route("complete")]
[HttpPut]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<CompleteOrderDTO>> CompleteOrderAsync([FromBody] CompleteOrderCommand command)
{
CompleteOrderDTO completeOrderDTO = new CompleteOrderDTO();
_logger.LogInformation(
"Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
command.GetGenericTypeName(),
nameof(command.OrderId),
command.OrderId,
command);

completeOrderDTO = await _mediator.Send(command);
return completeOrderDTO;
}
}
Original file line number Diff line number Diff line change
@@ -17,28 +17,28 @@ await policy.ExecuteAsync(async () =>
var contentRootPath = env.ContentRootPath;


using (context)
{
context.Database.Migrate();

if (!context.CardTypes.Any())
{
context.CardTypes.AddRange(useCustomizationData
? GetCardTypesFromFile(contentRootPath, logger)
: GetPredefinedCardTypes());
//using (context)
//{
context.Database.Migrate();

await context.SaveChangesAsync();
}

if (!context.OrderStatus.Any())
{
context.OrderStatus.AddRange(useCustomizationData
? GetOrderStatusFromFile(contentRootPath, logger)
: GetPredefinedOrderStatus());
}
if (!context.CardTypes.Any())
{
context.CardTypes.AddRange(useCustomizationData
? GetCardTypesFromFile(contentRootPath, logger)
: GetPredefinedCardTypes());

await context.SaveChangesAsync();
}

if (!context.OrderStatus.Any())
{
context.OrderStatus.AddRange(useCustomizationData
? GetOrderStatusFromFile(contentRootPath, logger)
: GetPredefinedOrderStatus());
}

await context.SaveChangesAsync();
//}
});
}

@@ -115,6 +115,7 @@ private IEnumerable<OrderStatus> GetOrderStatusFromFile(string contentRootPath,
.Where(x => x != null);
}


private OrderStatus CreateOrderStatus(string value, ref int id)
{
if (string.IsNullOrEmpty(value))
9 changes: 7 additions & 2 deletions src/Services/Ordering/Ordering.API/Proto/ordering.proto
Original file line number Diff line number Diff line change
@@ -6,13 +6,16 @@ package OrderingApi;

service OrderingGrpc {
rpc CreateOrderDraftFromBasketData(CreateOrderDraftCommand) returns (OrderDraftDTO) {}
rpc CompleteOrder(CompleteOrderCommand) returns (CompleteOrderDTO) {}
}

message CreateOrderDraftCommand {
string buyerId = 1;
repeated BasketItem items = 2;
}

message CompleteOrderCommand {
string orderId = 1;
}

message BasketItem {
string id = 1;
@@ -23,11 +26,13 @@ message BasketItem {
int32 quantity = 6;
string pictureUrl = 7;
}

message OrderDraftDTO {
double total = 1;
repeated OrderItemDTO orderItems = 2;
}
message CompleteOrderDTO {
string CompleteStatus = 1;
}
message OrderItemDTO {
int32 productId = 1;
string productName = 2;
3 changes: 2 additions & 1 deletion src/Services/Ordering/Ordering.API/Setup/OrderStatus.csv
Original file line number Diff line number Diff line change
@@ -4,4 +4,5 @@ AwaitingValidation
StockConfirmed
Paid
Shipped
Cancelled
Cancelled
Completed
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
using Ordering.Domain.Events;

namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;

public class Order
: Entity, IAggregateRoot
@@ -157,6 +159,19 @@ public void SetCancelledStatus()
_description = $"The order was cancelled.";
AddDomainEvent(new OrderCancelledDomainEvent(this));
}
public void CompleteOrder()
{
if (_orderStatusId == OrderStatus.Shipped.Id ||
_orderStatusId == OrderStatus.Completed.Id)
{
StatusChangeException(OrderStatus.Completed);
}

_orderStatusId = OrderStatus.Cancelled.Id;
_description = $"The order was completed.";
AddDomainEvent(new OrderCompletedDomainEvent(this));
}


public void SetCancelledStatusWhenStockIsRejected(IEnumerable<int> orderStockRejectedItems)
{
Original file line number Diff line number Diff line change
@@ -11,14 +11,15 @@ 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)
{
}

public static IEnumerable<OrderStatus> List() =>
new[] { Submitted, AwaitingValidation, StockConfirmed, Paid, Shipped, Cancelled };
new[] { Submitted, AwaitingValidation, StockConfirmed, Paid, Shipped, Cancelled, Completed };

public static OrderStatus FromName(string name)
{
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Ordering.Domain.Events
{
public class OrderCompletedDomainEvent : INotification
{
public Order Order { get; }

public OrderCompletedDomainEvent(Order order)
{
Order = order;
}
}
}
Original file line number Diff line number Diff line change
@@ -131,4 +131,34 @@ public async Task Get_cardTypes_success()
//Assert
Assert.Equal((actionResult.Result as OkObjectResult).StatusCode, (int)System.Net.HttpStatusCode.OK);
}
/// <summary>
/// Unit tests for CompleteOrderAsync.
/// </summary>
/// <returns></returns>
[Fact]
public async Task CompleteOrderAsync_WithValidCommand_ReturnsOkResult()
{
// Arrange
var validCommand = new CompleteOrderCommand(1);

var validOrderDTO = new CompleteOrderDTO
{
CompleteStatus = "Completed",

};

_mediatorMock.Setup(x => x.Send(It.IsAny<CompleteOrderCommand>(), default))
.ReturnsAsync(validOrderDTO);

// Act
var ordersController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object);
var actionResult = await ordersController.CompleteOrderAsync(validCommand) as ActionResult<CompleteOrderDTO>;

// Assert
var okResult = actionResult.Result as OkObjectResult;
Assert.NotNull(okResult);
Assert.Equal((int)System.Net.HttpStatusCode.OK, okResult.StatusCode);
var returnedOrderDTO = okResult.Value as CompleteOrderDTO;
Assert.Equal(validOrderDTO, returnedOrderDTO);
}
}
15,784 changes: 7,892 additions & 7,892 deletions src/Web/WebSPA/Client/yarn.lock

Large diffs are not rendered by default.