From a3fd171712f42d8209c8e5007fa84bbf4b0689ae Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 8 Mar 2017 16:56:26 -0500 Subject: [PATCH 01/25] inject dependencies into the WebForms application --- .../Catalog.WebForms/Catalog.WebForms.csproj | 1 + .../Catalog.WebForms/Default.aspx.cs | 18 ++-- .../Catalog.WebForms/Global.asax.cs | 24 +----- .../Modules/AutoFacHttpModule.cs | 84 +++++++++++++++++++ .../Catalog.WebForms/Web.config | 4 + 5 files changed, 100 insertions(+), 31 deletions(-) create mode 100644 src/Web/Catalog.WebForms/Catalog.WebForms/Modules/AutoFacHttpModule.cs diff --git a/src/Web/Catalog.WebForms/Catalog.WebForms/Catalog.WebForms.csproj b/src/Web/Catalog.WebForms/Catalog.WebForms/Catalog.WebForms.csproj index 5661b2fdb..06bd309ca 100644 --- a/src/Web/Catalog.WebForms/Catalog.WebForms/Catalog.WebForms.csproj +++ b/src/Web/Catalog.WebForms/Catalog.WebForms/Catalog.WebForms.csproj @@ -221,6 +221,7 @@ + diff --git a/src/Web/Catalog.WebForms/Catalog.WebForms/Default.aspx.cs b/src/Web/Catalog.WebForms/Catalog.WebForms/Default.aspx.cs index bd222c5af..ee4efea3b 100644 --- a/src/Web/Catalog.WebForms/Catalog.WebForms/Default.aspx.cs +++ b/src/Web/Catalog.WebForms/Catalog.WebForms/Default.aspx.cs @@ -18,6 +18,13 @@ namespace Microsoft.eShopOnContainers.Catalog.WebForms private ICatalogService catalog; + protected _Default() { } + + public _Default(ICatalogService catalog) + { + this.catalog = catalog; + } + protected override void OnLoad(EventArgs e) { RegisterAsyncTask(new PageAsyncTask(LoadCatalogDataAsync)); @@ -27,14 +34,9 @@ namespace Microsoft.eShopOnContainers.Catalog.WebForms private async Task LoadCatalogDataAsync() { - var container = Application.Get("container") as IContainer; - using (scope = container?.BeginLifetimeScope()) - { - catalog = container?.Resolve(); - var collection = await catalog?.GetCatalogAsync(); - catalogList.DataSource = collection; - catalogList.DataBind(); - } + var collection = await catalog?.GetCatalogAsync(); + catalogList.DataSource = collection; + catalogList.DataBind(); } protected void Page_Load(object sender, EventArgs e) diff --git a/src/Web/Catalog.WebForms/Catalog.WebForms/Global.asax.cs b/src/Web/Catalog.WebForms/Catalog.WebForms/Global.asax.cs index 48e1d8dbc..f0e204417 100644 --- a/src/Web/Catalog.WebForms/Catalog.WebForms/Global.asax.cs +++ b/src/Web/Catalog.WebForms/Catalog.WebForms/Global.asax.cs @@ -1,14 +1,7 @@ -using Autofac; -using eShopOnContainers.Core.Services.Catalog; -using System; -using System.Collections.Generic; -using System.Linq; +using System; using System.Web; -using System.Web.Configuration; using System.Web.Optimization; using System.Web.Routing; -using System.Web.Security; -using System.Web.SessionState; namespace Microsoft.eShopOnContainers.Catalog.WebForms { @@ -21,21 +14,6 @@ namespace Microsoft.eShopOnContainers.Catalog.WebForms RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); - // Register Containers: - var settings= WebConfigurationManager.AppSettings; - var useFake = settings["usefake"]; - bool fake = useFake == "true"; - var builder = new ContainerBuilder(); - if (fake) - { - builder.RegisterType() - .As(); - } else { - builder.RegisterType() - .As(); - } - var container = builder.Build(); - Application.Add("container", container); } } } \ No newline at end of file diff --git a/src/Web/Catalog.WebForms/Catalog.WebForms/Modules/AutoFacHttpModule.cs b/src/Web/Catalog.WebForms/Catalog.WebForms/Modules/AutoFacHttpModule.cs new file mode 100644 index 000000000..d0d7a60e9 --- /dev/null +++ b/src/Web/Catalog.WebForms/Catalog.WebForms/Modules/AutoFacHttpModule.cs @@ -0,0 +1,84 @@ +using Autofac; +using eShopOnContainers.Core.Services.Catalog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Configuration; +using System.Web.UI; + +namespace Microsoft.eShopOnContainers.Catalog.WebForms.Modules +{ + // Using DI with WebForms is not yet implemented. + // This implementation has been adapted from this post: + // https://blogs.msdn.microsoft.com/webdev/2016/10/19/modern-asp-net-web-forms-development-dependency-injection/ + + public class AutoFacHttpModule : IHttpModule + { + private static IContainer Container => lazyContainer.Value; + + private static Lazy lazyContainer = new Lazy(() => CreateContainer()); + + private static IContainer CreateContainer() + { + // Configure AutoFac: + // Register Containers: + var settings = WebConfigurationManager.AppSettings; + var useFake = settings["usefake"]; + bool fake = useFake == "true"; + var builder = new ContainerBuilder(); + if (fake) + { + builder.RegisterType() + .As(); + } + else + { + builder.RegisterType() + .As(); + } + var container = builder.Build(); + return container; + } + + public void Dispose() + { + Container.Dispose(); + } + + public void Init(HttpApplication context) + { + context.PreRequestHandlerExecute += (_, __) => InjectDependencies(); + } + + private void InjectDependencies() + { + if (HttpContext.Current.CurrentHandler is Page) + { + var page = HttpContext.Current.CurrentHandler as Page; + // Get the code-behind class that we may have written + var pageType = page.GetType().BaseType; + + // Determine if there is a constructor to inject, and grab it + var ctor = (from c in pageType.GetConstructors() + where c.GetParameters().Length > 0 + select c).FirstOrDefault(); + + if (ctor != null) + { + // Resolve the parameters for the constructor + var args = (from parm in ctor.GetParameters() + select Container.Resolve(parm.ParameterType)) + .ToArray(); + + // Execute the constructor method with the arguments resolved + ctor.Invoke(page, args); + } + + // Use the Autofac method to inject any properties that can be filled by Autofac + Container.InjectProperties(page); + + } + } + } +} \ No newline at end of file diff --git a/src/Web/Catalog.WebForms/Catalog.WebForms/Web.config b/src/Web/Catalog.WebForms/Catalog.WebForms/Web.config index 9da53af59..47ad4e633 100644 --- a/src/Web/Catalog.WebForms/Catalog.WebForms/Web.config +++ b/src/Web/Catalog.WebForms/Catalog.WebForms/Web.config @@ -20,6 +20,8 @@ + + @@ -39,6 +41,8 @@ + + From 74fbbecf5821b9c562513a464afc8579a6e39ed5 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 9 Mar 2017 16:38:02 -0500 Subject: [PATCH 02/25] Update Compiler package This is necessary for C# 7 feature support. See the pattern match is statement in AutoFacHttpModule --- .../Catalog.WebForms/Catalog.WebForms/Catalog.WebForms.csproj | 4 ++-- src/Web/Catalog.WebForms/Catalog.WebForms/Default.aspx.cs | 2 -- .../Catalog.WebForms/Modules/AutoFacHttpModule.cs | 3 +-- src/Web/Catalog.WebForms/Catalog.WebForms/Web.config | 4 ++-- src/Web/Catalog.WebForms/Catalog.WebForms/packages.config | 2 +- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/Web/Catalog.WebForms/Catalog.WebForms/Catalog.WebForms.csproj b/src/Web/Catalog.WebForms/Catalog.WebForms/Catalog.WebForms.csproj index 06bd309ca..391983c31 100644 --- a/src/Web/Catalog.WebForms/Catalog.WebForms/Catalog.WebForms.csproj +++ b/src/Web/Catalog.WebForms/Catalog.WebForms/Catalog.WebForms.csproj @@ -1,7 +1,7 @@  + - Debug @@ -292,8 +292,8 @@ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + - + @@ -21,7 +21,7 @@ - + diff --git a/src/Web/Catalog.WebForms/Catalog.WebForms/packages.config b/src/Web/Catalog.WebForms/Catalog.WebForms/packages.config index dd268d3f3..756f567ce 100644 --- a/src/Web/Catalog.WebForms/Catalog.WebForms/packages.config +++ b/src/Web/Catalog.WebForms/Catalog.WebForms/packages.config @@ -20,7 +20,7 @@ - + From 3ec61e0df64097b7c6524fbe58fee7a6173c84ed Mon Sep 17 00:00:00 2001 From: etomas Date: Sat, 18 Mar 2017 11:13:20 +0100 Subject: [PATCH 03/25] Tried patch proposed by Sandeep Bansal --- _docker/redis/Dockerfile.nanowin | 2 ++ src/Services/Basket/Basket.API/Dockerfile.nanowin | 1 + src/Services/Catalog/Catalog.API/Dockerfile.nanowin | 1 + src/Services/Identity/Identity.API/Dockerfile.nanowin | 1 + src/Services/Ordering/Ordering.API/Dockerfile.nanowin | 1 + src/Web/WebMVC/Dockerfile.nanowin | 1 + src/Web/WebSPA/Dockerfile.nanowin | 1 + 7 files changed, 8 insertions(+) diff --git a/_docker/redis/Dockerfile.nanowin b/_docker/redis/Dockerfile.nanowin index 6a36d1947..a08bcb0b6 100644 --- a/_docker/redis/Dockerfile.nanowin +++ b/_docker/redis/Dockerfile.nanowin @@ -22,6 +22,8 @@ RUN Get-Content redis.windows.conf | Where { $_ -notmatch 'bind 127.0.0.1' } | S EXPOSE 6379 +RUN set-itemproperty -path 'HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters' -Name ServerPriorityTimeLimit -Value 0 -Type DWord + # Define our command to be run when launching the container CMD .\\redis-server.exe .\\redis.unprotected.conf --port 6379 ; \ Write-Host Redis Started... ; \ diff --git a/src/Services/Basket/Basket.API/Dockerfile.nanowin b/src/Services/Basket/Basket.API/Dockerfile.nanowin index 41127e339..0541a26cc 100644 --- a/src/Services/Basket/Basket.API/Dockerfile.nanowin +++ b/src/Services/Basket/Basket.API/Dockerfile.nanowin @@ -1,6 +1,7 @@ FROM microsoft/dotnet:1.1-runtime-nanoserver ARG source WORKDIR /app +RUN set-itemproperty -path 'HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters' -Name ServerPriorityTimeLimit -Value 0 -Type DWord EXPOSE 80 COPY ${source:-obj/Docker/publish} . ENTRYPOINT ["dotnet", "Basket.API.dll"] diff --git a/src/Services/Catalog/Catalog.API/Dockerfile.nanowin b/src/Services/Catalog/Catalog.API/Dockerfile.nanowin index 5aad21ad1..c40d3a4d0 100644 --- a/src/Services/Catalog/Catalog.API/Dockerfile.nanowin +++ b/src/Services/Catalog/Catalog.API/Dockerfile.nanowin @@ -1,6 +1,7 @@ FROM microsoft/dotnet:1.1-runtime-nanoserver ARG source WORKDIR /app +RUN set-itemproperty -path 'HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters' -Name ServerPriorityTimeLimit -Value 0 -Type DWord EXPOSE 80 COPY ${source:-obj/Docker/publish} . ENTRYPOINT ["dotnet", "Catalog.API.dll"] diff --git a/src/Services/Identity/Identity.API/Dockerfile.nanowin b/src/Services/Identity/Identity.API/Dockerfile.nanowin index 01f5eb8c5..b950d8334 100644 --- a/src/Services/Identity/Identity.API/Dockerfile.nanowin +++ b/src/Services/Identity/Identity.API/Dockerfile.nanowin @@ -1,6 +1,7 @@ FROM microsoft/dotnet:1.1-runtime-nanoserver ARG source WORKDIR /app +RUN set-itemproperty -path 'HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters' -Name ServerPriorityTimeLimit -Value 0 -Type DWord EXPOSE 80 COPY ${source:-obj/Docker/publish} . ENTRYPOINT ["dotnet", "Identity.API.dll"] diff --git a/src/Services/Ordering/Ordering.API/Dockerfile.nanowin b/src/Services/Ordering/Ordering.API/Dockerfile.nanowin index 85b1f46b4..eac299fc7 100644 --- a/src/Services/Ordering/Ordering.API/Dockerfile.nanowin +++ b/src/Services/Ordering/Ordering.API/Dockerfile.nanowin @@ -1,6 +1,7 @@ FROM microsoft/dotnet:1.1-runtime-nanoserver ARG source WORKDIR /app +RUN set-itemproperty -path 'HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters' -Name ServerPriorityTimeLimit -Value 0 -Type DWord EXPOSE 80 COPY ${source:-obj/Docker/publish} . ENTRYPOINT ["dotnet", "Ordering.API.dll"] diff --git a/src/Web/WebMVC/Dockerfile.nanowin b/src/Web/WebMVC/Dockerfile.nanowin index fe622e8de..302526330 100644 --- a/src/Web/WebMVC/Dockerfile.nanowin +++ b/src/Web/WebMVC/Dockerfile.nanowin @@ -1,6 +1,7 @@ FROM microsoft/dotnet:1.1-runtime-nanoserver ARG source WORKDIR /app +RUN set-itemproperty -path 'HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters' -Name ServerPriorityTimeLimit -Value 0 -Type DWord EXPOSE 80 COPY ${source:-obj/Docker/publish} . ENTRYPOINT ["dotnet", "WebMVC.dll"] diff --git a/src/Web/WebSPA/Dockerfile.nanowin b/src/Web/WebSPA/Dockerfile.nanowin index 8c784ca6e..5f9a48294 100644 --- a/src/Web/WebSPA/Dockerfile.nanowin +++ b/src/Web/WebSPA/Dockerfile.nanowin @@ -1,6 +1,7 @@ FROM microsoft/dotnet:1.1-runtime-nanoserver ARG source WORKDIR /app +RUN set-itemproperty -path 'HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters' -Name ServerPriorityTimeLimit -Value 0 -Type DWord EXPOSE 80 COPY ${source:-obj/Docker/publish} . ENTRYPOINT ["dotnet", "WebSPA.dll"] From 7a63490558e9a7f40229d746db309839c3d5e21d Mon Sep 17 00:00:00 2001 From: dsanz Date: Wed, 22 Mar 2017 16:10:46 +0100 Subject: [PATCH 04/25] Add the sql implementation for the storage of Integration events. --- eShopOnContainers-ServicesAndWebApps.sln | 53 +++++++++- .../EventStateEnum.cs | 2 +- .../IntegrationEventLogContext.cs | 48 +++++++++ .../IntegrationEventLogEF.csproj | 27 +++++ .../IntegrationEventLogEntry.cs | 3 +- .../Catalog/Catalog.API/Catalog.API.csproj | 1 + .../Controllers/CatalogController.cs | 50 +++++----- .../Infrastructure/CatalogContext.cs | 42 ++++---- .../20161103152832_Initial.Designer.cs | 0 .../20161103152832_Initial.cs | 0 ...0161103153420_UpdateTableNames.Designer.cs | 0 .../20161103153420_UpdateTableNames.cs | 0 .../20170314083211_AddEventTable.Designer.cs | 0 .../20170314083211_AddEventTable.cs | 0 ...factoringToIntegrationEventLog.Designer.cs | 0 ...012921_RefactoringToIntegrationEventLog.cs | 0 ..._RefactoringEventBusNamespaces.Designer.cs | 0 ...316120022_RefactoringEventBusNamespaces.cs | 0 ...244_RemoveIntegrationEventLogs.Designer.cs | 99 +++++++++++++++++++ ...170322124244_RemoveIntegrationEventLogs.cs | 34 +++++++ .../CatalogContextModelSnapshot.cs | 23 ----- ...145434_IntegrationEventInitial.Designer.cs | 43 ++++++++ .../20170322145434_IntegrationEventInitial.cs | 34 +++++++ ...IntegrationEventLogContextModelSnapshot.cs | 42 ++++++++ src/Services/Catalog/Catalog.API/Startup.cs | 15 ++- 25 files changed, 443 insertions(+), 73 deletions(-) rename src/BuildingBlocks/EventBus/{EventBus/Events/IntegrationEventLog => IntegrationEventLogEF}/EventStateEnum.cs (68%) create mode 100644 src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogContext.cs create mode 100644 src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj rename src/BuildingBlocks/EventBus/{EventBus/Events/IntegrationEventLog => IntegrationEventLogEF}/IntegrationEventLogEntry.cs (85%) rename src/Services/Catalog/Catalog.API/Infrastructure/{Migrations => CatalogMigrations}/20161103152832_Initial.Designer.cs (100%) rename src/Services/Catalog/Catalog.API/Infrastructure/{Migrations => CatalogMigrations}/20161103152832_Initial.cs (100%) rename src/Services/Catalog/Catalog.API/Infrastructure/{Migrations => CatalogMigrations}/20161103153420_UpdateTableNames.Designer.cs (100%) rename src/Services/Catalog/Catalog.API/Infrastructure/{Migrations => CatalogMigrations}/20161103153420_UpdateTableNames.cs (100%) rename src/Services/Catalog/Catalog.API/Infrastructure/{Migrations => CatalogMigrations}/20170314083211_AddEventTable.Designer.cs (100%) rename src/Services/Catalog/Catalog.API/Infrastructure/{Migrations => CatalogMigrations}/20170314083211_AddEventTable.cs (100%) rename src/Services/Catalog/Catalog.API/Infrastructure/{Migrations => CatalogMigrations}/20170316012921_RefactoringToIntegrationEventLog.Designer.cs (100%) rename src/Services/Catalog/Catalog.API/Infrastructure/{Migrations => CatalogMigrations}/20170316012921_RefactoringToIntegrationEventLog.cs (100%) rename src/Services/Catalog/Catalog.API/Infrastructure/{Migrations => CatalogMigrations}/20170316120022_RefactoringEventBusNamespaces.Designer.cs (100%) rename src/Services/Catalog/Catalog.API/Infrastructure/{Migrations => CatalogMigrations}/20170316120022_RefactoringEventBusNamespaces.cs (100%) create mode 100644 src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170322124244_RemoveIntegrationEventLogs.Designer.cs create mode 100644 src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170322124244_RemoveIntegrationEventLogs.cs rename src/Services/Catalog/Catalog.API/Infrastructure/{Migrations => CatalogMigrations}/CatalogContextModelSnapshot.cs (84%) create mode 100644 src/Services/Catalog/Catalog.API/Infrastructure/IntegrationEventMigrations/20170322145434_IntegrationEventInitial.Designer.cs create mode 100644 src/Services/Catalog/Catalog.API/Infrastructure/IntegrationEventMigrations/20170322145434_IntegrationEventInitial.cs create mode 100644 src/Services/Catalog/Catalog.API/Infrastructure/IntegrationEventMigrations/IntegrationEventLogContextModelSnapshot.cs diff --git a/eShopOnContainers-ServicesAndWebApps.sln b/eShopOnContainers-ServicesAndWebApps.sln index 619dcf4c3..e497a67e5 100644 --- a/eShopOnContainers-ServicesAndWebApps.sln +++ b/eShopOnContainers-ServicesAndWebApps.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26228.4 +VisualStudioVersion = 15.0.26228.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{932D8224-11F6-4D07-B109-DA28AD288A63}" EndProject @@ -72,6 +72,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventBus", "src\BuildingBlo EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventBusRabbitMQ", "src\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj", "{8088F3FC-6787-45FA-A924-816EC81CBFAC}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationEventLogEF", "src\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj", "{9EE28E45-1533-472B-8267-56C48855BA0E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Ad-Hoc|Any CPU = Ad-Hoc|Any CPU @@ -662,6 +664,54 @@ Global {8088F3FC-6787-45FA-A924-816EC81CBFAC}.Release|x64.Build.0 = Release|Any CPU {8088F3FC-6787-45FA-A924-816EC81CBFAC}.Release|x86.ActiveCfg = Release|Any CPU {8088F3FC-6787-45FA-A924-816EC81CBFAC}.Release|x86.Build.0 = Release|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|ARM.Build.0 = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|iPhone.Build.0 = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|x64.ActiveCfg = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|x64.Build.0 = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|x86.ActiveCfg = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.AppStore|x86.Build.0 = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|ARM.ActiveCfg = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|ARM.Build.0 = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|iPhone.Build.0 = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|x64.ActiveCfg = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|x64.Build.0 = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|x86.ActiveCfg = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Debug|x86.Build.0 = Debug|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Release|Any CPU.Build.0 = Release|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Release|ARM.ActiveCfg = Release|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Release|ARM.Build.0 = Release|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Release|iPhone.ActiveCfg = Release|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Release|iPhone.Build.0 = Release|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Release|x64.ActiveCfg = Release|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Release|x64.Build.0 = Release|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Release|x86.ActiveCfg = Release|Any CPU + {9EE28E45-1533-472B-8267-56C48855BA0E}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -689,5 +739,6 @@ Global {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F} = {DB0EFB20-B024-4E5E-A75C-52143C131D25} {0044B293-1DCC-4224-B948-00CF6DC7F510} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F} {8088F3FC-6787-45FA-A924-816EC81CBFAC} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F} + {9EE28E45-1533-472B-8267-56C48855BA0E} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F} EndGlobalSection EndGlobal diff --git a/src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEventLog/EventStateEnum.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/EventStateEnum.cs similarity index 68% rename from src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEventLog/EventStateEnum.cs rename to src/BuildingBlocks/EventBus/IntegrationEventLogEF/EventStateEnum.cs index 41ddc119c..3efb78e74 100644 --- a/src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEventLog/EventStateEnum.cs +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/EventStateEnum.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events.IntegrationEventLog +namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF { public enum EventStateEnum { diff --git a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogContext.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogContext.cs new file mode 100644 index 000000000..de8754e03 --- /dev/null +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogContext.cs @@ -0,0 +1,48 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF +{ + public class IntegrationEventLogContext : DbContext + { + public IntegrationEventLogContext(DbContextOptions options) : base(options) + { + } + + public DbSet IntegrationEventLogs { get; set; } + + protected override void OnModelCreating(ModelBuilder builder) + { + builder.Entity(ConfigureIntegrationEventLogEntry); + } + + void ConfigureIntegrationEventLogEntry(EntityTypeBuilder builder) + { + builder.ToTable("IntegrationEventLog"); + + 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/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj new file mode 100644 index 000000000..039b26ae2 --- /dev/null +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj @@ -0,0 +1,27 @@ + + + + netcoreapp1.1 + 1.1.0 + Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEventLog/IntegrationEventLogEntry.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs similarity index 85% rename from src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEventLog/IntegrationEventLogEntry.cs rename to src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs index a74c600e4..2b31e6681 100644 --- a/src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEventLog/IntegrationEventLogEntry.cs +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs @@ -2,8 +2,9 @@ using System.Collections.Generic; using System.Text; using Newtonsoft.Json; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; -namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events.IntegrationEventLog +namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF { public class IntegrationEventLogEntry { diff --git a/src/Services/Catalog/Catalog.API/Catalog.API.csproj b/src/Services/Catalog/Catalog.API/Catalog.API.csproj index 5cef06749..f4567b88a 100644 --- a/src/Services/Catalog/Catalog.API/Catalog.API.csproj +++ b/src/Services/Catalog/Catalog.API/Catalog.API.csproj @@ -58,6 +58,7 @@ + diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index b60ddc27c..2feaa423d 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events.IntegrationEventLog; +using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events; using Microsoft.eShopOnContainers.Services.Catalog.API.Model; @@ -16,28 +16,31 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers [Route("api/v1/[controller]")] public class CatalogController : ControllerBase { - private readonly CatalogContext _context; + private readonly CatalogContext _catalogContext; + private readonly IntegrationEventLogContext _integrationEventLogContext; private readonly IOptionsSnapshot _settings; private readonly IEventBus _eventBus; - public CatalogController(CatalogContext context, IOptionsSnapshot settings, IEventBus eventBus) + public CatalogController(CatalogContext catalogContext, IntegrationEventLogContext integrationEventLogContext, IOptionsSnapshot settings, IEventBus eventBus) { - _context = context; + _catalogContext = catalogContext; + _integrationEventLogContext = integrationEventLogContext; _settings = settings; _eventBus = eventBus; - ((DbContext)context).ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; + ((DbContext)catalogContext).ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; } // GET api/v1/[controller]/items[?pageSize=3&pageIndex=10] [HttpGet] [Route("[action]")] public async Task Items([FromQuery]int pageSize = 10, [FromQuery]int pageIndex = 0) + { - var totalItems = await _context.CatalogItems + var totalItems = await _catalogContext.CatalogItems .LongCountAsync(); - var itemsOnPage = await _context.CatalogItems + var itemsOnPage = await _catalogContext.CatalogItems .OrderBy(c=>c.Name) .Skip(pageSize * pageIndex) .Take(pageSize) @@ -57,11 +60,11 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers public async Task Items(string name, [FromQuery]int pageSize = 10, [FromQuery]int pageIndex = 0) { - var totalItems = await _context.CatalogItems + var totalItems = await _catalogContext.CatalogItems .Where(c => c.Name.StartsWith(name)) .LongCountAsync(); - var itemsOnPage = await _context.CatalogItems + var itemsOnPage = await _catalogContext.CatalogItems .Where(c => c.Name.StartsWith(name)) .Skip(pageSize * pageIndex) .Take(pageSize) @@ -80,7 +83,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers [Route("[action]/type/{catalogTypeId}/brand/{catalogBrandId}")] public async Task Items(int? catalogTypeId, int? catalogBrandId, [FromQuery]int pageSize = 10, [FromQuery]int pageIndex = 0) { - var root = (IQueryable)_context.CatalogItems; + var root = (IQueryable)_catalogContext.CatalogItems; if (catalogTypeId.HasValue) { @@ -113,7 +116,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers [Route("[action]")] public async Task CatalogTypes() { - var items = await _context.CatalogTypes + var items = await _catalogContext.CatalogTypes .ToListAsync(); return Ok(items); @@ -124,7 +127,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers [Route("[action]")] public async Task CatalogBrands() { - var items = await _context.CatalogBrands + var items = await _catalogContext.CatalogBrands .ToListAsync(); return Ok(items); @@ -135,7 +138,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers [HttpPost] public async Task EditProduct([FromBody]CatalogItem product) { - var item = await _context.CatalogItems.SingleOrDefaultAsync(i => i.Id == product.Id); + var item = await _catalogContext.CatalogItems.SingleOrDefaultAsync(i => i.Id == product.Id); if (item == null) { @@ -146,20 +149,21 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers { var oldPrice = item.Price; item.Price = product.Price; - _context.CatalogItems.Update(item); + _catalogContext.CatalogItems.Update(item); var @event = new ProductPriceChangedIntegrationEvent(item.Id, item.Price, oldPrice); var eventLogEntry = new IntegrationEventLogEntry(@event); - _context.IntegrationEventLog.Add(eventLogEntry); + _integrationEventLogContext.IntegrationEventLogs.Add(eventLogEntry); - await _context.SaveChangesAsync(); + await _integrationEventLogContext.SaveChangesAsync(); + await _catalogContext.SaveChangesAsync(); _eventBus.Publish(@event); eventLogEntry.TimesSent++; eventLogEntry.State = EventStateEnum.Published; - _context.IntegrationEventLog.Update(eventLogEntry); - await _context.SaveChangesAsync(); + _integrationEventLogContext.IntegrationEventLogs.Update(eventLogEntry); + await _integrationEventLogContext.SaveChangesAsync(); } return Ok(); @@ -170,7 +174,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers [HttpPost] public async Task CreateProduct([FromBody]CatalogItem product) { - _context.CatalogItems.Add( + _catalogContext.CatalogItems.Add( new CatalogItem { CatalogBrandId = product.CatalogBrandId, @@ -181,7 +185,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers Price = product.Price }); - await _context.SaveChangesAsync(); + await _catalogContext.SaveChangesAsync(); return Ok(); } @@ -191,15 +195,15 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers [HttpDelete] public async Task DeleteProduct(int id) { - var product = _context.CatalogItems.SingleOrDefault(x => x.Id == id); + var product = _catalogContext.CatalogItems.SingleOrDefault(x => x.Id == id); if (product == null) { return NotFound(); } - _context.CatalogItems.Remove(product); - await _context.SaveChangesAsync(); + _catalogContext.CatalogItems.Remove(product); + await _catalogContext.SaveChangesAsync(); return Ok(); } diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContext.cs b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContext.cs index 846b1dbb5..b8ae183c8 100644 --- a/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContext.cs +++ b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContext.cs @@ -3,24 +3,24 @@ using EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore; using Model; - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events.IntegrationEventLog; + using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; public class CatalogContext : DbContext { - public CatalogContext(DbContextOptions options) : base(options) + public CatalogContext(DbContextOptions options) : base(options) { } public DbSet CatalogItems { get; set; } public DbSet CatalogBrands { get; set; } public DbSet CatalogTypes { get; set; } - public DbSet IntegrationEventLog { get; set; } + //public DbSet IntegrationEventLog { get; set; } protected override void OnModelCreating(ModelBuilder builder) { builder.Entity(ConfigureCatalogBrand); builder.Entity(ConfigureCatalogType); builder.Entity(ConfigureCatalogItem); - builder.Entity(ConfigureIntegrationEventLogEntry); + //builder.Entity(ConfigureIntegrationEventLogEntry); } void ConfigureCatalogItem(EntityTypeBuilder builder) @@ -80,30 +80,30 @@ .HasMaxLength(100); } - void ConfigureIntegrationEventLogEntry(EntityTypeBuilder builder) - { - builder.ToTable("IntegrationEventLog"); + //void ConfigureIntegrationEventLogEntry(EntityTypeBuilder builder) + //{ + // builder.ToTable("IntegrationEventLog"); - builder.HasKey(e => e.EventId); + // builder.HasKey(e => e.EventId); - builder.Property(e => e.EventId) - .IsRequired(); + // builder.Property(e => e.EventId) + // .IsRequired(); - builder.Property(e => e.Content) - .IsRequired(); + // builder.Property(e => e.Content) + // .IsRequired(); - builder.Property(e => e.CreationTime) - .IsRequired(); + // builder.Property(e => e.CreationTime) + // .IsRequired(); - builder.Property(e => e.State) - .IsRequired(); + // builder.Property(e => e.State) + // .IsRequired(); - builder.Property(e => e.TimesSent) - .IsRequired(); + // builder.Property(e => e.TimesSent) + // .IsRequired(); - builder.Property(e => e.EventTypeName) - .IsRequired(); + // builder.Property(e => e.EventTypeName) + // .IsRequired(); - } + //} } } diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20161103152832_Initial.Designer.cs b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20161103152832_Initial.Designer.cs similarity index 100% rename from src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20161103152832_Initial.Designer.cs rename to src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20161103152832_Initial.Designer.cs diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20161103152832_Initial.cs b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20161103152832_Initial.cs similarity index 100% rename from src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20161103152832_Initial.cs rename to src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20161103152832_Initial.cs diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20161103153420_UpdateTableNames.Designer.cs b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20161103153420_UpdateTableNames.Designer.cs similarity index 100% rename from src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20161103153420_UpdateTableNames.Designer.cs rename to src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20161103153420_UpdateTableNames.Designer.cs diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20161103153420_UpdateTableNames.cs b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20161103153420_UpdateTableNames.cs similarity index 100% rename from src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20161103153420_UpdateTableNames.cs rename to src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20161103153420_UpdateTableNames.cs diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170314083211_AddEventTable.Designer.cs b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170314083211_AddEventTable.Designer.cs similarity index 100% rename from src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170314083211_AddEventTable.Designer.cs rename to src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170314083211_AddEventTable.Designer.cs diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170314083211_AddEventTable.cs b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170314083211_AddEventTable.cs similarity index 100% rename from src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170314083211_AddEventTable.cs rename to src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170314083211_AddEventTable.cs diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170316012921_RefactoringToIntegrationEventLog.Designer.cs b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170316012921_RefactoringToIntegrationEventLog.Designer.cs similarity index 100% rename from src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170316012921_RefactoringToIntegrationEventLog.Designer.cs rename to src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170316012921_RefactoringToIntegrationEventLog.Designer.cs diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170316012921_RefactoringToIntegrationEventLog.cs b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170316012921_RefactoringToIntegrationEventLog.cs similarity index 100% rename from src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170316012921_RefactoringToIntegrationEventLog.cs rename to src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170316012921_RefactoringToIntegrationEventLog.cs diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170316120022_RefactoringEventBusNamespaces.Designer.cs b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170316120022_RefactoringEventBusNamespaces.Designer.cs similarity index 100% rename from src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170316120022_RefactoringEventBusNamespaces.Designer.cs rename to src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170316120022_RefactoringEventBusNamespaces.Designer.cs diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170316120022_RefactoringEventBusNamespaces.cs b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170316120022_RefactoringEventBusNamespaces.cs similarity index 100% rename from src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170316120022_RefactoringEventBusNamespaces.cs rename to src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170316120022_RefactoringEventBusNamespaces.cs diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170322124244_RemoveIntegrationEventLogs.Designer.cs b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170322124244_RemoveIntegrationEventLogs.Designer.cs new file mode 100644 index 000000000..aea1086f6 --- /dev/null +++ b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170322124244_RemoveIntegrationEventLogs.Designer.cs @@ -0,0 +1,99 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; + +namespace Catalog.API.Infrastructure.Migrations +{ + [DbContext(typeof(CatalogContext))] + [Migration("20170322124244_RemoveIntegrationEventLogs")] + partial class RemoveIntegrationEventLogs + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "1.1.1") + .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.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/CatalogMigrations/20170322124244_RemoveIntegrationEventLogs.cs b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170322124244_RemoveIntegrationEventLogs.cs new file mode 100644 index 000000000..88e0c76db --- /dev/null +++ b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/20170322124244_RemoveIntegrationEventLogs.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Catalog.API.Infrastructure.Migrations +{ + public partial class RemoveIntegrationEventLogs : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "IntegrationEventLog"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "IntegrationEventLog", + columns: table => new + { + EventId = table.Column(nullable: false), + Content = table.Column(nullable: false), + CreationTime = table.Column(nullable: false), + EventTypeName = table.Column(nullable: false), + State = table.Column(nullable: false), + TimesSent = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IntegrationEventLog", x => x.EventId); + }); + } + } +} diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/Migrations/CatalogContextModelSnapshot.cs b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/CatalogContextModelSnapshot.cs similarity index 84% rename from src/Services/Catalog/Catalog.API/Infrastructure/Migrations/CatalogContextModelSnapshot.cs rename to src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/CatalogContextModelSnapshot.cs index 77c820212..ba1151672 100644 --- a/src/Services/Catalog/Catalog.API/Infrastructure/Migrations/CatalogContextModelSnapshot.cs +++ b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogMigrations/CatalogContextModelSnapshot.cs @@ -4,7 +4,6 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; namespace Catalog.API.Infrastructure.Migrations { @@ -20,28 +19,6 @@ namespace Catalog.API.Infrastructure.Migrations .HasAnnotation("SqlServer:Sequence:.catalog_type_hilo", "'catalog_type_hilo', '', '1', '10', '', '', 'Int64', 'False'") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - modelBuilder.Entity("Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events.IntegrationEventLogEntry", b => - { - b.Property("EventId") - .ValueGeneratedOnAdd(); - - b.Property("Content") - .IsRequired(); - - b.Property("CreationTime"); - - b.Property("EventTypeName") - .IsRequired(); - - b.Property("State"); - - b.Property("TimesSent"); - - b.HasKey("EventId"); - - b.ToTable("IntegrationEventLog"); - }); - modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogBrand", b => { b.Property("Id") diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/IntegrationEventMigrations/20170322145434_IntegrationEventInitial.Designer.cs b/src/Services/Catalog/Catalog.API/Infrastructure/IntegrationEventMigrations/20170322145434_IntegrationEventInitial.Designer.cs new file mode 100644 index 000000000..a3cdeb53f --- /dev/null +++ b/src/Services/Catalog/Catalog.API/Infrastructure/IntegrationEventMigrations/20170322145434_IntegrationEventInitial.Designer.cs @@ -0,0 +1,43 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; + +namespace Catalog.API.Migrations +{ + [DbContext(typeof(IntegrationEventLogContext))] + [Migration("20170322145434_IntegrationEventInitial")] + partial class IntegrationEventInitial + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "1.1.1") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.IntegrationEventLogEntry", b => + { + b.Property("EventId") + .ValueGeneratedOnAdd(); + + b.Property("Content") + .IsRequired(); + + b.Property("CreationTime"); + + b.Property("EventTypeName") + .IsRequired(); + + b.Property("State"); + + b.Property("TimesSent"); + + b.HasKey("EventId"); + + b.ToTable("IntegrationEventLog"); + }); + } + } +} diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/IntegrationEventMigrations/20170322145434_IntegrationEventInitial.cs b/src/Services/Catalog/Catalog.API/Infrastructure/IntegrationEventMigrations/20170322145434_IntegrationEventInitial.cs new file mode 100644 index 000000000..fff1ad04c --- /dev/null +++ b/src/Services/Catalog/Catalog.API/Infrastructure/IntegrationEventMigrations/20170322145434_IntegrationEventInitial.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Catalog.API.Migrations +{ + public partial class IntegrationEventInitial : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "IntegrationEventLog", + columns: table => new + { + EventId = table.Column(nullable: false), + Content = table.Column(nullable: false), + CreationTime = table.Column(nullable: false), + EventTypeName = table.Column(nullable: false), + State = table.Column(nullable: false), + TimesSent = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IntegrationEventLog", x => x.EventId); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "IntegrationEventLog"); + } + } +} diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/IntegrationEventMigrations/IntegrationEventLogContextModelSnapshot.cs b/src/Services/Catalog/Catalog.API/Infrastructure/IntegrationEventMigrations/IntegrationEventLogContextModelSnapshot.cs new file mode 100644 index 000000000..ab2414bb5 --- /dev/null +++ b/src/Services/Catalog/Catalog.API/Infrastructure/IntegrationEventMigrations/IntegrationEventLogContextModelSnapshot.cs @@ -0,0 +1,42 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; + +namespace Catalog.API.Migrations +{ + [DbContext(typeof(IntegrationEventLogContext))] + partial class IntegrationEventLogContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "1.1.1") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.IntegrationEventLogEntry", b => + { + b.Property("EventId") + .ValueGeneratedOnAdd(); + + b.Property("Content") + .IsRequired(); + + b.Property("CreationTime"); + + b.Property("EventTypeName") + .IsRequired(); + + b.Property("State"); + + b.Property("TimesSent"); + + b.HasKey("EventId"); + + b.ToTable("IntegrationEventLog"); + }); + } + } +} diff --git a/src/Services/Catalog/Catalog.API/Startup.cs b/src/Services/Catalog/Catalog.API/Startup.cs index 818855d69..05c6409fe 100644 --- a/src/Services/Catalog/Catalog.API/Startup.cs +++ b/src/Services/Catalog/Catalog.API/Startup.cs @@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; + using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -45,6 +46,12 @@ //Check Client vs. Server evaluation: https://docs.microsoft.com/en-us/ef/core/querying/client-eval }); + services.AddDbContext(c => + { + c.UseSqlServer(Configuration["ConnectionString"], b => b.MigrationsAssembly("Catalog.API")); + c.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); + }); + services.Configure(Configuration); // Add framework services. @@ -77,7 +84,7 @@ services.AddMvc(); } - public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IntegrationEventLogContext integrationEventLogContext) { //Configure logs @@ -98,9 +105,11 @@ //Seed Data CatalogContextSeed.SeedAsync(app, loggerFactory) - .Wait(); + .Wait(); + + // TODO: move this creation to a db initializer + integrationEventLogContext.Database.Migrate(); - } } } From 6e4d9461de69adfee709f210218d053033a5a203 Mon Sep 17 00:00:00 2001 From: dsanz Date: Thu, 23 Mar 2017 13:24:17 +0100 Subject: [PATCH 05/25] Add shared scope transaction between updating catalog product priceand store ProductPriceChangedIntegrationEvent. Added service to encapsulate logic for storage of integration event logs. --- .../IntegrationEventLogEntry.cs | 1 + .../Controllers/CatalogController.cs | 38 +++++++------- .../IIntegrationEventLogService.cs | 14 +++++ .../IntegrationEventLogService.cs | 51 +++++++++++++++++++ src/Services/Catalog/Catalog.API/Startup.cs | 15 ++++-- 5 files changed, 96 insertions(+), 23 deletions(-) create mode 100644 src/Services/Catalog/Catalog.API/IntegrationEvents/IIntegrationEventLogService.cs create mode 100644 src/Services/Catalog/Catalog.API/IntegrationEvents/IntegrationEventLogService.cs diff --git a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs index 2b31e6681..0b68e56a0 100644 --- a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs @@ -8,6 +8,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF { public class IntegrationEventLogEntry { + private IntegrationEventLogEntry() { } public IntegrationEventLogEntry(IntegrationEvent @event) { EventId = @event.Id; diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index 2feaa423d..bbc30a772 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -1,5 +1,7 @@ -using Microsoft.AspNetCore.Mvc; +using Catalog.API.IntegrationEvents; +using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; @@ -17,18 +19,18 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers public class CatalogController : ControllerBase { private readonly CatalogContext _catalogContext; - private readonly IntegrationEventLogContext _integrationEventLogContext; private readonly IOptionsSnapshot _settings; private readonly IEventBus _eventBus; + private readonly IIntegrationEventLogService _integrationEventLogService; - public CatalogController(CatalogContext catalogContext, IntegrationEventLogContext integrationEventLogContext, IOptionsSnapshot settings, IEventBus eventBus) + public CatalogController(CatalogContext Context, IOptionsSnapshot settings, IEventBus eventBus, IIntegrationEventLogService integrationEventLogService) { - _catalogContext = catalogContext; - _integrationEventLogContext = integrationEventLogContext; + _catalogContext = Context; _settings = settings; _eventBus = eventBus; + _integrationEventLogService = integrationEventLogService; - ((DbContext)catalogContext).ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; + ((DbContext)Context).ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; } // GET api/v1/[controller]/items[?pageSize=3&pageIndex=10] @@ -149,21 +151,21 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers { var oldPrice = item.Price; item.Price = product.Price; - _catalogContext.CatalogItems.Update(item); - var @event = new ProductPriceChangedIntegrationEvent(item.Id, item.Price, oldPrice); - var eventLogEntry = new IntegrationEventLogEntry(@event); - _integrationEventLogContext.IntegrationEventLogs.Add(eventLogEntry); - await _integrationEventLogContext.SaveChangesAsync(); - await _catalogContext.SaveChangesAsync(); - + using (var transaction = _catalogContext.Database.BeginTransaction()) + { + _catalogContext.CatalogItems.Update(item); + await _catalogContext.SaveChangesAsync(); + + await _integrationEventLogService.SaveEventAsync(@event); + + transaction.Commit(); + } + _eventBus.Publish(@event); - - eventLogEntry.TimesSent++; - eventLogEntry.State = EventStateEnum.Published; - _integrationEventLogContext.IntegrationEventLogs.Update(eventLogEntry); - await _integrationEventLogContext.SaveChangesAsync(); + + await _integrationEventLogService.MarkEventAsPublishedAsync(@event); } return Ok(); diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/IIntegrationEventLogService.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/IIntegrationEventLogService.cs new file mode 100644 index 000000000..423a0eb98 --- /dev/null +++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/IIntegrationEventLogService.cs @@ -0,0 +1,14 @@ +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Catalog.API.IntegrationEvents +{ + public interface IIntegrationEventLogService + { + Task SaveEventAsync(IntegrationEvent @event); + Task MarkEventAsPublishedAsync(IntegrationEvent @event); + } +} diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/IntegrationEventLogService.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/IntegrationEventLogService.cs new file mode 100644 index 000000000..e9859c5ae --- /dev/null +++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/IntegrationEventLogService.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Catalog.API.IntegrationEvents +{ + public class IntegrationEventLogService : IIntegrationEventLogService + { + private readonly IntegrationEventLogContext _integrationEventLogContext; + private readonly CatalogContext _catalogContext; + + public IntegrationEventLogService(CatalogContext catalogContext) + { + _catalogContext = catalogContext; + _integrationEventLogContext = new IntegrationEventLogContext( + new DbContextOptionsBuilder() + .UseSqlServer(catalogContext.Database.GetDbConnection()) + .ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)) + .Options); + } + + public Task SaveEventAsync(IntegrationEvent @event) + { + var eventLogEntry = new IntegrationEventLogEntry(@event); + + // as a constraint this transaction has to be done together with a catalogContext transaction + _integrationEventLogContext.Database.UseTransaction(_catalogContext.Database.CurrentTransaction.GetDbTransaction()); + _integrationEventLogContext.IntegrationEventLogs.Add(eventLogEntry); + + return _integrationEventLogContext.SaveChangesAsync(); + } + + public Task MarkEventAsPublishedAsync(IntegrationEvent @event) + { + var eventLogEntry = _integrationEventLogContext.IntegrationEventLogs.Single(ie => ie.EventId == @event.Id); + eventLogEntry.TimesSent++; + eventLogEntry.State = EventStateEnum.Published; + + _integrationEventLogContext.IntegrationEventLogs.Update(eventLogEntry); + + return _integrationEventLogContext.SaveChangesAsync(); + } + } +} diff --git a/src/Services/Catalog/Catalog.API/Startup.cs b/src/Services/Catalog/Catalog.API/Startup.cs index 05c6409fe..1598eb5a0 100644 --- a/src/Services/Catalog/Catalog.API/Startup.cs +++ b/src/Services/Catalog/Catalog.API/Startup.cs @@ -1,5 +1,6 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API { + using global::Catalog.API.IntegrationEvents; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; @@ -12,6 +13,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; + using System.Data.SqlClient; using System.Reflection; public class Startup @@ -37,9 +39,11 @@ public void ConfigureServices(IServiceCollection services) { + var sqlConnection = new SqlConnection(Configuration["ConnectionString"]); + services.AddDbContext(c => { - c.UseSqlServer(Configuration["ConnectionString"]); + c.UseSqlServer(sqlConnection); // Changing default behavior when client evaluation occurs to throw. // Default in EF Core would be to log a warning when client evaluation is performed. c.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); @@ -48,7 +52,7 @@ services.AddDbContext(c => { - c.UseSqlServer(Configuration["ConnectionString"], b => b.MigrationsAssembly("Catalog.API")); + c.UseSqlServer(sqlConnection, b => b.MigrationsAssembly("Catalog.API")); c.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); }); @@ -77,6 +81,8 @@ .AllowCredentials()); }); + services.AddTransient(); + var serviceProvider = services.BuildServiceProvider(); var configuration = serviceProvider.GetRequiredService>().Value; services.AddSingleton(new EventBusRabbitMQ(configuration.EventBusConnection)); @@ -106,9 +112,8 @@ //Seed Data CatalogContextSeed.SeedAsync(app, loggerFactory) .Wait(); - - // TODO: move this creation to a db initializer - integrationEventLogContext.Database.Migrate(); + + integrationEventLogContext.Database.Migrate(); } } From 94c7fd31e1d0da1fb36b087aea327e45a52bbbe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ram=C3=B3n=20Tom=C3=A1s?= Date: Thu, 23 Mar 2017 13:44:15 +0100 Subject: [PATCH 06/25] Added decorator for validation in commands Added Validation for orders and identified commands --- .../Decorators/ValidatorDecorator.cs | 45 +++++++++++++++++++ .../CreateOrderCommandValidator.cs | 39 ++++++++++++++++ .../Validations/IdentifierCommandValidator.cs | 17 +++++++ .../AutofacModules/MediatorModule.cs | 26 ++++++++--- .../Ordering/Ordering.API/Ordering.API.csproj | 2 + 5 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 src/Services/Ordering/Ordering.API/Application/Decorators/ValidatorDecorator.cs create mode 100644 src/Services/Ordering/Ordering.API/Application/Validations/CreateOrderCommandValidator.cs create mode 100644 src/Services/Ordering/Ordering.API/Application/Validations/IdentifierCommandValidator.cs diff --git a/src/Services/Ordering/Ordering.API/Application/Decorators/ValidatorDecorator.cs b/src/Services/Ordering/Ordering.API/Application/Decorators/ValidatorDecorator.cs new file mode 100644 index 000000000..5bdff8330 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Application/Decorators/ValidatorDecorator.cs @@ -0,0 +1,45 @@ +using FluentValidation; +using MediatR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Ordering.API.Application.Decorators +{ + public class ValidatorDecorator + : IAsyncRequestHandler + where TRequest : IAsyncRequest + { + private readonly IAsyncRequestHandler _inner; + private readonly IValidator[] _validators; + + + public ValidatorDecorator( + IAsyncRequestHandler inner, + IValidator[] validators) + { + _inner = inner; + _validators = validators; + } + + public async Task Handle(TRequest message) + { + var failures = _validators + .Select(v => v.Validate(message)) + .SelectMany(result => result.Errors) + .Where(error => error != null) + .ToList(); + + if (failures.Any()) + { + throw new ValidationException( + $"Command Validation Errors for type {typeof(TRequest).Name}", failures); + } + + var response = await _inner.Handle(message); + + return response; + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Application/Validations/CreateOrderCommandValidator.cs b/src/Services/Ordering/Ordering.API/Application/Validations/CreateOrderCommandValidator.cs new file mode 100644 index 000000000..449f95df5 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Application/Validations/CreateOrderCommandValidator.cs @@ -0,0 +1,39 @@ +using FluentValidation; +using MediatR; +using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using static Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands.CreateOrderCommand; + +namespace Ordering.API.Application.Validations +{ + public class CreateOrderCommandValidator : AbstractValidator + { + public CreateOrderCommandValidator() + { + RuleFor(order => order.City).NotEmpty(); + RuleFor(order => order.Street).NotEmpty(); + RuleFor(order => order.State).NotEmpty(); + RuleFor(order => order.Country).NotEmpty(); + RuleFor(order => order.ZipCode).NotEmpty(); + RuleFor(order => order.CardNumber).NotEmpty().Length(12, 19); + RuleFor(order => order.CardHolderName).NotEmpty(); + RuleFor(order => order.CardExpiration).NotEmpty().Must(BeValidExpirationDate).WithMessage("Please specify a valid card expiration date"); + RuleFor(order => order.CardSecurityNumber).NotEmpty().Length(3); + RuleFor(order => order.CardTypeId).NotEmpty(); + RuleFor(order => order.OrderItems).Must(ContainOrderItems).WithMessage("No order items found"); + } + + private bool BeValidExpirationDate(DateTime dateTime) + { + return dateTime >= DateTime.UtcNow; + } + + private bool ContainOrderItems(IEnumerable orderItems) + { + return orderItems.Any(); + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Application/Validations/IdentifierCommandValidator.cs b/src/Services/Ordering/Ordering.API/Application/Validations/IdentifierCommandValidator.cs new file mode 100644 index 000000000..44b374ee6 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Application/Validations/IdentifierCommandValidator.cs @@ -0,0 +1,17 @@ +using FluentValidation; +using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Ordering.API.Application.Validations +{ + public class IdentifierCommandValidator : AbstractValidator> + { + public IdentifierCommandValidator() + { + RuleFor(customer => customer.Id).NotEmpty(); + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs b/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs index a6864e0ef..e741644f3 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs @@ -1,9 +1,12 @@ using Autofac; using Autofac.Core; +using FluentValidation; using MediatR; using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Decorators; +using Ordering.API.Application.Decorators; using Ordering.API.Application.DomainEventHandlers.OrderStartedEvent; +using Ordering.API.Application.Validations; using Ordering.Domain.Events; using System.Collections.Generic; using System.Linq; @@ -24,11 +27,17 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Autof .Where(i => i.IsClosedTypeOf(typeof(IAsyncRequestHandler<,>))) .Select(i => new KeyedService("IAsyncRequestHandler", i))); - // Register all the Domain Event Handler classes (they implement IAsyncNotificationHandler<>) in assembly holding the Domain Events + // Register all the event classes (they implement IAsyncNotificationHandler) in assembly holding the Commands + builder.RegisterAssemblyTypes(typeof(ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler).GetTypeInfo().Assembly) + .As(o => o.GetInterfaces() + .Where(i => i.IsClosedTypeOf(typeof(IAsyncNotificationHandler<>))) + .Select(i => new KeyedService("IAsyncNotificationHandler", i))); + builder - .RegisterAssemblyTypes(typeof(ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler).GetTypeInfo().Assembly) - .Where(t => t.IsClosedTypeOf(typeof(IAsyncNotificationHandler<>))) - .AsImplementedInterfaces(); + .RegisterAssemblyTypes(typeof(CreateOrderCommandValidator).GetTypeInfo().Assembly) + .Where(t => t.IsClosedTypeOf(typeof(IValidator<>))) + .AsImplementedInterfaces(); + builder.Register(context => { @@ -44,9 +53,16 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Autof return t => (IEnumerable)componentContext.Resolve(typeof(IEnumerable<>).MakeGenericType(t)); }); + + builder.RegisterGenericDecorator(typeof(LogDecorator<,>), typeof(IAsyncRequestHandler<,>), - "IAsyncRequestHandler"); + "IAsyncRequestHandler") + .Keyed("handlerDecorator", typeof(IAsyncRequestHandler<,>)); + + builder.RegisterGenericDecorator(typeof(ValidatorDecorator<,>), + typeof(IAsyncRequestHandler<,>), + fromKey: "handlerDecorator"); } } } diff --git a/src/Services/Ordering/Ordering.API/Ordering.API.csproj b/src/Services/Ordering/Ordering.API/Ordering.API.csproj index d0102c19f..d7dd2bc1a 100644 --- a/src/Services/Ordering/Ordering.API/Ordering.API.csproj +++ b/src/Services/Ordering/Ordering.API/Ordering.API.csproj @@ -28,6 +28,8 @@ + + From 5d8628c9e2502e6548c12b6ecd5046b6ca357069 Mon Sep 17 00:00:00 2001 From: dsanz Date: Thu, 23 Mar 2017 17:01:55 +0100 Subject: [PATCH 07/25] Fix test that was failing sporadically. --- .../Services/Ordering/OrderingScenarios.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/test/Services/FunctionalTests/Services/Ordering/OrderingScenarios.cs b/test/Services/FunctionalTests/Services/Ordering/OrderingScenarios.cs index f15797793..756bbed2a 100644 --- a/test/Services/FunctionalTests/Services/Ordering/OrderingScenarios.cs +++ b/test/Services/FunctionalTests/Services/Ordering/OrderingScenarios.cs @@ -1,10 +1,9 @@ -using Microsoft.AspNetCore.TestHost; -using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; +using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; using Microsoft.eShopOnContainers.WebMVC.ViewModels; using Newtonsoft.Json; using System; using System.Collections.Generic; -using System.Data.SqlClient; +using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; @@ -22,20 +21,20 @@ namespace FunctionalTests.Services.Ordering { var client = server.CreateClient(); - //Arrange + // GIVEN an order is created await client.PostAsync(Post.AddNewOrder, new StringContent(BuildOrder(), UTF8Encoding.UTF8, "application/json")); var ordersResponse = await client.GetAsync(Get.Orders); var responseBody = await ordersResponse.Content.ReadAsStringAsync(); - dynamic orders = JsonConvert.DeserializeObject(responseBody); - string orderId = orders[0].ordernumber; + var orders = JsonConvert.DeserializeObject>(responseBody); + string orderId = orders.OrderByDescending(o => o.Date).First().OrderNumber; - //Act + //WHEN we request the order bit its id var order= await client.GetAsync(Get.OrderBy(int.Parse(orderId))); var orderBody = await order.Content.ReadAsStringAsync(); var result = JsonConvert.DeserializeObject(orderBody); - //Assert + //THEN the requested order is returned Assert.Equal(orderId, result.OrderNumber); Assert.Equal("inprocess", result.Status); Assert.Equal(1, result.OrderItems.Count); @@ -63,7 +62,7 @@ namespace FunctionalTests.Services.Ordering order.AddOrderItem(new OrderItemDTO() { ProductId = 1, - Discount = 10M, + Discount = 12M, UnitPrice = 10, Units = 1, ProductName = "Some name" From 85b65753c95b5ebf15c3ab75169e92a1b970a9d3 Mon Sep 17 00:00:00 2001 From: Cesar De la Torre Date: Thu, 23 Mar 2017 14:45:57 -0700 Subject: [PATCH 08/25] Minor update --- .../Ordering/Ordering.Infrastructure/OrderingContext.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs b/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs index b74c7631d..dcf7c54e9 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs @@ -246,7 +246,8 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure await _mediator.DispatchDomainEventsAsync(this); - // After executing this line all the changes performed thought the DbContext will be commited + // After executing this line all the changes (from the Command Handler and Domain Event Handlers) + // performed thought the DbContext will be commited var result = await base.SaveChangesAsync(); return result; From 0372fada824862626dbfc3bdab5987e7ea80bdd1 Mon Sep 17 00:00:00 2001 From: dsanz Date: Fri, 24 Mar 2017 12:37:44 +0100 Subject: [PATCH 09/25] IntegrationEventLogService refactoring --- .../Services}/IIntegrationEventLogService.cs | 5 +-- .../Services}/IntegrationEventLogService.cs | 34 ++++++++++--------- .../Controllers/CatalogController.cs | 20 ++++++----- src/Services/Catalog/Catalog.API/Startup.cs | 28 +++++++-------- 4 files changed, 46 insertions(+), 41 deletions(-) rename src/{Services/Catalog/Catalog.API/IntegrationEvents => BuildingBlocks/EventBus/IntegrationEventLogEF/Services}/IIntegrationEventLogService.cs (60%) rename src/{Services/Catalog/Catalog.API/IntegrationEvents => BuildingBlocks/EventBus/IntegrationEventLogEF/Services}/IntegrationEventLogService.cs (66%) diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/IIntegrationEventLogService.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IIntegrationEventLogService.cs similarity index 60% rename from src/Services/Catalog/Catalog.API/IntegrationEvents/IIntegrationEventLogService.cs rename to src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IIntegrationEventLogService.cs index 423a0eb98..ed1f74616 100644 --- a/src/Services/Catalog/Catalog.API/IntegrationEvents/IIntegrationEventLogService.cs +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IIntegrationEventLogService.cs @@ -1,14 +1,15 @@ using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; using System; using System.Collections.Generic; +using System.Data.Common; using System.Linq; using System.Threading.Tasks; -namespace Catalog.API.IntegrationEvents +namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services { public interface IIntegrationEventLogService { - Task SaveEventAsync(IntegrationEvent @event); + Task SaveEventAsync(IntegrationEvent @event, DbTransaction transaction); Task MarkEventAsPublishedAsync(IntegrationEvent @event); } } diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/IntegrationEventLogService.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IntegrationEventLogService.cs similarity index 66% rename from src/Services/Catalog/Catalog.API/IntegrationEvents/IntegrationEventLogService.cs rename to src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IntegrationEventLogService.cs index e9859c5ae..bef74b452 100644 --- a/src/Services/Catalog/Catalog.API/IntegrationEvents/IntegrationEventLogService.cs +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IntegrationEventLogService.cs @@ -1,37 +1,39 @@ -using System; -using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +using System.Data.Common; using System.Linq; using System.Threading.Tasks; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; -using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; -using Microsoft.EntityFrameworkCore.Infrastructure; +using System; -namespace Catalog.API.IntegrationEvents +namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services { public class IntegrationEventLogService : IIntegrationEventLogService { private readonly IntegrationEventLogContext _integrationEventLogContext; - private readonly CatalogContext _catalogContext; + private readonly DbConnection _dbConnection; - public IntegrationEventLogService(CatalogContext catalogContext) + public IntegrationEventLogService(DbConnection dbConnection) { - _catalogContext = catalogContext; + _dbConnection = dbConnection?? throw new ArgumentNullException("dbConnection"); _integrationEventLogContext = new IntegrationEventLogContext( new DbContextOptionsBuilder() - .UseSqlServer(catalogContext.Database.GetDbConnection()) + .UseSqlServer(_dbConnection) .ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)) .Options); } - public Task SaveEventAsync(IntegrationEvent @event) + public Task SaveEventAsync(IntegrationEvent @event, DbTransaction transaction) { + if(transaction == null) + { + throw new ArgumentNullException("transaction", $"A {typeof(DbTransaction).FullName} is required as a pre-requisite to save the event."); + } + var eventLogEntry = new IntegrationEventLogEntry(@event); - // as a constraint this transaction has to be done together with a catalogContext transaction - _integrationEventLogContext.Database.UseTransaction(_catalogContext.Database.CurrentTransaction.GetDbTransaction()); + _integrationEventLogContext.Database.UseTransaction(transaction); _integrationEventLogContext.IntegrationEventLogs.Add(eventLogEntry); return _integrationEventLogContext.SaveChangesAsync(); diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index bbc30a772..f7b95a069 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -1,15 +1,17 @@ -using Catalog.API.IntegrationEvents; -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; +using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events; using Microsoft.eShopOnContainers.Services.Catalog.API.Model; using Microsoft.eShopOnContainers.Services.Catalog.API.ViewModel; using Microsoft.Extensions.Options; +using System; using System.Collections.Generic; +using System.Data.Common; using System.Linq; using System.Threading.Tasks; @@ -21,14 +23,14 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers private readonly CatalogContext _catalogContext; private readonly IOptionsSnapshot _settings; private readonly IEventBus _eventBus; - private readonly IIntegrationEventLogService _integrationEventLogService; + private readonly Func _integrationEventLogServiceFactory; - public CatalogController(CatalogContext Context, IOptionsSnapshot settings, IEventBus eventBus, IIntegrationEventLogService integrationEventLogService) + public CatalogController(CatalogContext Context, IOptionsSnapshot settings, IEventBus eventBus, Func integrationEventLogServiceFactory) { _catalogContext = Context; _settings = settings; _eventBus = eventBus; - _integrationEventLogService = integrationEventLogService; + _integrationEventLogServiceFactory = integrationEventLogServiceFactory; ((DbContext)Context).ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; } @@ -152,20 +154,22 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers var oldPrice = item.Price; item.Price = product.Price; var @event = new ProductPriceChangedIntegrationEvent(item.Id, item.Price, oldPrice); + var eventLogService = _integrationEventLogServiceFactory(_catalogContext.Database.GetDbConnection()); using (var transaction = _catalogContext.Database.BeginTransaction()) { _catalogContext.CatalogItems.Update(item); await _catalogContext.SaveChangesAsync(); - await _integrationEventLogService.SaveEventAsync(@event); + + await eventLogService.SaveEventAsync(@event, _catalogContext.Database.CurrentTransaction.GetDbTransaction()); transaction.Commit(); } _eventBus.Publish(@event); - await _integrationEventLogService.MarkEventAsPublishedAsync(@event); + await eventLogService.MarkEventAsPublishedAsync(@event); } return Ok(); diff --git a/src/Services/Catalog/Catalog.API/Startup.cs b/src/Services/Catalog/Catalog.API/Startup.cs index 1598eb5a0..4a6961ba3 100644 --- a/src/Services/Catalog/Catalog.API/Startup.cs +++ b/src/Services/Catalog/Catalog.API/Startup.cs @@ -1,6 +1,5 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API { - using global::Catalog.API.IntegrationEvents; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; @@ -8,12 +7,14 @@ using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; + using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; - using System.Data.SqlClient; + using System; + using System.Data.Common; using System.Reflection; public class Startup @@ -39,23 +40,15 @@ public void ConfigureServices(IServiceCollection services) { - var sqlConnection = new SqlConnection(Configuration["ConnectionString"]); - services.AddDbContext(c => { - c.UseSqlServer(sqlConnection); + c.UseSqlServer(Configuration["ConnectionString"]); // Changing default behavior when client evaluation occurs to throw. // Default in EF Core would be to log a warning when client evaluation is performed. c.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); //Check Client vs. Server evaluation: https://docs.microsoft.com/en-us/ef/core/querying/client-eval }); - services.AddDbContext(c => - { - c.UseSqlServer(sqlConnection, b => b.MigrationsAssembly("Catalog.API")); - c.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); - }); - services.Configure(Configuration); // Add framework services. @@ -81,8 +74,9 @@ .AllowCredentials()); }); - services.AddTransient(); - + services.AddTransient>( + sp => (DbConnection c) => new IntegrationEventLogService(c)); + var serviceProvider = services.BuildServiceProvider(); var configuration = serviceProvider.GetRequiredService>().Value; services.AddSingleton(new EventBusRabbitMQ(configuration.EventBusConnection)); @@ -90,7 +84,7 @@ services.AddMvc(); } - public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IntegrationEventLogContext integrationEventLogContext) + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { //Configure logs @@ -112,7 +106,11 @@ //Seed Data CatalogContextSeed.SeedAsync(app, loggerFactory) .Wait(); - + + var integrationEventLogContext = new IntegrationEventLogContext( + new DbContextOptionsBuilder() + .UseSqlServer(Configuration["ConnectionString"], b => b.MigrationsAssembly("Catalog.API")) + .Options); integrationEventLogContext.Database.Migrate(); } From 5b9b92397d3286ddcf797232ddc5210e198cca70 Mon Sep 17 00:00:00 2001 From: Eduard Tomas Date: Fri, 24 Mar 2017 13:15:40 +0100 Subject: [PATCH 10/25] Updates to dockerfile / docker-compose for windows containers --- _docker/rabbitmq/Dockerfile.nanowin | 29 +++++++ _docker/rabbitmq/enabled_plugins | 1 + _docker/rabbitmq/rabbitmq.config | 1 + docker-compose-windows.override.yml | 79 ++++++++++++++++++ docker-compose-windows.prod.yml | 80 +++++++++++++++++++ docker-compose-windows.yml | 80 +++++++++++++++++++ docker-compose.prod.yml | 2 +- .../Basket/Basket.API/Dockerfile.nanowin | 1 + .../Catalog/Catalog.API/Dockerfile.nanowin | 1 + .../Identity/Identity.API/Dockerfile.nanowin | 1 + .../Ordering/Ordering.API/Dockerfile.nanowin | 1 + src/Web/WebMVC/Dockerfile.nanowin | 1 + src/Web/WebMVC/wwwroot/css/site.min.css | 2 +- src/Web/WebSPA/Dockerfile.nanowin | 1 + 14 files changed, 278 insertions(+), 2 deletions(-) create mode 100644 _docker/rabbitmq/Dockerfile.nanowin create mode 100644 _docker/rabbitmq/enabled_plugins create mode 100644 _docker/rabbitmq/rabbitmq.config create mode 100644 docker-compose-windows.override.yml create mode 100644 docker-compose-windows.prod.yml create mode 100644 docker-compose-windows.yml diff --git a/_docker/rabbitmq/Dockerfile.nanowin b/_docker/rabbitmq/Dockerfile.nanowin new file mode 100644 index 000000000..26474c235 --- /dev/null +++ b/_docker/rabbitmq/Dockerfile.nanowin @@ -0,0 +1,29 @@ +#https://github.com/spring2/dockerfiles/tree/master/rabbitmq + +FROM microsoft/windowsservercore + +SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"] + +ENV chocolateyUseWindowsCompression false + +RUN iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1')); \ + choco install -y curl; + +RUN choco install -y erlang +ENV ERLANG_SERVICE_MANAGER_PATH="C:\Program Files\erl8.2\erts-8.2\bin" +RUN choco install -y rabbitmq +ENV RABBITMQ_SERVER="C:\Program Files\RabbitMQ Server\rabbitmq_server-3.6.5" + +ENV RABBITMQ_CONFIG_FILE="c:\rabbitmq" +COPY rabbitmq.config C:/ +COPY rabbitmq.config C:/Users/ContainerAdministrator/AppData/Roaming/RabbitMQ/ +COPY enabled_plugins C:/Users/ContainerAdministrator/AppData/Roaming/RabbitMQ/ + + +EXPOSE 4369 +EXPOSE 5672 +EXPOSE 5671 +EXPOSE 15672 + +WORKDIR C:/Program\ Files/RabbitMQ\ Server/rabbitmq_server-3.6.5/sbin +CMD .\rabbitmq-server.bat \ No newline at end of file diff --git a/_docker/rabbitmq/enabled_plugins b/_docker/rabbitmq/enabled_plugins new file mode 100644 index 000000000..9eafc419b --- /dev/null +++ b/_docker/rabbitmq/enabled_plugins @@ -0,0 +1 @@ +[rabbitmq_amqp1_0,rabbitmq_management]. diff --git a/_docker/rabbitmq/rabbitmq.config b/_docker/rabbitmq/rabbitmq.config new file mode 100644 index 000000000..f7837f213 --- /dev/null +++ b/_docker/rabbitmq/rabbitmq.config @@ -0,0 +1 @@ +[{rabbit, [{loopback_users, []}]}]. \ No newline at end of file diff --git a/docker-compose-windows.override.yml b/docker-compose-windows.override.yml new file mode 100644 index 000000000..c85864d72 --- /dev/null +++ b/docker-compose-windows.override.yml @@ -0,0 +1,79 @@ +version: '2.1' + +# The default docker-compose.override file can use the "localhost" as the external name for testing web apps within the same dev machine. +# The ESHOP_EXTERNAL_DNS_NAME_OR_IP environment variable is taken, by default, from the ".env" file defined like: +# ESHOP_EXTERNAL_DNS_NAME_OR_IP=localhost +# but values present in the environment vars at runtime will always override those defined inside the .env file +# An external IP or DNS name has to be used (instead localhost and the 10.0.75.1 IP) when testing the Web apps and the Xamarin apps from remote machines/devices using the same WiFi, for instance. + +services: + + basket.api: + environment: + - ASPNETCORE_ENVIRONMENT=Development + - 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" + + catalog.api: + environment: + - ASPNETCORE_ENVIRONMENT=Development + - 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://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5101 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. + - EventBusConnection=rabbitmq + ports: + - "5101:5101" + + identity.api: + environment: + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_URLS=http://0.0.0.0:5105 + - SpaClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5104 + - ConnectionStrings__DefaultConnection=Server=sql.data;Database=Microsoft.eShopOnContainers.Service.IdentityDb;User Id=sa;Password=Pass@word + - MvcClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5100 #Local: You need to open your local dev-machine firewall at range 5100-5105. + ports: + - "5105:5105" + + ordering.api: + environment: + - ASPNETCORE_ENVIRONMENT=Development + - 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" + + webspa: + environment: + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_URLS=http://0.0.0.0:5104 + - CatalogUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5101 + - OrderingUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5102 + - IdentityUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. + - BasketUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5103 + ports: + - "5104:5104" + + webmvc: + environment: + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_URLS=http://0.0.0.0:5100 + - CatalogUrl=http://catalog.api:5101 + - OrderingUrl=http://ordering.api:5102 + - BasketUrl=http://basket.api:5103 + - IdentityUrl=http://10.0.75.1:5105 #Local: Use 10.0.75.1 in a "Docker for Windows" environment, if using "localhost" from browser. + #Remote: Use ${ESHOP_EXTERNAL_DNS_NAME_OR_IP} if using external IP or DNS name from browser. + ports: + - "5100:5100" + + sql.data: + environment: + - SA_PASSWORD=Pass@word + - ACCEPT_EULA=Y + ports: + - "5433:1433" \ No newline at end of file diff --git a/docker-compose-windows.prod.yml b/docker-compose-windows.prod.yml new file mode 100644 index 000000000..8d00df4ca --- /dev/null +++ b/docker-compose-windows.prod.yml @@ -0,0 +1,80 @@ +version: '2.1' + +# The Production docker-compose file has to have the external/real IPs or DNS names for the services +# The ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP environment variable is taken, by default, from the ".env" file defined like: +# ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP=192.168.88.248 +# but values present in the environment vars at runtime will always override those defined inside the .env file +# An external IP or DNS name has to be used when testing the Web apps and the Xamarin apps from remote machines/devices using the same WiFi, for instance. +# +# Set ASPNETCORE_ENVIRONMENT=Development to get errors while testing. +# +# You need to start it with the following CLI command: +# docker-compose -f docker-compose-windows.yml -f docker-compose-windows.prod.yml up -d + +services: + + basket.api: + environment: + - ASPNETCORE_ENVIRONMENT=Production + - ASPNETCORE_URLS=http://0.0.0.0:5103 + - ConnectionString=basket.data + - identityUrl=http://identity.api:5105 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105. + ports: + - "5103:5103" + + catalog.api: + environment: + - ASPNETCORE_ENVIRONMENT=Production + - 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://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5101 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105. + ports: + - "5101:5101" + + identity.api: + environment: + - ASPNETCORE_ENVIRONMENT=Production + - ASPNETCORE_URLS=http://0.0.0.0:5105 + - SpaClient=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5104 + - ConnectionStrings__DefaultConnection=Server=sql.data;Database=Microsoft.eShopOnContainers.Service.IdentityDb;User Id=sa;Password=Pass@word + - MvcClient=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5100 #Local: You need to open your host's firewall at range 5100-5105. + ports: + - "5105:5105" + + ordering.api: + environment: + - ASPNETCORE_ENVIRONMENT=Production + - 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 host's firewall at range 5100-5105. at range 5100-5105. + ports: + - "5102:5102" + + webspa: + environment: + - ASPNETCORE_ENVIRONMENT=Production + - ASPNETCORE_URLS=http://0.0.0.0:5104 + - CatalogUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5101 + - OrderingUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5102 + - IdentityUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105. + - BasketUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5103 + ports: + - "5104:5104" + + webmvc: + environment: + - ASPNETCORE_ENVIRONMENT=Production + - ASPNETCORE_URLS=http://0.0.0.0:5100 + - CatalogUrl=http://catalog.api:5101 + - OrderingUrl=http://ordering.api:5102 + - IdentityUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser. + - BasketUrl=http://basket.api:5103 + ports: + - "5100:5100" + + sql.data: + environment: + - SA_PASSWORD=Pass@word + - ACCEPT_EULA=Y + ports: + - "5433:1433" \ No newline at end of file diff --git a/docker-compose-windows.yml b/docker-compose-windows.yml new file mode 100644 index 000000000..b8eaabfc8 --- /dev/null +++ b/docker-compose-windows.yml @@ -0,0 +1,80 @@ +version: '2.1' + +services: + basket.api: + image: eshop/basket.api + build: + context: ./src/Services/Basket/Basket.API + dockerfile: Dockerfile.nanowin + depends_on: + - basket.data + - identity.api + + catalog.api: + image: eshop/catalog.api + build: + context: ./src/Services/Catalog/Catalog.API + dockerfile: Dockerfile.nanowin + depends_on: + - sql.data + + identity.api: + image: eshop/identity.api + build: + context: ./src/Services/Identity/Identity.API + dockerfile: Dockerfile.nanowin + depends_on: + - sql.data + + ordering.api: + image: eshop/ordering.api + build: + context: ./src/Services/Ordering/Ordering.API + dockerfile: Dockerfile.nanowin + depends_on: + - sql.data + + webspa: + image: eshop/webspa + build: + context: ./src/Web/WebSPA + dockerfile: Dockerfile.nanowin + depends_on: + - identity.api + - basket.api + + webmvc: + image: eshop/webmvc + build: + context: ./src/Web/WebMVC + dockerfile: Dockerfile.nanowin + depends_on: + - catalog.api + - ordering.api + - identity.api + - basket.api + + sql.data: + image: microsoft/mssql-server-windows + + basket.data: + image: redis + build: + context: ./_docker/redis + dockerfile: Dockerfile.nanowin + ports: + - "6379:6379" + + rabbitmq: + image: rabbitmq + build: + context: ./_docker/rabbitmq + dockerfile: Dockerfile.nanowin + ports: + - "5672:5672" + +networks: + default: + external: + name: nat + diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 14d8114ea..e760fed47 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -8,7 +8,7 @@ version: '2' # # Set ASPNETCORE_ENVIRONMENT=Development to get errors while testing. # -# You need to start it with the following CLI command: +# You need to start it with the following CLI command: # docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d services: diff --git a/src/Services/Basket/Basket.API/Dockerfile.nanowin b/src/Services/Basket/Basket.API/Dockerfile.nanowin index 0541a26cc..9c664f4e4 100644 --- a/src/Services/Basket/Basket.API/Dockerfile.nanowin +++ b/src/Services/Basket/Basket.API/Dockerfile.nanowin @@ -1,4 +1,5 @@ FROM microsoft/dotnet:1.1-runtime-nanoserver +SHELL ["powershell"] ARG source WORKDIR /app RUN set-itemproperty -path 'HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters' -Name ServerPriorityTimeLimit -Value 0 -Type DWord diff --git a/src/Services/Catalog/Catalog.API/Dockerfile.nanowin b/src/Services/Catalog/Catalog.API/Dockerfile.nanowin index c40d3a4d0..193ddaef6 100644 --- a/src/Services/Catalog/Catalog.API/Dockerfile.nanowin +++ b/src/Services/Catalog/Catalog.API/Dockerfile.nanowin @@ -1,4 +1,5 @@ FROM microsoft/dotnet:1.1-runtime-nanoserver +SHELL ["powershell"] ARG source WORKDIR /app RUN set-itemproperty -path 'HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters' -Name ServerPriorityTimeLimit -Value 0 -Type DWord diff --git a/src/Services/Identity/Identity.API/Dockerfile.nanowin b/src/Services/Identity/Identity.API/Dockerfile.nanowin index b950d8334..9d24ccf1a 100644 --- a/src/Services/Identity/Identity.API/Dockerfile.nanowin +++ b/src/Services/Identity/Identity.API/Dockerfile.nanowin @@ -1,4 +1,5 @@ FROM microsoft/dotnet:1.1-runtime-nanoserver +SHELL ["powershell"] ARG source WORKDIR /app RUN set-itemproperty -path 'HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters' -Name ServerPriorityTimeLimit -Value 0 -Type DWord diff --git a/src/Services/Ordering/Ordering.API/Dockerfile.nanowin b/src/Services/Ordering/Ordering.API/Dockerfile.nanowin index eac299fc7..653531d0f 100644 --- a/src/Services/Ordering/Ordering.API/Dockerfile.nanowin +++ b/src/Services/Ordering/Ordering.API/Dockerfile.nanowin @@ -1,4 +1,5 @@ FROM microsoft/dotnet:1.1-runtime-nanoserver +SHELL ["powershell"] ARG source WORKDIR /app RUN set-itemproperty -path 'HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters' -Name ServerPriorityTimeLimit -Value 0 -Type DWord diff --git a/src/Web/WebMVC/Dockerfile.nanowin b/src/Web/WebMVC/Dockerfile.nanowin index 302526330..4eaad3b22 100644 --- a/src/Web/WebMVC/Dockerfile.nanowin +++ b/src/Web/WebMVC/Dockerfile.nanowin @@ -1,4 +1,5 @@ FROM microsoft/dotnet:1.1-runtime-nanoserver +SHELL ["powershell"] ARG source WORKDIR /app RUN set-itemproperty -path 'HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters' -Name ServerPriorityTimeLimit -Value 0 -Type DWord diff --git a/src/Web/WebMVC/wwwroot/css/site.min.css b/src/Web/WebMVC/wwwroot/css/site.min.css index f95f6d90e..4d03fa783 100644 --- a/src/Web/WebMVC/wwwroot/css/site.min.css +++ b/src/Web/WebMVC/wwwroot/css/site.min.css @@ -1 +1 @@ -.esh-app-footer{background-color:#000;border-top:1px solid #eee;margin-top:2.5rem;padding-bottom:2.5rem;padding-top:2.5rem;width:100%}.esh-app-footer-brand{height:50px;width:230px}.esh-app-footer-text{color:#83d01b;line-height:50px;text-align:right;width:100%}@font-face{font-family:Montserrat;font-weight:400;src:url("../fonts/Montserrat-Regular.eot?") format("eot"),url("../fonts/Montserrat-Regular.woff") format("woff"),url("../fonts/Montserrat-Regular.ttf") format("truetype"),url("../fonts/Montserrat-Regular.svg#Montserrat") format("svg")}@font-face{font-family:Montserrat;font-weight:700;src:url("../fonts/Montserrat-Bold.eot?") format("eot"),url("../fonts/Montserrat-Bold.woff") format("woff"),url("../fonts/Montserrat-Bold.ttf") format("truetype"),url("../fonts/Montserrat-Bold.svg#Montserrat") format("svg")}html,body{font-family:Montserrat,sans-serif;font-size:16px;font-weight:400;z-index:10}*,*::after,*::before{box-sizing:border-box}.preloading{color:#00a69c;display:block;font-size:1.5rem;left:50%;position:fixed;top:50%;transform:translate(-50%,-50%)}select::-ms-expand{display:none}@media screen and (min-width:992px){.form-input{max-width:360px;width:360px}}.form-input{border-radius:0;height:45px;padding:10px}.form-input-small{max-width:100px !important}.form-input-medium{width:150px !important}.alert{padding-left:0}.alert-danger{background-color:transparent;border:0;color:#fb0d0d;font-size:12px}a,a:active,a:hover,a:visited{color:#000;text-decoration:none;transition:color .35s}a:hover,a:active{color:#75b918;transition:color .35s}.esh-pager-wrapper{padding-top:1rem;text-align:center}.esh-pager-item{margin:0 5vw}.esh-pager-item--navigable{display:inline-block;cursor:pointer}.esh-pager-item--navigable.is-disabled{opacity:0;pointer-events:none}.esh-pager-item--navigable:hover{color:#83d01b}@media screen and (max-width:1280px){.esh-pager-item{font-size:.85rem}}@media screen and (max-width:1024px){.esh-pager-item{margin:0 4vw}}.esh-identity{line-height:3rem;position:relative;text-align:right}.esh-identity-section{display:inline-block;width:100%}.esh-identity-name{display:inline-block}.esh-identity-name--upper{text-transform:uppercase}@media screen and (max-width:768px){.esh-identity-name{font-size:.85rem}}.esh-identity-image{display:inline-block}.esh-identity-drop{background:#fff;height:0;min-width:14rem;right:0;overflow:hidden;padding:.5rem;position:absolute;top:2.5rem;transition:height .35s}.esh-identity:hover .esh-identity-drop{border:1px solid #eee;height:7rem;transition:height .35s}.esh-identity-item{cursor:pointer;display:block;transition:color .35s}.esh-identity-item:hover{color:#75b918;transition:color .35s}.esh-header{background-color:#00a69c;height:4rem}.esh-header-back{color:rgba(255,255,255,.5) !important;line-height:4rem;text-transform:uppercase;text-decoration:none;transition:color .35s}.esh-header-back:hover{color:#fff !important;transition:color .35s}.esh-orders{min-height:80vh;overflow-x:hidden}.esh-orders-header{background-color:#00a69c;height:4rem}.esh-orders-back{color:rgba(255,255,255,.4);line-height:4rem;text-transform:uppercase;text-decoration:none;transition:color .35s}.esh-orders-back:hover{color:#fff;transition:color .35s}.esh-orders-titles{padding-bottom:1rem;padding-top:2rem}.esh-orders-title{text-transform:uppercase}.esh-orders-items{height:2rem;line-height:2rem;position:relative}.esh-orders-items:nth-of-type(2n+1):before{background-color:#eef;content:'';height:100%;left:0;margin-left:-100vw;position:absolute;top:0;width:200vw;z-index:-1}.esh-orders-item{font-weight:300}.esh-orders-item--hover{opacity:0;pointer-events:none}.esh-orders-items:hover .esh-orders-item--hover{opacity:1;pointer-events:all}.esh-orders-link{color:#83d01b;text-decoration:none;transition:color .35s}.esh-orders-link:hover{color:#75b918;transition:color .35s}.esh-orders_new{min-height:80vh}.esh-orders_new-header{background-color:#00a69c;height:4rem}.esh-orders_new-back{color:rgba(255,255,255,.4);line-height:4rem;text-decoration:none;text-transform:uppercase;transition:color .35s}.esh-orders_new-back:hover{color:#fff;transition:color .35s}.esh-orders_new-section{padding:1rem 0}.esh-orders_new-section--right{text-align:right}.esh-orders_new-placeOrder{background-color:#83d01b;border:0;border-radius:0;color:#fff;display:inline-block;font-size:1rem;font-weight:400;margin-top:1rem;padding:1rem 1.5rem;text-align:center;text-transform:uppercase;transition:all .35s}.esh-orders_new-placeOrder:hover{background-color:#4a760f;transition:all .35s}.esh-orders_new-titles{padding-bottom:1rem;padding-top:2rem}.esh-orders_new-title{font-size:1.25rem;text-transform:uppercase}.esh-orders_new-items--border{border-bottom:1px solid #eee;padding:.5rem 0}.esh-orders_new-items--border:last-of-type{border-color:transparent}.esh-orders_new-item{font-size:1rem;font-weight:300}.esh-orders_new-item--middle{line-height:8rem}@media screen and (max-width:768px){.esh-orders_new-item--middle{line-height:1rem}}.esh-orders_new-item--mark{color:#83d01b}.esh-orders_new-image{height:8rem}.esh-orders_detail{min-height:80vh}.esh-orders_detail-section{padding:1rem 0}.esh-orders_detail-section--right{text-align:right}.esh-orders_detail-titles{padding-bottom:1rem;padding-top:2rem}.esh-orders_detail-title{text-transform:uppercase}.esh-orders_detail-items--border{border-bottom:1px solid #eee;padding:.5rem 0}.esh-orders_detail-items--border:last-of-type{border-color:transparent}.esh-orders_detail-item{font-size:1rem;font-weight:300}.esh-orders_detail-item--middle{line-height:8rem}@media screen and (max-width:768px){.esh-orders_detail-item--middle{line-height:1rem}}.esh-orders_detail-item--mark{color:#83d01b}.esh-orders_detail-image{height:8rem}.esh-catalog-hero{background-image:url("../images/main_banner.png");background-size:cover;height:260px;width:100%}.esh-catalog-title{position:relative;top:74.28571px}.esh-catalog-filters{background-color:#00a69c;height:65px}.esh-catalog-filter{background-color:transparent;border-color:#00d9cc;color:#fff;cursor:pointer;margin-right:1rem;margin-top:.5rem;outline-color:#83d01b;padding-bottom:0;padding-left:.5rem;padding-right:.5rem;padding-top:1.5rem;min-width:140px;-webkit-appearance:none}.esh-catalog-filter option{background-color:#00a69c}.esh-catalog-label{display:inline-block;position:relative;z-index:0}.esh-catalog-label::before{color:rgba(255,255,255,.5);content:attr(data-title);font-size:.65rem;margin-top:.65rem;margin-left:.5rem;position:absolute;text-transform:uppercase;z-index:1}.esh-catalog-label::after{background-image:url("../images/arrow-down.png");height:7px;content:'';position:absolute;right:1.5rem;top:2.5rem;width:10px;z-index:1}.esh-catalog-send{background-color:#83d01b;color:#fff;cursor:pointer;font-size:1rem;transform:translateY(.5rem);padding:.5rem;transition:all .35s}.esh-catalog-send:hover{background-color:#4a760f;transition:all .35s}.esh-catalog-items{margin-top:1rem}.esh-catalog-item{text-align:center;margin-bottom:1.5rem;width:33%;display:inline-block;float:none !important}@media screen and (max-width:1024px){.esh-catalog-item{width:50%}}@media screen and (max-width:768px){.esh-catalog-item{width:100%}}.esh-catalog-thumbnail{max-width:370px;width:100%}.esh-catalog-button{background-color:#83d01b;border:none;color:#fff;cursor:pointer;font-size:1rem;height:3rem;margin-top:1rem;transition:all .35s;width:80%}.esh-catalog-button.is-disabled{opacity:.5;pointer-events:none}.esh-catalog-button:hover{background-color:#4a760f;transition:all .35s}.esh-catalog-name{font-size:1rem;font-weight:300;margin-top:.5rem;text-align:center;text-transform:uppercase}.esh-catalog-price{text-align:center;font-weight:900;font-size:28px}.esh-catalog-price::before{content:'$'}.esh-basket{min-height:80vh}.esh-basket-titles{padding-bottom:1rem;padding-top:2rem}.esh-basket-titles--clean{padding-bottom:0;padding-top:0}.esh-basket-title{text-transform:uppercase}.esh-basket-items--border{border-bottom:1px solid #eee;padding:.5rem 0}.esh-basket-items--border:last-of-type{border-color:transparent}.esh-basket-item{font-size:1rem;font-weight:300}.esh-basket-item--middle{line-height:8rem}@media screen and (max-width:1024px){.esh-basket-item--middle{line-height:1rem}}.esh-basket-item--mark{color:#00a69c}.esh-basket-image{height:8rem}.esh-basket-input{line-height:1rem;width:100%}.esh-basket-checkout{border:none;border-radius:0;background-color:#83d01b;color:#fff;display:inline-block;font-size:1rem;font-weight:400;margin-top:1rem;padding:1rem 1.5rem;text-align:center;text-transform:uppercase;transition:all .35s}.esh-basket-checkout:hover{background-color:#4a760f;transition:all .35s}.esh-basketstatus{cursor:pointer;display:inline-block;float:right;position:relative;transition:all .35s}.esh-basketstatus.is-disabled{opacity:.5;pointer-events:none}.esh-basketstatus-image{height:36px;margin-top:.5rem}.esh-basketstatus-badge{background-color:#83d01b;border-radius:50%;color:#fff;display:block;height:1.5rem;left:50%;position:absolute;text-align:center;top:0;transform:translateX(-38%);transition:all .35s;width:1.5rem}.esh-basketstatus:hover .esh-basketstatus-badge{background-color:transparent;color:#75b918;transition:all .35s} \ No newline at end of file +.esh-app-footer{background-color:#000;border-top:1px solid #eee;margin-top:2.5rem;padding-bottom:2.5rem;padding-top:2.5rem;width:100%}.esh-app-footer-brand{height:50px;width:230px}.esh-app-footer-text{color:#83d01b;line-height:50px;text-align:right;width:100%}@font-face{font-family:Montserrat;font-weight:400;src:url("../fonts/Montserrat-Regular.eot?") format("eot"),url("../fonts/Montserrat-Regular.woff") format("woff"),url("../fonts/Montserrat-Regular.ttf") format("truetype"),url("../fonts/Montserrat-Regular.svg#Montserrat") format("svg")}@font-face{font-family:Montserrat;font-weight:700;src:url("../fonts/Montserrat-Bold.eot?") format("eot"),url("../fonts/Montserrat-Bold.woff") format("woff"),url("../fonts/Montserrat-Bold.ttf") format("truetype"),url("../fonts/Montserrat-Bold.svg#Montserrat") format("svg")}html,body{font-family:Montserrat,sans-serif;font-size:16px;font-weight:400;z-index:10}*,*::after,*::before{box-sizing:border-box}.preloading{color:#00a69c;display:block;font-size:1.5rem;left:50%;position:fixed;top:50%;transform:translate(-50%,-50%)}select::-ms-expand{display:none}@media screen and (min-width:992px){.form-input{max-width:360px;width:360px}}.form-input{border-radius:0;height:45px;padding:10px}.form-input-small{max-width:100px !important}.form-input-medium{width:150px !important}.alert{padding-left:0}.alert-danger{background-color:transparent;border:0;color:#fb0d0d;font-size:12px}a,a:active,a:hover,a:visited{color:#000;text-decoration:none;transition:color .35s}a:hover,a:active{color:#75b918;transition:color .35s}.esh-basket{min-height:80vh}.esh-basket-titles{padding-bottom:1rem;padding-top:2rem}.esh-basket-titles--clean{padding-bottom:0;padding-top:0}.esh-basket-title{text-transform:uppercase}.esh-basket-items--border{border-bottom:1px solid #eee;padding:.5rem 0}.esh-basket-items--border:last-of-type{border-color:transparent}.esh-basket-item{font-size:1rem;font-weight:300}.esh-basket-item--middle{line-height:8rem}@media screen and (max-width:1024px){.esh-basket-item--middle{line-height:1rem}}.esh-basket-item--mark{color:#00a69c}.esh-basket-image{height:8rem}.esh-basket-input{line-height:1rem;width:100%}.esh-basket-checkout{border:none;border-radius:0;background-color:#83d01b;color:#fff;display:inline-block;font-size:1rem;font-weight:400;margin-top:1rem;padding:1rem 1.5rem;text-align:center;text-transform:uppercase;transition:all .35s}.esh-basket-checkout:hover{background-color:#4a760f;transition:all .35s}.esh-basket-margin12{margin-left:12px}.esh-basketstatus{cursor:pointer;display:inline-block;float:right;position:relative;transition:all .35s}.esh-basketstatus.is-disabled{opacity:.5;pointer-events:none}.esh-basketstatus-image{height:36px;margin-top:.5rem}.esh-basketstatus-badge{background-color:#83d01b;border-radius:50%;color:#fff;display:block;height:1.5rem;left:50%;position:absolute;text-align:center;top:0;transform:translateX(-38%);transition:all .35s;width:1.5rem}.esh-basketstatus:hover .esh-basketstatus-badge{background-color:transparent;color:#75b918;transition:all .35s}.esh-catalog-hero{background-image:url("../images/main_banner.png");background-size:cover;height:260px;width:100%}.esh-catalog-title{position:relative;top:74.28571px}.esh-catalog-filters{background-color:#00a69c;height:65px}.esh-catalog-filter{background-color:transparent;border-color:#00d9cc;color:#fff;cursor:pointer;margin-right:1rem;margin-top:.5rem;outline-color:#83d01b;padding-bottom:0;padding-left:.5rem;padding-right:.5rem;padding-top:1.5rem;min-width:140px;-webkit-appearance:none}.esh-catalog-filter option{background-color:#00a69c}.esh-catalog-label{display:inline-block;position:relative;z-index:0}.esh-catalog-label::before{color:rgba(255,255,255,.5);content:attr(data-title);font-size:.65rem;margin-top:.65rem;margin-left:.5rem;position:absolute;text-transform:uppercase;z-index:1}.esh-catalog-label::after{background-image:url("../images/arrow-down.png");height:7px;content:'';position:absolute;right:1.5rem;top:2.5rem;width:10px;z-index:1}.esh-catalog-send{background-color:#83d01b;color:#fff;cursor:pointer;font-size:1rem;transform:translateY(.5rem);padding:.5rem;transition:all .35s}.esh-catalog-send:hover{background-color:#4a760f;transition:all .35s}.esh-catalog-items{margin-top:1rem}.esh-catalog-item{text-align:center;margin-bottom:1.5rem;width:33%;display:inline-block;float:none !important}@media screen and (max-width:1024px){.esh-catalog-item{width:50%}}@media screen and (max-width:768px){.esh-catalog-item{width:100%}}.esh-catalog-thumbnail{max-width:370px;width:100%}.esh-catalog-button{background-color:#83d01b;border:none;color:#fff;cursor:pointer;font-size:1rem;height:3rem;margin-top:1rem;transition:all .35s;width:80%}.esh-catalog-button.is-disabled{opacity:.5;pointer-events:none}.esh-catalog-button:hover{background-color:#4a760f;transition:all .35s}.esh-catalog-name{font-size:1rem;font-weight:300;margin-top:.5rem;text-align:center;text-transform:uppercase}.esh-catalog-price{text-align:center;font-weight:900;font-size:28px}.esh-catalog-price::before{content:'$'}.esh-orders{min-height:80vh;overflow-x:hidden}.esh-orders-header{background-color:#00a69c;height:4rem}.esh-orders-back{color:rgba(255,255,255,.4);line-height:4rem;text-transform:uppercase;text-decoration:none;transition:color .35s}.esh-orders-back:hover{color:#fff;transition:color .35s}.esh-orders-titles{padding-bottom:1rem;padding-top:2rem}.esh-orders-title{text-transform:uppercase}.esh-orders-items{height:2rem;line-height:2rem;position:relative}.esh-orders-items:nth-of-type(2n+1):before{background-color:#eef;content:'';height:100%;left:0;margin-left:-100vw;position:absolute;top:0;width:200vw;z-index:-1}.esh-orders-item{font-weight:300}.esh-orders-item--hover{opacity:0;pointer-events:none}.esh-orders-items:hover .esh-orders-item--hover{opacity:1;pointer-events:all}.esh-orders-link{color:#83d01b;text-decoration:none;transition:color .35s}.esh-orders-link:hover{color:#75b918;transition:color .35s}.esh-orders_detail{min-height:80vh}.esh-orders_detail-section{padding:1rem 0}.esh-orders_detail-section--right{text-align:right}.esh-orders_detail-titles{padding-bottom:1rem;padding-top:2rem}.esh-orders_detail-title{text-transform:uppercase}.esh-orders_detail-items--border{border-bottom:1px solid #eee;padding:.5rem 0}.esh-orders_detail-items--border:last-of-type{border-color:transparent}.esh-orders_detail-item{font-size:1rem;font-weight:300}.esh-orders_detail-item--middle{line-height:8rem}@media screen and (max-width:768px){.esh-orders_detail-item--middle{line-height:1rem}}.esh-orders_detail-item--mark{color:#83d01b}.esh-orders_detail-image{height:8rem}.esh-orders_new{min-height:80vh}.esh-orders_new-header{background-color:#00a69c;height:4rem}.esh-orders_new-back{color:rgba(255,255,255,.4);line-height:4rem;text-decoration:none;text-transform:uppercase;transition:color .35s}.esh-orders_new-back:hover{color:#fff;transition:color .35s}.esh-orders_new-section{padding:1rem 0}.esh-orders_new-section--right{text-align:right}.esh-orders_new-placeOrder{background-color:#83d01b;border:0;border-radius:0;color:#fff;display:inline-block;font-size:1rem;font-weight:400;margin-top:1rem;padding:1rem 1.5rem;text-align:center;text-transform:uppercase;transition:all .35s}.esh-orders_new-placeOrder:hover{background-color:#4a760f;transition:all .35s}.esh-orders_new-titles{padding-bottom:1rem;padding-top:2rem}.esh-orders_new-title{font-size:1.25rem;text-transform:uppercase}.esh-orders_new-items--border{border-bottom:1px solid #eee;padding:.5rem 0}.esh-orders_new-items--border:last-of-type{border-color:transparent}.esh-orders_new-item{font-size:1rem;font-weight:300}.esh-orders_new-item--middle{line-height:8rem}@media screen and (max-width:768px){.esh-orders_new-item--middle{line-height:1rem}}.esh-orders_new-item--mark{color:#83d01b}.esh-orders_new-image{height:8rem}.esh-header{background-color:#00a69c;height:4rem}.esh-header-back{color:rgba(255,255,255,.5) !important;line-height:4rem;text-transform:uppercase;text-decoration:none;transition:color .35s}.esh-header-back:hover{color:#fff !important;transition:color .35s}.esh-identity{line-height:3rem;position:relative;text-align:right}.esh-identity-section{display:inline-block;width:100%}.esh-identity-name{display:inline-block}.esh-identity-name--upper{text-transform:uppercase}@media screen and (max-width:768px){.esh-identity-name{font-size:.85rem}}.esh-identity-image{display:inline-block}.esh-identity-drop{background:#fff;height:0;min-width:14rem;right:0;overflow:hidden;padding:.5rem;position:absolute;top:2.5rem;transition:height .35s}.esh-identity:hover .esh-identity-drop{border:1px solid #eee;height:7rem;transition:height .35s}.esh-identity-item{cursor:pointer;display:block;transition:color .35s}.esh-identity-item:hover{color:#75b918;transition:color .35s}.esh-pager-wrapper{padding-top:1rem;text-align:center}.esh-pager-item{margin:0 5vw}.esh-pager-item--navigable{display:inline-block;cursor:pointer}.esh-pager-item--navigable.is-disabled{opacity:0;pointer-events:none}.esh-pager-item--navigable:hover{color:#83d01b}@media screen and (max-width:1280px){.esh-pager-item{font-size:.85rem}}@media screen and (max-width:1024px){.esh-pager-item{margin:0 4vw}} \ No newline at end of file diff --git a/src/Web/WebSPA/Dockerfile.nanowin b/src/Web/WebSPA/Dockerfile.nanowin index 5f9a48294..700c23391 100644 --- a/src/Web/WebSPA/Dockerfile.nanowin +++ b/src/Web/WebSPA/Dockerfile.nanowin @@ -1,4 +1,5 @@ FROM microsoft/dotnet:1.1-runtime-nanoserver +SHELL ["powershell"] ARG source WORKDIR /app RUN set-itemproperty -path 'HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters' -Name ServerPriorityTimeLimit -Value 0 -Type DWord From f25f56cc2c975045aaa9e65f1a058c4d3bb74f64 Mon Sep 17 00:00:00 2001 From: dsanz Date: Fri, 24 Mar 2017 13:34:07 +0100 Subject: [PATCH 11/25] Fix test --- .../FunctionalTests/Services/Ordering/OrderingScenarios.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Services/FunctionalTests/Services/Ordering/OrderingScenarios.cs b/test/Services/FunctionalTests/Services/Ordering/OrderingScenarios.cs index 756bbed2a..11040aeaa 100644 --- a/test/Services/FunctionalTests/Services/Ordering/OrderingScenarios.cs +++ b/test/Services/FunctionalTests/Services/Ordering/OrderingScenarios.cs @@ -62,7 +62,7 @@ namespace FunctionalTests.Services.Ordering order.AddOrderItem(new OrderItemDTO() { ProductId = 1, - Discount = 12M, + Discount = 8M, UnitPrice = 10, Units = 1, ProductName = "Some name" From 8c57048c3ccf6d2b1f95ef2a41f3b705591d48ee Mon Sep 17 00:00:00 2001 From: dsanz Date: Fri, 24 Mar 2017 15:21:17 +0100 Subject: [PATCH 12/25] Add idempotent for the client header in tests --- .../Extensions/HttpClientExtensions.cs | 18 ++++++++++++++++++ .../Services/Ordering/OrderingScenarios.cs | 5 +++-- .../Extensions/HttpClientExtensions.cs | 18 ++++++++++++++++++ .../Services/Ordering/OrderingScenarios.cs | 10 ++++++---- 4 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 test/Services/FunctionalTests/Extensions/HttpClientExtensions.cs create mode 100644 test/Services/IntegrationTests/Services/Extensions/HttpClientExtensions.cs diff --git a/test/Services/FunctionalTests/Extensions/HttpClientExtensions.cs b/test/Services/FunctionalTests/Extensions/HttpClientExtensions.cs new file mode 100644 index 000000000..a41ffd3a2 --- /dev/null +++ b/test/Services/FunctionalTests/Extensions/HttpClientExtensions.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.TestHost; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; + +namespace FunctionalTests.Extensions +{ + static class HttpClientExtensions + { + public static HttpClient CreateIdempotentClient(this TestServer server) + { + var client = server.CreateClient(); + client.DefaultRequestHeaders.Add("x-requestid", Guid.NewGuid().ToString()); + return client; + } + } +} diff --git a/test/Services/FunctionalTests/Services/Ordering/OrderingScenarios.cs b/test/Services/FunctionalTests/Services/Ordering/OrderingScenarios.cs index 11040aeaa..5f52e1771 100644 --- a/test/Services/FunctionalTests/Services/Ordering/OrderingScenarios.cs +++ b/test/Services/FunctionalTests/Services/Ordering/OrderingScenarios.cs @@ -1,4 +1,5 @@ -using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; +using FunctionalTests.Extensions; +using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; using Microsoft.eShopOnContainers.WebMVC.ViewModels; using Newtonsoft.Json; using System; @@ -19,7 +20,7 @@ namespace FunctionalTests.Services.Ordering { using (var server = CreateServer()) { - var client = server.CreateClient(); + var client = server.CreateIdempotentClient(); // GIVEN an order is created await client.PostAsync(Post.AddNewOrder, new StringContent(BuildOrder(), UTF8Encoding.UTF8, "application/json")); diff --git a/test/Services/IntegrationTests/Services/Extensions/HttpClientExtensions.cs b/test/Services/IntegrationTests/Services/Extensions/HttpClientExtensions.cs new file mode 100644 index 000000000..00ed918b6 --- /dev/null +++ b/test/Services/IntegrationTests/Services/Extensions/HttpClientExtensions.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.TestHost; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; + +namespace IntegrationTests.Services.Extensions +{ + static class HttpClientExtensions + { + public static HttpClient CreateIdempotentClient(this TestServer server) + { + var client = server.CreateClient(); + client.DefaultRequestHeaders.Add("x-requestid", Guid.NewGuid().ToString()); + return client; + } + } +} diff --git a/test/Services/IntegrationTests/Services/Ordering/OrderingScenarios.cs b/test/Services/IntegrationTests/Services/Ordering/OrderingScenarios.cs index ba77a3e7c..3e2350c9d 100644 --- a/test/Services/IntegrationTests/Services/Ordering/OrderingScenarios.cs +++ b/test/Services/IntegrationTests/Services/Ordering/OrderingScenarios.cs @@ -1,5 +1,7 @@ namespace IntegrationTests.Services.Ordering { + using IntegrationTests.Services.Extensions; + using Microsoft.AspNetCore.TestHost; using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; using Newtonsoft.Json; using System; @@ -28,9 +30,9 @@ public async Task AddNewOrder_add_new_order_and_response_ok_status_code() { using (var server = CreateServer()) - { + { var content = new StringContent(BuildOrder(), UTF8Encoding.UTF8, "application/json"); - var response = await server.CreateClient() + var response = await server.CreateIdempotentClient() .PostAsync(Post.AddNewOrder, content); response.EnsureSuccessStatusCode(); @@ -44,7 +46,7 @@ { var content = new StringContent(BuildOrderWithInvalidExperationTime(), UTF8Encoding.UTF8, "application/json"); - var response = await server.CreateClient() + var response = await server.CreateIdempotentClient() .PostAsync(Post.AddNewOrder, content); Assert.True(response.StatusCode == System.Net.HttpStatusCode.BadRequest); @@ -102,5 +104,5 @@ return JsonConvert.SerializeObject(order); } - } + } } From 313976da80d4cd796bcd62590860231495a18909 Mon Sep 17 00:00:00 2001 From: Cesar De la Torre Date: Sat, 25 Mar 2017 14:47:23 -0700 Subject: [PATCH 13/25] Fixed bugs related to the Product Update and PriceChangedIntegrationEvent. It was updating only when the price was changing... additional refactoring performed, too. --- ...ductPriceChangedIntegrationEventHandler.cs | 4 +- .../Controllers/CatalogController.cs | 48 +++++++++++-------- .../Infrastructure/CatalogContextSeed.cs | 8 ++-- .../Identity.API/Views/Account/Login.cshtml | 9 ++++ src/Web/WebMVC/Views/Account/Login.cshtml | 11 ++++- .../Shared/Components/CartList/Default.cshtml | 2 +- .../modules/basket/basket.component.html | 2 +- 7 files changed, 56 insertions(+), 28 deletions(-) diff --git a/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs b/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs index 84d874271..28c9a9afa 100644 --- a/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs +++ b/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs @@ -20,11 +20,11 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Even foreach (var id in userIds) { var basket = await _repository.GetBasket(id); - await UpdateBasket(@event.ProductId, @event.NewPrice, basket); + await UpdatePriceInBasketItems(@event.ProductId, @event.NewPrice, basket); } } - private async Task UpdateBasket(int productId, decimal newPrice, CustomerBasket basket) + private async Task UpdatePriceInBasketItems(int productId, decimal newPrice, CustomerBasket basket) { var itemsToUpdate = basket?.Items?.Where(x => int.Parse(x.ProductId) == productId).ToList(); if (itemsToUpdate != null) diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index bbc30a772..c3035576c 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events; @@ -138,36 +139,45 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers //POST api/v1/[controller]/edit [Route("edit")] [HttpPost] - public async Task EditProduct([FromBody]CatalogItem product) + public async Task EditProduct([FromBody]CatalogItem productToUpdate) { - var item = await _catalogContext.CatalogItems.SingleOrDefaultAsync(i => i.Id == product.Id); + var catalogItem = await _catalogContext.CatalogItems.SingleOrDefaultAsync(i => i.Id == productToUpdate.Id); + if (catalogItem == null) return NotFound(); - if (item == null) + bool raiseProductPriceChangedEvent = false; + IntegrationEvent priceChangedEvent = null; + + if (catalogItem.Price != productToUpdate.Price) raiseProductPriceChangedEvent = true; + + if (raiseProductPriceChangedEvent) // Create event if price has changed { - return NotFound(); + var oldPrice = catalogItem.Price; + priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice); } - if (item.Price != product.Price) - { - var oldPrice = item.Price; - item.Price = product.Price; - var @event = new ProductPriceChangedIntegrationEvent(item.Id, item.Price, oldPrice); + //Update current product + catalogItem = productToUpdate; - using (var transaction = _catalogContext.Database.BeginTransaction()) - { - _catalogContext.CatalogItems.Update(item); + // Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction + using (var transaction = _catalogContext.Database.BeginTransaction()) + { + _catalogContext.CatalogItems.Update(catalogItem); await _catalogContext.SaveChangesAsync(); - await _integrationEventLogService.SaveEventAsync(@event); + //Save to EventLog only if product price changed + if(raiseProductPriceChangedEvent) + await _integrationEventLogService.SaveEventAsync(priceChangedEvent); transaction.Commit(); - } - - _eventBus.Publish(@event); - - await _integrationEventLogService.MarkEventAsPublishedAsync(@event); - } + } + //Publish to Event Bus only if product price changed + if (raiseProductPriceChangedEvent) + { + _eventBus.Publish(priceChangedEvent); + await _integrationEventLogService.MarkEventAsPublishedAsync(priceChangedEvent); + } + return Ok(); } diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs index 55ed81d23..cb1befc48 100644 --- a/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs +++ b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs @@ -84,14 +84,14 @@ { return new List() { - new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Bot Black Sweatshirt", Name = ".NET Bot Black Sweatshirt", Price = 19.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/1" }, + new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Bot Black Hoodie", Name = ".NET Bot Black Hoodie", Price = 19.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/1" }, new CatalogItem() { CatalogTypeId=1,CatalogBrandId=2, Description = ".NET Black & White Mug", Name = ".NET Black & White Mug", Price= 8.50M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/2" }, new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Prism White T-Shirt", Name = "Prism White T-Shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/3" }, - new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Foundation Sweatshirt", Name = ".NET Foundation Sweatshirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/4" }, + new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Foundation T-shirt", Name = ".NET Foundation T-shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/4" }, new CatalogItem() { CatalogTypeId=3,CatalogBrandId=5, Description = "Roslyn Red Sheet", Name = "Roslyn Red Sheet", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/5" }, - new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Blue Sweatshirt", Name = ".NET Blue Sweatshirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/6" }, + new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Blue Hoodie", Name = ".NET Blue Hoodie", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/6" }, new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Roslyn Red T-Shirt", Name = "Roslyn Red T-Shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/7" }, - new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Kudu Purple Sweatshirt", Name = "Kudu Purple Sweatshirt", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/8" }, + new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Kudu Purple Hoodie", Name = "Kudu Purple Hoodie", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/8" }, new CatalogItem() { CatalogTypeId=1,CatalogBrandId=5, Description = "Cup White Mug", Name = "Cup White Mug", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/9" }, new CatalogItem() { CatalogTypeId=3,CatalogBrandId=2, Description = ".NET Foundation Sheet", Name = ".NET Foundation Sheet", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/10" }, new CatalogItem() { CatalogTypeId=3,CatalogBrandId=2, Description = "Cup Sheet", Name = "Cup Sheet", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/11" }, diff --git a/src/Services/Identity/Identity.API/Views/Account/Login.cshtml b/src/Services/Identity/Identity.API/Views/Account/Login.cshtml index 88f6884cc..da36c31a4 100644 --- a/src/Services/Identity/Identity.API/Views/Account/Login.cshtml +++ b/src/Services/Identity/Identity.API/Views/Account/Login.cshtml @@ -45,6 +45,15 @@

Register as a new user?

+

+ Note that for demo purposes you don't need to register and can login with these credentials: +

+

+ User: demouser@microsoft.com +

+

+ Password: Pass@word1 +

diff --git a/src/Web/WebMVC/Views/Account/Login.cshtml b/src/Web/WebMVC/Views/Account/Login.cshtml index 595305f95..031998d69 100644 --- a/src/Web/WebMVC/Views/Account/Login.cshtml +++ b/src/Web/WebMVC/Views/Account/Login.cshtml @@ -44,7 +44,16 @@

Register as a new user?

- +

+ Note that for demo purposes you don't need to register and can login with these credentials: +

+

+ User: demouser@microsoft.com +

+

+ Password: Pass@word1 +

+ diff --git a/src/Web/WebMVC/Views/Shared/Components/CartList/Default.cshtml b/src/Web/WebMVC/Views/Shared/Components/CartList/Default.cshtml index 6ee70b035..43d3b9f55 100644 --- a/src/Web/WebMVC/Views/Shared/Components/CartList/Default.cshtml +++ b/src/Web/WebMVC/Views/Shared/Components/CartList/Default.cshtml @@ -38,7 +38,7 @@
@if (item.OldUnitPrice != 0) { - + }

diff --git a/src/Web/WebSPA/Client/modules/basket/basket.component.html b/src/Web/WebSPA/Client/modules/basket/basket.component.html index 7cab7221a..a23a4e91f 100644 --- a/src/Web/WebSPA/Client/modules/basket/basket.component.html +++ b/src/Web/WebSPA/Client/modules/basket/basket.component.html @@ -29,7 +29,7 @@
- +
From c07665aef6e1ac5ade6916f4bafb6fd66b28b3cd Mon Sep 17 00:00:00 2001 From: Cesar De la Torre Date: Sat, 25 Mar 2017 15:50:26 -0700 Subject: [PATCH 14/25] Renamed from EditProduct to UpdateProduct, also its HTTP Route, etc. --- .../Catalog/Catalog.API/Controllers/CatalogController.cs | 6 +++--- .../Services/Catalog/CatalogScenariosBase.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index c3035576c..3cd220657 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -136,10 +136,10 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers return Ok(items); } - //POST api/v1/[controller]/edit - [Route("edit")] + //POST api/v1/[controller]/update + [Route("update")] [HttpPost] - public async Task EditProduct([FromBody]CatalogItem productToUpdate) + public async Task UpdateProduct([FromBody]CatalogItem productToUpdate) { var catalogItem = await _catalogContext.CatalogItems.SingleOrDefaultAsync(i => i.Id == productToUpdate.Id); if (catalogItem == null) return NotFound(); diff --git a/test/Services/FunctionalTests/Services/Catalog/CatalogScenariosBase.cs b/test/Services/FunctionalTests/Services/Catalog/CatalogScenariosBase.cs index f7a847934..e524eedc9 100644 --- a/test/Services/FunctionalTests/Services/Catalog/CatalogScenariosBase.cs +++ b/test/Services/FunctionalTests/Services/Catalog/CatalogScenariosBase.cs @@ -34,7 +34,7 @@ namespace FunctionalTests.Services.Catalog public static class Post { - public static string UpdateCatalogProduct = "api/v1/catalog/edit"; + public static string UpdateCatalogProduct = "api/v1/catalog/update"; } } } From 0d78461a082f95467f21af37c93e2e6a456af85e Mon Sep 17 00:00:00 2001 From: Cesar De la Torre Date: Sun, 26 Mar 2017 18:00:04 -0700 Subject: [PATCH 15/25] Implemented EF Core DB connections resiliency with explicit retries and execution strategy when using multiple DbContexts --- .../Controllers/CatalogController.cs | 16 ++++++++--- src/Services/Catalog/Catalog.API/Startup.cs | 27 ++++++++++++++----- src/Services/Ordering/Ordering.API/Startup.cs | 10 ++++--- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index 3cd220657..7eb19c63a 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -158,18 +158,26 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers //Update current product catalogItem = productToUpdate; - // Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction - using (var transaction = _catalogContext.Database.BeginTransaction()) + //Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction(): + //See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency + var strategy = _catalogContext.Database.CreateExecutionStrategy(); + + await strategy.ExecuteAsync(async () => { + // Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction + using (var transaction = _catalogContext.Database.BeginTransaction()) + { _catalogContext.CatalogItems.Update(catalogItem); await _catalogContext.SaveChangesAsync(); //Save to EventLog only if product price changed - if(raiseProductPriceChangedEvent) + if (raiseProductPriceChangedEvent) await _integrationEventLogService.SaveEventAsync(priceChangedEvent); transaction.Commit(); - } + } + }); + //Publish to Event Bus only if product price changed if (raiseProductPriceChangedEvent) diff --git a/src/Services/Catalog/Catalog.API/Startup.cs b/src/Services/Catalog/Catalog.API/Startup.cs index 1598eb5a0..87986612f 100644 --- a/src/Services/Catalog/Catalog.API/Startup.cs +++ b/src/Services/Catalog/Catalog.API/Startup.cs @@ -13,6 +13,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; + using System; using System.Data.SqlClient; using System.Reflection; @@ -39,21 +40,35 @@ public void ConfigureServices(IServiceCollection services) { + //Using the same SqlConnection for both DbContexts (CatalogContext and IntegrationEventLogContext) var sqlConnection = new SqlConnection(Configuration["ConnectionString"]); - services.AddDbContext(c => + services.AddDbContext(options => { - c.UseSqlServer(sqlConnection); + options.UseSqlServer(sqlConnection, + sqlServerOptionsAction: sqlOptions => + { + sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); + //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency + sqlOptions.EnableRetryOnFailure(maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); + }); // Changing default behavior when client evaluation occurs to throw. // Default in EF Core would be to log a warning when client evaluation is performed. - c.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); + options.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); //Check Client vs. Server evaluation: https://docs.microsoft.com/en-us/ef/core/querying/client-eval }); - services.AddDbContext(c => + services.AddDbContext(options => { - c.UseSqlServer(sqlConnection, b => b.MigrationsAssembly("Catalog.API")); - c.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); + options.UseSqlServer(sqlConnection, + sqlServerOptionsAction: sqlOptions => + { + sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); + //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency + sqlOptions.EnableRetryOnFailure(maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); + }); + + options.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); }); services.Configure(Configuration); diff --git a/src/Services/Ordering/Ordering.API/Startup.cs b/src/Services/Ordering/Ordering.API/Startup.cs index 3f0840b07..92edf1974 100644 --- a/src/Services/Ordering/Ordering.API/Startup.cs +++ b/src/Services/Ordering/Ordering.API/Startup.cs @@ -52,10 +52,14 @@ services.AddEntityFrameworkSqlServer() .AddDbContext(options => { - options.UseSqlServer(Configuration["ConnectionString"], - sqlop => sqlop.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name)); + options.UseSqlServer(Configuration["ConnectionString"], + sqlServerOptionsAction: sqlOptions => + { + sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); + sqlOptions.EnableRetryOnFailure(maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); + }); }, - ServiceLifetime.Scoped //DbContext is shared across the HTTP request scope (graph of objects started in the HTTP request) + ServiceLifetime.Scoped //Showing explicitly that the DbContext is shared across the HTTP request scope (graph of objects started in the HTTP request) ); services.AddSwaggerGen(); From f974d5ea865226c15eaea4bde51f02e52e2579c6 Mon Sep 17 00:00:00 2001 From: dsanz Date: Mon, 27 Mar 2017 10:31:25 +0200 Subject: [PATCH 16/25] Fix test after changes in Catalog Controller --- .../Services/IntegrationEventsScenarios.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/test/Services/FunctionalTests/Services/IntegrationEventsScenarios.cs b/test/Services/FunctionalTests/Services/IntegrationEventsScenarios.cs index 57254db1f..8ed11be3a 100644 --- a/test/Services/FunctionalTests/Services/IntegrationEventsScenarios.cs +++ b/test/Services/FunctionalTests/Services/IntegrationEventsScenarios.cs @@ -43,7 +43,7 @@ namespace FunctionalTests.Services var itemToModify = basket.Items[2]; var oldPrice = itemToModify.UnitPrice; var newPrice = oldPrice + priceModification; - var pRes = await catalogClient.PostAsync(CatalogScenariosBase.Post.UpdateCatalogProduct, new StringContent(ChangePrice(itemToModify, newPrice), UTF8Encoding.UTF8, "application/json")); + var pRes = await catalogClient.PostAsync(CatalogScenariosBase.Post.UpdateCatalogProduct, new StringContent(ChangePrice(itemToModify, newPrice, originalCatalogProducts), UTF8Encoding.UTF8, "application/json")); var modifiedCatalogProducts = await GetCatalogAsync(catalogClient); @@ -100,14 +100,11 @@ namespace FunctionalTests.Services return JsonConvert.DeserializeObject>(items); } - private string ChangePrice(BasketItem itemToModify, decimal newPrice) + private string ChangePrice(BasketItem itemToModify, decimal newPrice, PaginatedItemsViewModel catalogProducts) { - var item = new CatalogItem() - { - Id = int.Parse(itemToModify.ProductId), - Price = newPrice - }; - return JsonConvert.SerializeObject(item); + var catalogProduct = catalogProducts.Data.Single(pr => pr.Id == int.Parse(itemToModify.ProductId)); + catalogProduct.Price = newPrice; + return JsonConvert.SerializeObject(catalogProduct); } private CustomerBasket ComposeBasket(string customerId, IEnumerable items) From d8d1e5a8b29d39a27ee1e954662a4fa919ef2d28 Mon Sep 17 00:00:00 2001 From: dsanz Date: Mon, 27 Mar 2017 11:55:54 +0200 Subject: [PATCH 17/25] Remove unused code --- src/Services/Catalog/Catalog.API/Startup.cs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/Services/Catalog/Catalog.API/Startup.cs b/src/Services/Catalog/Catalog.API/Startup.cs index 593d5f577..7ebe47b44 100644 --- a/src/Services/Catalog/Catalog.API/Startup.cs +++ b/src/Services/Catalog/Catalog.API/Startup.cs @@ -41,9 +41,6 @@ public void ConfigureServices(IServiceCollection services) { - //Using the same SqlConnection for both DbContexts (CatalogContext and IntegrationEventLogContext) - //var sqlConnection = new SqlConnection(Configuration["ConnectionString"]); - services.AddDbContext(options => { options.UseSqlServer(Configuration["ConnectionString"], @@ -59,19 +56,6 @@ //Check Client vs. Server evaluation: https://docs.microsoft.com/en-us/ef/core/querying/client-eval }); - //services.AddDbContext(options => - //{ - // options.UseSqlServer(sqlConnection, - // sqlServerOptionsAction: sqlOptions => - // { - // sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); - // //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency - // sqlOptions.EnableRetryOnFailure(maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); - // }); - - // options.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); - //}); - services.Configure(Configuration); // Add framework services. From 3a7a14bdb718a42f2136b9b48063e0f28a6d7ece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ram=C3=B3n=20Tom=C3=A1s?= Date: Mon, 27 Mar 2017 14:05:28 +0200 Subject: [PATCH 18/25] Created global filters for web apis Fix bug BadRequest response after creating order --- .../InternalServerErrorObjectResult.cs | 18 +++++ .../Exceptions/BasketDomainException.cs | 24 +++++++ .../Filters/HttpGlobalExceptionFilter.cs | 67 +++++++++++++++++++ src/Services/Basket/Basket.API/Startup.cs | 7 +- .../InternalServerErrorObjectResult.cs | 18 +++++ .../Exceptions/CatalogDomainException.cs | 24 +++++++ .../Filters/HttpGlobalExceptionFilter.cs | 63 +++++++++++++++++ src/Services/Catalog/Catalog.API/Startup.cs | 16 ++--- .../Commands/CreateOrderCommandHandler.cs | 5 +- .../Commands/IdentifierCommandHandler.cs | 4 +- .../Filters/HttpGlobalExceptionFilter.cs | 9 ++- src/Services/Ordering/Ordering.API/Startup.cs | 9 +-- .../BuyerAggregate/PaymentMethod.cs | 9 +-- .../OrderAggregate/OrderItem.cs | 9 +-- .../OrderAggregate/OrderStatus.cs | 5 +- .../Exceptions/OrderingDomainException.cs | 23 +++++++ .../Ordering.Domain/SeedWork/IUnitOfWork.cs | 2 +- .../OrderingContext.cs | 4 +- .../Repositories/RequestManager.cs | 3 +- src/Web/WebMVC/Services/BasketService.cs | 2 + src/Web/WebMVC/Services/OrderingService.cs | 2 + .../Utilities/HttpApiClientWrapper.cs | 20 ++++-- 22 files changed, 297 insertions(+), 46 deletions(-) create mode 100644 src/Services/Basket/Basket.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs create mode 100644 src/Services/Basket/Basket.API/Infrastructure/Exceptions/BasketDomainException.cs create mode 100644 src/Services/Basket/Basket.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs create mode 100644 src/Services/Catalog/Catalog.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs create mode 100644 src/Services/Catalog/Catalog.API/Infrastructure/Exceptions/CatalogDomainException.cs create mode 100644 src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs create mode 100644 src/Services/Ordering/Ordering.Domain/Exceptions/OrderingDomainException.cs diff --git a/src/Services/Basket/Basket.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs b/src/Services/Basket/Basket.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs new file mode 100644 index 000000000..2ec3727a6 --- /dev/null +++ b/src/Services/Basket/Basket.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Basket.API.Infrastructure.ActionResults +{ + public class InternalServerErrorObjectResult : ObjectResult + { + public InternalServerErrorObjectResult(object error) + : base(error) + { + StatusCode = StatusCodes.Status500InternalServerError; + } + } +} diff --git a/src/Services/Basket/Basket.API/Infrastructure/Exceptions/BasketDomainException.cs b/src/Services/Basket/Basket.API/Infrastructure/Exceptions/BasketDomainException.cs new file mode 100644 index 000000000..199409ecc --- /dev/null +++ b/src/Services/Basket/Basket.API/Infrastructure/Exceptions/BasketDomainException.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Basket.API.Infrastructure.Exceptions +{ + /// + /// Exception type for app exceptions + /// + public class BasketDomainException : Exception + { + public BasketDomainException() + { } + + public BasketDomainException(string message) + : base(message) + { } + + public BasketDomainException(string message, Exception innerException) + : base(message, innerException) + { } + } +} diff --git a/src/Services/Basket/Basket.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs b/src/Services/Basket/Basket.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs new file mode 100644 index 000000000..5acd0bbdc --- /dev/null +++ b/src/Services/Basket/Basket.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs @@ -0,0 +1,67 @@ +using Basket.API.Infrastructure.ActionResults; +using Basket.API.Infrastructure.Exceptions; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading.Tasks; + +namespace Basket.API.Infrastructure.Filters +{ + public class HttpGlobalExceptionFilter : IExceptionFilter + { + private readonly IHostingEnvironment env; + private readonly ILogger logger; + + public HttpGlobalExceptionFilter(IHostingEnvironment env, ILogger logger) + { + this.env = env; + this.logger = logger; + } + + public void OnException(ExceptionContext context) + { + logger.LogError(new EventId(context.Exception.HResult), + context.Exception, + context.Exception.Message); + + if (context.Exception.GetType() == typeof(BasketDomainException)) + { + var json = new JsonErrorResponse + { + Messages = new[] { context.Exception.Message } + }; + + context.Result = new BadRequestObjectResult(json); + context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; + } + else + { + var json = new JsonErrorResponse + { + Messages = new[] { "An error ocurr.Try it again." } + }; + + if (env.IsDevelopment()) + { + json.DeveloperMeesage = context.Exception; + } + + context.Result = new InternalServerErrorObjectResult(json); + context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + } + context.ExceptionHandled = true; + } + + private class JsonErrorResponse + { + public string[] Messages { get; set; } + + public object DeveloperMeesage { get; set; } + } + } +} diff --git a/src/Services/Basket/Basket.API/Startup.cs b/src/Services/Basket/Basket.API/Startup.cs index db72792bd..39c5755c8 100644 --- a/src/Services/Basket/Basket.API/Startup.cs +++ b/src/Services/Basket/Basket.API/Startup.cs @@ -14,6 +14,7 @@ using Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Events; using Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.EventHandling; using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; using System; +using Basket.API.Infrastructure.Filters; namespace Microsoft.eShopOnContainers.Services.Basket.API { @@ -35,7 +36,11 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API public void ConfigureServices(IServiceCollection services) { // Add framework services. - services.AddMvc(); + services.AddMvc(options => + { + options.Filters.Add(typeof(HttpGlobalExceptionFilter)); + }).AddControllersAsServices(); + services.Configure(Configuration); //By connecting here we are making sure that our service diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs b/src/Services/Catalog/Catalog.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs new file mode 100644 index 000000000..3ac3d0f78 --- /dev/null +++ b/src/Services/Catalog/Catalog.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Catalog.API.Infrastructure.ActionResults +{ + public class InternalServerErrorObjectResult : ObjectResult + { + public InternalServerErrorObjectResult(object error) + : base(error) + { + StatusCode = StatusCodes.Status500InternalServerError; + } + } +} diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/Exceptions/CatalogDomainException.cs b/src/Services/Catalog/Catalog.API/Infrastructure/Exceptions/CatalogDomainException.cs new file mode 100644 index 000000000..0b27131cf --- /dev/null +++ b/src/Services/Catalog/Catalog.API/Infrastructure/Exceptions/CatalogDomainException.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Catalog.API.Infrastructure.Exceptions +{ + /// + /// Exception type for app exceptions + /// + public class CatalogDomainException : Exception + { + public CatalogDomainException() + { } + + public CatalogDomainException(string message) + : base(message) + { } + + public CatalogDomainException(string message, Exception innerException) + : base(message, innerException) + { } + } +} diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs b/src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs new file mode 100644 index 000000000..e618d3eed --- /dev/null +++ b/src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs @@ -0,0 +1,63 @@ +using Catalog.API.Infrastructure.ActionResults; +using Catalog.API.Infrastructure.Exceptions; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Logging; +using System.Net; + +namespace Catalog.API.Infrastructure.Filters +{ + public class HttpGlobalExceptionFilter : IExceptionFilter + { + private readonly IHostingEnvironment env; + private readonly ILogger logger; + + public HttpGlobalExceptionFilter(IHostingEnvironment env, ILogger logger) + { + this.env = env; + this.logger = logger; + } + + public void OnException(ExceptionContext context) + { + logger.LogError(new EventId(context.Exception.HResult), + context.Exception, + context.Exception.Message); + + if (context.Exception.GetType() == typeof(CatalogDomainException)) + { + var json = new JsonErrorResponse + { + Messages = new[] { context.Exception.Message } + }; + + context.Result = new BadRequestObjectResult(json); + context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; + } + else + { + var json = new JsonErrorResponse + { + Messages = new[] { "An error ocurr.Try it again." } + }; + + if (env.IsDevelopment()) + { + json.DeveloperMeesage = context.Exception; + } + + context.Result = new InternalServerErrorObjectResult(json); + context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + } + context.ExceptionHandled = true; + } + + private class JsonErrorResponse + { + public string[] Messages { get; set; } + + public object DeveloperMeesage { get; set; } + } + } +} diff --git a/src/Services/Catalog/Catalog.API/Startup.cs b/src/Services/Catalog/Catalog.API/Startup.cs index 1598eb5a0..2a7e84b2f 100644 --- a/src/Services/Catalog/Catalog.API/Startup.cs +++ b/src/Services/Catalog/Catalog.API/Startup.cs @@ -1,5 +1,6 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API { + using global::Catalog.API.Infrastructure.Filters; using global::Catalog.API.IntegrationEvents; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -41,6 +42,12 @@ { var sqlConnection = new SqlConnection(Configuration["ConnectionString"]); + // Add framework services. + services.AddMvc(options => + { + options.Filters.Add(typeof(HttpGlobalExceptionFilter)); + }).AddControllersAsServices(); + services.AddDbContext(c => { c.UseSqlServer(sqlConnection); @@ -85,20 +92,13 @@ var serviceProvider = services.BuildServiceProvider(); var configuration = serviceProvider.GetRequiredService>().Value; - services.AddSingleton(new EventBusRabbitMQ(configuration.EventBusConnection)); - - services.AddMvc(); + services.AddSingleton(new EventBusRabbitMQ(configuration.EventBusConnection)); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IntegrationEventLogContext integrationEventLogContext) { //Configure logs - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs index 6e8ebb381..2132d2983 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs @@ -51,11 +51,8 @@ _orderRepository.Add(order); - var result = await _orderRepository.UnitOfWork + return await _orderRepository.UnitOfWork .SaveEntitiesAsync(); - - return result > 0; - } } } diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/IdentifierCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/IdentifierCommandHandler.cs index 5fdc0181b..60a48ae89 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/IdentifierCommandHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/IdentifierCommandHandler.cs @@ -48,9 +48,9 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands return CreateResultForDuplicateRequest(); } else - { - await _requestManager.CreateRequestForCommandAsync(message.Id); + { var result = await _mediator.SendAsync(message.Command); + await _requestManager.CreateRequestForCommandAsync(message.Id); return result; } } diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs b/src/Services/Ordering/Ordering.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs index 3d181cd0c..25cf46830 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs @@ -1,11 +1,12 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Filters { using AspNetCore.Mvc; + using global::Ordering.Domain.Exceptions; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.ActionResults; using Microsoft.Extensions.Logging; - using System; + using System.Net; public class HttpGlobalExceptionFilter : IExceptionFilter { @@ -24,7 +25,7 @@ context.Exception, context.Exception.Message); - if (context.Exception.GetType() == typeof(ArgumentException)) //TODO:Select a common exception for application like EshopException + if (context.Exception.GetType() == typeof(OrderingDomainException)) { var json = new JsonErrorResponse { @@ -32,6 +33,7 @@ }; context.Result = new BadRequestObjectResult(json); + context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; } else { @@ -46,8 +48,9 @@ } context.Result = new InternalServerErrorObjectResult(json); + context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; } - + context.ExceptionHandled = true; } private class JsonErrorResponse diff --git a/src/Services/Ordering/Ordering.API/Startup.cs b/src/Services/Ordering/Ordering.API/Startup.cs index 3f0840b07..1c419fc0f 100644 --- a/src/Services/Ordering/Ordering.API/Startup.cs +++ b/src/Services/Ordering/Ordering.API/Startup.cs @@ -45,7 +45,7 @@ // Add framework services. services.AddMvc(options => { - options.Filters.Add(typeof(HttpGlobalExceptionFilter)); + options.Filters.Add(typeof(HttpGlobalExceptionFilter)); }).AddControllersAsServices(); //Injecting Controllers themselves thru DI //For further info see: http://docs.autofac.org/en/latest/integration/aspnetcore.html#controllers-as-services @@ -102,12 +102,7 @@ { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); - - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - + app.UseCors("CorsPolicy"); app.UseFailingMiddleware(); diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/PaymentMethod.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/PaymentMethod.cs index 0b1e2ceb0..2920ee2d9 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/PaymentMethod.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/PaymentMethod.cs @@ -1,4 +1,5 @@ using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; +using Ordering.Domain.Exceptions; using System; namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate @@ -22,13 +23,13 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.B public PaymentMethod(int cardTypeId, string alias, string cardNumber, string securityNumber, string cardHolderName, DateTime expiration) { - _cardNumber = !string.IsNullOrWhiteSpace(cardNumber) ? cardNumber : throw new ArgumentException(nameof(cardNumber)); - _securityNumber = !string.IsNullOrWhiteSpace(securityNumber) ? securityNumber : throw new ArgumentException(nameof(securityNumber)); - _cardHolderName = !string.IsNullOrWhiteSpace(cardHolderName) ? cardHolderName : throw new ArgumentException(nameof(cardHolderName)); + _cardNumber = !string.IsNullOrWhiteSpace(cardNumber) ? cardNumber : throw new OrderingDomainException(nameof(cardNumber)); + _securityNumber = !string.IsNullOrWhiteSpace(securityNumber) ? securityNumber : throw new OrderingDomainException(nameof(securityNumber)); + _cardHolderName = !string.IsNullOrWhiteSpace(cardHolderName) ? cardHolderName : throw new OrderingDomainException(nameof(cardHolderName)); if (expiration < DateTime.UtcNow) { - throw new ArgumentException(nameof(expiration)); + throw new OrderingDomainException(nameof(expiration)); } _alias = alias; diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderItem.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderItem.cs index 22c7cc93c..9376da971 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderItem.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderItem.cs @@ -1,4 +1,5 @@ using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; +using Ordering.Domain.Exceptions; using System; namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate @@ -24,12 +25,12 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O { if (units <= 0) { - throw new ArgumentNullException("Invalid number of units"); + throw new OrderingDomainException("Invalid number of units"); } if ((unitPrice * units) < discount) { - throw new ArgumentException("The total of order item is lower than applied discount"); + throw new OrderingDomainException("The total of order item is lower than applied discount"); } ProductId = productId; @@ -58,7 +59,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O { if (discount < 0) { - throw new ArgumentException("Discount is not valid"); + throw new OrderingDomainException("Discount is not valid"); } _discount = discount; @@ -68,7 +69,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O { if (units < 0) { - throw new ArgumentException("Invalid units"); + throw new OrderingDomainException("Invalid units"); } _units += units; diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderStatus.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderStatus.cs index cb4d863f9..6e3ff74b1 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderStatus.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderStatus.cs @@ -1,5 +1,6 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate { + using global::Ordering.Domain.Exceptions; using Seedwork; using SeedWork; using System; @@ -34,7 +35,7 @@ if (state == null) { - throw new ArgumentException($"Possible values for OrderStatus: {String.Join(",", List().Select(s => s.Name))}"); + throw new OrderingDomainException($"Possible values for OrderStatus: {String.Join(",", List().Select(s => s.Name))}"); } return state; @@ -46,7 +47,7 @@ if (state == null) { - throw new ArgumentException($"Possible values for OrderStatus: {String.Join(",", List().Select(s => s.Name))}"); + throw new OrderingDomainException($"Possible values for OrderStatus: {String.Join(",", List().Select(s => s.Name))}"); } return state; diff --git a/src/Services/Ordering/Ordering.Domain/Exceptions/OrderingDomainException.cs b/src/Services/Ordering/Ordering.Domain/Exceptions/OrderingDomainException.cs new file mode 100644 index 000000000..7a7320dbf --- /dev/null +++ b/src/Services/Ordering/Ordering.Domain/Exceptions/OrderingDomainException.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Ordering.Domain.Exceptions +{ + /// + /// Exception type for domain exceptions + /// + public class OrderingDomainException : Exception + { + public OrderingDomainException() + { } + + public OrderingDomainException(string message) + : base(message) + { } + + public OrderingDomainException(string message, Exception innerException) + : base(message, innerException) + { } + } +} diff --git a/src/Services/Ordering/Ordering.Domain/SeedWork/IUnitOfWork.cs b/src/Services/Ordering/Ordering.Domain/SeedWork/IUnitOfWork.cs index 581f905a6..810f0b2af 100644 --- a/src/Services/Ordering/Ordering.Domain/SeedWork/IUnitOfWork.cs +++ b/src/Services/Ordering/Ordering.Domain/SeedWork/IUnitOfWork.cs @@ -7,6 +7,6 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork public interface IUnitOfWork : IDisposable { Task SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken)); - Task SaveEntitiesAsync(CancellationToken cancellationToken = default(CancellationToken)); + Task SaveEntitiesAsync(CancellationToken cancellationToken = default(CancellationToken)); } } diff --git a/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs b/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs index b74c7631d..f9f68cfb3 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs @@ -235,7 +235,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure .IsRequired(); } - public async Task SaveEntitiesAsync(CancellationToken cancellationToken = default(CancellationToken)) + public async Task SaveEntitiesAsync(CancellationToken cancellationToken = default(CancellationToken)) { // Dispatch Domain Events collection. // Choices: @@ -249,7 +249,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure // After executing this line all the changes performed thought the DbContext will be commited var result = await base.SaveChangesAsync(); - return result; + return true; } } } diff --git a/src/Services/Ordering/Ordering.Infrastructure/Repositories/RequestManager.cs b/src/Services/Ordering/Ordering.Infrastructure/Repositories/RequestManager.cs index 1661ab6e5..a6d795214 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/Repositories/RequestManager.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/Repositories/RequestManager.cs @@ -1,4 +1,5 @@ using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; +using Ordering.Domain.Exceptions; using System; using System.Collections.Generic; using System.Text; @@ -26,7 +27,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositor var exists = await ExistAsync(id); var request = exists ? - throw new Exception($"Request with {id} already exists") : + throw new OrderingDomainException($"Request with {id} already exists") : new ClientRequest() { Id = id, diff --git a/src/Web/WebMVC/Services/BasketService.cs b/src/Web/WebMVC/Services/BasketService.cs index 0e6b13ab5..d9fc23854 100644 --- a/src/Web/WebMVC/Services/BasketService.cs +++ b/src/Web/WebMVC/Services/BasketService.cs @@ -56,6 +56,8 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services var response = await _apiClient.PostAsync(basketUrl, basket); + response.EnsureSuccessStatusCode(); + return basket; } diff --git a/src/Web/WebMVC/Services/OrderingService.cs b/src/Web/WebMVC/Services/OrderingService.cs index d53db3090..06c388a3b 100644 --- a/src/Web/WebMVC/Services/OrderingService.cs +++ b/src/Web/WebMVC/Services/OrderingService.cs @@ -88,6 +88,8 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError) throw new Exception("Error creating order, try later"); + + response.EnsureSuccessStatusCode(); } public void OverrideUserInfoIntoOrder(Order original, Order destination) diff --git a/src/Web/WebMVC/Services/Utilities/HttpApiClientWrapper.cs b/src/Web/WebMVC/Services/Utilities/HttpApiClientWrapper.cs index 06172eb53..6904e2d7a 100644 --- a/src/Web/WebMVC/Services/Utilities/HttpApiClientWrapper.cs +++ b/src/Web/WebMVC/Services/Utilities/HttpApiClientWrapper.cs @@ -1,8 +1,10 @@ -using Microsoft.Extensions.Logging; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Polly; using Polly.Wrap; using System; +using System.Net; using System.Net.Http; using System.Threading.Tasks; @@ -76,13 +78,17 @@ namespace WebMVC.Services.Utilities // a new StringContent must be created for each retry // as it is disposed after each call HttpInvoker(() => + { + var response = _client.PostAsync(uri, new StringContent(JsonConvert.SerializeObject(item), System.Text.Encoding.UTF8, "application/json")); + // raise exception if HttpResponseCode 500 + // needed for circuit breaker to track fails + if (response.Result.StatusCode == HttpStatusCode.InternalServerError) { - var response = _client.PostAsync(uri, new StringContent(JsonConvert.SerializeObject(item), System.Text.Encoding.UTF8, "application/json")); - // raise exception if not success response - // needed for circuit breaker to track fails - response.Result.EnsureSuccessStatusCode(); - return response; - }); + throw new HttpRequestException(); + } + + return response; + }); public Task DeleteAsync(string uri) => HttpInvoker(() => _client.DeleteAsync(uri)); From 2119ae19bd192012dda80ccd9ce4229deda3227e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ram=C3=B3n=20Tom=C3=A1s?= Date: Mon, 27 Mar 2017 15:24:29 +0200 Subject: [PATCH 19/25] Fix startup after merge --- src/Services/Catalog/Catalog.API/Startup.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Services/Catalog/Catalog.API/Startup.cs b/src/Services/Catalog/Catalog.API/Startup.cs index 92e7a658c..bfdba9d61 100644 --- a/src/Services/Catalog/Catalog.API/Startup.cs +++ b/src/Services/Catalog/Catalog.API/Startup.cs @@ -1,7 +1,6 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API { using global::Catalog.API.Infrastructure.Filters; - using global::Catalog.API.IntegrationEvents; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; @@ -17,7 +16,6 @@ using Microsoft.Extensions.Options; using System; using System.Data.Common; - using System.Data.SqlClient; using System.Reflection; public class Startup @@ -43,14 +41,13 @@ public void ConfigureServices(IServiceCollection services) { - // Add framework services. services.AddMvc(options => { options.Filters.Add(typeof(HttpGlobalExceptionFilter)); }).AddControllersAsServices(); - services.AddDbContext(c => + services.AddDbContext(options => { options.UseSqlServer(Configuration["ConnectionString"], sqlServerOptionsAction: sqlOptions => From 782a2e40d5f3a8b56a32a275518948f4f10c36dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ram=C3=B3n=20Tom=C3=A1s?= Date: Mon, 27 Mar 2017 17:21:50 +0200 Subject: [PATCH 20/25] Fix issue no cardtpyeid submitted in SPA Fix issue multiple order creation. Now the create order button is disabled until the request is processed --- .../orders/orders-new/orders-new.component.html | 2 +- .../orders/orders-new/orders-new.component.ts | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.html b/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.html index 6fc2274b8..8a5021243 100644 --- a/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.html +++ b/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.html @@ -105,7 +105,7 @@
- +
diff --git a/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.ts b/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.ts index aadcabc1c..321c72a9c 100644 --- a/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.ts +++ b/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.ts @@ -1,4 +1,5 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; import { OrdersService } from '../orders.service'; import { IOrder } from '../../shared/models/order.model'; import { BasketWrapperService } from '../../shared/services/basket.wrapper.service'; @@ -13,6 +14,7 @@ import { Router } from '@angular/router'; }) export class OrdersNewComponent implements OnInit { private newOrderForm: FormGroup; // new order form + private isOrderProcessing: Boolean; private order: IOrder; constructor(private service: OrdersService, fb: FormBuilder, private router: Router, private basketEvents: BasketWrapperService) { @@ -39,16 +41,24 @@ export class OrdersNewComponent implements OnInit { this.order.state = this.newOrderForm.controls['state'].value; this.order.country = this.newOrderForm.controls['country'].value; this.order.cardnumber = this.newOrderForm.controls['cardnumber'].value; + this.order.cardtypeid = 1; this.order.cardholdername = this.newOrderForm.controls['cardholdername'].value; this.order.cardexpiration = new Date(20 + this.newOrderForm.controls['expirationdate'].value.split('/')[1], this.newOrderForm.controls['expirationdate'].value.split('/')[0]); this.order.cardsecuritynumber = this.newOrderForm.controls['securitycode'].value; - this.service.postOrder(this.order).subscribe(res => { + this.service.postOrder(this.order) + .catch((errMessage) => { + this.isOrderProcessing = false; + return Observable.throw(errMessage); + }) + .subscribe(res => { // this will emit an observable. Basket service is subscribed to this observable, and will react deleting the basket for the current user. this.basketEvents.orderCreated(); - + this.router.navigate(['orders']); }); + + this.isOrderProcessing = true; } } From 53bdf6de045e4fb63612bcd0ff423483a1613d51 Mon Sep 17 00:00:00 2001 From: dsanz Date: Tue, 28 Mar 2017 11:03:59 +0200 Subject: [PATCH 21/25] Add error message in WebSPA when create order returns exception --- .../modules/orders/orders-new/orders-new.component.html | 3 +++ .../modules/orders/orders-new/orders-new.component.scss | 4 ++++ .../modules/orders/orders-new/orders-new.component.ts | 8 +++++--- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.html b/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.html index 8a5021243..cfac7d281 100644 --- a/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.html +++ b/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.html @@ -5,6 +5,9 @@
+

Shipping Address

diff --git a/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.scss b/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.scss index 0d06d0920..bae6fb6ee 100644 --- a/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.scss +++ b/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.scss @@ -94,4 +94,8 @@ &-image { height: $item-height; } + + &-alert { + margin-top: 10px; + } } diff --git a/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.ts b/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.ts index 321c72a9c..72114631f 100644 --- a/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.ts +++ b/src/Web/WebSPA/Client/modules/orders/orders-new/orders-new.component.ts @@ -15,10 +15,11 @@ import { Router } from '@angular/router'; export class OrdersNewComponent implements OnInit { private newOrderForm: FormGroup; // new order form private isOrderProcessing: Boolean; + private errorReceived: Boolean; private order: IOrder; constructor(private service: OrdersService, fb: FormBuilder, private router: Router, private basketEvents: BasketWrapperService) { - // Obtener información del perfil de usuario. + // Obtain user profile information this.order = service.mapBasketAndIdentityInfoNewOrder(); this.newOrderForm = fb.group({ 'street': [this.order.street, Validators.required], @@ -48,6 +49,7 @@ export class OrdersNewComponent implements OnInit { this.service.postOrder(this.order) .catch((errMessage) => { + this.errorReceived = true; this.isOrderProcessing = false; return Observable.throw(errMessage); }) @@ -56,8 +58,8 @@ export class OrdersNewComponent implements OnInit { this.basketEvents.orderCreated(); this.router.navigate(['orders']); - }); - + }); + this.errorReceived = false; this.isOrderProcessing = true; } } From cea960022780528b4069d31f7511d5a587dea8ee Mon Sep 17 00:00:00 2001 From: dsanz Date: Tue, 28 Mar 2017 12:02:30 +0200 Subject: [PATCH 22/25] Refactoring of idempotent elements and logic. --- .../EventBus/EventBus/Events/IntegrationEvent.cs | 2 ++ .../IntegrationEventLogEF/IntegrationEventLogEntry.cs | 2 +- .../ProductPriceChangedIntegrationEventHandler.cs | 8 ++++---- .../Application/Commands/CreateOrderCommandHandler.cs | 2 +- .../Application/Commands/IdentifierCommandHandler.cs | 5 +---- .../Infrastructure/AutofacModules/ApplicationModule.cs | 1 + .../{Repositories => Idempotency}/IRequestManager.cs | 2 +- .../{Repositories => Idempotency}/RequestManager.cs | 2 +- .../Ordering/Application/IdentifierCommandHandlerTest.cs | 2 +- .../UnitTest/Ordering/Domain/BuyerAggregateTest.cs | 3 ++- .../UnitTest/Ordering/Domain/OrderAggregateTest.cs | 9 +++++---- 11 files changed, 20 insertions(+), 18 deletions(-) rename src/Services/Ordering/Ordering.Infrastructure/{Repositories => Idempotency}/IRequestManager.cs (93%) rename src/Services/Ordering/Ordering.Infrastructure/{Repositories => Idempotency}/RequestManager.cs (98%) diff --git a/src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEvent.cs b/src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEvent.cs index e1802704e..c9e60a0cf 100644 --- a/src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEvent.cs +++ b/src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEvent.cs @@ -9,8 +9,10 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events public IntegrationEvent() { Id = Guid.NewGuid(); + CreationDate = DateTime.UtcNow; } public Guid Id { get; } + public DateTime CreationDate { get; } } } diff --git a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs index 0b68e56a0..3cab9e500 100644 --- a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs @@ -12,7 +12,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF public IntegrationEventLogEntry(IntegrationEvent @event) { EventId = @event.Id; - CreationTime = DateTime.UtcNow; + CreationTime = @event.CreationDate; EventTypeName = @event.GetType().FullName; Content = JsonConvert.SerializeObject(@event); State = EventStateEnum.NotPublished; diff --git a/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs b/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs index 28c9a9afa..08aa9704b 100644 --- a/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs +++ b/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs @@ -20,18 +20,18 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Even foreach (var id in userIds) { var basket = await _repository.GetBasket(id); - await UpdatePriceInBasketItems(@event.ProductId, @event.NewPrice, basket); + await UpdatePriceInBasketItems(@event.ProductId, @event.NewPrice, @event.OldPrice, basket); } } - private async Task UpdatePriceInBasketItems(int productId, decimal newPrice, CustomerBasket basket) + private async Task UpdatePriceInBasketItems(int productId, decimal newPrice, decimal oldPrice, CustomerBasket basket) { var itemsToUpdate = basket?.Items?.Where(x => int.Parse(x.ProductId) == productId).ToList(); if (itemsToUpdate != null) { foreach (var item in itemsToUpdate) { - if(item.UnitPrice != newPrice) + if(item.UnitPrice == oldPrice) { var originalPrice = item.UnitPrice; item.UnitPrice = newPrice; @@ -39,7 +39,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Even } } await _repository.UpdateBasket(basket); - } + } } } } diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs index 2132d2983..eee53a670 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs @@ -3,7 +3,7 @@ using Domain.AggregatesModel.OrderAggregate; using MediatR; using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; - using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositories; + using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; using System; using System.Threading.Tasks; diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/IdentifierCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/IdentifierCommandHandler.cs index 60a48ae89..de7dc4fea 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/IdentifierCommandHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/IdentifierCommandHandler.cs @@ -1,8 +1,5 @@ using MediatR; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositories; -using System; -using System.Collections.Generic; -using System.Linq; +using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/ApplicationModule.cs b/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/ApplicationModule.cs index 0d8e34476..6e87f385f 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/ApplicationModule.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/ApplicationModule.cs @@ -2,6 +2,7 @@ using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; +using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositories; namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.AutofacModules diff --git a/src/Services/Ordering/Ordering.Infrastructure/Repositories/IRequestManager.cs b/src/Services/Ordering/Ordering.Infrastructure/Idempotency/IRequestManager.cs similarity index 93% rename from src/Services/Ordering/Ordering.Infrastructure/Repositories/IRequestManager.cs rename to src/Services/Ordering/Ordering.Infrastructure/Idempotency/IRequestManager.cs index ecb144695..a8a02f8ca 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/Repositories/IRequestManager.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/Idempotency/IRequestManager.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Text; using System.Threading.Tasks; -namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositories +namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency { public interface IRequestManager { diff --git a/src/Services/Ordering/Ordering.Infrastructure/Repositories/RequestManager.cs b/src/Services/Ordering/Ordering.Infrastructure/Idempotency/RequestManager.cs similarity index 98% rename from src/Services/Ordering/Ordering.Infrastructure/Repositories/RequestManager.cs rename to src/Services/Ordering/Ordering.Infrastructure/Idempotency/RequestManager.cs index a6d795214..0ef005161 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/Repositories/RequestManager.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/Idempotency/RequestManager.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Text; using System.Threading.Tasks; -namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositories +namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency { public class RequestManager : IRequestManager { diff --git a/test/Services/UnitTest/Ordering/Application/IdentifierCommandHandlerTest.cs b/test/Services/UnitTest/Ordering/Application/IdentifierCommandHandlerTest.cs index 63e2e01e1..66070c497 100644 --- a/test/Services/UnitTest/Ordering/Application/IdentifierCommandHandlerTest.cs +++ b/test/Services/UnitTest/Ordering/Application/IdentifierCommandHandlerTest.cs @@ -6,7 +6,7 @@ namespace UnitTest.Ordering.Application { using MediatR; using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; - using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositories; + using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; using Moq; using System.Collections; using System.Collections.Generic; diff --git a/test/Services/UnitTest/Ordering/Domain/BuyerAggregateTest.cs b/test/Services/UnitTest/Ordering/Domain/BuyerAggregateTest.cs index 32e3bb807..76fd4e9a5 100644 --- a/test/Services/UnitTest/Ordering/Domain/BuyerAggregateTest.cs +++ b/test/Services/UnitTest/Ordering/Domain/BuyerAggregateTest.cs @@ -1,4 +1,5 @@ using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; +using Ordering.Domain.Exceptions; using System; using Xunit; @@ -82,7 +83,7 @@ public class BuyerAggregateTest var expiration = DateTime.Now.AddYears(-1); //Act - Assert - Assert.Throws(() => new PaymentMethod(cardTypeId, alias, cardNumber, securityNumber, cardHolderName, expiration)); + Assert.Throws(() => new PaymentMethod(cardTypeId, alias, cardNumber, securityNumber, cardHolderName, expiration)); } [Fact] diff --git a/test/Services/UnitTest/Ordering/Domain/OrderAggregateTest.cs b/test/Services/UnitTest/Ordering/Domain/OrderAggregateTest.cs index 61fac6cf1..40bc66431 100644 --- a/test/Services/UnitTest/Ordering/Domain/OrderAggregateTest.cs +++ b/test/Services/UnitTest/Ordering/Domain/OrderAggregateTest.cs @@ -1,5 +1,6 @@ using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; using Ordering.Domain.Events; +using Ordering.Domain.Exceptions; using System; using Xunit; @@ -38,7 +39,7 @@ public class OrderAggregateTest var units = -1; //Act - Assert - Assert.Throws(() => new OrderItem(productId, productName, unitPrice, discount, pictureUrl, units)); + Assert.Throws(() => new OrderItem(productId, productName, unitPrice, discount, pictureUrl, units)); } [Fact] @@ -53,7 +54,7 @@ public class OrderAggregateTest var units = 1; //Act - Assert - Assert.Throws(() => new OrderItem(productId, productName, unitPrice, discount, pictureUrl, units)); + Assert.Throws(() => new OrderItem(productId, productName, unitPrice, discount, pictureUrl, units)); } [Fact] @@ -71,7 +72,7 @@ public class OrderAggregateTest var fakeOrderItem = new OrderItem(productId, productName, unitPrice, discount, pictureUrl, units); //Assert - Assert.Throws(() => fakeOrderItem.SetNewDiscount(-1)); + Assert.Throws(() => fakeOrderItem.SetNewDiscount(-1)); } [Fact] @@ -89,7 +90,7 @@ public class OrderAggregateTest var fakeOrderItem = new OrderItem(productId, productName, unitPrice, discount, pictureUrl, units); //Assert - Assert.Throws(() => fakeOrderItem.AddUnits(-1)); + Assert.Throws(() => fakeOrderItem.AddUnits(-1)); } [Fact] From 5fed56db51930225a7a4ae2d5e99dc289485ad1d Mon Sep 17 00:00:00 2001 From: dsanz Date: Tue, 28 Mar 2017 13:50:16 +0200 Subject: [PATCH 23/25] Domain validation errors must throw domain exceptions. --- .../Application/Decorators/ValidatorDecorator.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Services/Ordering/Ordering.API/Application/Decorators/ValidatorDecorator.cs b/src/Services/Ordering/Ordering.API/Application/Decorators/ValidatorDecorator.cs index 5bdff8330..20318adf8 100644 --- a/src/Services/Ordering/Ordering.API/Application/Decorators/ValidatorDecorator.cs +++ b/src/Services/Ordering/Ordering.API/Application/Decorators/ValidatorDecorator.cs @@ -1,5 +1,6 @@ using FluentValidation; using MediatR; +using Ordering.Domain.Exceptions; using System; using System.Collections.Generic; using System.Linq; @@ -33,8 +34,8 @@ namespace Ordering.API.Application.Decorators if (failures.Any()) { - throw new ValidationException( - $"Command Validation Errors for type {typeof(TRequest).Name}", failures); + throw new OrderingDomainException( + $"Command Validation Errors for type {typeof(TRequest).Name}", new ValidationException("Validation exception", failures)); } var response = await _inner.Handle(message); From e596926b9b91b8d7deae156e96e8e83f642b3321 Mon Sep 17 00:00:00 2001 From: dsanz Date: Tue, 28 Mar 2017 13:55:27 +0200 Subject: [PATCH 24/25] add comment about known bug in .net core 1.1 --- .../Infrastructure/Filters/HttpGlobalExceptionFilter.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs b/src/Services/Ordering/Ordering.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs index 25cf46830..7d19815b9 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs @@ -32,6 +32,8 @@ Messages = new[] { context.Exception.Message } }; + // Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1 + //It will be fixed in .net core 1.1.2. See https://github.com/aspnet/Mvc/issues/5594 for more information context.Result = new BadRequestObjectResult(json); context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; } @@ -47,6 +49,8 @@ json.DeveloperMeesage = context.Exception; } + // Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1 + // It will be fixed in .net core 1.1.2. See https://github.com/aspnet/Mvc/issues/5594 for more information context.Result = new InternalServerErrorObjectResult(json); context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; } From 7254710184d33a4a7f829b89d0c5359ca51f2a07 Mon Sep 17 00:00:00 2001 From: Eduard Tomas Date: Tue, 28 Mar 2017 13:56:40 +0200 Subject: [PATCH 25/25] Updated sln with new dcproj Also added new projects and arranged build order. Solves #55 --- eShopOnContainers.sln | 232 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 222 insertions(+), 10 deletions(-) diff --git a/eShopOnContainers.sln b/eShopOnContainers.sln index 33d6e2812..c9b250135 100644 --- a/eShopOnContainers.sln +++ b/eShopOnContainers.sln @@ -1,18 +1,10 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26213.1 +VisualStudioVersion = 15.0.26228.9 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{932D8224-11F6-4D07-B109-DA28AD288A63}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3AF739CD-81D8-428D-A08A-0A58372DEBF6}" - ProjectSection(SolutionItems) = preProject - docker-compose.override.yml = docker-compose.override.yml - docker-compose.yml = docker-compose.yml - global.json = global.json - NuGet.config = NuGet.config - EndProjectSection -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Services", "Services", "{91CF7717-08AB-4E65-B10E-0B426F01E2E8}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web Apps", "Web Apps", "{E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04}" @@ -49,7 +41,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared Code", "Shared Code" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{9CC7814B-72A6-465B-A61C-57B512DEE303}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebSPA", "src\Web\WebSPA\WebSPA.csproj"", "{9842DB3A-1391-48C7-A49C-2FABD0A18AC2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebSPA", "src\Web\WebSPA\WebSPA.csproj", "{9842DB3A-1391-48C7-A49C-2FABD0A18AC2}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mobile Apps", "Mobile Apps", "{B7B1D395-4E06-4036-BE86-C216756B9367}" EndProject @@ -75,6 +67,28 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTest", "test\Services\U EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Identity.API", "src\Services\Identity\Identity.API\Identity.API.csproj", "{A579E108-5445-403D-A407-339AC4D1611B}" EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{FEA0C318-FFED-4D39-8781-265718CA43DD}" + ProjectSection(ProjectDependencies) = postProject + {A579E108-5445-403D-A407-339AC4D1611B} = {A579E108-5445-403D-A407-339AC4D1611B} + {9842DB3A-1391-48C7-A49C-2FABD0A18AC2} = {9842DB3A-1391-48C7-A49C-2FABD0A18AC2} + {F0333D8E-0B27-42B7-B2C6-78F3657624E2} = {F0333D8E-0B27-42B7-B2C6-78F3657624E2} + {42681D9D-750A-4DF7-BD9F-9292CFD5C253} = {42681D9D-750A-4DF7-BD9F-9292CFD5C253} + {2110CBB0-3B38-4EE4-A743-DF6968D80D90} = {2110CBB0-3B38-4EE4-A743-DF6968D80D90} + {231226CE-690B-4979-8870-9A79D80928E2} = {231226CE-690B-4979-8870-9A79D80928E2} + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BuildingBlocks", "BuildingBlocks", "{1EF3AC0F-F27C-46DD-AC53-D762D2C11C45}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EventBus", "EventBus", "{B473B70F-0796-4862-B1AD-BB742D93B868}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventBus", "src\BuildingBlocks\EventBus\EventBus\EventBus.csproj", "{3D6B7A87-162E-4479-B256-1291BEB503B6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventBusRabbitMQ", "src\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj", "{52AF222A-258C-4032-ACDD-857D7251BC1E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationEventLogEF", "src\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj", "{438B774F-5569-4DE2-AA62-3F8BAEB31C55}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionalTests", "test\Services\FunctionalTests\FunctionalTests.csproj", "{2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Ad-Hoc|Any CPU = Ad-Hoc|Any CPU @@ -1004,6 +1018,198 @@ Global {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|x64.Build.0 = Release|Any CPU {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|x86.ActiveCfg = Release|Any CPU {FEA0C318-FFED-4D39-8781-265718CA43DD}.Release|x86.Build.0 = Release|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|ARM.Build.0 = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|iPhone.Build.0 = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|x64.ActiveCfg = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|x64.Build.0 = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|x86.ActiveCfg = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.AppStore|x86.Build.0 = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|ARM.ActiveCfg = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|ARM.Build.0 = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|iPhone.Build.0 = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|x64.ActiveCfg = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|x64.Build.0 = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|x86.ActiveCfg = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Debug|x86.Build.0 = Debug|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|Any CPU.Build.0 = Release|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|ARM.ActiveCfg = Release|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|ARM.Build.0 = Release|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|iPhone.ActiveCfg = Release|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|iPhone.Build.0 = Release|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|x64.ActiveCfg = Release|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|x64.Build.0 = Release|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|x86.ActiveCfg = Release|Any CPU + {3D6B7A87-162E-4479-B256-1291BEB503B6}.Release|x86.Build.0 = Release|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|ARM.Build.0 = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|iPhone.Build.0 = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|x64.ActiveCfg = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|x64.Build.0 = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|x86.ActiveCfg = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.AppStore|x86.Build.0 = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|ARM.ActiveCfg = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|ARM.Build.0 = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|iPhone.Build.0 = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|x64.ActiveCfg = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|x64.Build.0 = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|x86.ActiveCfg = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Debug|x86.Build.0 = Debug|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|Any CPU.Build.0 = Release|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|ARM.ActiveCfg = Release|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|ARM.Build.0 = Release|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|iPhone.ActiveCfg = Release|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|iPhone.Build.0 = Release|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|x64.ActiveCfg = Release|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|x64.Build.0 = Release|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|x86.ActiveCfg = Release|Any CPU + {52AF222A-258C-4032-ACDD-857D7251BC1E}.Release|x86.Build.0 = Release|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|ARM.Build.0 = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|iPhone.Build.0 = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|x64.ActiveCfg = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|x64.Build.0 = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|x86.ActiveCfg = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.AppStore|x86.Build.0 = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|Any CPU.Build.0 = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|ARM.ActiveCfg = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|ARM.Build.0 = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|iPhone.Build.0 = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|x64.ActiveCfg = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|x64.Build.0 = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|x86.ActiveCfg = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Debug|x86.Build.0 = Debug|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|Any CPU.ActiveCfg = Release|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|Any CPU.Build.0 = Release|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|ARM.ActiveCfg = Release|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|ARM.Build.0 = Release|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|iPhone.ActiveCfg = Release|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|iPhone.Build.0 = Release|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|x64.ActiveCfg = Release|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|x64.Build.0 = Release|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|x86.ActiveCfg = Release|Any CPU + {438B774F-5569-4DE2-AA62-3F8BAEB31C55}.Release|x86.Build.0 = Release|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|ARM.Build.0 = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|iPhone.Build.0 = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|x64.ActiveCfg = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|x64.Build.0 = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|x86.ActiveCfg = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.AppStore|x86.Build.0 = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|ARM.ActiveCfg = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|ARM.Build.0 = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|iPhone.Build.0 = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|x64.ActiveCfg = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|x64.Build.0 = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|x86.ActiveCfg = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Debug|x86.Build.0 = Debug|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|Any CPU.Build.0 = Release|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|ARM.ActiveCfg = Release|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|ARM.Build.0 = Release|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|iPhone.ActiveCfg = Release|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|iPhone.Build.0 = Release|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|x64.ActiveCfg = Release|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|x64.Build.0 = Release|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|x86.ActiveCfg = Release|Any CPU + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1039,5 +1245,11 @@ Global {67F9D3A8-F71E-4428-913F-C37AE82CDB24} = {778289CA-31F7-4464-8C2A-612EE846F8A7} {7796F5D8-31FC-45A4-B673-19DE5BA194CF} = {EF0337F2-ED00-4643-89FD-EE10863F1870} {A579E108-5445-403D-A407-339AC4D1611B} = {02DF7FEE-C302-433D-A6CD-237A2569F236} + {1EF3AC0F-F27C-46DD-AC53-D762D2C11C45} = {932D8224-11F6-4D07-B109-DA28AD288A63} + {B473B70F-0796-4862-B1AD-BB742D93B868} = {1EF3AC0F-F27C-46DD-AC53-D762D2C11C45} + {3D6B7A87-162E-4479-B256-1291BEB503B6} = {B473B70F-0796-4862-B1AD-BB742D93B868} + {52AF222A-258C-4032-ACDD-857D7251BC1E} = {B473B70F-0796-4862-B1AD-BB742D93B868} + {438B774F-5569-4DE2-AA62-3F8BAEB31C55} = {B473B70F-0796-4862-B1AD-BB742D93B868} + {2DA840CE-FCEA-4CF7-B1A1-ADD7775E7357} = {EF0337F2-ED00-4643-89FD-EE10863F1870} EndGlobalSection EndGlobal