From 9b26253c4a65c363d07680c5041e002286f58aca Mon Sep 17 00:00:00 2001 From: dsanz Date: Wed, 8 Mar 2017 15:44:53 +0100 Subject: [PATCH 01/12] Add rabbitMQ container --- docker-compose.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index f3baeb346..788cfc264 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -61,3 +61,8 @@ services: image: redis ports: - "6379:6379" + + rabbitmq: + image: rabbitmq + ports: + - "5672:5672" From 18a402044ed0f779681d5a7ddca54b2c43035f39 Mon Sep 17 00:00:00 2001 From: dsanz Date: Thu, 9 Mar 2017 15:56:34 +0100 Subject: [PATCH 02/12] Add basic first implementation of Event bus with rabbitMQ --- docker-compose.yml | 2 + eShopOnContainers-ServicesAndWebApps.sln | 54 ++++++++++ .../Catalog/Catalog.API/Catalog.API.csproj | 4 + .../Controllers/CatalogController.cs | 16 ++- src/Services/Catalog/Catalog.API/Startup.cs | 3 + .../Catalog/CatalogPriceChanged.cs | 20 ++++ .../Catalog/CatalogPriceChangedHandler.cs | 14 +++ .../Common/Infrastructure/EventBus.cs | 101 ++++++++++++++++++ .../Common/Infrastructure/IEventBus.cs | 13 +++ .../Infrastructure/IIntegrationEvent.cs | 11 ++ .../IIntegrationEventHandler.cs | 14 +++ .../Infrastructure/Infrastructure.csproj | 10 ++ 12 files changed, 259 insertions(+), 3 deletions(-) create mode 100644 src/Services/Common/Infrastructure/Catalog/CatalogPriceChanged.cs create mode 100644 src/Services/Common/Infrastructure/Catalog/CatalogPriceChangedHandler.cs create mode 100644 src/Services/Common/Infrastructure/EventBus.cs create mode 100644 src/Services/Common/Infrastructure/IEventBus.cs create mode 100644 src/Services/Common/Infrastructure/IIntegrationEvent.cs create mode 100644 src/Services/Common/Infrastructure/IIntegrationEventHandler.cs create mode 100644 src/Services/Common/Infrastructure/Infrastructure.csproj diff --git a/docker-compose.yml b/docker-compose.yml index 788cfc264..09380e299 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,6 +9,7 @@ services: depends_on: - basket.data - identity.api + - rabbitmq catalog.api: image: eshop/catalog.api @@ -17,6 +18,7 @@ services: dockerfile: Dockerfile depends_on: - sql.data + - rabbitmq identity.api: image: eshop/identity.api diff --git a/eShopOnContainers-ServicesAndWebApps.sln b/eShopOnContainers-ServicesAndWebApps.sln index c92dc8c7d..539aec7b0 100644 --- a/eShopOnContainers-ServicesAndWebApps.sln +++ b/eShopOnContainers-ServicesAndWebApps.sln @@ -48,6 +48,10 @@ Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-co EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebSPA", "src\Web\WebSPA\WebSPA.csproj", "{F16E3C6A-1C94-4EAB-BE91-099618060B68}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{47857844-D05A-4C37-BFB2-AF19B7EC418D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure", "src\Services\Common\Infrastructure\Infrastructure.csproj", "{B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Ad-Hoc|Any CPU = Ad-Hoc|Any CPU @@ -494,6 +498,54 @@ Global {F16E3C6A-1C94-4EAB-BE91-099618060B68}.Release|x64.Build.0 = Release|Any CPU {F16E3C6A-1C94-4EAB-BE91-099618060B68}.Release|x86.ActiveCfg = Release|Any CPU {F16E3C6A-1C94-4EAB-BE91-099618060B68}.Release|x86.Build.0 = Release|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.AppStore|ARM.Build.0 = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.AppStore|iPhone.Build.0 = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.AppStore|x64.ActiveCfg = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.AppStore|x64.Build.0 = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.AppStore|x86.ActiveCfg = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.AppStore|x86.Build.0 = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Debug|ARM.ActiveCfg = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Debug|ARM.Build.0 = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Debug|iPhone.Build.0 = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Debug|x64.ActiveCfg = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Debug|x64.Build.0 = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Debug|x86.ActiveCfg = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Debug|x86.Build.0 = Debug|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Release|Any CPU.Build.0 = Release|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Release|ARM.ActiveCfg = Release|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Release|ARM.Build.0 = Release|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Release|iPhone.ActiveCfg = Release|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Release|iPhone.Build.0 = Release|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Release|x64.ActiveCfg = Release|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Release|x64.Build.0 = Release|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Release|x86.ActiveCfg = Release|Any CPU + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -516,5 +568,7 @@ Global {7796F5D8-31FC-45A4-B673-19DE5BA194CF} = {EF0337F2-ED00-4643-89FD-EE10863F1870} {A579E108-5445-403D-A407-339AC4D1611B} = {24CD3B53-141E-4A07-9B0D-796641E1CF78} {F16E3C6A-1C94-4EAB-BE91-099618060B68} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04} + {47857844-D05A-4C37-BFB2-AF19B7EC418D} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8} + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0} = {47857844-D05A-4C37-BFB2-AF19B7EC418D} EndGlobalSection EndGlobal diff --git a/src/Services/Catalog/Catalog.API/Catalog.API.csproj b/src/Services/Catalog/Catalog.API/Catalog.API.csproj index 6811e43cb..1c0fddf92 100644 --- a/src/Services/Catalog/Catalog.API/Catalog.API.csproj +++ b/src/Services/Catalog/Catalog.API/Catalog.API.csproj @@ -52,6 +52,10 @@ + + + + Always diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index 547cdfe21..bba7564e9 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -3,6 +3,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; using Microsoft.eShopOnContainers.Services.Catalog.API.Model; using Microsoft.eShopOnContainers.Services.Catalog.API.ViewModel; +using Microsoft.eShopOnContainers.Services.Common.Infrastructure; +using Microsoft.eShopOnContainers.Services.Common.Infrastructure.Catalog; using Microsoft.Extensions.Options; using System.Collections.Generic; using System.Linq; @@ -15,11 +17,13 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers { private readonly CatalogContext _context; private readonly IOptionsSnapshot _settings; + private readonly IEventBus _eventBus; - public CatalogController(CatalogContext context, IOptionsSnapshot settings) + public CatalogController(CatalogContext context, IOptionsSnapshot settings, IEventBus eventBus) { _context = context; _settings = settings; + _eventBus = eventBus; ((DbContext)context).ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; } @@ -41,7 +45,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers itemsOnPage = ComposePicUri(itemsOnPage); var model = new PaginatedItemsViewModel( - pageIndex, pageSize, totalItems, itemsOnPage); + pageIndex, pageSize, totalItems, itemsOnPage); return Ok(model); } @@ -99,7 +103,13 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers var model = new PaginatedItemsViewModel( pageIndex, pageSize, totalItems, itemsOnPage); - + + //hook to run integration tests until POST methods are created + if (catalogTypeId.HasValue && catalogTypeId == 1) + { + _eventBus.Publish(new CatalogPriceChanged()); + } + return Ok(model); } diff --git a/src/Services/Catalog/Catalog.API/Startup.cs b/src/Services/Catalog/Catalog.API/Startup.cs index fb6cc3907..c8ecf72fd 100644 --- a/src/Services/Catalog/Catalog.API/Startup.cs +++ b/src/Services/Catalog/Catalog.API/Startup.cs @@ -7,6 +7,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; + using Microsoft.eShopOnContainers.Services.Common.Infrastructure; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -73,6 +74,8 @@ .AllowCredentials()); }); + services.AddSingleton(); + services.AddMvc(); } diff --git a/src/Services/Common/Infrastructure/Catalog/CatalogPriceChanged.cs b/src/Services/Common/Infrastructure/Catalog/CatalogPriceChanged.cs new file mode 100644 index 000000000..ad22ae49f --- /dev/null +++ b/src/Services/Common/Infrastructure/Catalog/CatalogPriceChanged.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure.Catalog +{ + public class CatalogPriceChanged : IIntegrationEvent + { + private readonly string _eventName = "catalogpricechanged"; + + public string Name { + get + { + return _eventName; + } + } + + public string Message { get { return "CatalogPriceChanged!!"; } } + } +} diff --git a/src/Services/Common/Infrastructure/Catalog/CatalogPriceChangedHandler.cs b/src/Services/Common/Infrastructure/Catalog/CatalogPriceChangedHandler.cs new file mode 100644 index 000000000..8fc045ba0 --- /dev/null +++ b/src/Services/Common/Infrastructure/Catalog/CatalogPriceChangedHandler.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure.Catalog +{ + public class CatalogPriceChangedHandler : IIntegrationEventHandler + { + public void Handle(CatalogPriceChanged @event) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Services/Common/Infrastructure/EventBus.cs b/src/Services/Common/Infrastructure/EventBus.cs new file mode 100644 index 000000000..42ef384f1 --- /dev/null +++ b/src/Services/Common/Infrastructure/EventBus.cs @@ -0,0 +1,101 @@ + +using Microsoft.eShopOnContainers.Services.Common.Infrastructure.Catalog; +using RabbitMQ.Client; +using RabbitMQ.Client.Events; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure +{ + public class EventBus : IEventBus + { + private readonly Dictionary> _handlers; + private readonly Dictionary> _listeners; + + public EventBus() + { + _handlers = new Dictionary>(); + _listeners = new Dictionary>(); + } + public void Publish(IIntegrationEvent @event) + { + var factory = new ConnectionFactory() { HostName = "172.20.0.1" }; + using (var connection = factory.CreateConnection()) + using (var channel = connection.CreateModel()) + { + channel.QueueDeclare(queue: @event.Name, + durable: false, + exclusive: false, + autoDelete: false, + arguments: null); + + string message = ((CatalogPriceChanged)@event).Message; + var body = Encoding.UTF8.GetBytes(message); + + channel.BasicPublish(exchange: "", + routingKey: @event.Name, + basicProperties: null, + body: body); + } + + } + + public void Subscribe(IIntegrationEventHandler handler) where T : IIntegrationEvent + { + var eventName = typeof(T).Name; + if (_handlers.ContainsKey(eventName)) + { + _handlers[eventName].Add(handler); + } + else + { + var factory = new ConnectionFactory() { HostName = "172.18.0.1" }; + var connection = factory.CreateConnection(); + var channel = connection.CreateModel(); + + channel.QueueDeclare(queue: eventName, + durable: false, + exclusive: false, + autoDelete: false, + arguments: null); + + var consumer = new EventingBasicConsumer(channel); + consumer.Received += (model, ea) => + { + var body = ea.Body; + var message = Encoding.UTF8.GetString(body); + }; + channel.BasicConsume(queue: "hello", + noAck: true, + consumer: consumer); + ; + + _listeners.Add(eventName, new Tuple(channel, connection)); + _handlers.Add(eventName, new List()); + _handlers[eventName].Add(handler); + } + + } + + public void Unsubscribe(IIntegrationEventHandler handler) where T : IIntegrationEvent + { + var eventName = typeof(T).Name; + if (_handlers.ContainsKey(eventName) && _handlers[eventName].Contains(handler)) + { + _handlers[eventName].Remove(handler); + + if (_handlers[eventName].Count == 0) + { + _handlers.Remove(eventName); + + var connectionItems =_listeners[eventName]; + _listeners.Remove(eventName); + + connectionItems.Item1.Close(); + connectionItems.Item2.Close(); + } + } + } + } +} diff --git a/src/Services/Common/Infrastructure/IEventBus.cs b/src/Services/Common/Infrastructure/IEventBus.cs new file mode 100644 index 000000000..5751f1b81 --- /dev/null +++ b/src/Services/Common/Infrastructure/IEventBus.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure +{ + public interface IEventBus + { + void Subscribe(IIntegrationEventHandler handler) where T: IIntegrationEvent; + void Unsubscribe(IIntegrationEventHandler handler) where T : IIntegrationEvent; + void Publish(IIntegrationEvent @event); + } +} diff --git a/src/Services/Common/Infrastructure/IIntegrationEvent.cs b/src/Services/Common/Infrastructure/IIntegrationEvent.cs new file mode 100644 index 000000000..9e92db3a4 --- /dev/null +++ b/src/Services/Common/Infrastructure/IIntegrationEvent.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure +{ + public interface IIntegrationEvent + { + string Name { get; } + } +} diff --git a/src/Services/Common/Infrastructure/IIntegrationEventHandler.cs b/src/Services/Common/Infrastructure/IIntegrationEventHandler.cs new file mode 100644 index 000000000..b2d902ca2 --- /dev/null +++ b/src/Services/Common/Infrastructure/IIntegrationEventHandler.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure +{ + public interface IIntegrationEventHandler : IIntegrationEventHandler + where TIntegrationEvent: IIntegrationEvent + { + void Handle(TIntegrationEvent @event); + } + + public interface IIntegrationEventHandler { } +} diff --git a/src/Services/Common/Infrastructure/Infrastructure.csproj b/src/Services/Common/Infrastructure/Infrastructure.csproj new file mode 100644 index 000000000..fc7038ce9 --- /dev/null +++ b/src/Services/Common/Infrastructure/Infrastructure.csproj @@ -0,0 +1,10 @@ + + + netcoreapp1.1 + 1.1.0 + Microsoft.eShopOnContainers.Services.Common.Infrastructure + + + + + \ No newline at end of file From 5b38a49f110b320ada36f305fd2df72ac924e4cc Mon Sep 17 00:00:00 2001 From: dsanz Date: Fri, 10 Mar 2017 13:17:16 +0100 Subject: [PATCH 03/12] Add Basket handler and subscription to events. Change in EventBus to use broker and one message queue per microservice http://www.rabbitmq.com/tutorials/tutorial-four-dotnet.html --- .../Basket/Basket.API/Basket.API.csproj | 4 + .../Events/CatalogPriceChangedHandler.cs | 25 ++++ src/Services/Basket/Basket.API/Startup.cs | 6 + .../Controllers/CatalogController.cs | 2 +- .../Catalog/CatalogPriceChanged.cs | 19 +-- .../Catalog/CatalogPriceChangedHandler.cs | 14 -- .../Common/Infrastructure/EventBus.cs | 123 ++++++++++++------ .../Infrastructure/IIntegrationEvent.cs | 3 +- .../IIntegrationEventHandler.cs | 4 +- .../Infrastructure/Infrastructure.csproj | 1 + 10 files changed, 136 insertions(+), 65 deletions(-) create mode 100644 src/Services/Basket/Basket.API/Events/CatalogPriceChangedHandler.cs delete mode 100644 src/Services/Common/Infrastructure/Catalog/CatalogPriceChangedHandler.cs diff --git a/src/Services/Basket/Basket.API/Basket.API.csproj b/src/Services/Basket/Basket.API/Basket.API.csproj index fc040b3b0..1017dbd05 100644 --- a/src/Services/Basket/Basket.API/Basket.API.csproj +++ b/src/Services/Basket/Basket.API/Basket.API.csproj @@ -39,6 +39,10 @@ + + + + Always diff --git a/src/Services/Basket/Basket.API/Events/CatalogPriceChangedHandler.cs b/src/Services/Basket/Basket.API/Events/CatalogPriceChangedHandler.cs new file mode 100644 index 000000000..67fc6616f --- /dev/null +++ b/src/Services/Basket/Basket.API/Events/CatalogPriceChangedHandler.cs @@ -0,0 +1,25 @@ +using Microsoft.eShopOnContainers.Services.Basket.API.Model; +using Microsoft.eShopOnContainers.Services.Common.Infrastructure; +using Microsoft.eShopOnContainers.Services.Common.Infrastructure.Catalog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Basket.API.Events +{ + public class CatalogPriceChangedHandler : IIntegrationEventHandler + { + private readonly IBasketRepository _repository; + public CatalogPriceChangedHandler() + { + //_repository = repository; + } + + public void Handle(CatalogPriceChanged @event) + { + + } + } +} + diff --git a/src/Services/Basket/Basket.API/Startup.cs b/src/Services/Basket/Basket.API/Startup.cs index c72d68533..2eb6a36c1 100644 --- a/src/Services/Basket/Basket.API/Startup.cs +++ b/src/Services/Basket/Basket.API/Startup.cs @@ -13,6 +13,9 @@ using Microsoft.Extensions.Options; using System.Net; using Swashbuckle.Swagger.Model; using Microsoft.eShopOnContainers.Services.Basket.API.Auth.Server; +using Microsoft.eShopOnContainers.Services.Common.Infrastructure; +using Microsoft.eShopOnContainers.Services.Common.Infrastructure.Catalog; +using Basket.API.Events; namespace Microsoft.eShopOnContainers.Services.Basket.API { @@ -76,6 +79,9 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API }); services.AddTransient(); + var eventBus = new EventBus(); + services.AddSingleton(eventBus); + eventBus.Subscribe(new CatalogPriceChangedHandler()); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index bba7564e9..3558c55f5 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -107,7 +107,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers //hook to run integration tests until POST methods are created if (catalogTypeId.HasValue && catalogTypeId == 1) { - _eventBus.Publish(new CatalogPriceChanged()); + _eventBus.Publish(new CatalogPriceChanged(2, 10.4M)); } return Ok(model); diff --git a/src/Services/Common/Infrastructure/Catalog/CatalogPriceChanged.cs b/src/Services/Common/Infrastructure/Catalog/CatalogPriceChanged.cs index ad22ae49f..6e5715925 100644 --- a/src/Services/Common/Infrastructure/Catalog/CatalogPriceChanged.cs +++ b/src/Services/Common/Infrastructure/Catalog/CatalogPriceChanged.cs @@ -6,15 +6,16 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure.Catalog { public class CatalogPriceChanged : IIntegrationEvent { - private readonly string _eventName = "catalogpricechanged"; + public string Message { get { return "CatalogPriceChanged here!!"; } } - public string Name { - get - { - return _eventName; - } - } + public int ItemId { get; private set; } + + public decimal NewPrice { get; private set; } - public string Message { get { return "CatalogPriceChanged!!"; } } - } + public CatalogPriceChanged(int itemId, decimal newPrice) + { + ItemId = itemId; + NewPrice = newPrice; + } +} } diff --git a/src/Services/Common/Infrastructure/Catalog/CatalogPriceChangedHandler.cs b/src/Services/Common/Infrastructure/Catalog/CatalogPriceChangedHandler.cs deleted file mode 100644 index 8fc045ba0..000000000 --- a/src/Services/Common/Infrastructure/Catalog/CatalogPriceChangedHandler.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure.Catalog -{ - public class CatalogPriceChangedHandler : IIntegrationEventHandler - { - public void Handle(CatalogPriceChanged @event) - { - throw new NotImplementedException(); - } - } -} diff --git a/src/Services/Common/Infrastructure/EventBus.cs b/src/Services/Common/Infrastructure/EventBus.cs index 42ef384f1..a606a6d33 100644 --- a/src/Services/Common/Infrastructure/EventBus.cs +++ b/src/Services/Common/Infrastructure/EventBus.cs @@ -1,40 +1,47 @@  using Microsoft.eShopOnContainers.Services.Common.Infrastructure.Catalog; +using Newtonsoft.Json; using RabbitMQ.Client; using RabbitMQ.Client.Events; using System; +using System.Collections; using System.Collections.Generic; +using System.Linq; +using System.Reflection; using System.Text; namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure { public class EventBus : IEventBus { - private readonly Dictionary> _handlers; - private readonly Dictionary> _listeners; + private readonly string _brokerName = "event_bus"; + private readonly Dictionary> _handlers; + private readonly List _eventTypes; + + private Tuple _connection; + private string _queueName; + public EventBus() { _handlers = new Dictionary>(); - _listeners = new Dictionary>(); + _eventTypes = new List(); } public void Publish(IIntegrationEvent @event) { + var eventName = @event.GetType().Name; var factory = new ConnectionFactory() { HostName = "172.20.0.1" }; using (var connection = factory.CreateConnection()) using (var channel = connection.CreateModel()) - { - channel.QueueDeclare(queue: @event.Name, - durable: false, - exclusive: false, - autoDelete: false, - arguments: null); + { + channel.ExchangeDeclare(exchange: _brokerName, + type: "direct"); - string message = ((CatalogPriceChanged)@event).Message; + string message = JsonConvert.SerializeObject(@event); var body = Encoding.UTF8.GetBytes(message); - channel.BasicPublish(exchange: "", - routingKey: @event.Name, + channel.BasicPublish(exchange: _brokerName, + routingKey: eventName, basicProperties: null, body: body); } @@ -50,30 +57,14 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure } else { - var factory = new ConnectionFactory() { HostName = "172.18.0.1" }; - var connection = factory.CreateConnection(); - var channel = connection.CreateModel(); - - channel.QueueDeclare(queue: eventName, - durable: false, - exclusive: false, - autoDelete: false, - arguments: null); - - var consumer = new EventingBasicConsumer(channel); - consumer.Received += (model, ea) => - { - var body = ea.Body; - var message = Encoding.UTF8.GetString(body); - }; - channel.BasicConsume(queue: "hello", - noAck: true, - consumer: consumer); - ; - - _listeners.Add(eventName, new Tuple(channel, connection)); + var channel = GetChannel(); + channel.QueueBind(queue: _queueName, + exchange: _brokerName, + routingKey: eventName); + _handlers.Add(eventName, new List()); _handlers[eventName].Add(handler); + _eventTypes.Add(typeof(T)); } } @@ -88,13 +79,69 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure if (_handlers[eventName].Count == 0) { _handlers.Remove(eventName); + var eventType = _eventTypes.Single(e => e.Name == eventName); + _eventTypes.Remove(eventType); + _connection.Item1.QueueUnbind(queue: _queueName, + exchange: _brokerName, + routingKey: eventName); + + if (_handlers.Keys.Count == 0) + { + _queueName = string.Empty; + _connection.Item1.Close(); + _connection.Item2.Close(); + } + + } + } + } - var connectionItems =_listeners[eventName]; - _listeners.Remove(eventName); + private IModel GetChannel() + { + if (_connection != null) + { + return _connection.Item1; + } + else + { + var factory = new ConnectionFactory() { HostName = "172.20.0.1" }; + var connection = factory.CreateConnection(); + var channel = connection.CreateModel(); - connectionItems.Item1.Close(); - connectionItems.Item2.Close(); + channel.ExchangeDeclare(exchange: _brokerName, + type: "direct"); + if (string.IsNullOrEmpty(_queueName)) + { + _queueName = channel.QueueDeclare().QueueName; } + + var consumer = new EventingBasicConsumer(channel); + consumer.Received += (model, ea) => + { + var eventName = ea.RoutingKey; + if (_handlers.ContainsKey(eventName)) + { + var message = Encoding.UTF8.GetString(ea.Body); + Type eventType = _eventTypes.Single(t => t.Name == eventName); + + var integrationEvent = JsonConvert.DeserializeObject(message, eventType); + var handlers = _handlers[eventName]; + + + var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType); + + foreach (var handler in handlers) + { + concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent }); + } + } + }; + channel.BasicConsume(queue: _queueName, + noAck: true, + consumer: consumer); + _connection = new Tuple(channel, connection); + + return _connection.Item1; } } } diff --git a/src/Services/Common/Infrastructure/IIntegrationEvent.cs b/src/Services/Common/Infrastructure/IIntegrationEvent.cs index 9e92db3a4..48d991283 100644 --- a/src/Services/Common/Infrastructure/IIntegrationEvent.cs +++ b/src/Services/Common/Infrastructure/IIntegrationEvent.cs @@ -5,7 +5,6 @@ using System.Text; namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure { public interface IIntegrationEvent - { - string Name { get; } + { } } diff --git a/src/Services/Common/Infrastructure/IIntegrationEventHandler.cs b/src/Services/Common/Infrastructure/IIntegrationEventHandler.cs index b2d902ca2..49d194ecb 100644 --- a/src/Services/Common/Infrastructure/IIntegrationEventHandler.cs +++ b/src/Services/Common/Infrastructure/IIntegrationEventHandler.cs @@ -10,5 +10,7 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure void Handle(TIntegrationEvent @event); } - public interface IIntegrationEventHandler { } + public interface IIntegrationEventHandler + { + } } diff --git a/src/Services/Common/Infrastructure/Infrastructure.csproj b/src/Services/Common/Infrastructure/Infrastructure.csproj index fc7038ce9..34f8bc5d5 100644 --- a/src/Services/Common/Infrastructure/Infrastructure.csproj +++ b/src/Services/Common/Infrastructure/Infrastructure.csproj @@ -5,6 +5,7 @@ Microsoft.eShopOnContainers.Services.Common.Infrastructure + \ No newline at end of file From 4d1269b8f2424d7629879b8819d93be8168b4a94 Mon Sep 17 00:00:00 2001 From: dsanz Date: Fri, 10 Mar 2017 18:34:58 +0100 Subject: [PATCH 04/12] Add handler logic for basket repository. Add Post to CatalogController (only for price update). --- .../Events/CatalogPriceChangedHandler.cs | 31 ++++++++++++--- .../Basket/Basket.API/Model/BasketItem.cs | 1 + .../Basket.API/Model/IBasketRepository.cs | 1 + .../Basket.API/Model/RedisBasketRepository.cs | 38 +++++++++++++++++-- src/Services/Basket/Basket.API/Startup.cs | 19 +++++++++- .../Controllers/CatalogController.cs | 26 ++++++++++++- .../Catalog/CatalogPriceChanged.cs | 9 +++-- .../Common/Infrastructure/EventBus.cs | 5 ++- .../IIntegrationEventHandler.cs | 3 +- 9 files changed, 114 insertions(+), 19 deletions(-) diff --git a/src/Services/Basket/Basket.API/Events/CatalogPriceChangedHandler.cs b/src/Services/Basket/Basket.API/Events/CatalogPriceChangedHandler.cs index 67fc6616f..db116b0c4 100644 --- a/src/Services/Basket/Basket.API/Events/CatalogPriceChangedHandler.cs +++ b/src/Services/Basket/Basket.API/Events/CatalogPriceChangedHandler.cs @@ -11,15 +11,36 @@ namespace Basket.API.Events public class CatalogPriceChangedHandler : IIntegrationEventHandler { private readonly IBasketRepository _repository; - public CatalogPriceChangedHandler() + public CatalogPriceChangedHandler(IBasketRepository repository) { - //_repository = repository; + _repository = repository; } - public void Handle(CatalogPriceChanged @event) + public async Task Handle(CatalogPriceChanged @event) { - - } + var userIds = await _repository.GetUsers(); + foreach (var id in userIds) + { + var basket = await _repository.GetBasket(id); + await UpdateBasket(@event.ItemId, @event.NewPrice, basket); + } + } + + private async Task UpdateBasket(int itemId, decimal newPrice, CustomerBasket basket) + { + //TODO here seems to be a problem with the format + var itemsToUpdate = basket?.Items?.Where(x => int.Parse(x.Id) == itemId).ToList(); + if (itemsToUpdate != null) + { + foreach (var item in itemsToUpdate) + { + var originalPrice = item.UnitPrice; + item.UnitPrice = newPrice; + item.OldUnitPrice = originalPrice; + } + await _repository.UpdateBasket(basket); + } + } } } diff --git a/src/Services/Basket/Basket.API/Model/BasketItem.cs b/src/Services/Basket/Basket.API/Model/BasketItem.cs index c3f9b6212..76835ade9 100644 --- a/src/Services/Basket/Basket.API/Model/BasketItem.cs +++ b/src/Services/Basket/Basket.API/Model/BasketItem.cs @@ -11,6 +11,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Model public string ProductId { get; set; } public string ProductName { get; set; } public decimal UnitPrice { get; set; } + public decimal OldUnitPrice { get; set; } public int Quantity { get; set; } public string PictureUrl { get; set; } } diff --git a/src/Services/Basket/Basket.API/Model/IBasketRepository.cs b/src/Services/Basket/Basket.API/Model/IBasketRepository.cs index 3f0b99acf..017de8d51 100644 --- a/src/Services/Basket/Basket.API/Model/IBasketRepository.cs +++ b/src/Services/Basket/Basket.API/Model/IBasketRepository.cs @@ -8,6 +8,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Model public interface IBasketRepository { Task GetBasket(string customerId); + Task> GetUsers(); Task UpdateBasket(CustomerBasket basket); Task DeleteBasket(string id); } diff --git a/src/Services/Basket/Basket.API/Model/RedisBasketRepository.cs b/src/Services/Basket/Basket.API/Model/RedisBasketRepository.cs index 19d8e7055..fc5c256f8 100644 --- a/src/Services/Basket/Basket.API/Model/RedisBasketRepository.cs +++ b/src/Services/Basket/Basket.API/Model/RedisBasketRepository.cs @@ -31,6 +31,18 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Model return await database.KeyDeleteAsync(id.ToString()); } + public async Task> GetUsers() + { + var server = await GetServer(); + + IEnumerable data = server.Keys(); + if (data == null) + { + return null; + } + return data.Select(k => k.ToString()); + } + public async Task GetBasket(string customerId) { var database = await GetDatabase(); @@ -63,14 +75,32 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Model { if (_redis == null) { - //TODO: Need to make this more robust. Also want to understand why the static connection method cannot accept dns names. - var ips = await Dns.GetHostAddressesAsync(_settings.ConnectionString); - _logger.LogInformation($"Connecting to database {_settings.ConnectionString} at IP {ips.First().ToString()}"); - _redis = await ConnectionMultiplexer.ConnectAsync(ips.First().ToString()); + await ConnectToRedisAsync(); } return _redis.GetDatabase(); } + + private async Task GetServer() + { + if (_redis == null) + { + await ConnectToRedisAsync(); + } + var endpoint = _redis.GetEndPoints(); + + return _redis.GetServer(endpoint.First()); + } + + private async Task ConnectToRedisAsync() + { + //TODO: Need to make this more robust. Also want to understand why the static connection method cannot accept dns names. + var ips = await Dns.GetHostAddressesAsync(_settings.ConnectionString); + _logger.LogInformation($"Connecting to database {_settings.ConnectionString} at IP {ips.First().ToString()}"); + _redis = await ConnectionMultiplexer.ConnectAsync(ips.First().ToString()); + } + + } } diff --git a/src/Services/Basket/Basket.API/Startup.cs b/src/Services/Basket/Basket.API/Startup.cs index 2eb6a36c1..f7fa06df8 100644 --- a/src/Services/Basket/Basket.API/Startup.cs +++ b/src/Services/Basket/Basket.API/Startup.cs @@ -79,9 +79,24 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API }); services.AddTransient(); - var eventBus = new EventBus(); + services.AddTransient, CatalogPriceChangedHandler>(); + + var eventBus = new EventBus(); services.AddSingleton(eventBus); - eventBus.Subscribe(new CatalogPriceChangedHandler()); + + var serviceProvider = services.BuildServiceProvider(); + var catalogPriceHandler = serviceProvider.GetService>(); + eventBus.Subscribe(catalogPriceHandler); + + //var container = new ContainerBuilder(); + //container.Populate(services); + //container.RegisterModule(new ApplicationModule()); + + //return new AutofacServiceProvider(container.Build()); + + //var eventBus = new EventBus(); + //services.AddSingleton(eventBus); + //eventBus.Subscribe(new CatalogPriceChangedHandler(new RedisBasketRepository()); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index 3558c55f5..9d8cc0b11 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -107,7 +107,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers //hook to run integration tests until POST methods are created if (catalogTypeId.HasValue && catalogTypeId == 1) { - _eventBus.Publish(new CatalogPriceChanged(2, 10.4M)); + _eventBus.Publish(new CatalogPriceChanged(2, 10.4M, 8.4M)); } return Ok(model); @@ -135,6 +135,30 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers return Ok(items); } + [HttpPost] + public async Task Post([FromBody]CatalogItem value) + { + var item = await _context.CatalogItems.SingleOrDefaultAsync(i => i.Id == value.Id); + + if (item == null) + { + return NotFound(); + } + + if (item.Price != value.Price) + { + var oldPrice = item.Price; + item.Price = value.Price; + + _context.CatalogItems.Update(item); + await _context.SaveChangesAsync(); + + _eventBus.Publish(new CatalogPriceChanged(item.Id, item.Price, oldPrice)); + } + + return Ok(); + } + private List ComposePicUri(List items) { var baseUri = _settings.Value.ExternalCatalogBaseUrl; items.ForEach(x => diff --git a/src/Services/Common/Infrastructure/Catalog/CatalogPriceChanged.cs b/src/Services/Common/Infrastructure/Catalog/CatalogPriceChanged.cs index 6e5715925..427aa69c2 100644 --- a/src/Services/Common/Infrastructure/Catalog/CatalogPriceChanged.cs +++ b/src/Services/Common/Infrastructure/Catalog/CatalogPriceChanged.cs @@ -5,17 +5,18 @@ using System.Text; namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure.Catalog { public class CatalogPriceChanged : IIntegrationEvent - { - public string Message { get { return "CatalogPriceChanged here!!"; } } - + { public int ItemId { get; private set; } public decimal NewPrice { get; private set; } - public CatalogPriceChanged(int itemId, decimal newPrice) + public decimal OldPrice { get; set; } + + public CatalogPriceChanged(int itemId, decimal newPrice, decimal oldPrice) { ItemId = itemId; NewPrice = newPrice; + OldPrice = oldPrice; } } } diff --git a/src/Services/Common/Infrastructure/EventBus.cs b/src/Services/Common/Infrastructure/EventBus.cs index a606a6d33..dd273d571 100644 --- a/src/Services/Common/Infrastructure/EventBus.cs +++ b/src/Services/Common/Infrastructure/EventBus.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; +using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure { @@ -116,7 +117,7 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure } var consumer = new EventingBasicConsumer(channel); - consumer.Received += (model, ea) => + consumer.Received += async (model, ea) => { var eventName = ea.RoutingKey; if (_handlers.ContainsKey(eventName)) @@ -132,7 +133,7 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure foreach (var handler in handlers) { - concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent }); + await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent }); } } }; diff --git a/src/Services/Common/Infrastructure/IIntegrationEventHandler.cs b/src/Services/Common/Infrastructure/IIntegrationEventHandler.cs index 49d194ecb..6824a1665 100644 --- a/src/Services/Common/Infrastructure/IIntegrationEventHandler.cs +++ b/src/Services/Common/Infrastructure/IIntegrationEventHandler.cs @@ -1,13 +1,14 @@ using System; using System.Collections.Generic; using System.Text; +using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure { public interface IIntegrationEventHandler : IIntegrationEventHandler where TIntegrationEvent: IIntegrationEvent { - void Handle(TIntegrationEvent @event); + Task Handle(TIntegrationEvent @event); } public interface IIntegrationEventHandler From 172367d9d1788bb43042a10778a7d4b651fa9040 Mon Sep 17 00:00:00 2001 From: dsanz Date: Mon, 13 Mar 2017 11:03:35 +0100 Subject: [PATCH 05/12] Connect to the rabbitmq container using settings. --- docker-compose.override.yml | 3 +++ .../Basket/Basket.API/BasketSettings.cs | 2 ++ .../Events/CatalogPriceChangedHandler.cs | 5 ++--- src/Services/Basket/Basket.API/Startup.cs | 17 +++++------------ src/Services/Catalog/Catalog.API/Startup.cs | 5 ++++- src/Services/Catalog/Catalog.API/settings.cs | 1 + src/Services/Common/Infrastructure/EventBus.cs | 8 +++++--- 7 files changed, 22 insertions(+), 19 deletions(-) diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 4e24592cf..2fe32722e 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -8,6 +8,7 @@ services: - ASPNETCORE_URLS=http://0.0.0.0:5103 - ConnectionString=basket.data - identityUrl=http://identity.api:5105 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. + - EventBusConnection=rabbitmq ports: - "5103:5103" @@ -17,6 +18,7 @@ services: - ASPNETCORE_URLS=http://0.0.0.0:5101 - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word - ExternalCatalogBaseUrl=http://localhost:5101 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. + - EventBusConnection=rabbitmq ports: - "5101:5101" @@ -36,6 +38,7 @@ services: - ASPNETCORE_URLS=http://0.0.0.0:5102 - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word - identityUrl=http://identity.api:5105 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. + - EventBusConnection=rabbitmq ports: - "5102:5102" diff --git a/src/Services/Basket/Basket.API/BasketSettings.cs b/src/Services/Basket/Basket.API/BasketSettings.cs index d584c102f..6aae45015 100644 --- a/src/Services/Basket/Basket.API/BasketSettings.cs +++ b/src/Services/Basket/Basket.API/BasketSettings.cs @@ -8,5 +8,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API public class BasketSettings { public string ConnectionString { get; set; } + + public string EventBusConnection { get; set; } } } diff --git a/src/Services/Basket/Basket.API/Events/CatalogPriceChangedHandler.cs b/src/Services/Basket/Basket.API/Events/CatalogPriceChangedHandler.cs index db116b0c4..006c82119 100644 --- a/src/Services/Basket/Basket.API/Events/CatalogPriceChangedHandler.cs +++ b/src/Services/Basket/Basket.API/Events/CatalogPriceChangedHandler.cs @@ -28,8 +28,7 @@ namespace Basket.API.Events private async Task UpdateBasket(int itemId, decimal newPrice, CustomerBasket basket) { - //TODO here seems to be a problem with the format - var itemsToUpdate = basket?.Items?.Where(x => int.Parse(x.Id) == itemId).ToList(); + var itemsToUpdate = basket?.Items?.Where(x => int.Parse(x.ProductId) == itemId).ToList(); if (itemsToUpdate != null) { foreach (var item in itemsToUpdate) @@ -39,7 +38,7 @@ namespace Basket.API.Events item.OldUnitPrice = originalPrice; } await _repository.UpdateBasket(basket); - } + } } } } diff --git a/src/Services/Basket/Basket.API/Startup.cs b/src/Services/Basket/Basket.API/Startup.cs index f7fa06df8..e0bc9c076 100644 --- a/src/Services/Basket/Basket.API/Startup.cs +++ b/src/Services/Basket/Basket.API/Startup.cs @@ -81,22 +81,15 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API services.AddTransient(); services.AddTransient, CatalogPriceChangedHandler>(); - var eventBus = new EventBus(); + var serviceProvider = services.BuildServiceProvider(); + var configuration = serviceProvider.GetRequiredService>().Value; + var eventBus = new EventBus(configuration.EventBusConnection); services.AddSingleton(eventBus); - var serviceProvider = services.BuildServiceProvider(); + var catalogPriceHandler = serviceProvider.GetService>(); eventBus.Subscribe(catalogPriceHandler); - - //var container = new ContainerBuilder(); - //container.Populate(services); - //container.RegisterModule(new ApplicationModule()); - - //return new AutofacServiceProvider(container.Build()); - - //var eventBus = new EventBus(); - //services.AddSingleton(eventBus); - //eventBus.Subscribe(new CatalogPriceChangedHandler(new RedisBasketRepository()); + } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/src/Services/Catalog/Catalog.API/Startup.cs b/src/Services/Catalog/Catalog.API/Startup.cs index c8ecf72fd..9e0da8d00 100644 --- a/src/Services/Catalog/Catalog.API/Startup.cs +++ b/src/Services/Catalog/Catalog.API/Startup.cs @@ -11,6 +11,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Options; using System; using System.IO; using System.Reflection; @@ -74,7 +75,9 @@ .AllowCredentials()); }); - services.AddSingleton(); + var serviceProvider = services.BuildServiceProvider(); + var configuration = serviceProvider.GetRequiredService>().Value; + services.AddSingleton(new EventBus(configuration.EventBusConnection)); services.AddMvc(); } diff --git a/src/Services/Catalog/Catalog.API/settings.cs b/src/Services/Catalog/Catalog.API/settings.cs index 76091c409..a6e959552 100644 --- a/src/Services/Catalog/Catalog.API/settings.cs +++ b/src/Services/Catalog/Catalog.API/settings.cs @@ -9,5 +9,6 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API public class Settings { public string ExternalCatalogBaseUrl {get;set;} + public string EventBusConnection { get; set; } } } diff --git a/src/Services/Common/Infrastructure/EventBus.cs b/src/Services/Common/Infrastructure/EventBus.cs index dd273d571..47e51d28d 100644 --- a/src/Services/Common/Infrastructure/EventBus.cs +++ b/src/Services/Common/Infrastructure/EventBus.cs @@ -16,6 +16,7 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure public class EventBus : IEventBus { private readonly string _brokerName = "event_bus"; + private readonly string _connectionString; private readonly Dictionary> _handlers; private readonly List _eventTypes; @@ -23,15 +24,16 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure private string _queueName; - public EventBus() + public EventBus(string connectionString) { + _connectionString = connectionString; _handlers = new Dictionary>(); _eventTypes = new List(); } public void Publish(IIntegrationEvent @event) { var eventName = @event.GetType().Name; - var factory = new ConnectionFactory() { HostName = "172.20.0.1" }; + var factory = new ConnectionFactory() { HostName = _connectionString }; using (var connection = factory.CreateConnection()) using (var channel = connection.CreateModel()) { @@ -105,7 +107,7 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure } else { - var factory = new ConnectionFactory() { HostName = "172.20.0.1" }; + var factory = new ConnectionFactory() { HostName = _connectionString }; var connection = factory.CreateConnection(); var channel = connection.CreateModel(); From 66179813035acb31ed8210870e7494e14f2198bd Mon Sep 17 00:00:00 2001 From: dsanz Date: Mon, 13 Mar 2017 11:34:25 +0100 Subject: [PATCH 06/12] Fix for #80 --- src/Services/Identity/Identity.API/Configuration/Config.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Services/Identity/Identity.API/Configuration/Config.cs b/src/Services/Identity/Identity.API/Configuration/Config.cs index 764320096..9c799ca68 100644 --- a/src/Services/Identity/Identity.API/Configuration/Config.cs +++ b/src/Services/Identity/Identity.API/Configuration/Config.cs @@ -91,8 +91,8 @@ namespace Identity.API.Configuration }, PostLogoutRedirectUris = new List { - $"{clientsUrl["Mvc"]}/", - "http://localhost:5100/" + $"{clientsUrl["Mvc"]}/signout-callback-oidc", + "http://localhost:5100/signout-callback-oidc" }, AllowedScopes = new List { From 4e5e32c6aa094d70dfed7f5bcff4d95590455a9d Mon Sep 17 00:00:00 2001 From: dsanz Date: Mon, 13 Mar 2017 12:15:41 +0100 Subject: [PATCH 07/12] Add old price property for the basket in the WebMVC --- src/Web/WebMVC/ViewModels/BasketItem.cs | 1 + .../Shared/Components/CartList/Default.cshtml | 29 ++++++++++++------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/Web/WebMVC/ViewModels/BasketItem.cs b/src/Web/WebMVC/ViewModels/BasketItem.cs index 9e38c5f8d..ab767f8f5 100644 --- a/src/Web/WebMVC/ViewModels/BasketItem.cs +++ b/src/Web/WebMVC/ViewModels/BasketItem.cs @@ -11,6 +11,7 @@ namespace Microsoft.eShopOnContainers.WebMVC.ViewModels public string ProductId { get; set; } public string ProductName { get; set; } public decimal UnitPrice { get; set; } + public decimal OldUnitPrice { get; set; } public int Quantity { get; set; } public string PictureUrl { get; set; } } diff --git a/src/Web/WebMVC/Views/Shared/Components/CartList/Default.cshtml b/src/Web/WebMVC/Views/Shared/Components/CartList/Default.cshtml index d3c2abaed..33e62bafb 100644 --- a/src/Web/WebMVC/Views/Shared/Components/CartList/Default.cshtml +++ b/src/Web/WebMVC/Views/Shared/Components/CartList/Default.cshtml @@ -18,17 +18,24 @@ var item = Model.Items[i];
- -
- -
-
@item.ProductName
-
$ @item.UnitPrice
-
- - -
-
$ @Math.Round(item.Quantity * item.UnitPrice, 2)
+
+
+ +
+
@item.ProductName
+
$ @item.UnitPrice
+
+ + +
+
$ @Math.Round(item.Quantity * item.UnitPrice, 2)
+
+ @if (item.OldUnitPrice != 0) + { +
+ The price of the item has changed. Old price was @item.OldUnitPrice $ +
+ }
} From f502c2388fde2af1c9971d64965a50a0919aefa1 Mon Sep 17 00:00:00 2001 From: dsanz Date: Tue, 14 Mar 2017 09:47:36 +0100 Subject: [PATCH 08/12] Add persistence of published integration events for Catalog (the only microservice publishing integration events by the moment). --- .../Catalog/Catalog.API/Catalog.API.csproj | 1 + .../Controllers/CatalogController.cs | 30 +++-- .../Infrastructure/CatalogContext.cs | 32 ++++- .../20170314083211_AddEventTable.Designer.cs | 123 ++++++++++++++++++ .../20170314083211_AddEventTable.cs | 34 +++++ .../Migrations/CatalogContextModelSnapshot.cs | 44 +++++-- .../Catalog/CatalogPriceChanged.cs | 2 +- .../EventStateEnum.cs} | 7 +- .../Infrastructure/Data/IntegrationEvent.cs | 26 ++++ .../Common/Infrastructure/EventBus.cs | 6 +- .../Common/Infrastructure/IEventBus.cs | 6 +- .../IIntegrationEventHandler.cs | 2 +- .../Infrastructure/IntegrationEventBase.cs | 16 +++ 13 files changed, 300 insertions(+), 29 deletions(-) create mode 100644 src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170314083211_AddEventTable.Designer.cs create mode 100644 src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170314083211_AddEventTable.cs rename src/Services/Common/Infrastructure/{IIntegrationEvent.cs => Data/EventStateEnum.cs} (59%) create mode 100644 src/Services/Common/Infrastructure/Data/IntegrationEvent.cs create mode 100644 src/Services/Common/Infrastructure/IntegrationEventBase.cs diff --git a/src/Services/Catalog/Catalog.API/Catalog.API.csproj b/src/Services/Catalog/Catalog.API/Catalog.API.csproj index 1c0fddf92..387908b90 100644 --- a/src/Services/Catalog/Catalog.API/Catalog.API.csproj +++ b/src/Services/Catalog/Catalog.API/Catalog.API.csproj @@ -45,6 +45,7 @@ +
diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index 9d8cc0b11..71dcd5d1e 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -5,7 +5,9 @@ using Microsoft.eShopOnContainers.Services.Catalog.API.Model; using Microsoft.eShopOnContainers.Services.Catalog.API.ViewModel; using Microsoft.eShopOnContainers.Services.Common.Infrastructure; using Microsoft.eShopOnContainers.Services.Common.Infrastructure.Catalog; +using Microsoft.eShopOnContainers.Services.Common.Infrastructure.Data; using Microsoft.Extensions.Options; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -104,12 +106,6 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers var model = new PaginatedItemsViewModel( pageIndex, pageSize, totalItems, itemsOnPage); - //hook to run integration tests until POST methods are created - if (catalogTypeId.HasValue && catalogTypeId == 1) - { - _eventBus.Publish(new CatalogPriceChanged(2, 10.4M, 8.4M)); - } - return Ok(model); } @@ -153,11 +149,12 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers _context.CatalogItems.Update(item); await _context.SaveChangesAsync(); - _eventBus.Publish(new CatalogPriceChanged(item.Id, item.Price, oldPrice)); + var @event = new CatalogPriceChanged(item.Id, item.Price, oldPrice); + await ProcessEventAsync(@event); } return Ok(); - } + } private List ComposePicUri(List items) { var baseUri = _settings.Value.ExternalCatalogBaseUrl; @@ -168,5 +165,22 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers return items; } + + private async Task ProcessEventAsync(IntegrationEventBase @event) + { + _eventBus.Publish(@event); + var eventData = new IntegrationEvent(@event); + eventData.TimesSent++; + eventData.State = EventStateEnum.Sent; + try + { + _context.IntegrationEvents.Add(eventData); + await _context.SaveChangesAsync(); + } + catch (Exception ex) + { + var t = ex.Message; + } + } } } diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContext.cs b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContext.cs index 5c5498099..aac778940 100644 --- a/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContext.cs +++ b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContext.cs @@ -3,6 +3,7 @@ using EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore; using Model; + using Microsoft.eShopOnContainers.Services.Common.Infrastructure.Data; public class CatalogContext : DbContext { @@ -12,12 +13,15 @@ public DbSet CatalogItems { get; set; } public DbSet CatalogBrands { get; set; } public DbSet CatalogTypes { get; set; } + public DbSet IntegrationEvents { get; set; } + protected override void OnModelCreating(ModelBuilder builder) { builder.Entity(ConfigureCatalogBrand); builder.Entity(ConfigureCatalogType); builder.Entity(ConfigureCatalogItem); - } + builder.Entity(ConfigureIntegrationEvent); + } void ConfigureCatalogItem(EntityTypeBuilder builder) { @@ -75,5 +79,31 @@ .IsRequired() .HasMaxLength(100); } + + void ConfigureIntegrationEvent(EntityTypeBuilder builder) + { + builder.ToTable("IntegrationEvent"); + + builder.HasKey(e => e.EventId); + + builder.Property(e => e.EventId) + .IsRequired(); + + builder.Property(e => e.Content) + .IsRequired(); + + builder.Property(e => e.CreationTime) + .IsRequired(); + + builder.Property(e => e.State) + .IsRequired(); + + builder.Property(e => e.TimesSent) + .IsRequired(); + + builder.Property(e => e.EventTypeName) + .IsRequired(); + + } } } diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170314083211_AddEventTable.Designer.cs b/src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170314083211_AddEventTable.Designer.cs new file mode 100644 index 000000000..126047b85 --- /dev/null +++ b/src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170314083211_AddEventTable.Designer.cs @@ -0,0 +1,123 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; +using Microsoft.eShopOnContainers.Services.Common.Infrastructure; + +namespace Catalog.API.Infrastructure.Migrations +{ + [DbContext(typeof(CatalogContext))] + [Migration("20170314083211_AddEventTable")] + partial class AddEventTable + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "1.1.0-rtm-22752") + .HasAnnotation("SqlServer:Sequence:.catalog_brand_hilo", "'catalog_brand_hilo', '', '1', '10', '', '', 'Int64', 'False'") + .HasAnnotation("SqlServer:Sequence:.catalog_hilo", "'catalog_hilo', '', '1', '10', '', '', 'Int64', 'False'") + .HasAnnotation("SqlServer:Sequence:.catalog_type_hilo", "'catalog_type_hilo', '', '1', '10', '', '', 'Int64', 'False'") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogBrand", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:HiLoSequenceName", "catalog_brand_hilo") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); + + b.Property("Brand") + .IsRequired() + .HasMaxLength(100); + + b.HasKey("Id"); + + b.ToTable("CatalogBrand"); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:HiLoSequenceName", "catalog_hilo") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); + + b.Property("CatalogBrandId"); + + b.Property("CatalogTypeId"); + + b.Property("Description"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50); + + b.Property("PictureUri"); + + b.Property("Price"); + + b.HasKey("Id"); + + b.HasIndex("CatalogBrandId"); + + b.HasIndex("CatalogTypeId"); + + b.ToTable("Catalog"); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:HiLoSequenceName", "catalog_type_hilo") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); + + b.Property("Type") + .IsRequired() + .HasMaxLength(100); + + b.HasKey("Id"); + + b.ToTable("CatalogType"); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Common.Infrastructure.Data.IntegrationEvent", b => + { + b.Property("EventId") + .ValueGeneratedOnAdd(); + + b.Property("Content") + .IsRequired(); + + b.Property("CreationTime"); + + b.Property("EventTypeName") + .IsRequired() + .HasMaxLength(200); + + b.Property("State"); + + b.Property("TimesSent"); + + b.HasKey("EventId"); + + b.ToTable("IntegrationEvent"); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogItem", b => + { + b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogBrand", "CatalogBrand") + .WithMany() + .HasForeignKey("CatalogBrandId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogType", "CatalogType") + .WithMany() + .HasForeignKey("CatalogTypeId") + .OnDelete(DeleteBehavior.Cascade); + }); + } + } +} diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170314083211_AddEventTable.cs b/src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170314083211_AddEventTable.cs new file mode 100644 index 000000000..e6494d2a3 --- /dev/null +++ b/src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170314083211_AddEventTable.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Catalog.API.Infrastructure.Migrations +{ + public partial class AddEventTable : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "IntegrationEvent", + columns: table => new + { + EventId = table.Column(nullable: false), + Content = table.Column(nullable: false), + CreationTime = table.Column(nullable: false), + EventTypeName = table.Column(maxLength: 200, nullable: false), + State = table.Column(nullable: false), + TimesSent = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IntegrationEvent", x => x.EventId); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "IntegrationEvent"); + } + } +} diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/Migrations/CatalogContextModelSnapshot.cs b/src/Services/Catalog/Catalog.API/Infrastructure/Migrations/CatalogContextModelSnapshot.cs index c0eb1db72..4ea056e0d 100644 --- a/src/Services/Catalog/Catalog.API/Infrastructure/Migrations/CatalogContextModelSnapshot.cs +++ b/src/Services/Catalog/Catalog.API/Infrastructure/Migrations/CatalogContextModelSnapshot.cs @@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; +using Microsoft.eShopOnContainers.Services.Common.Infrastructure; namespace Catalog.API.Infrastructure.Migrations { @@ -13,13 +14,13 @@ namespace Catalog.API.Infrastructure.Migrations protected override void BuildModel(ModelBuilder modelBuilder) { modelBuilder - .HasAnnotation("ProductVersion", "1.0.1") + .HasAnnotation("ProductVersion", "1.1.0-rtm-22752") .HasAnnotation("SqlServer:Sequence:.catalog_brand_hilo", "'catalog_brand_hilo', '', '1', '10', '', '', 'Int64', 'False'") .HasAnnotation("SqlServer:Sequence:.catalog_hilo", "'catalog_hilo', '', '1', '10', '', '', 'Int64', 'False'") .HasAnnotation("SqlServer:Sequence:.catalog_type_hilo", "'catalog_type_hilo', '', '1', '10', '', '', 'Int64', 'False'") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.CatalogBrand", b => + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogBrand", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -28,14 +29,14 @@ namespace Catalog.API.Infrastructure.Migrations b.Property("Brand") .IsRequired() - .HasAnnotation("MaxLength", 100); + .HasMaxLength(100); b.HasKey("Id"); b.ToTable("CatalogBrand"); }); - modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.CatalogItem", b => + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogItem", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -50,7 +51,7 @@ namespace Catalog.API.Infrastructure.Migrations b.Property("Name") .IsRequired() - .HasAnnotation("MaxLength", 50); + .HasMaxLength(50); b.Property("PictureUri"); @@ -65,7 +66,7 @@ namespace Catalog.API.Infrastructure.Migrations b.ToTable("Catalog"); }); - modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.CatalogType", b => + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogType", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -74,21 +75,44 @@ namespace Catalog.API.Infrastructure.Migrations b.Property("Type") .IsRequired() - .HasAnnotation("MaxLength", 100); + .HasMaxLength(100); b.HasKey("Id"); b.ToTable("CatalogType"); }); - modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.CatalogItem", b => + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Common.Infrastructure.Data.IntegrationEvent", b => { - b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.CatalogBrand", "CatalogBrand") + b.Property("EventId") + .ValueGeneratedOnAdd(); + + b.Property("Content") + .IsRequired(); + + b.Property("CreationTime"); + + b.Property("EventTypeName") + .IsRequired() + .HasMaxLength(200); + + b.Property("State"); + + b.Property("TimesSent"); + + b.HasKey("EventId"); + + b.ToTable("IntegrationEvent"); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogItem", b => + { + b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogBrand", "CatalogBrand") .WithMany() .HasForeignKey("CatalogBrandId") .OnDelete(DeleteBehavior.Cascade); - b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.CatalogType", "CatalogType") + b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogType", "CatalogType") .WithMany() .HasForeignKey("CatalogTypeId") .OnDelete(DeleteBehavior.Cascade); diff --git a/src/Services/Common/Infrastructure/Catalog/CatalogPriceChanged.cs b/src/Services/Common/Infrastructure/Catalog/CatalogPriceChanged.cs index 427aa69c2..2c31a85b6 100644 --- a/src/Services/Common/Infrastructure/Catalog/CatalogPriceChanged.cs +++ b/src/Services/Common/Infrastructure/Catalog/CatalogPriceChanged.cs @@ -4,7 +4,7 @@ using System.Text; namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure.Catalog { - public class CatalogPriceChanged : IIntegrationEvent + public class CatalogPriceChanged : IntegrationEventBase { public int ItemId { get; private set; } diff --git a/src/Services/Common/Infrastructure/IIntegrationEvent.cs b/src/Services/Common/Infrastructure/Data/EventStateEnum.cs similarity index 59% rename from src/Services/Common/Infrastructure/IIntegrationEvent.cs rename to src/Services/Common/Infrastructure/Data/EventStateEnum.cs index 48d991283..e952b7742 100644 --- a/src/Services/Common/Infrastructure/IIntegrationEvent.cs +++ b/src/Services/Common/Infrastructure/Data/EventStateEnum.cs @@ -4,7 +4,10 @@ using System.Text; namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure { - public interface IIntegrationEvent - { + public enum EventStateEnum + { + NotSend = 0, + Sent = 1, + SendingFailed = 2 } } diff --git a/src/Services/Common/Infrastructure/Data/IntegrationEvent.cs b/src/Services/Common/Infrastructure/Data/IntegrationEvent.cs new file mode 100644 index 000000000..97b313021 --- /dev/null +++ b/src/Services/Common/Infrastructure/Data/IntegrationEvent.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Newtonsoft.Json; + +namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure.Data +{ + public class IntegrationEvent + { + public IntegrationEvent(IntegrationEventBase @event) + { + EventId = @event.Id; + CreationTime = DateTime.UtcNow; + EventTypeName = @event.GetType().FullName; + Content = JsonConvert.SerializeObject(@event); + State = EventStateEnum.NotSend; + TimesSent = 0; + } + public Guid EventId { get; private set; } + public string EventTypeName { get; private set; } + public EventStateEnum State { get; set; } + public int TimesSent { get; set; } + public DateTime CreationTime { get; private set; } + public string Content { get; private set; } + } +} diff --git a/src/Services/Common/Infrastructure/EventBus.cs b/src/Services/Common/Infrastructure/EventBus.cs index 47e51d28d..4ff33e79b 100644 --- a/src/Services/Common/Infrastructure/EventBus.cs +++ b/src/Services/Common/Infrastructure/EventBus.cs @@ -30,7 +30,7 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure _handlers = new Dictionary>(); _eventTypes = new List(); } - public void Publish(IIntegrationEvent @event) + public void Publish(IntegrationEventBase @event) { var eventName = @event.GetType().Name; var factory = new ConnectionFactory() { HostName = _connectionString }; @@ -51,7 +51,7 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure } - public void Subscribe(IIntegrationEventHandler handler) where T : IIntegrationEvent + public void Subscribe(IIntegrationEventHandler handler) where T : IntegrationEventBase { var eventName = typeof(T).Name; if (_handlers.ContainsKey(eventName)) @@ -72,7 +72,7 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure } - public void Unsubscribe(IIntegrationEventHandler handler) where T : IIntegrationEvent + public void Unsubscribe(IIntegrationEventHandler handler) where T : IntegrationEventBase { var eventName = typeof(T).Name; if (_handlers.ContainsKey(eventName) && _handlers[eventName].Contains(handler)) diff --git a/src/Services/Common/Infrastructure/IEventBus.cs b/src/Services/Common/Infrastructure/IEventBus.cs index 5751f1b81..483e550ec 100644 --- a/src/Services/Common/Infrastructure/IEventBus.cs +++ b/src/Services/Common/Infrastructure/IEventBus.cs @@ -6,8 +6,8 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure { public interface IEventBus { - void Subscribe(IIntegrationEventHandler handler) where T: IIntegrationEvent; - void Unsubscribe(IIntegrationEventHandler handler) where T : IIntegrationEvent; - void Publish(IIntegrationEvent @event); + void Subscribe(IIntegrationEventHandler handler) where T: IntegrationEventBase; + void Unsubscribe(IIntegrationEventHandler handler) where T : IntegrationEventBase; + void Publish(IntegrationEventBase @event); } } diff --git a/src/Services/Common/Infrastructure/IIntegrationEventHandler.cs b/src/Services/Common/Infrastructure/IIntegrationEventHandler.cs index 6824a1665..f4c00fa6b 100644 --- a/src/Services/Common/Infrastructure/IIntegrationEventHandler.cs +++ b/src/Services/Common/Infrastructure/IIntegrationEventHandler.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure { public interface IIntegrationEventHandler : IIntegrationEventHandler - where TIntegrationEvent: IIntegrationEvent + where TIntegrationEvent: IntegrationEventBase { Task Handle(TIntegrationEvent @event); } diff --git a/src/Services/Common/Infrastructure/IntegrationEventBase.cs b/src/Services/Common/Infrastructure/IntegrationEventBase.cs new file mode 100644 index 000000000..b11c17815 --- /dev/null +++ b/src/Services/Common/Infrastructure/IntegrationEventBase.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure +{ + public class IntegrationEventBase + { + public IntegrationEventBase() + { + Id = Guid.NewGuid(); + } + + public Guid Id { get; private set; } + } +} From 254d47958204e96d2ef3b0a1deff65cbd38f2942 Mon Sep 17 00:00:00 2001 From: dsanz Date: Tue, 14 Mar 2017 09:54:30 +0100 Subject: [PATCH 09/12] Renaming. --- .../Basket.API/Events/CatalogPriceChangedHandler.cs | 6 +++--- src/Services/Basket/Basket.API/Startup.cs | 8 ++++---- .../Catalog/Catalog.API/Controllers/CatalogController.cs | 2 +- src/Services/Catalog/Catalog.API/Startup.cs | 2 +- .../{CatalogPriceChanged.cs => ProductPriceChanged.cs} | 4 ++-- .../Infrastructure/{EventBus.cs => EventBusRabbitMQ.cs} | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) rename src/Services/Common/Infrastructure/Catalog/{CatalogPriceChanged.cs => ProductPriceChanged.cs} (78%) rename src/Services/Common/Infrastructure/{EventBus.cs => EventBusRabbitMQ.cs} (98%) diff --git a/src/Services/Basket/Basket.API/Events/CatalogPriceChangedHandler.cs b/src/Services/Basket/Basket.API/Events/CatalogPriceChangedHandler.cs index 006c82119..280692c17 100644 --- a/src/Services/Basket/Basket.API/Events/CatalogPriceChangedHandler.cs +++ b/src/Services/Basket/Basket.API/Events/CatalogPriceChangedHandler.cs @@ -8,15 +8,15 @@ using System.Threading.Tasks; namespace Basket.API.Events { - public class CatalogPriceChangedHandler : IIntegrationEventHandler + public class ProductPriceChangedHandler : IIntegrationEventHandler { private readonly IBasketRepository _repository; - public CatalogPriceChangedHandler(IBasketRepository repository) + public ProductPriceChangedHandler(IBasketRepository repository) { _repository = repository; } - public async Task Handle(CatalogPriceChanged @event) + public async Task Handle(ProductPriceChanged @event) { var userIds = await _repository.GetUsers(); foreach (var id in userIds) diff --git a/src/Services/Basket/Basket.API/Startup.cs b/src/Services/Basket/Basket.API/Startup.cs index e0bc9c076..ec5141de3 100644 --- a/src/Services/Basket/Basket.API/Startup.cs +++ b/src/Services/Basket/Basket.API/Startup.cs @@ -79,16 +79,16 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API }); services.AddTransient(); - services.AddTransient, CatalogPriceChangedHandler>(); + services.AddTransient, ProductPriceChangedHandler>(); var serviceProvider = services.BuildServiceProvider(); var configuration = serviceProvider.GetRequiredService>().Value; - var eventBus = new EventBus(configuration.EventBusConnection); + var eventBus = new EventBusRabbitMQ(configuration.EventBusConnection); services.AddSingleton(eventBus); - var catalogPriceHandler = serviceProvider.GetService>(); - eventBus.Subscribe(catalogPriceHandler); + var catalogPriceHandler = serviceProvider.GetService>(); + eventBus.Subscribe(catalogPriceHandler); } diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index 71dcd5d1e..2909b141c 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -149,7 +149,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers _context.CatalogItems.Update(item); await _context.SaveChangesAsync(); - var @event = new CatalogPriceChanged(item.Id, item.Price, oldPrice); + var @event = new ProductPriceChanged(item.Id, item.Price, oldPrice); await ProcessEventAsync(@event); } diff --git a/src/Services/Catalog/Catalog.API/Startup.cs b/src/Services/Catalog/Catalog.API/Startup.cs index 9e0da8d00..cb4f6254c 100644 --- a/src/Services/Catalog/Catalog.API/Startup.cs +++ b/src/Services/Catalog/Catalog.API/Startup.cs @@ -77,7 +77,7 @@ var serviceProvider = services.BuildServiceProvider(); var configuration = serviceProvider.GetRequiredService>().Value; - services.AddSingleton(new EventBus(configuration.EventBusConnection)); + services.AddSingleton(new EventBusRabbitMQ(configuration.EventBusConnection)); services.AddMvc(); } diff --git a/src/Services/Common/Infrastructure/Catalog/CatalogPriceChanged.cs b/src/Services/Common/Infrastructure/Catalog/ProductPriceChanged.cs similarity index 78% rename from src/Services/Common/Infrastructure/Catalog/CatalogPriceChanged.cs rename to src/Services/Common/Infrastructure/Catalog/ProductPriceChanged.cs index 2c31a85b6..4f28d4b1f 100644 --- a/src/Services/Common/Infrastructure/Catalog/CatalogPriceChanged.cs +++ b/src/Services/Common/Infrastructure/Catalog/ProductPriceChanged.cs @@ -4,7 +4,7 @@ using System.Text; namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure.Catalog { - public class CatalogPriceChanged : IntegrationEventBase + public class ProductPriceChanged : IntegrationEventBase { public int ItemId { get; private set; } @@ -12,7 +12,7 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure.Catalog public decimal OldPrice { get; set; } - public CatalogPriceChanged(int itemId, decimal newPrice, decimal oldPrice) + public ProductPriceChanged(int itemId, decimal newPrice, decimal oldPrice) { ItemId = itemId; NewPrice = newPrice; diff --git a/src/Services/Common/Infrastructure/EventBus.cs b/src/Services/Common/Infrastructure/EventBusRabbitMQ.cs similarity index 98% rename from src/Services/Common/Infrastructure/EventBus.cs rename to src/Services/Common/Infrastructure/EventBusRabbitMQ.cs index 4ff33e79b..aa17553a7 100644 --- a/src/Services/Common/Infrastructure/EventBus.cs +++ b/src/Services/Common/Infrastructure/EventBusRabbitMQ.cs @@ -13,7 +13,7 @@ using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure { - public class EventBus : IEventBus + public class EventBusRabbitMQ : IEventBus { private readonly string _brokerName = "event_bus"; private readonly string _connectionString; @@ -24,7 +24,7 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure private string _queueName; - public EventBus(string connectionString) + public EventBusRabbitMQ(string connectionString) { _connectionString = connectionString; _handlers = new Dictionary>(); From 23fcfd0dc4fcbb1513fbdf977aba4859a68ef337 Mon Sep 17 00:00:00 2001 From: "DESKTOP-V1VLQ15\\dsanz" Date: Tue, 14 Mar 2017 12:17:05 +0100 Subject: [PATCH 10/12] Fix merge issue. --- eShopOnContainers-ServicesAndWebApps.sln | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/eShopOnContainers-ServicesAndWebApps.sln b/eShopOnContainers-ServicesAndWebApps.sln index 6d59a9a18..f7c3dcde6 100644 --- a/eShopOnContainers-ServicesAndWebApps.sln +++ b/eShopOnContainers-ServicesAndWebApps.sln @@ -54,6 +54,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{478578 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure", "src\Services\Common\Infrastructure\Infrastructure.csproj", "{B08BA891-ABA2-4BD5-80E8-40B7546C3BE0}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{9C2566F6-3A98-4B1D-A6B2-35820CE88B73}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Infrastructure", "Infrastructure", "{7B41F409-D86D-4697-99BC-F8EBA21BB18F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Ad-Hoc|Any CPU = Ad-Hoc|Any CPU @@ -619,6 +623,8 @@ Global {F16E3C6A-1C94-4EAB-BE91-099618060B68} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04} {5B810E3D-112E-4857-B197-F09D2FD41E27} = {EF0337F2-ED00-4643-89FD-EE10863F1870} {CFE2FACB-4538-4B99-8A10-306F3882952D} = {EF0337F2-ED00-4643-89FD-EE10863F1870} - {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0} = {EF0337F2-ED00-4643-89FD-EE10863F1870} + {B08BA891-ABA2-4BD5-80E8-40B7546C3BE0} = {7B41F409-D86D-4697-99BC-F8EBA21BB18F} + {9C2566F6-3A98-4B1D-A6B2-35820CE88B73} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8} + {7B41F409-D86D-4697-99BC-F8EBA21BB18F} = {9C2566F6-3A98-4B1D-A6B2-35820CE88B73} EndGlobalSection EndGlobal From f6b2335518cdd469abb9a270f87b65c4cf7df177 Mon Sep 17 00:00:00 2001 From: "DESKTOP-V1VLQ15\\dsanz" Date: Tue, 14 Mar 2017 14:15:34 +0100 Subject: [PATCH 11/12] Fix issue with tests and Authorize attribute --- .../Common/Infrastructure/EventBusRabbitMQ.cs | 39 ++++++++++--------- .../Controllers/OrdersController.cs | 2 +- .../Middleware/AutoAuthorizeMiddleware.cs | 2 +- .../Middleware/AutoAuthorizeMiddleware.cs | 2 +- 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/Services/Common/Infrastructure/EventBusRabbitMQ.cs b/src/Services/Common/Infrastructure/EventBusRabbitMQ.cs index aa17553a7..f9c3d6a57 100644 --- a/src/Services/Common/Infrastructure/EventBusRabbitMQ.cs +++ b/src/Services/Common/Infrastructure/EventBusRabbitMQ.cs @@ -15,7 +15,7 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure { public class EventBusRabbitMQ : IEventBus { - private readonly string _brokerName = "event_bus"; + private readonly string _brokerName = "eshop_event_bus"; private readonly string _connectionString; private readonly Dictionary> _handlers; private readonly List _eventTypes; @@ -120,24 +120,11 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure var consumer = new EventingBasicConsumer(channel); consumer.Received += async (model, ea) => - { + { var eventName = ea.RoutingKey; - if (_handlers.ContainsKey(eventName)) - { - var message = Encoding.UTF8.GetString(ea.Body); - Type eventType = _eventTypes.Single(t => t.Name == eventName); - - var integrationEvent = JsonConvert.DeserializeObject(message, eventType); - var handlers = _handlers[eventName]; - - - var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType); - - foreach (var handler in handlers) - { - await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent }); - } - } + var message = Encoding.UTF8.GetString(ea.Body); + + await ProcessEvent(eventName, message); }; channel.BasicConsume(queue: _queueName, noAck: true, @@ -147,5 +134,21 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure return _connection.Item1; } } + + private async Task ProcessEvent(string eventName, string message) + { + if (_handlers.ContainsKey(eventName)) + { + Type eventType = _eventTypes.Single(t => t.Name == eventName); + var integrationEvent = JsonConvert.DeserializeObject(message, eventType); + var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType); + var handlers = _handlers[eventName]; + + foreach (var handler in handlers) + { + await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent }); + } + } + } } } diff --git a/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs b/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs index d01150b9c..ea97fc2fa 100644 --- a/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs +++ b/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs @@ -11,7 +11,7 @@ using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers { [Route("api/v1/[controller]")] - //[Authorize] + [Authorize] public class OrdersController : Controller { private readonly IMediator _mediator; diff --git a/test/Services/FunctionalTests/Middleware/AutoAuthorizeMiddleware.cs b/test/Services/FunctionalTests/Middleware/AutoAuthorizeMiddleware.cs index 72248e29d..41f1dfc88 100644 --- a/test/Services/FunctionalTests/Middleware/AutoAuthorizeMiddleware.cs +++ b/test/Services/FunctionalTests/Middleware/AutoAuthorizeMiddleware.cs @@ -17,7 +17,7 @@ namespace FunctionalTests.Middleware public async Task Invoke(HttpContext httpContext) { - var identity = new ClaimsIdentity(); + var identity = new ClaimsIdentity("cookies"); identity.AddClaim(new Claim("sub", "1234")); httpContext.User.AddIdentity(identity); await _next.Invoke(httpContext); diff --git a/test/Services/IntegrationTests/Middleware/AutoAuthorizeMiddleware.cs b/test/Services/IntegrationTests/Middleware/AutoAuthorizeMiddleware.cs index ce8172afa..598e4d0dd 100644 --- a/test/Services/IntegrationTests/Middleware/AutoAuthorizeMiddleware.cs +++ b/test/Services/IntegrationTests/Middleware/AutoAuthorizeMiddleware.cs @@ -17,7 +17,7 @@ namespace IntegrationTests.Middleware public async Task Invoke(HttpContext httpContext) { - var identity = new ClaimsIdentity(); + var identity = new ClaimsIdentity("cookies"); identity.AddClaim(new Claim("sub", "1234")); httpContext.User.AddIdentity(identity); await _next.Invoke(httpContext); From 8a9492e5733a68a07cc4cfb2c9791bd1da9ff4e3 Mon Sep 17 00:00:00 2001 From: "DESKTOP-V1VLQ15\\dsanz" Date: Tue, 14 Mar 2017 16:52:33 +0100 Subject: [PATCH 12/12] Add catalog test. --- .../FunctionalTests/FunctionalTests.csproj | 1 + .../Services/Catalog/CatalogScenarios.cs | 55 +++++++++++++++++++ .../Services/Catalog/CatalogScenariosBase.cs | 38 +++++++++++++ .../Services/Catalog/CatalogTestsStartup.cs | 15 +++++ test/Services/FunctionalTests/settings.json | 3 +- 5 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 test/Services/FunctionalTests/Services/Catalog/CatalogScenarios.cs create mode 100644 test/Services/FunctionalTests/Services/Catalog/CatalogScenariosBase.cs create mode 100644 test/Services/FunctionalTests/Services/Catalog/CatalogTestsStartup.cs diff --git a/test/Services/FunctionalTests/FunctionalTests.csproj b/test/Services/FunctionalTests/FunctionalTests.csproj index 039a734a2..0e24f233d 100644 --- a/test/Services/FunctionalTests/FunctionalTests.csproj +++ b/test/Services/FunctionalTests/FunctionalTests.csproj @@ -13,6 +13,7 @@ + diff --git a/test/Services/FunctionalTests/Services/Catalog/CatalogScenarios.cs b/test/Services/FunctionalTests/Services/Catalog/CatalogScenarios.cs new file mode 100644 index 000000000..ec4643ce7 --- /dev/null +++ b/test/Services/FunctionalTests/Services/Catalog/CatalogScenarios.cs @@ -0,0 +1,55 @@ +using Microsoft.eShopOnContainers.Services.Catalog.API.Model; +using Microsoft.eShopOnContainers.Services.Catalog.API.ViewModel; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace FunctionalTests.Services.Catalog +{ + public class CatalogScenarios : CatalogScenariosBase + { + [Fact] + public async Task Post_update_a_catalogitem_price_and_catalogitem_is_returned_modified() + { + using (var server = CreateServer()) + { + var client = server.CreateClient(); + + // Arrange + var itemToModify = GetCatalogItem(); + var newPrice = new Random().Next(1, 200); + itemToModify.Price = newPrice; + + // Act + var postRes = await client.PostAsync(Post.UpdateCatalogProduct, + new StringContent(JsonConvert.SerializeObject(itemToModify), + UTF8Encoding.UTF8, "application/json")); + var response = await client.GetAsync(Get.ProductByName(itemToModify.Name)); + var result = JsonConvert.DeserializeObject>(await response.Content.ReadAsStringAsync()); + var item = result.Data.First(); + + // Assert + Assert.Equal(result.Count, 1); + Assert.Equal(itemToModify.Id, item.Id); + Assert.Equal(newPrice, item.Price); + } + + } + + private CatalogItem GetCatalogItem() + { + return new CatalogItem() + { + Id = 1, + Price = 12.5M, + Name = ".NET Bot Black Sweatshirt" + }; + } + + } +} diff --git a/test/Services/FunctionalTests/Services/Catalog/CatalogScenariosBase.cs b/test/Services/FunctionalTests/Services/Catalog/CatalogScenariosBase.cs new file mode 100644 index 000000000..b4107b3bc --- /dev/null +++ b/test/Services/FunctionalTests/Services/Catalog/CatalogScenariosBase.cs @@ -0,0 +1,38 @@ +using FunctionalTests.Middleware; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.eShopOnContainers.Services.Catalog.API; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace FunctionalTests.Services.Catalog +{ + public class CatalogScenariosBase + { + public TestServer CreateServer() + { + var webHostBuilder = new WebHostBuilder(); + webHostBuilder.UseContentRoot(Directory.GetCurrentDirectory()); + webHostBuilder.UseStartup(); + + return new TestServer(webHostBuilder); + } + + public static class Get + { + public static string Orders = "api/v1/orders"; + + public static string ProductByName(string name) + { + return $"api/v1/catalog/items/withname/{name}"; + } + } + + public static class Post + { + public static string UpdateCatalogProduct = "api/v1/catalog"; + } + } +} diff --git a/test/Services/FunctionalTests/Services/Catalog/CatalogTestsStartup.cs b/test/Services/FunctionalTests/Services/Catalog/CatalogTestsStartup.cs new file mode 100644 index 000000000..f06f9b248 --- /dev/null +++ b/test/Services/FunctionalTests/Services/Catalog/CatalogTestsStartup.cs @@ -0,0 +1,15 @@ +using Microsoft.eShopOnContainers.Services.Catalog.API; +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.AspNetCore.Hosting; + +namespace FunctionalTests.Services.Catalog +{ + public class CatalogTestsStartup : Startup + { + public CatalogTestsStartup(IHostingEnvironment env) : base(env) + { + } + } +} diff --git a/test/Services/FunctionalTests/settings.json b/test/Services/FunctionalTests/settings.json index 3b6023956..7091ec819 100644 --- a/test/Services/FunctionalTests/settings.json +++ b/test/Services/FunctionalTests/settings.json @@ -1,5 +1,6 @@ { "ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;", "IdentityUrl": "http://localhost:5105", - "isTest": "true" + "isTest": "true", + "EventBusConnection": "localhost" }