Browse Source

Add coupon support to api services

pull/2020/head
facepalm 2 years ago
parent
commit
86cedb4bcf
41 changed files with 575 additions and 50 deletions
  1. +21
    -0
      src/ApiGateways/Envoy/config/webshopping/envoy.yaml
  2. +2
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Config/UrlsConfig.cs
  3. +26
    -2
      src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs
  4. +7
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Models/CouponData.cs
  5. +28
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Services/CouponService.cs
  6. +6
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Services/ICouponService.cs
  7. +14
    -2
      src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs
  8. +18
    -3
      src/Services/Basket/Basket.API/Controllers/BasketController.cs
  9. +9
    -1
      src/Services/Basket/Basket.API/IntegrationEvents/Events/UserCheckoutAcceptedIntegrationEvent.cs
  10. +4
    -0
      src/Services/Basket/Basket.API/Model/BasketCheckout.cs
  11. +24
    -4
      src/Services/Identity/Identity.API/Configuration/Config.cs
  12. +1
    -0
      src/Services/Identity/Identity.API/Data/ConfigurationDbContextSeed.cs
  13. +17
    -0
      src/Services/Ordering/Ordering.API/Application/Commands/CouponConfirmedCommand.cs
  14. +41
    -0
      src/Services/Ordering/Ordering.API/Application/Commands/CouponConfirmedCommandHandler.cs
  15. +9
    -1
      src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs
  16. +1
    -1
      src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs
  17. +1
    -1
      src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCancelled/OrderCancelledDomainEventHandler.cs
  18. +37
    -0
      src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCoupon/OrderStatusChangedToAwaitingCouponValidationDomainEventHandler.cs
  19. +32
    -0
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderCouponConfirmedIntegrationEventHandler.cs
  20. +33
    -0
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderCouponRejectedIntegrationEventHandler.cs
  21. +7
    -4
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/UserCheckoutAcceptedIntegrationEventHandler.cs
  22. +12
    -0
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderCouponConfirmedIntegrationEvent.cs
  23. +12
    -0
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderCouponRejectedIntegrationEvent.cs
  24. +20
    -0
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToAwaitingCouponValidationIntegrationEvent.cs
  25. +3
    -1
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToCancelledIntegrationEvent.cs
  26. +8
    -2
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/UserCheckoutAcceptedIntegrationEvent.cs
  27. +31
    -9
      src/Services/Ordering/Ordering.API/Application/Queries/OrderQueries.cs
  28. +3
    -0
      src/Services/Ordering/Ordering.API/Application/Queries/OrderViewModel.cs
  29. +1
    -0
      src/Services/Ordering/Ordering.API/Infrastructure/OrderingContextSeed.cs
  30. +5
    -0
      src/Services/Ordering/Ordering.API/Startup.cs
  31. +48
    -6
      src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs
  32. +6
    -5
      src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderStatus.cs
  33. +19
    -0
      src/Services/Ordering/Ordering.Domain/Events/OrderStatusChangedToAwaitingCouponValidationDomainEvent.cs
  34. +4
    -0
      src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/OrderEntityTypeConfiguration.cs
  35. +30
    -0
      src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingCouponValidationIntegrationEventHandler.cs
  36. +17
    -0
      src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToAwaitingCouponValidationIntegrationEvent.cs
  37. +4
    -0
      src/Services/Ordering/Ordering.SignalrHub/Startup.cs
  38. +3
    -1
      src/Services/Ordering/Ordering.UnitTests/Application/IdentifiedCommandHandlerTest.cs
  39. +5
    -3
      src/Services/Ordering/Ordering.UnitTests/Application/NewOrderCommandHandlerTest.cs
  40. +3
    -1
      src/Services/Ordering/Ordering.UnitTests/Builders.cs
  41. +3
    -3
      src/Services/Ordering/Ordering.UnitTests/Domain/OrderAggregateTest.cs

+ 21
- 0
src/ApiGateways/Envoy/config/webshopping/envoy.yaml View File

@ -72,6 +72,19 @@ static_resources:
route:
auto_host_rewrite: true
cluster: basket
- name: "cp-short"
match:
prefix: "/cp/"
route:
auto_host_rewrite: true
prefix_rewrite: "/coupon-api/"
cluster: coupon
- name: "cp-long"
match:
prefix: "/coupon-api/"
route:
auto_host_rewrite: true
cluster: coupon
- name: "agg"
match:
prefix: "/"
@ -140,3 +153,11 @@ static_resources:
- socket_address:
address: ordering-signalrhub
port_value: 80
- name: coupon
connect_timeout: 0.25s
type: strict_dns
lb_policy: round_robin
hosts:
- socket_address:
address: coupon-api
port_value: 80

+ 2
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Config/UrlsConfig.cs View File

@ -37,5 +37,7 @@ public class UrlsConfig
public string GrpcCatalog { get; set; }
public string GrpcOrdering { get; set; }
public string Coupon { get; set; }
}

+ 26
- 2
src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs View File

@ -1,4 +1,6 @@
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers;
using Newtonsoft.Json;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers;
[Route("api/v1/[controller]")]
[Authorize]
@ -7,11 +9,13 @@ public class BasketController : ControllerBase
{
private readonly ICatalogService _catalog;
private readonly IBasketService _basket;
private readonly ICouponService _coupon;
public BasketController(ICatalogService catalogService, IBasketService basketService)
public BasketController(ICatalogService catalogService, IBasketService basketService, ICouponService couponService)
{
_catalog = catalogService;
_basket = basketService;
_coupon = couponService;
}
[HttpPost]
@ -151,4 +155,24 @@ public class BasketController : ControllerBase
return Ok();
}
[HttpGet]
[Route("coupon/{code}")]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
[ProducesResponseType(typeof(CouponData), (int)HttpStatusCode.OK)]
public async Task<ActionResult<CouponData>> CheckCouponAsync(string code)
{
var response = await _coupon.CheckCouponByCodeNumberAsync(code);
if (!response.IsSuccessStatusCode)
{
return NotFound();
}
var couponResponse = await response.Content.ReadAsStringAsync();
var data = JsonConvert.DeserializeObject<CouponData>(couponResponse);
return Ok(data);
}
}

+ 7
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Models/CouponData.cs View File

@ -0,0 +1,7 @@
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
public class CouponData
{
public int Discount { get; set; }
public string Code { get; set; }
}

+ 28
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Services/CouponService.cs View File

@ -0,0 +1,28 @@
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services;
public class CouponService : ICouponService
{
public readonly HttpClient _httpClient;
private readonly UrlsConfig _urls;
private readonly ILogger<CouponService> _logger;
public CouponService(HttpClient httpClient, IOptions<UrlsConfig> config, ILogger<CouponService> logger)
{
_urls = config.Value;
_httpClient = httpClient;
_logger = logger;
}
public async Task<HttpResponseMessage> CheckCouponByCodeNumberAsync(string codeNumber)
{
_logger.LogInformation("Call coupon api with codenumber: {codeNumber}", codeNumber);
var url = new Uri($"{_urls.Catalog}/api/v1/coupon/{codeNumber}");
var response = await _httpClient.GetAsync(url);
_logger.LogInformation("Coupon api response: {@response}", response);
return response;
}
}

+ 6
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Services/ICouponService.cs View File

@ -0,0 +1,6 @@
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services;
public interface ICouponService
{
Task<HttpResponseMessage> CheckCouponByCodeNumberAsync(string codeNumber);
}

+ 14
- 2
src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs View File

@ -1,4 +1,6 @@
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator;
public class Startup
{
@ -12,7 +14,7 @@ public class Startup
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddHealthChecks()
var healthCheckBuilder = services.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy())
.AddUrlGroup(new Uri(Configuration["CatalogUrlHC"]), name: "catalogapi-check", tags: new string[] { "catalogapi" })
.AddUrlGroup(new Uri(Configuration["OrderingUrlHC"]), name: "orderingapi-check", tags: new string[] { "orderingapi" })
@ -20,6 +22,13 @@ public class Startup
.AddUrlGroup(new Uri(Configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" })
.AddUrlGroup(new Uri(Configuration["PaymentUrlHC"]), name: "paymentapi-check", tags: new string[] { "paymentapi" });
// HC dependency is configured this way to save one build step on ACR
var couponHealthEndpoint = Configuration["CouponUrlHC"];
if (!string.IsNullOrWhiteSpace(couponHealthEndpoint))
{
healthCheckBuilder.AddUrlGroup(new Uri(couponHealthEndpoint), name: "couponapi-check", tags: new string[] { "couponapi" });
}
services.AddCustomMvc(Configuration)
.AddCustomAuthentication(Configuration)
.AddDevspaces()
@ -163,6 +172,9 @@ public static class ServiceCollectionExtensions
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
.AddDevspacesSupport();
services.AddHttpClient<ICouponService, CouponService>()
. AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>();
return services;
}


+ 18
- 3
src/Services/Basket/Basket.API/Controllers/BasketController.cs View File

@ -58,9 +58,24 @@ public class BasketController : ControllerBase
var userName = this.HttpContext.User.FindFirst(x => x.Type == ClaimTypes.Name).Value;
var eventMessage = new UserCheckoutAcceptedIntegrationEvent(userId, userName, basketCheckout.City, basketCheckout.Street,
basketCheckout.State, basketCheckout.Country, basketCheckout.ZipCode, basketCheckout.CardNumber, basketCheckout.CardHolderName,
basketCheckout.CardExpiration, basketCheckout.CardSecurityNumber, basketCheckout.CardTypeId, basketCheckout.Buyer, basketCheckout.RequestId, basket);
var eventMessage = new UserCheckoutAcceptedIntegrationEvent(
userId,
userName,
basketCheckout.City,
basketCheckout.Street,
basketCheckout.State,
basketCheckout.Country,
basketCheckout.ZipCode,
basketCheckout.CardNumber,
basketCheckout.CardHolderName,
basketCheckout.CardExpiration,
basketCheckout.CardSecurityNumber,
basketCheckout.CardTypeId,
basketCheckout.Buyer,
basketCheckout.RequestId,
basket,
basketCheckout.Coupon,
basketCheckout.Discount);
// Once basket is checkout, sends an integration event to
// ordering.api to convert basket to order and proceeds with


+ 9
- 1
src/Services/Basket/Basket.API/IntegrationEvents/Events/UserCheckoutAcceptedIntegrationEvent.cs View File

@ -34,10 +34,16 @@ public record UserCheckoutAcceptedIntegrationEvent : IntegrationEvent
public CustomerBasket Basket { get; }
public string CodeDiscount { get; set; }
public int Discount { get; set; }
public UserCheckoutAcceptedIntegrationEvent(string userId, string userName, string city, string street,
string state, string country, string zipCode, string cardNumber, string cardHolderName,
DateTime cardExpiration, string cardSecurityNumber, int cardTypeId, string buyer, Guid requestId,
CustomerBasket basket)
CustomerBasket basket,
string codeDiscount,
int discount)
{
UserId = userId;
UserName = userName;
@ -54,6 +60,8 @@ public record UserCheckoutAcceptedIntegrationEvent : IntegrationEvent
Buyer = buyer;
Basket = basket;
RequestId = requestId;
CodeDiscount = codeDiscount;
Discount = discount;
}
}

+ 4
- 0
src/Services/Basket/Basket.API/Model/BasketCheckout.cs View File

@ -25,4 +25,8 @@ public class BasketCheckout
public string Buyer { get; set; }
public Guid RequestId { get; set; }
public string Coupon { get; set; }
public int Discount { get; set; }
}

+ 24
- 4
src/Services/Identity/Identity.API/Configuration/Config.cs View File

@ -15,6 +15,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
new ApiResource("webshoppingagg", "Web Shopping Aggregator"),
new ApiResource("orders.signalrhub", "Ordering Signalr Hub"),
new ApiResource("webhooks", "Webhooks registration Service"),
new ApiResource("coupon", "Coupon Service"),
};
}
@ -53,7 +54,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
"basket",
"webshoppingagg",
"orders.signalrhub",
"webhooks"
"webhooks",
"coupon"
},
},
new Client
@ -79,7 +81,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
"orders",
"basket",
"mobileshoppingagg",
"webhooks"
"webhooks",
"coupon"
},
//Allow requesting refresh tokens for long lived API access
AllowOfflineAccess = true,
@ -117,7 +120,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
"basket",
"webshoppingagg",
"orders.signalrhub",
"webhooks"
"webhooks",
"coupon"
},
AccessTokenLifetime = 60*60*2, // 2 hours
IdentityTokenLifetime= 60*60*2 // 2 hours
@ -183,7 +187,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
"orders",
"basket",
"webshoppingagg",
"webhooks"
"webhooks",
"coupon"
},
},
new Client
@ -261,6 +266,21 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
{
"webhooks"
}
},
new Client
{
ClientId = "couponswaggerui",
ClientName = "Coupon Swagger UI",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RedirectUris = { $"{clientsUrl["CouponApi"]}/swagger/oauth2-redirect.html" },
PostLogoutRedirectUris = { $"{clientsUrl["CouponApi"]}/swagger/" },
AllowedScopes =
{
"coupon"
}
}
};
}


+ 1
- 0
src/Services/Identity/Identity.API/Data/ConfigurationDbContextSeed.cs View File

@ -19,6 +19,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data
clientUrls.Add("WebShoppingAgg", configuration.GetValue<string>("WebShoppingAggClient"));
clientUrls.Add("WebhooksApi", configuration.GetValue<string>("WebhooksApiClient"));
clientUrls.Add("WebhooksWeb", configuration.GetValue<string>("WebhooksWebClient"));
clientUrls.Add("CouponApi", configuration.GetValue<string>("CouponApiClient"));
if (!context.Clients.Any())
{


+ 17
- 0
src/Services/Ordering/Ordering.API/Application/Commands/CouponConfirmedCommand.cs View File

@ -0,0 +1,17 @@
namespace Ordering.API.Application.Commands;
public class CouponConfirmedCommand : IRequest<bool>
{
[DataMember]
public int OrderNumber { get; private set; }
[DataMember]
public int Discount { get; private set; }
public CouponConfirmedCommand(int orderNumber, int discount)
{
OrderNumber = orderNumber;
Discount = discount;
}
}

+ 41
- 0
src/Services/Ordering/Ordering.API/Application/Commands/CouponConfirmedCommandHandler.cs View File

@ -0,0 +1,41 @@
namespace Ordering.API.Application.Commands;
public class CouponConfirmedCommandHandler : IRequestHandler<CouponConfirmedCommand, bool>
{
private readonly IOrderRepository _orderRepository;
public CouponConfirmedCommandHandler(IOrderRepository orderRepository)
{
_orderRepository = orderRepository;
}
public async Task<bool> Handle(CouponConfirmedCommand command, CancellationToken cancellationToken)
{
var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber);
if (orderToUpdate == null)
{
return false;
}
orderToUpdate.ProcessCouponConfirmed();
return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
}
}
public class CouponConfirmIdenfifiedCommandHandler : IdentifiedCommandHandler<CouponConfirmedCommand, bool>
{
public CouponConfirmIdenfifiedCommandHandler(
IMediator mediator,
IRequestManager requestManager,
ILogger<IdentifiedCommandHandler<CouponConfirmedCommand, bool>> logger)
: base(mediator, requestManager, logger)
{
}
protected override bool CreateResultForDuplicateRequest()
{
return true; // Ignore duplicate requests for processing order.
}
}

+ 9
- 1
src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs View File

@ -54,6 +54,12 @@ public class CreateOrderCommand
[DataMember]
public int CardTypeId { get; private set; }
[DataMember]
public string CodeDiscount { get; private set; }
[DataMember]
public decimal Discount { get; private set; }
[DataMember]
public IEnumerable<OrderItemDTO> OrderItems => _orderItems;
@ -64,7 +70,7 @@ public class CreateOrderCommand
public CreateOrderCommand(List<BasketItem> basketItems, string userId, string userName, string city, string street, string state, string country, string zipcode,
string cardNumber, string cardHolderName, DateTime cardExpiration,
string cardSecurityNumber, int cardTypeId) : this()
string cardSecurityNumber, int cardTypeId, string codeDiscount, decimal discount) : this()
{
_orderItems = basketItems.ToOrderItemsDTO().ToList();
UserId = userId;
@ -79,6 +85,8 @@ public class CreateOrderCommand
CardExpiration = cardExpiration;
CardSecurityNumber = cardSecurityNumber;
CardTypeId = cardTypeId;
CodeDiscount = codeDiscount;
Discount = discount;
}


+ 1
- 1
src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs View File

@ -37,7 +37,7 @@ public class CreateOrderCommandHandler
// methods and constructor so validations, invariants and business logic
// make sure that consistency is preserved across the whole aggregate
var address = new Address(message.Street, message.City, message.State, message.Country, message.ZipCode);
var order = new Order(message.UserId, message.UserName, address, message.CardTypeId, message.CardNumber, message.CardSecurityNumber, message.CardHolderName, message.CardExpiration);
var order = new Order(message.UserId, message.UserName, address, message.CardTypeId, message.CardNumber, message.CardSecurityNumber, message.CardHolderName, message.CardExpiration, "", 0);
foreach (var item in message.OrderItems)
{


+ 1
- 1
src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCancelled/OrderCancelledDomainEventHandler.cs View File

@ -31,7 +31,7 @@ public class OrderCancelledDomainEventHandler
var order = await _orderRepository.GetAsync(orderCancelledDomainEvent.Order.Id);
var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString());
var orderStatusChangedToCancelledIntegrationEvent = new OrderStatusChangedToCancelledIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name);
var orderStatusChangedToCancelledIntegrationEvent = new OrderStatusChangedToCancelledIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name, order.DiscountCode);
await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedToCancelledIntegrationEvent);
}
}

+ 37
- 0
src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCoupon/OrderStatusChangedToAwaitingCouponValidationDomainEventHandler.cs View File

@ -0,0 +1,37 @@
using Ordering.API.Application.IntegrationEvents.Events;
using Ordering.Domain.Events;
namespace Ordering.API.Application.DomainEventHandlers.OrderCoupon;
public class OrderStatusChangedToAwaitingCouponValidationDomainEventHandler : INotificationHandler<OrderStatusChangedToAwaitingCouponValidationDomainEvent>
{
private readonly IOrderRepository _orderRepository;
private readonly IBuyerRepository _buyerRepository;
private readonly ILoggerFactory _logger;
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
public OrderStatusChangedToAwaitingCouponValidationDomainEventHandler(
IOrderRepository orderRepository,
IBuyerRepository buyerRepository,
ILoggerFactory logger,
IOrderingIntegrationEventService orderingIntegrationEventService)
{
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
_buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_orderingIntegrationEventService = orderingIntegrationEventService;
}
public async Task Handle(OrderStatusChangedToAwaitingCouponValidationDomainEvent domainEvent, CancellationToken cancellationToken)
{
_logger.CreateLogger<OrderStatusChangedToAwaitingCouponValidationDomainEventHandler>()
.LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})", domainEvent.OrderId, nameof(OrderStatus.StockConfirmed), OrderStatus.StockConfirmed.Id);
var order = await _orderRepository.GetAsync(domainEvent.OrderId);
var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString());
var integrationEvent = new OrderStatusChangedToAwaitingCouponValidationIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name, order.DiscountCode);
await _orderingIntegrationEventService.AddAndSaveEventAsync(integrationEvent);
}
}

+ 32
- 0
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderCouponConfirmedIntegrationEventHandler.cs View File

@ -0,0 +1,32 @@
using Ordering.API.Application.Commands;
using Ordering.API.Application.IntegrationEvents.Events;
namespace Ordering.API.Application.IntegrationEvents.EventHandling;
public class OrderCouponConfirmedIntegrationEventHandler : IIntegrationEventHandler<OrderCouponConfirmedIntegrationEvent>
{
private readonly IMediator _mediator;
public OrderCouponConfirmedIntegrationEventHandler(IMediator mediator)
{
_mediator = mediator;
}
public async Task Handle(OrderCouponConfirmedIntegrationEvent @event)
{
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
{
Log.Information("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
var command = new CouponConfirmedCommand(@event.OrderId, @event.Discount);
Log.Information("----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
command.GetGenericTypeName(),
nameof(command.OrderNumber),
command.OrderNumber,
command);
await _mediator.Send(command);
}
}
}

+ 33
- 0
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderCouponRejectedIntegrationEventHandler.cs View File

@ -0,0 +1,33 @@
using Ordering.API.Application.IntegrationEvents.Events;
namespace Ordering.API.Application.IntegrationEvents.EventHandling;
public class OrderCouponRejectedIntegrationEventHandler : IIntegrationEventHandler<OrderCouponRejectedIntegrationEvent>
{
private readonly IMediator _mediator;
public OrderCouponRejectedIntegrationEventHandler(IMediator mediator)
{
_mediator = mediator;
}
public async Task Handle(OrderCouponRejectedIntegrationEvent @event)
{
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
{
Log.Information("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
Log.Warning("Discount failed, cancelling order {OrderId}", @event.OrderId);
var command = new CancelOrderCommand(@event.OrderId);
Log.Information("----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
command.GetGenericTypeName(),
nameof(command.OrderNumber),
command.OrderNumber,
command);
await _mediator.Send(command);
}
}
}

+ 7
- 4
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/UserCheckoutAcceptedIntegrationEventHandler.cs View File

@ -1,4 +1,6 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.EventHandling;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.EventHandling;
public class UserCheckoutAcceptedIntegrationEventHandler : IIntegrationEventHandler<UserCheckoutAcceptedIntegrationEvent>
{
@ -34,10 +36,11 @@ public class UserCheckoutAcceptedIntegrationEventHandler : IIntegrationEventHand
{
using (LogContext.PushProperty("IdentifiedCommandId", @event.RequestId))
{
var createOrderCommand = new CreateOrderCommand(@event.Basket.Items, @event.UserId, @event.UserName, @event.City, @event.Street,
var createOrderCommand = new CreateOrderCommand(
@event.Basket.Items, @event.UserId, @event.UserName, @event.City, @event.Street,
@event.State, @event.Country, @event.ZipCode,
@event.CardNumber, @event.CardHolderName, @event.CardExpiration,
@event.CardSecurityNumber, @event.CardTypeId);
@event.CardNumber, @event.CardHolderName, @event.CardExpiration,
@event.CardSecurityNumber, @event.CardTypeId, @event.CodeDiscount, @event.Discount);
var requestCreateOrder = new IdentifiedCommand<CreateOrderCommand, bool>(createOrderCommand, @event.RequestId);


+ 12
- 0
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderCouponConfirmedIntegrationEvent.cs View File

@ -0,0 +1,12 @@
using Newtonsoft.Json;
namespace Ordering.API.Application.IntegrationEvents.Events;
public record OrderCouponConfirmedIntegrationEvent : IntegrationEvent
{
[JsonProperty]
public int OrderId { get; private set; }
[JsonProperty]
public int Discount { get; private set; }
}

+ 12
- 0
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderCouponRejectedIntegrationEvent.cs View File

@ -0,0 +1,12 @@
using Newtonsoft.Json;
namespace Ordering.API.Application.IntegrationEvents.Events;
public record OrderCouponRejectedIntegrationEvent : IntegrationEvent
{
[JsonProperty]
public int OrderId { get; private set; }
[JsonProperty]
public string Code { get; private set; }
}

+ 20
- 0
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToAwaitingCouponValidationIntegrationEvent.cs View File

@ -0,0 +1,20 @@
namespace Ordering.API.Application.IntegrationEvents.Events;
public record OrderStatusChangedToAwaitingCouponValidationIntegrationEvent : IntegrationEvent
{
public int OrderId { get; }
public string OrderStatus { get; }
public string BuyerName { get; }
public string Code { get; set; }
public OrderStatusChangedToAwaitingCouponValidationIntegrationEvent(int orderId, string orderStatus, string buyerName, string code)
{
OrderId = orderId;
OrderStatus = orderStatus;
BuyerName = buyerName;
Code = code;
}
}

+ 3
- 1
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToCancelledIntegrationEvent.cs View File

@ -5,11 +5,13 @@ public record OrderStatusChangedToCancelledIntegrationEvent : IntegrationEvent
public int OrderId { get; }
public string OrderStatus { get; }
public string BuyerName { get; }
public string DiscountCode { get; }
public OrderStatusChangedToCancelledIntegrationEvent(int orderId, string orderStatus, string buyerName)
public OrderStatusChangedToCancelledIntegrationEvent(int orderId, string orderStatus, string buyerName, string discountCode)
{
OrderId = orderId;
OrderStatus = orderStatus;
BuyerName = buyerName;
DiscountCode = discountCode;
}
}

+ 8
- 2
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/UserCheckoutAcceptedIntegrationEvent.cs View File

@ -32,10 +32,14 @@ public record UserCheckoutAcceptedIntegrationEvent : IntegrationEvent
public CustomerBasket Basket { get; }
public string CodeDiscount { get; set; }
public int Discount { get; set; }
public UserCheckoutAcceptedIntegrationEvent(string userId, string userName, string city, string street,
string state, string country, string zipCode, string cardNumber, string cardHolderName,
DateTime cardExpiration, string cardSecurityNumber, int cardTypeId, string buyer, Guid requestId,
CustomerBasket basket)
CustomerBasket basket, string codeDiscount, int discount)
{
UserId = userId;
City = city;
@ -51,7 +55,9 @@ public record UserCheckoutAcceptedIntegrationEvent : IntegrationEvent
Buyer = buyer;
Basket = basket;
RequestId = requestId;
UserName = userName;
UserName = userName;
CodeDiscount = codeDiscount;
Discount = discount;
}
}

+ 31
- 9
src/Services/Ordering/Ordering.API/Application/Queries/OrderQueries.cs View File

@ -21,6 +21,8 @@ public class OrderQueries
o.Address_City as city, o.Address_Country as country, o.Address_State as state, o.Address_Street as street, o.Address_ZipCode as zipcode,
os.Name as status,
oi.ProductName as productname, oi.Units as units, oi.UnitPrice as unitprice, oi.PictureUrl as pictureurl
o.DiscountCode as coupon
o.Discount as discount,
FROM ordering.Orders o
LEFT JOIN ordering.Orderitems oi ON o.Id = oi.orderid
LEFT JOIN ordering.orderstatus os on o.OrderStatusId = os.Id
@ -39,14 +41,27 @@ public class OrderQueries
using var connection = new SqlConnection(_connectionString);
connection.Open();
return await connection.QueryAsync<OrderSummary>(@"SELECT o.[Id] as ordernumber,o.[OrderDate] as [date],os.[Name] as [status], SUM(oi.units*oi.unitprice) as total
FROM [ordering].[Orders] o
LEFT JOIN[ordering].[orderitems] oi ON o.Id = oi.orderid
LEFT JOIN[ordering].[orderstatus] os on o.OrderStatusId = os.Id
LEFT JOIN[ordering].[buyers] ob on o.BuyerId = ob.Id
WHERE ob.IdentityGuid = @userId
GROUP BY o.[Id], o.[OrderDate], os.[Name]
ORDER BY o.[Id]", new { userId });
return await connection.QueryAsync<OrderSummary>(@"
with OrderTotal as (
select
OrderId,
sum(Units * UnitPrice) subtotal
from ordering.orderitems
group by OrderId)
select
o.Id ordernumber,
o.OrderDate date,
os.Name status,
case
when ot.subtotal > isnull(o.Discount, 0)
then ot.subtotal - isnull(o.Discount, 0)
else 0
end total
from ordering.orders o
join OrderTotal ot on ot.OrderId = o.Id
join ordering.orderstatus os on os.Id = o.OrderStatusId
join ordering.buyers b on b.Id = o.BuyerId",
new { userId });
}
public async Task<IEnumerable<CardType>> GetCardTypesAsync()
@ -70,6 +85,9 @@ public class OrderQueries
zipcode = result[0].zipcode,
country = result[0].country,
orderitems = new List<Orderitem>(),
subtotal = 0,
coupon = result[0].coupon,
discount = result[0].discount ?? 0m,
total = 0
};
@ -83,10 +101,14 @@ public class OrderQueries
pictureurl = item.pictureurl
};
order.total += item.units * item.unitprice;
order.subtotal += item.units * item.unitprice;
order.orderitems.Add(orderitem);
}
order.total = order.discount < order.subtotal
? order.subtotal - order.discount
: 0;
return order;
}
}

+ 3
- 0
src/Services/Ordering/Ordering.API/Application/Queries/OrderViewModel.cs View File

@ -19,7 +19,10 @@ public record Order
public string zipcode { get; init; }
public string country { get; init; }
public List<Orderitem> orderitems { get; set; }
public decimal subtotal { get; set; }
public decimal total { get; set; }
public string coupon { get; set; }
public decimal discount { get; set; }
}
public record OrderSummary


+ 1
- 0
src/Services/Ordering/Ordering.API/Infrastructure/OrderingContextSeed.cs View File

@ -132,6 +132,7 @@ public class OrderingContextSeed
OrderStatus.Submitted,
OrderStatus.AwaitingValidation,
OrderStatus.StockConfirmed,
OrderStatus.AwaitingCouponValidation,
OrderStatus.Paid,
OrderStatus.Shipped,
OrderStatus.Cancelled


+ 5
- 0
src/Services/Ordering/Ordering.API/Startup.cs View File

@ -1,3 +1,5 @@
using Ordering.API.Application.IntegrationEvents.Events;
namespace Microsoft.eShopOnContainers.Services.Ordering.API;
public class Startup
@ -106,6 +108,9 @@ public class Startup
eventBus.Subscribe<OrderStockRejectedIntegrationEvent, IIntegrationEventHandler<OrderStockRejectedIntegrationEvent>>();
eventBus.Subscribe<OrderPaymentFailedIntegrationEvent, IIntegrationEventHandler<OrderPaymentFailedIntegrationEvent>>();
eventBus.Subscribe<OrderPaymentSucceededIntegrationEvent, IIntegrationEventHandler<OrderPaymentSucceededIntegrationEvent>>();
eventBus.Subscribe<OrderCouponRejectedIntegrationEvent, IIntegrationEventHandler<OrderCouponRejectedIntegrationEvent>>();
eventBus.Subscribe<OrderCouponConfirmedIntegrationEvent, IIntegrationEventHandler<OrderCouponConfirmedIntegrationEvent>>();
}
protected virtual void ConfigureAuth(IApplicationBuilder app)


+ 48
- 6
src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs View File

@ -1,4 +1,5 @@
using Microsoft.eShopOnContainers.Services.Ordering.Domain.Events;
using Ordering.Domain.Events;
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
@ -17,10 +18,16 @@ public class Order
private int? _buyerId;
public OrderStatus OrderStatus { get; private set; }
public bool? DiscountConfirmed { get; private set; }
private int _orderStatusId;
private string _description;
public string DiscountCode { get; private set; }
public decimal? Discount { get; private set; }
// Draft orders have this set to true. Currently we don't check anywhere the draft status of an Order, but we could do it if needed
@ -49,13 +56,15 @@ public class Order
}
public Order(string userId, string userName, Address address, int cardTypeId, string cardNumber, string cardSecurityNumber,
string cardHolderName, DateTime cardExpiration, int? buyerId = null, int? paymentMethodId = null) : this()
string cardHolderName, DateTime cardExpiration, string discountCode, decimal? discount, int? buyerId = null, int? paymentMethodId = null) : this()
{
_buyerId = buyerId;
_paymentMethodId = paymentMethodId;
_orderStatusId = OrderStatus.Submitted.Id;
_orderDate = DateTime.UtcNow;
Address = address;
DiscountCode = discountCode;
Discount = discountCode == null ? null : discount;
// Add the OrderStarterDomainEvent to the domain events collection
// to be raised/dispatched when comitting changes into the Database [ After DbContext.SaveChanges() ]
@ -113,15 +122,46 @@ public class Order
public void SetStockConfirmedStatus()
{
if (_orderStatusId == OrderStatus.AwaitingValidation.Id)
// If there's no Couponm, then it's validated
if (DiscountCode == null)
{
AddDomainEvent(new OrderStatusChangedToStockConfirmedDomainEvent(Id));
if (_orderStatusId == OrderStatus.AwaitingValidation.Id)
{
AddDomainEvent(new OrderStatusChangedToStockConfirmedDomainEvent(Id));
_orderStatusId = OrderStatus.StockConfirmed.Id;
_description = "All the items were confirmed with available stock.";
}
}
else
{
if (_orderStatusId != OrderStatus.AwaitingValidation.Id)
{
StatusChangeException(OrderStatus.AwaitingCouponValidation);
}
_orderStatusId = OrderStatus.AwaitingCouponValidation.Id;
_description = "Validate discount code";
_orderStatusId = OrderStatus.StockConfirmed.Id;
_description = "All the items were confirmed with available stock.";
AddDomainEvent(new OrderStatusChangedToAwaitingCouponValidationDomainEvent(Id, DiscountCode));
}
}
public void ProcessCouponConfirmed()
{
if (_orderStatusId != OrderStatus.AwaitingCouponValidation.Id)
{
StatusChangeException(OrderStatus.StockConfirmed);
}
DiscountConfirmed = true;
_orderStatusId = OrderStatus.StockConfirmed.Id;
_description = "Discount coupon validated.";
AddDomainEvent(new OrderStatusChangedToStockConfirmedDomainEvent(Id));
}
public void SetPaidStatus()
{
if (_orderStatusId == OrderStatus.StockConfirmed.Id)
@ -190,6 +230,8 @@ public class Order
public decimal GetTotal()
{
return _orderItems.Sum(o => o.GetUnits() * o.GetUnitPrice());
var result = _orderItems.Sum(o => o.GetUnits() * o.GetUnitPrice()) - (Discount ?? 0);
return result < 0 ? 0 : result;
}
}

+ 6
- 5
src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderStatus.cs View File

@ -7,10 +7,11 @@ public class OrderStatus
{
public static OrderStatus Submitted = new OrderStatus(1, nameof(Submitted).ToLowerInvariant());
public static OrderStatus AwaitingValidation = new OrderStatus(2, nameof(AwaitingValidation).ToLowerInvariant());
public static OrderStatus StockConfirmed = new OrderStatus(3, nameof(StockConfirmed).ToLowerInvariant());
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 AwaitingCouponValidation = new OrderStatus(3, nameof(AwaitingCouponValidation).ToLowerInvariant());
public static OrderStatus StockConfirmed = new OrderStatus(4, nameof(StockConfirmed).ToLowerInvariant());
public static OrderStatus Paid = new OrderStatus(5, nameof(Paid).ToLowerInvariant());
public static OrderStatus Shipped = new OrderStatus(6, nameof(Shipped).ToLowerInvariant());
public static OrderStatus Cancelled = new OrderStatus(7, nameof(Cancelled).ToLowerInvariant());
public OrderStatus(int id, string name)
: base(id, name)
@ -18,7 +19,7 @@ public class OrderStatus
}
public static IEnumerable<OrderStatus> List() =>
new[] { Submitted, AwaitingValidation, StockConfirmed, Paid, Shipped, Cancelled };
new[] { Submitted, AwaitingValidation, StockConfirmed, AwaitingCouponValidation, Paid, Shipped, Cancelled };
public static OrderStatus FromName(string name)
{


+ 19
- 0
src/Services/Ordering/Ordering.Domain/Events/OrderStatusChangedToAwaitingCouponValidationDomainEvent.cs View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Ordering.Domain.Events;
public class OrderStatusChangedToAwaitingCouponValidationDomainEvent : INotification
{
public int OrderId { get; }
public string Code { get; set; }
public OrderStatusChangedToAwaitingCouponValidationDomainEvent(int orderId, string code)
{
OrderId = orderId;
Code = code;
}
}

+ 4
- 0
src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/OrderEntityTypeConfiguration.cs View File

@ -51,6 +51,10 @@ class OrderEntityTypeConfiguration : IEntityTypeConfiguration<Order>
orderConfiguration.Property<string>("Description").IsRequired(false);
orderConfiguration
.Property(e => e.DiscountCode)
.HasMaxLength(50);
var navigation = orderConfiguration.Metadata.FindNavigation(nameof(Order.OrderItems));
// DDD Patterns comment:


+ 30
- 0
src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingCouponValidationIntegrationEventHandler.cs View File

@ -0,0 +1,30 @@
using Ordering.SignalrHub.IntegrationEvents.Events;
namespace Ordering.SignalrHub.IntegrationEvents.EventHandling;
public class OrderStatusChangedToAwaitingCouponValidationIntegrationEventHandler : IIntegrationEventHandler<OrderStatusChangedToAwaitingCouponValidationIntegrationEvent>
{
private readonly IHubContext<NotificationsHub> _hubContext;
private readonly ILogger<OrderStatusChangedToAwaitingCouponValidationIntegrationEventHandler> _logger;
public OrderStatusChangedToAwaitingCouponValidationIntegrationEventHandler(
IHubContext<NotificationsHub> hubContext,
ILogger<OrderStatusChangedToAwaitingCouponValidationIntegrationEventHandler> logger)
{
_hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task Handle(OrderStatusChangedToAwaitingCouponValidationIntegrationEvent @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 });
}
}
}

+ 17
- 0
src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToAwaitingCouponValidationIntegrationEvent.cs View File

@ -0,0 +1,17 @@
using Newtonsoft.Json;
namespace Ordering.SignalrHub.IntegrationEvents.Events;
public record OrderStatusChangedToAwaitingCouponValidationIntegrationEvent : IntegrationEvent
{
[JsonProperty]
public int OrderId { get; private set; }
[JsonProperty]
public string OrderStatus { get; private set; }
[JsonProperty]
public string BuyerName { get; private set; }
[JsonProperty]
public string Code { get; private set; }
}

+ 4
- 0
src/Services/Ordering/Ordering.SignalrHub/Startup.cs View File

@ -1,3 +1,6 @@
using Ordering.SignalrHub.IntegrationEvents.EventHandling;
using Ordering.SignalrHub.IntegrationEvents.Events;
namespace Microsoft.eShopOnContainers.Services.Ordering.SignalrHub;
public class Startup
@ -142,6 +145,7 @@ public class Startup
eventBus.Subscribe<OrderStatusChangedToShippedIntegrationEvent, OrderStatusChangedToShippedIntegrationEventHandler>();
eventBus.Subscribe<OrderStatusChangedToCancelledIntegrationEvent, OrderStatusChangedToCancelledIntegrationEventHandler>();
eventBus.Subscribe<OrderStatusChangedToSubmittedIntegrationEvent, OrderStatusChangedToSubmittedIntegrationEventHandler>();
eventBus.Subscribe<OrderStatusChangedToAwaitingCouponValidationIntegrationEvent, OrderStatusChangedToAwaitingCouponValidationIntegrationEventHandler>();
}
private void ConfigureAuthService(IServiceCollection services)


+ 3
- 1
src/Services/Ordering/Ordering.UnitTests/Application/IdentifiedCommandHandlerTest.cs View File

@ -74,6 +74,8 @@ public class IdentifiedCommandHandlerTest
cardExpiration: args != null && args.ContainsKey("cardExpiration") ? (DateTime)args["cardExpiration"] : DateTime.MinValue,
cardSecurityNumber: args != null && args.ContainsKey("cardSecurityNumber") ? (string)args["cardSecurityNumber"] : "123",
cardHolderName: args != null && args.ContainsKey("cardHolderName") ? (string)args["cardHolderName"] : "XXX",
cardTypeId: args != null && args.ContainsKey("cardTypeId") ? (int)args["cardTypeId"] : 0);
cardTypeId: args != null && args.ContainsKey("cardTypeId") ? (int)args["cardTypeId"] : 0,
codeDiscount: args != null && args.ContainsKey("codeDiscount") ? (string)args["codeDiscount"] : "",
discount: args != null && args.ContainsKey("discount") ? (int)args["discount"] : 0);
}
}

+ 5
- 3
src/Services/Ordering/Ordering.UnitTests/Application/NewOrderCommandHandlerTest.cs View File

@ -60,11 +60,11 @@ public class NewOrderRequestHandlerTest
private Order FakeOrder()
{
return new Order("1", "fakeName", new Address("street", "city", "state", "country", "zipcode"), 1, "12", "111", "fakeName", DateTime.Now.AddYears(1));
return new Order("1", "fakeName", new Address("street", "city", "state", "country", "zipcode"), 1, "12", "111", "fakeName", DateTime.Now.AddYears(1), "", 0);
}
private CreateOrderCommand FakeOrderRequestWithBuyer(Dictionary<string, object> args = null)
{
{
return new CreateOrderCommand(
new List<BasketItem>(),
userId: args != null && args.ContainsKey("userId") ? (string)args["userId"] : null,
@ -78,6 +78,8 @@ public class NewOrderRequestHandlerTest
cardExpiration: args != null && args.ContainsKey("cardExpiration") ? (DateTime)args["cardExpiration"] : DateTime.MinValue,
cardSecurityNumber: args != null && args.ContainsKey("cardSecurityNumber") ? (string)args["cardSecurityNumber"] : "123",
cardHolderName: args != null && args.ContainsKey("cardHolderName") ? (string)args["cardHolderName"] : "XXX",
cardTypeId: args != null && args.ContainsKey("cardTypeId") ? (int)args["cardTypeId"] : 0);
cardTypeId: args != null && args.ContainsKey("cardTypeId") ? (int)args["cardTypeId"] : 0,
codeDiscount: args != null && args.ContainsKey("codeDiscount") ? (string)args["codeDiscount"] : "",
discount: args != null && args.ContainsKey("discount") ? (int)args["discount"] : 0);
}
}

+ 3
- 1
src/Services/Ordering/Ordering.UnitTests/Builders.cs View File

@ -24,7 +24,9 @@ public class OrderBuilder
cardNumber: "12",
cardSecurityNumber: "123",
cardHolderName: "name",
cardExpiration: DateTime.UtcNow);
cardExpiration: DateTime.UtcNow,
"",
0);
}
public OrderBuilder AddOne(


+ 3
- 3
src/Services/Ordering/Ordering.UnitTests/Domain/OrderAggregateTest.cs View File

@ -119,7 +119,7 @@ public class OrderAggregateTest
var expectedResult = 1;
//Act
var fakeOrder = new Order("1", "fakeName", new Address(street, city, state, country, zipcode), cardTypeId, cardNumber, cardSecurityNumber, cardHolderName, cardExpiration);
var fakeOrder = new Order("1", "fakeName", new Address(street, city, state, country, zipcode), cardTypeId, cardNumber, cardSecurityNumber, cardHolderName, cardExpiration, "", 0);
//Assert
Assert.Equal(fakeOrder.DomainEvents.Count, expectedResult);
@ -142,7 +142,7 @@ public class OrderAggregateTest
var expectedResult = 2;
//Act
var fakeOrder = new Order("1", "fakeName", new Address(street, city, state, country, zipcode), cardTypeId, cardNumber, cardSecurityNumber, cardHolderName, cardExpiration);
var fakeOrder = new Order("1", "fakeName", new Address(street, city, state, country, zipcode), cardTypeId, cardNumber, cardSecurityNumber, cardHolderName, cardExpiration, "", 0);
fakeOrder.AddDomainEvent(new OrderStartedDomainEvent(fakeOrder, "fakeName", "1", cardTypeId, cardNumber, cardSecurityNumber, cardHolderName, cardExpiration));
//Assert
Assert.Equal(fakeOrder.DomainEvents.Count, expectedResult);
@ -162,7 +162,7 @@ public class OrderAggregateTest
var cardSecurityNumber = "123";
var cardHolderName = "FakeName";
var cardExpiration = DateTime.Now.AddYears(1);
var fakeOrder = new Order("1", "fakeName", new Address(street, city, state, country, zipcode), cardTypeId, cardNumber, cardSecurityNumber, cardHolderName, cardExpiration);
var fakeOrder = new Order("1", "fakeName", new Address(street, city, state, country, zipcode), cardTypeId, cardNumber, cardSecurityNumber, cardHolderName, cardExpiration, "", 0);
var @fakeEvent = new OrderStartedDomainEvent(fakeOrder, "1", "fakeName", cardTypeId, cardNumber, cardSecurityNumber, cardHolderName, cardExpiration);
var expectedResult = 1;


Loading…
Cancel
Save