diff --git a/src/BuildingBlocks/EventBus/EventBus/Abstractions/IDynamicIntegrationEventHandler.cs b/src/BuildingBlocks/EventBus/EventBus/Abstractions/IDynamicIntegrationEventHandler.cs new file mode 100644 index 000000000..55d62ade2 --- /dev/null +++ b/src/BuildingBlocks/EventBus/EventBus/Abstractions/IDynamicIntegrationEventHandler.cs @@ -0,0 +1,13 @@ +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions +{ + public interface IDynamicIntegrationEventHandler + { + Task Handle(dynamic eventData); + } +} diff --git a/src/BuildingBlocks/EventBus/EventBus/Abstractions/IEventBus.cs b/src/BuildingBlocks/EventBus/EventBus/Abstractions/IEventBus.cs index 9ab7a4499..7dd91541b 100644 --- a/src/BuildingBlocks/EventBus/EventBus/Abstractions/IEventBus.cs +++ b/src/BuildingBlocks/EventBus/EventBus/Abstractions/IEventBus.cs @@ -8,6 +8,12 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions void Subscribe(Func handler) where T : IntegrationEvent where TH : IIntegrationEventHandler; + void SubscribeDynamic(string eventName, Func handler) + where TH : IDynamicIntegrationEventHandler; + + void UnsubscribeDynamic(string eventName) + where TH : IDynamicIntegrationEventHandler; + void Unsubscribe() where TH : IIntegrationEventHandler where T : IntegrationEvent; diff --git a/src/BuildingBlocks/EventBus/EventBus/EventBus.csproj b/src/BuildingBlocks/EventBus/EventBus/EventBus.csproj index ad6867741..ed5efa621 100644 --- a/src/BuildingBlocks/EventBus/EventBus/EventBus.csproj +++ b/src/BuildingBlocks/EventBus/EventBus/EventBus.csproj @@ -6,10 +6,6 @@ Microsoft.eShopOnContainers.BuildingBlocks.EventBus - - - - diff --git a/src/BuildingBlocks/EventBus/EventBus/IEventBusSubscriptionsManager.cs b/src/BuildingBlocks/EventBus/EventBus/IEventBusSubscriptionsManager.cs index 2fdefc039..d46292356 100644 --- a/src/BuildingBlocks/EventBus/EventBus/IEventBusSubscriptionsManager.cs +++ b/src/BuildingBlocks/EventBus/EventBus/IEventBusSubscriptionsManager.cs @@ -2,6 +2,7 @@ using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; using System; using System.Collections.Generic; +using static Microsoft.eShopOnContainers.BuildingBlocks.EventBus.InMemoryEventBusSubscriptionsManager; namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus { @@ -9,18 +10,25 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus { bool IsEmpty { get; } event EventHandler OnEventRemoved; + void AddDynamicSubscription(string eventName, Func handler) + where TH : IDynamicIntegrationEventHandler; + void AddSubscription(Func handler) where T : IntegrationEvent where TH : IIntegrationEventHandler; - void RemoveSubscription() - where TH : IIntegrationEventHandler - where T : IntegrationEvent; + void RemoveSubscription() + where TH : IIntegrationEventHandler + where T : IntegrationEvent; + void RemoveDynamicSubscription(string eventName) + where TH : IDynamicIntegrationEventHandler; + bool HasSubscriptionsForEvent() where T : IntegrationEvent; bool HasSubscriptionsForEvent(string eventName); Type GetEventTypeByName(string eventName); void Clear(); - IEnumerable GetHandlersForEvent() where T : IntegrationEvent; - IEnumerable GetHandlersForEvent(string eventName); + IEnumerable GetHandlersForEvent() where T : IntegrationEvent; + IEnumerable GetHandlersForEvent(string eventName); + string GetEventKey(); } } \ No newline at end of file diff --git a/src/BuildingBlocks/EventBus/EventBus/InMemoryEventBusSubscriptionsManager.cs b/src/BuildingBlocks/EventBus/EventBus/InMemoryEventBusSubscriptionsManager.cs index 11fdba3c5..e85ef7064 100644 --- a/src/BuildingBlocks/EventBus/EventBus/InMemoryEventBusSubscriptionsManager.cs +++ b/src/BuildingBlocks/EventBus/EventBus/InMemoryEventBusSubscriptionsManager.cs @@ -8,64 +8,99 @@ using System.Text; namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus { - public class InMemoryEventBusSubscriptionsManager : IEventBusSubscriptionsManager + public partial class InMemoryEventBusSubscriptionsManager : IEventBusSubscriptionsManager { - private readonly Dictionary> _handlers; + + + private readonly Dictionary> _handlers; private readonly List _eventTypes; public event EventHandler OnEventRemoved; public InMemoryEventBusSubscriptionsManager() { - _handlers = new Dictionary>(); + _handlers = new Dictionary>(); _eventTypes = new List(); } public bool IsEmpty => !_handlers.Keys.Any(); public void Clear() => _handlers.Clear(); - public void AddSubscription(Func handler) + public void AddDynamicSubscription(string eventName, Func handler) + where TH : IDynamicIntegrationEventHandler + { + DoAddSubscription(handler, eventName, isDynamic: true); + } + + public void AddSubscription(Func handler) where T : IntegrationEvent where TH : IIntegrationEventHandler { - var key = GetEventKey(); - if (!HasSubscriptionsForEvent()) + var eventName = GetEventKey(); + DoAddSubscription(handler, eventName, isDynamic: false); + _eventTypes.Add(typeof(T)); + } + + private void DoAddSubscription(Delegate handler, string eventName, bool isDynamic) + { + if (!HasSubscriptionsForEvent(eventName)) { - _handlers.Add(key, new List()); + _handlers.Add(eventName, new List()); } - _handlers[key].Add(handler); - _eventTypes.Add(typeof(T)); + if (isDynamic) + { + _handlers[eventName].Add(SubscriptionInfo.Dynamic(handler)); + } + else + { + _handlers[eventName].Add(SubscriptionInfo.Typed(handler)); + } + } + + + public void RemoveDynamicSubscription(string eventName) + where TH : IDynamicIntegrationEventHandler + { + var handlerToRemove = FindDynamicSubscriptionToRemove(eventName); + DoRemoveHandler(eventName, handlerToRemove); } + public void RemoveSubscription() where TH : IIntegrationEventHandler where T : IntegrationEvent { - var handlerToRemove = FindHandlerToRemove(); - if (handlerToRemove != null) + var handlerToRemove = FindSubscriptionToRemove(); + var eventName = GetEventKey(); + DoRemoveHandler(eventName, handlerToRemove); + } + + + private void DoRemoveHandler(string eventName, SubscriptionInfo subsToRemove) + { + if (subsToRemove != null) { - var key = GetEventKey(); - _handlers[key].Remove(handlerToRemove); - if (!_handlers[key].Any()) + _handlers[eventName].Remove(subsToRemove); + if (!_handlers[eventName].Any()) { - _handlers.Remove(key); - var eventType = _eventTypes.SingleOrDefault(e => e.Name == key); + _handlers.Remove(eventName); + var eventType = _eventTypes.SingleOrDefault(e => e.Name == eventName); if (eventType != null) { _eventTypes.Remove(eventType); - RaiseOnEventRemoved(eventType.Name); } + RaiseOnEventRemoved(eventName); } - + } } - public IEnumerable GetHandlersForEvent() where T : IntegrationEvent + public IEnumerable GetHandlersForEvent() where T : IntegrationEvent { var key = GetEventKey(); return GetHandlersForEvent(key); } - public IEnumerable GetHandlersForEvent(string eventName) => _handlers[eventName]; + public IEnumerable GetHandlersForEvent(string eventName) => _handlers[eventName]; private void RaiseOnEventRemoved(string eventName) { @@ -76,22 +111,34 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus } } - private Delegate FindHandlerToRemove() - where T : IntegrationEvent - where TH : IIntegrationEventHandler + + private SubscriptionInfo FindDynamicSubscriptionToRemove(string eventName) + where TH : IDynamicIntegrationEventHandler { - if (!HasSubscriptionsForEvent()) + return DoFindHandlerToRemove(eventName, typeof(TH)); + } + + + private SubscriptionInfo FindSubscriptionToRemove() + where T : IntegrationEvent + where TH : IIntegrationEventHandler + { + var eventName = GetEventKey(); + return DoFindHandlerToRemove(eventName, typeof(TH)); + } + + private SubscriptionInfo DoFindHandlerToRemove(string eventName, Type handlerType) + { + if (!HasSubscriptionsForEvent(eventName)) { return null; } - - var key = GetEventKey(); - foreach (var func in _handlers[key]) + foreach (var subscription in _handlers[eventName]) { - var genericArgs = func.GetType().GetGenericArguments(); - if (genericArgs.SingleOrDefault() == typeof(TH)) + var genericArgs = subscription.Factory.GetType().GetGenericArguments(); + if (genericArgs.SingleOrDefault() == handlerType) { - return func; + return subscription; } } @@ -104,10 +151,10 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus return HasSubscriptionsForEvent(key); } public bool HasSubscriptionsForEvent(string eventName) => _handlers.ContainsKey(eventName); - - public Type GetEventTypeByName(string eventName) => _eventTypes.Single(t => t.Name == eventName); - private string GetEventKey() + public Type GetEventTypeByName(string eventName) => _eventTypes.SingleOrDefault(t => t.Name == eventName); + + public string GetEventKey() { return typeof(T).Name; } diff --git a/src/BuildingBlocks/EventBus/EventBus/SubscriptionInfo.cs b/src/BuildingBlocks/EventBus/EventBus/SubscriptionInfo.cs new file mode 100644 index 000000000..33c0aec26 --- /dev/null +++ b/src/BuildingBlocks/EventBus/EventBus/SubscriptionInfo.cs @@ -0,0 +1,28 @@ +using System; + +namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus +{ + public partial class InMemoryEventBusSubscriptionsManager : IEventBusSubscriptionsManager + { + public class SubscriptionInfo + { + public bool IsDynamic { get; } + public Delegate Factory { get; } + + private SubscriptionInfo(bool isDynamic, Delegate factory) + { + IsDynamic = isDynamic; + Factory = factory; + } + + public static SubscriptionInfo Dynamic(Delegate factory) + { + return new SubscriptionInfo(true, factory); + } + public static SubscriptionInfo Typed(Delegate factory) + { + return new SubscriptionInfo(false, factory); + } + } + } +} diff --git a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/CommandBusRabbitMQ.cs b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/CommandBusRabbitMQ.cs index 699bf3772..27e67d9ca 100644 --- a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/CommandBusRabbitMQ.cs +++ b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/CommandBusRabbitMQ.cs @@ -1,4 +1,4 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.CommandBus; +//using Microsoft.eShopOnContainers.BuildingBlocks.CommandBus; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Polly; @@ -12,6 +12,7 @@ using System.Net.Sockets; using System.Text; using System.Threading.Tasks; +/* namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ { public class CommandBusRabbitMQ : ICommandBus, IDisposable @@ -141,3 +142,4 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ } } +*/ \ No newline at end of file diff --git a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs index adbc52ad1..3d32073a3 100644 --- a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs +++ b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs @@ -3,6 +3,7 @@ using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; using Microsoft.Extensions.Logging; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using Polly; using Polly.Retry; using RabbitMQ.Client; @@ -96,12 +97,25 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ } } + public void SubscribeDynamic(string eventName, Func handler) + where TH: IDynamicIntegrationEventHandler + { + DoInternalSubscription(eventName); + _subsManager.AddDynamicSubscription(eventName,handler); + } + public void Subscribe(Func handler) where T : IntegrationEvent where TH : IIntegrationEventHandler { - var eventName = typeof(T).Name; - var containsKey = _subsManager.HasSubscriptionsForEvent(); + var eventName = _subsManager.GetEventKey(); + DoInternalSubscription(eventName); + _subsManager.AddSubscription(handler); + } + + private void DoInternalSubscription(string eventName) + { + var containsKey = _subsManager.HasSubscriptionsForEvent(eventName); if (!containsKey) { if (!_persistentConnection.IsConnected) @@ -116,9 +130,6 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ routingKey: eventName); } } - - _subsManager.AddSubscription(handler); - } public void Unsubscribe() @@ -128,19 +139,13 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ _subsManager.RemoveSubscription(); } - private static Func FindHandlerByType(Type handlerType, IEnumerable> handlers) + public void UnsubscribeDynamic(string eventName) + where TH: IDynamicIntegrationEventHandler { - foreach (var func in handlers) - { - if (func.GetMethodInfo().ReturnType == handlerType) - { - return func; - } - } - - return null; + _subsManager.RemoveDynamicSubscription(eventName); } + public void Dispose() { if (_consumerChannel != null) @@ -191,16 +196,25 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ { if (_subsManager.HasSubscriptionsForEvent(eventName)) - { - var eventType = _subsManager.GetEventTypeByName(eventName); - var integrationEvent = JsonConvert.DeserializeObject(message, eventType); - var handlers = _subsManager.GetHandlersForEvent(eventName); + { + var subscriptions = _subsManager.GetHandlersForEvent(eventName); - foreach (var handlerfactory in handlers) + foreach (var subscription in subscriptions) { - var handler = handlerfactory.DynamicInvoke(); - var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType); - await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent }); + if (subscription.IsDynamic) + { + var handler = subscription.Factory.DynamicInvoke() as IDynamicIntegrationEventHandler; + dynamic eventData = JObject.Parse(message); + await handler.Handle(eventData); + } + else + { + var eventType = _subsManager.GetEventTypeByName(eventName); + var integrationEvent = JsonConvert.DeserializeObject(message, eventType); + var handler = subscription.Factory.DynamicInvoke(); + var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType); + await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent }); + } } } } diff --git a/src/Services/Basket/Basket.API/Controllers/BasketController.cs b/src/Services/Basket/Basket.API/Controllers/BasketController.cs index 5bf23509c..16f008509 100644 --- a/src/Services/Basket/Basket.API/Controllers/BasketController.cs +++ b/src/Services/Basket/Basket.API/Controllers/BasketController.cs @@ -5,6 +5,10 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.eShopOnContainers.Services.Basket.API.Model; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +using Basket.API.IntegrationEvents.Events; +using Microsoft.eShopOnContainers.Services.Basket.API.Services; namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers { @@ -16,11 +20,17 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers [Authorize] public class BasketController : Controller { - private IBasketRepository _repository; + private readonly IBasketRepository _repository; + private readonly IIdentityService _identitySvc; + private readonly IEventBus _eventBus; - public BasketController(IBasketRepository repository) + public BasketController(IBasketRepository repository, + IIdentityService identityService, + IEventBus eventBus) { _repository = repository; + _identitySvc = identityService; + _eventBus = eventBus; } // GET api/values/5 [HttpGet("{id}")] @@ -40,11 +50,28 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers return Ok(basket); } + [HttpPost] + public async Task Checkout() + { + var userId = _identitySvc.GetUserIdentity(); + var basket = await _repository.GetBasketAsync(userId); + _eventBus.Publish(new UserCheckoutAccepted(userId, basket)); + if (basket == null) + { + return BadRequest(); + } + + + + return Accepted(); + } + // DELETE api/values/5 [HttpDelete("{id}")] public void Delete(string id) { _repository.DeleteBasketAsync(id); } + } } diff --git a/src/Services/Basket/Basket.API/IntegrationEvents/Events/UserCheckoutAccepted.cs b/src/Services/Basket/Basket.API/IntegrationEvents/Events/UserCheckoutAccepted.cs new file mode 100644 index 000000000..2d3e15ff3 --- /dev/null +++ b/src/Services/Basket/Basket.API/IntegrationEvents/Events/UserCheckoutAccepted.cs @@ -0,0 +1,21 @@ +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +using Microsoft.eShopOnContainers.Services.Basket.API.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Basket.API.IntegrationEvents.Events +{ + public class UserCheckoutAccepted : IntegrationEvent + { + public string UserId {get; } + CustomerBasket Basket { get; } + public UserCheckoutAccepted(string userId, CustomerBasket basket) + { + UserId = userId; + Basket = basket; + } + + } +} diff --git a/src/Services/Basket/Basket.API/Model/Basket.cs b/src/Services/Basket/Basket.API/Model/Basket.cs index d07c710e7..7b9f620d8 100644 --- a/src/Services/Basket/Basket.API/Model/Basket.cs +++ b/src/Services/Basket/Basket.API/Model/Basket.cs @@ -13,7 +13,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Model public CustomerBasket(string customerId) { BuyerId = customerId; - Items = new List(); + Items = new List(); } } } diff --git a/src/Services/Basket/Basket.API/Services/IIdentityService.cs b/src/Services/Basket/Basket.API/Services/IIdentityService.cs new file mode 100644 index 000000000..8cc7bd848 --- /dev/null +++ b/src/Services/Basket/Basket.API/Services/IIdentityService.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.Services.Basket.API.Services +{ + public interface IIdentityService + { + string GetUserIdentity(); + } +} diff --git a/src/Services/Basket/Basket.API/Services/IdentityService.cs b/src/Services/Basket/Basket.API/Services/IdentityService.cs new file mode 100644 index 000000000..08d1ffffa --- /dev/null +++ b/src/Services/Basket/Basket.API/Services/IdentityService.cs @@ -0,0 +1,24 @@ + +using Microsoft.AspNetCore.Http; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.Services.Basket.API.Services +{ + public class IdentityService : IIdentityService + { + private IHttpContextAccessor _context; + + public IdentityService(IHttpContextAccessor context) + { + _context = context ?? throw new ArgumentNullException(nameof(context)); + } + + public string GetUserIdentity() + { + return _context.HttpContext.User.FindFirst("sub").Value; + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/UserCheckoutAcceptedIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/UserCheckoutAcceptedIntegrationEventHandler.cs new file mode 100644 index 000000000..f846f5031 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/UserCheckoutAcceptedIntegrationEventHandler.cs @@ -0,0 +1,16 @@ +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Ordering.API.Application.IntegrationEvents.EventHandling +{ + public class UserCheckoutAcceptedIntegrationEventHandler : IDynamicIntegrationEventHandler + { + public async Task Handle(dynamic eventData) + { + int i = 0; + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Ordering.API.csproj b/src/Services/Ordering/Ordering.API/Ordering.API.csproj index 0f2af6382..d5ef524bb 100644 --- a/src/Services/Ordering/Ordering.API/Ordering.API.csproj +++ b/src/Services/Ordering/Ordering.API/Ordering.API.csproj @@ -80,7 +80,6 @@ - diff --git a/src/Services/Ordering/Ordering.API/Startup.cs b/src/Services/Ordering/Ordering.API/Startup.cs index 4bf529d78..f7af01f3b 100644 --- a/src/Services/Ordering/Ordering.API/Startup.cs +++ b/src/Services/Ordering/Ordering.API/Startup.cs @@ -4,6 +4,7 @@ using Autofac; using Autofac.Extensions.DependencyInjection; using global::Ordering.API.Application.IntegrationEvents; + using global::Ordering.API.Application.IntegrationEvents.EventHandling; using global::Ordering.API.Infrastructure.Middlewares; using Infrastructure; using Infrastructure.Auth; @@ -120,9 +121,10 @@ return new DefaultRabbitMQPersistentConnection(factory, logger); }); + services.AddSingleton(); services.AddSingleton(); - + services.AddTransient(); services.AddOptions(); //configure autofac @@ -136,6 +138,7 @@ return new AutofacServiceProvider(container.Build()); } + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); @@ -146,6 +149,7 @@ app.UseFailingMiddleware(); ConfigureAuth(app); + ConfigureEventBus(app); app.UseMvcWithDefaultRoute(); @@ -161,6 +165,15 @@ integrationEventLogContext.Database.Migrate(); } + private void ConfigureEventBus(IApplicationBuilder app) + { + var eventBus = app.ApplicationServices.GetRequiredService(); + eventBus.SubscribeDynamic( + "UserCheckoutAccepted", + () => app.ApplicationServices.GetRequiredService()); + + } + protected virtual void ConfigureAuth(IApplicationBuilder app) { var identityUrl = Configuration.GetValue("IdentityUrl"); diff --git a/test/Services/UnitTest/Basket/Application/BasketWebApiTest.cs b/test/Services/UnitTest/Basket/Application/BasketWebApiTest.cs index 6c5f116bd..b6e45425a 100644 --- a/test/Services/UnitTest/Basket/Application/BasketWebApiTest.cs +++ b/test/Services/UnitTest/Basket/Application/BasketWebApiTest.cs @@ -1,20 +1,27 @@ -using Microsoft.AspNetCore.Mvc; +using Basket.API.IntegrationEvents.Events; +using Microsoft.AspNetCore.Mvc; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; using Microsoft.eShopOnContainers.Services.Basket.API.Controllers; using Microsoft.eShopOnContainers.Services.Basket.API.Model; using Moq; using System.Collections.Generic; using System.Threading.Tasks; using Xunit; +using IBasketIdentityService = Microsoft.eShopOnContainers.Services.Basket.API.Services.IIdentityService; namespace UnitTest.Basket.Application { public class BasketWebApiTest { private readonly Mock _basketRepositoryMock; + private readonly Mock _identityServiceMock; + private readonly Mock _serviceBusMock; public BasketWebApiTest() { _basketRepositoryMock = new Mock(); + _identityServiceMock = new Mock(); + _serviceBusMock = new Mock(); } [Fact] @@ -26,9 +33,12 @@ namespace UnitTest.Basket.Application _basketRepositoryMock.Setup(x => x.GetBasketAsync(It.IsAny())) .Returns(Task.FromResult(fakeCustomerBasket)); + _identityServiceMock.Setup(x => x.GetUserIdentity()).Returns(fakeCustomerId); + _serviceBusMock.Setup(x => x.Publish(It.IsAny())); //Act - var basketController = new BasketController(_basketRepositoryMock.Object); + var basketController = new BasketController( + _basketRepositoryMock.Object, _identityServiceMock.Object, _serviceBusMock.Object); var actionResult = await basketController.Get(fakeCustomerId) as OkObjectResult; //Assert @@ -45,9 +55,12 @@ namespace UnitTest.Basket.Application _basketRepositoryMock.Setup(x => x.UpdateBasketAsync(It.IsAny())) .Returns(Task.FromResult(fakeCustomerBasket)); - + _identityServiceMock.Setup(x => x.GetUserIdentity()).Returns(fakeCustomerId); + _serviceBusMock.Setup(x => x.Publish(It.IsAny())); //Act - var basketController = new BasketController(_basketRepositoryMock.Object); + var basketController = new BasketController( + _basketRepositoryMock.Object, _identityServiceMock.Object, _serviceBusMock.Object); + var actionResult = await basketController.Post(fakeCustomerBasket) as OkObjectResult; //Assert @@ -55,6 +68,38 @@ namespace UnitTest.Basket.Application Assert.Equal(((CustomerBasket)actionResult.Value).BuyerId, fakeCustomerId); } + [Fact] + public async Task Doing_Checkout_Without_Basket_Should_Return_Bad_Request() + { + var fakeCustomerId = "2"; + _basketRepositoryMock.Setup(x => x.GetBasketAsync(It.IsAny())) + .Returns(Task.FromResult((CustomerBasket)null)); + _identityServiceMock.Setup(x => x.GetUserIdentity()).Returns(fakeCustomerId); + //Act + var basketController = new BasketController( + _basketRepositoryMock.Object, _identityServiceMock.Object, _serviceBusMock.Object); + + var result = await basketController.Checkout() as BadRequestResult; + Assert.NotNull(result); + } + + [Fact] + public async Task Doing_Checkout_Wit_Basket_Should_Publish_UserCheckoutAccepted_Integration_Event() + { + var fakeCustomerId = "1"; + var fakeCustomerBasket = GetCustomerBasketFake(fakeCustomerId); + _basketRepositoryMock.Setup(x => x.GetBasketAsync(It.IsAny())) + .Returns(Task.FromResult(fakeCustomerBasket)); + _identityServiceMock.Setup(x => x.GetUserIdentity()).Returns(fakeCustomerId); + //Act + var basketController = new BasketController( + _basketRepositoryMock.Object, _identityServiceMock.Object, _serviceBusMock.Object); + + var result = await basketController.Checkout() as AcceptedResult; + _serviceBusMock.Verify(mock => mock.Publish(It.IsAny()), Times.Once); + Assert.NotNull(result); + } + private CustomerBasket GetCustomerBasketFake(string fakeCustomerId) { return new CustomerBasket(fakeCustomerId) diff --git a/test/Services/UnitTest/UnitTest.csproj b/test/Services/UnitTest/UnitTest.csproj index 63eaf2fcd..ef467c5b6 100644 --- a/test/Services/UnitTest/UnitTest.csproj +++ b/test/Services/UnitTest/UnitTest.csproj @@ -13,6 +13,7 @@ +