Add coupon support to api services
This commit is contained in:
parent
e80e1b0d0e
commit
86cedb4bcf
@ -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
|
@ -37,5 +37,7 @@ public class UrlsConfig
|
||||
public string GrpcCatalog { get; set; }
|
||||
|
||||
public string GrpcOrdering { get; set; }
|
||||
|
||||
public string Coupon { get; set; }
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
|
||||
public class CouponData
|
||||
{
|
||||
public int Discount { get; set; }
|
||||
|
||||
public string Code { get; set; }
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services;
|
||||
|
||||
public interface ICouponService
|
||||
{
|
||||
Task<HttpResponseMessage> CheckCouponByCodeNumberAsync(string codeNumber);
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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; }
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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())
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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.
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
||||
|
@ -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; }
|
||||
}
|
@ -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; }
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
@ -52,6 +56,8 @@ public record UserCheckoutAcceptedIntegrationEvent : IntegrationEvent
|
||||
Basket = basket;
|
||||
RequestId = requestId;
|
||||
UserName = userName;
|
||||
CodeDiscount = codeDiscount;
|
||||
Discount = discount;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -132,6 +132,7 @@ public class OrderingContextSeed
|
||||
OrderStatus.Submitted,
|
||||
OrderStatus.AwaitingValidation,
|
||||
OrderStatus.StockConfirmed,
|
||||
OrderStatus.AwaitingCouponValidation,
|
||||
OrderStatus.Paid,
|
||||
OrderStatus.Shipped,
|
||||
OrderStatus.Cancelled
|
||||
|
@ -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)
|
||||
|
@ -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,13 +122,44 @@ 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.";
|
||||
_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";
|
||||
|
||||
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()
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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:
|
||||
|
@ -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 });
|
||||
}
|
||||
}
|
||||
}
|
@ -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; }
|
||||
}
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ 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)
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,9 @@ public class OrderBuilder
|
||||
cardNumber: "12",
|
||||
cardSecurityNumber: "123",
|
||||
cardHolderName: "name",
|
||||
cardExpiration: DateTime.UtcNow);
|
||||
cardExpiration: DateTime.UtcNow,
|
||||
"",
|
||||
0);
|
||||
}
|
||||
|
||||
public OrderBuilder AddOne(
|
||||
|
@ -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…
x
Reference in New Issue
Block a user