From 56f3a6b82e1305b5b701fc6675651dcf899253cb Mon Sep 17 00:00:00 2001 From: Erik Pique Date: Tue, 6 Aug 2019 16:10:39 +0200 Subject: [PATCH] move background task --- eShopOnContainers-ServicesAndWebApps.sln | 51 +++++++ .../Controllers/BasketController.cs | 33 ++-- .../IntegrationEventLogEntry.cs | 10 +- .../Services/IntegrationEventLogService.cs | 11 +- src/Services/Catalog/Catalog.API/Startup.cs | 2 +- .../Behaviors/TransactionBehaviour.cs | 2 + src/Services/Ordering/Ordering.API/Program.cs | 2 - src/Services/Ordering/Ordering.API/Startup.cs | 2 +- .../BackgroundTaskSettings.cs | 15 ++ .../Ordering.BackgroundTasks/Dockerfile | 18 +++ .../GracePeriodConfirmedIntegrationEvent.cs | 12 ++ .../Extensions/CustomExtensionMethods.cs | 141 ++++++++++++++++++ .../Ordering.BackgroundTasks.csproj | 33 ++++ .../Ordering.BackgroundTasks/Program.cs | 40 +++++ .../Properties/launchSettings.json | 13 ++ .../Tasks/GracePeriodManagerTask.cs | 11 +- .../appsettings.Development.json | 9 ++ .../Ordering.BackgroundTasks/appsettings.json | 26 ++++ src/Services/Webhooks/Webhooks.API/Startup.cs | 2 +- src/Web/WebSPA/Startup.cs | 1 + src/_build/dependencies.props | 2 + 21 files changed, 407 insertions(+), 29 deletions(-) create mode 100644 src/Services/Ordering/Ordering.BackgroundTasks/BackgroundTaskSettings.cs create mode 100644 src/Services/Ordering/Ordering.BackgroundTasks/Dockerfile create mode 100644 src/Services/Ordering/Ordering.BackgroundTasks/Events/GracePeriodConfirmedIntegrationEvent.cs create mode 100644 src/Services/Ordering/Ordering.BackgroundTasks/Extensions/CustomExtensionMethods.cs create mode 100644 src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj create mode 100644 src/Services/Ordering/Ordering.BackgroundTasks/Program.cs create mode 100644 src/Services/Ordering/Ordering.BackgroundTasks/Properties/launchSettings.json rename src/Services/Ordering/{Ordering.API/Infrastructure => Ordering.BackgroundTasks}/Tasks/GracePeriodManagerTask.cs (87%) create mode 100644 src/Services/Ordering/Ordering.BackgroundTasks/appsettings.Development.json create mode 100644 src/Services/Ordering/Ordering.BackgroundTasks/appsettings.json diff --git a/eShopOnContainers-ServicesAndWebApps.sln b/eShopOnContainers-ServicesAndWebApps.sln index 75cf60bae..52f49f96c 100644 --- a/eShopOnContainers-ServicesAndWebApps.sln +++ b/eShopOnContainers-ServicesAndWebApps.sln @@ -161,6 +161,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{3ABEEE src\ApiGateways\Envoy\config\envoy.yaml = src\ApiGateways\Envoy\config\envoy.yaml EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ordering.BackgroundTasks", "src\Services\Ordering\Ordering.BackgroundTasks\Ordering.BackgroundTasks.csproj", "{D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Ad-Hoc|Any CPU = Ad-Hoc|Any CPU @@ -1759,6 +1761,54 @@ Global {56C2EF0B-6BF2-41D9-BE07-6E6D08D06B35}.Release|x64.Build.0 = Release|Any CPU {56C2EF0B-6BF2-41D9-BE07-6E6D08D06B35}.Release|x86.ActiveCfg = Release|Any CPU {56C2EF0B-6BF2-41D9-BE07-6E6D08D06B35}.Release|x86.Build.0 = Release|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.AppStore|ARM.Build.0 = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.AppStore|iPhone.Build.0 = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.AppStore|x64.ActiveCfg = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.AppStore|x64.Build.0 = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.AppStore|x86.ActiveCfg = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.AppStore|x86.Build.0 = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Debug|ARM.ActiveCfg = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Debug|ARM.Build.0 = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Debug|iPhone.Build.0 = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Debug|x64.ActiveCfg = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Debug|x64.Build.0 = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Debug|x86.ActiveCfg = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Debug|x86.Build.0 = Debug|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Release|Any CPU.Build.0 = Release|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Release|ARM.ActiveCfg = Release|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Release|ARM.Build.0 = Release|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Release|iPhone.ActiveCfg = Release|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Release|iPhone.Build.0 = Release|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Release|x64.ActiveCfg = Release|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Release|x64.Build.0 = Release|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Release|x86.ActiveCfg = Release|Any CPU + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1826,6 +1876,7 @@ Global {56C2EF0B-6BF2-41D9-BE07-6E6D08D06B35} = {68F5041D-51F2-4630-94B6-B49789F5E51A} {882A8F3A-C61F-4C44-86DD-A5A258714BF2} = {77849D35-37D4-4802-81DC-9477B2775A40} {3ABEEE8C-35E0-4185-9825-C44326151F5B} = {882A8F3A-C61F-4C44-86DD-A5A258714BF2} + {D4DBA4A3-E4A5-4D9D-8ACF-F38F7D506191} = {0BD0DB92-2D98-44D9-9AC0-C59186D59B0B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {25728519-5F0F-4973-8A64-0A81EB4EA8D9} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs index 00cf506a4..851368af1 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs @@ -117,18 +117,27 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers // Step 2: Get current basket status var currentBasket = (await _basket.GetByIdAsync(data.BasketId)) ?? new BasketData(data.BasketId); - // Step 3: Merge current status with new product - currentBasket.Items.Add(new BasketDataItem() - { - UnitPrice = item.Price, - PictureUrl = item.PictureUri, - ProductId = item.Id.ToString(), - ProductName = item.Name, - Quantity = data.Quantity, - Id = Guid.NewGuid().ToString() - }); - - // Step 4: Update basket + // Step 3: Search if exist product into basket + var product = currentBasket.Items.SingleOrDefault(i => i.ProductId == item.Id.ToString()); + + if(product != null){ + // Step 4: Update quantity for product + product.Quantity += data.Quantity; + } + else{ + // Step 4: Merge current status with new product + currentBasket.Items.Add(new BasketDataItem() + { + UnitPrice = item.Price, + PictureUrl = item.PictureUri, + ProductId = item.Id.ToString(), + ProductName = item.Name, + Quantity = data.Quantity, + Id = Guid.NewGuid().ToString() + }); + } + + // Step 5: Update basket await _basket.UpdateAsync(currentBasket); return Ok(); diff --git a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs index dc65fa525..ec532d6da 100644 --- a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs @@ -6,13 +6,15 @@ using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; using System.Linq; using System.ComponentModel.DataAnnotations.Schema; using System.Reflection; +using Microsoft.Extensions.Logging; +using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF { public class IntegrationEventLogEntry { private IntegrationEventLogEntry() { } - public IntegrationEventLogEntry(IntegrationEvent @event, Guid transactionId) + public IntegrationEventLogEntry(IntegrationEvent @event, Guid transactionId, ILogger logger) { EventId = @event.Id; CreationTime = @event.CreationDate; @@ -21,8 +23,8 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF State = EventStateEnum.NotPublished; TimesSent = 0; TransactionId = transactionId.ToString(); - } + } public Guid EventId { get; private set; } public string EventTypeName { get; private set; } [NotMapped] @@ -35,8 +37,10 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF public string Content { get; private set; } public string TransactionId { get; private set; } - public IntegrationEventLogEntry DeserializeJsonContent(Type type) + public IntegrationEventLogEntry DeserializeJsonContent(Type type, ILogger logger) { + logger.LogInformation("----- DeserializeJsonContent {Content} {Type}", Content, type); + IntegrationEvent = JsonConvert.DeserializeObject(Content, type) as IntegrationEvent; return this; } diff --git a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IntegrationEventLogService.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IntegrationEventLogService.cs index 758055e69..b8e9eab9a 100644 --- a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IntegrationEventLogService.cs +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IntegrationEventLogService.cs @@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; using System; using System.Collections; @@ -17,11 +18,13 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Servi public class IntegrationEventLogService : IIntegrationEventLogService { private readonly IntegrationEventLogContext _integrationEventLogContext; + private readonly ILogger _logger; private readonly DbConnection _dbConnection; private readonly List _eventTypes; - public IntegrationEventLogService(DbConnection dbConnection) + public IntegrationEventLogService(DbConnection dbConnection, ILogger logger) { + _logger =logger; _dbConnection = dbConnection ?? throw new ArgumentNullException(nameof(dbConnection)); _integrationEventLogContext = new IntegrationEventLogContext( new DbContextOptionsBuilder() @@ -37,12 +40,14 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Servi public async Task> RetrieveEventLogsPendingToPublishAsync(Guid transactionId) { + _logger.LogInformation("----- RetrieveEventLogsPendingToPublishAsync {TransactionId}", transactionId); + var tid = transactionId.ToString(); return await _integrationEventLogContext.IntegrationEventLogs .Where(e => e.TransactionId == tid && e.State == EventStateEnum.NotPublished) .OrderBy(o => o.CreationTime) - .Select(e => e.DeserializeJsonContent(_eventTypes.Find(t=> t.Name == e.EventTypeShortName))) + .Select(e => e.DeserializeJsonContent(_eventTypes.Find(t=> t.Name == e.EventTypeShortName), _logger)) .ToListAsync(); } @@ -50,7 +55,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Servi { if (transaction == null) throw new ArgumentNullException(nameof(transaction)); - var eventLogEntry = new IntegrationEventLogEntry(@event, transaction.TransactionId); + var eventLogEntry = new IntegrationEventLogEntry(@event, transaction.TransactionId, _logger); _integrationEventLogContext.Database.UseTransaction(transaction.GetDbTransaction()); _integrationEventLogContext.IntegrationEventLogs.Add(eventLogEntry); diff --git a/src/Services/Catalog/Catalog.API/Startup.cs b/src/Services/Catalog/Catalog.API/Startup.cs index 64441f9ea..451d5d5cf 100644 --- a/src/Services/Catalog/Catalog.API/Startup.cs +++ b/src/Services/Catalog/Catalog.API/Startup.cs @@ -277,7 +277,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API public static IServiceCollection AddIntegrationServices(this IServiceCollection services, IConfiguration configuration) { services.AddTransient>( - sp => (DbConnection c) => new IntegrationEventLogService(c)); + sp => (DbConnection c) => new IntegrationEventLogService(c, sp.GetService>())); services.AddTransient(); diff --git a/src/Services/Ordering/Ordering.API/Application/Behaviors/TransactionBehaviour.cs b/src/Services/Ordering/Ordering.API/Application/Behaviors/TransactionBehaviour.cs index be32daeee..0320cbd97 100644 --- a/src/Services/Ordering/Ordering.API/Application/Behaviors/TransactionBehaviour.cs +++ b/src/Services/Ordering/Ordering.API/Application/Behaviors/TransactionBehaviour.cs @@ -56,6 +56,8 @@ namespace Ordering.API.Application.Behaviors await _dbContext.CommitTransactionAsync(transaction); transactionId = transaction.TransactionId; + + _logger.LogInformation("----- End transaction {TransactionId} for {CommandName}", transaction.TransactionId, typeName); } await _orderingIntegrationEventService.PublishEventsThroughEventBusAsync(transactionId); diff --git a/src/Services/Ordering/Ordering.API/Program.cs b/src/Services/Ordering/Ordering.API/Program.cs index 251a9cbf5..50f53360c 100644 --- a/src/Services/Ordering/Ordering.API/Program.cs +++ b/src/Services/Ordering/Ordering.API/Program.cs @@ -7,7 +7,6 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Ordering.API.Infrastructure.Tasks; using Serilog; using System; using System.IO; @@ -63,7 +62,6 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API WebHost.CreateDefaultBuilder(args) .CaptureStartupErrors(false) .UseStartup() - .ConfigureServices(service => service.AddHostedService()) .UseApplicationInsights() .UseContentRoot(Directory.GetCurrentDirectory()) .UseConfiguration(configuration) diff --git a/src/Services/Ordering/Ordering.API/Startup.cs b/src/Services/Ordering/Ordering.API/Startup.cs index fca490f28..572c83320 100644 --- a/src/Services/Ordering/Ordering.API/Startup.cs +++ b/src/Services/Ordering/Ordering.API/Startup.cs @@ -273,7 +273,7 @@ services.AddSingleton(); services.AddTransient(); services.AddTransient>( - sp => (DbConnection c) => new IntegrationEventLogService(c)); + sp => (DbConnection c) => new IntegrationEventLogService(c, sp.GetService>())); services.AddTransient(); diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/BackgroundTaskSettings.cs b/src/Services/Ordering/Ordering.BackgroundTasks/BackgroundTaskSettings.cs new file mode 100644 index 000000000..5171e7724 --- /dev/null +++ b/src/Services/Ordering/Ordering.BackgroundTasks/BackgroundTaskSettings.cs @@ -0,0 +1,15 @@ +namespace Ordering.BackgroundTasks +{ + public class BackgroundTaskSettings + { + public string ConnectionString { get; set; } + + public string EventBusConnection { get; set; } + + public int GracePeriodTime { get; set; } + + public int CheckUpdateTime { get; set; } + + public string SubscriptionClientName { get; set; } + } +} diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Dockerfile b/src/Services/Ordering/Ordering.BackgroundTasks/Dockerfile new file mode 100644 index 000000000..25f55a79c --- /dev/null +++ b/src/Services/Ordering/Ordering.BackgroundTasks/Dockerfile @@ -0,0 +1,18 @@ +FROM mcr.microsoft.com/dotnet/core/runtime:3.0-buster-slim AS base +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/core/sdk:3.0-buster AS build +WORKDIR /src +COPY ["src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj", "src/Services/Ordering/Ordering.BackgroundTasks/"] +RUN dotnet restore "src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj" +COPY . . +WORKDIR "/src/src/Services/Ordering/Ordering.BackgroundTasks" +RUN dotnet build "Ordering.BackgroundTasks.csproj" -c Release -o /app + +FROM build AS publish +RUN dotnet publish "Ordering.BackgroundTasks.csproj" -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "Ordering.BackgroundTasks.dll"] \ No newline at end of file diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Events/GracePeriodConfirmedIntegrationEvent.cs b/src/Services/Ordering/Ordering.BackgroundTasks/Events/GracePeriodConfirmedIntegrationEvent.cs new file mode 100644 index 000000000..8fe92df76 --- /dev/null +++ b/src/Services/Ordering/Ordering.BackgroundTasks/Events/GracePeriodConfirmedIntegrationEvent.cs @@ -0,0 +1,12 @@ +namespace Ordering.BackgroundTasks.Events +{ + using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; + + public class GracePeriodConfirmedIntegrationEvent : IntegrationEvent + { + public int OrderId { get; } + + public GracePeriodConfirmedIntegrationEvent(int orderId) => + OrderId = orderId; + } +} diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Extensions/CustomExtensionMethods.cs b/src/Services/Ordering/Ordering.BackgroundTasks/Extensions/CustomExtensionMethods.cs new file mode 100644 index 000000000..81d5472f4 --- /dev/null +++ b/src/Services/Ordering/Ordering.BackgroundTasks/Extensions/CustomExtensionMethods.cs @@ -0,0 +1,141 @@ +using Autofac; +using Microsoft.Azure.ServiceBus; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using RabbitMQ.Client; +using Serilog; + +namespace Ordering.BackgroundTasks.Extensions +{ + public static class CustomExtensionMethods + { + public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) + { + var hcBuilder = services.AddHealthChecks(); + + hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy()); + + hcBuilder.AddSqlServer( + configuration["ConnectionString"], + name: "OrderingTaskDB-check", + tags: new string[] { "orderingtaskdb" }); + + if (configuration.GetValue("AzureServiceBusEnabled")) + { + hcBuilder.AddAzureServiceBusTopic( + configuration["EventBusConnection"], + topicName: "eshop_event_bus", + name: "orderingtask-servicebus-check", + tags: new string[] { "servicebus" }); + } + else + { + hcBuilder.AddRabbitMQ( + $"amqp://{configuration["EventBusConnection"]}", + name: "orderingtask-rabbitmqbus-check", + tags: new string[] { "rabbitmqbus" }); + } + + return services; + } + + public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration) + { + var subscriptionClientName = configuration["SubscriptionClientName"]; + + if (configuration.GetValue("AzureServiceBusEnabled")) + { + services.AddSingleton(sp => + { + var logger = sp.GetRequiredService>(); + + var serviceBusConnectionString = configuration["EventBusConnection"]; + var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString); + + return new DefaultServiceBusPersisterConnection(serviceBusConnection, logger); + }); + + services.AddSingleton(sp => + { + var serviceBusPersisterConnection = sp.GetRequiredService(); + var iLifetimeScope = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); + + return new EventBusServiceBus(serviceBusPersisterConnection, logger, eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope); + }); + } + else + { + services.AddSingleton(sp => + { + var logger = sp.GetRequiredService>(); + + var factory = new ConnectionFactory() + { + HostName = configuration["EventBusConnection"], + DispatchConsumersAsync = true + }; + + if (!string.IsNullOrEmpty(configuration["EventBusUserName"])) + { + factory.UserName = configuration["EventBusUserName"]; + } + + if (!string.IsNullOrEmpty(configuration["EventBusPassword"])) + { + factory.Password = configuration["EventBusPassword"]; + } + + var retryCount = 5; + if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) + { + retryCount = int.Parse(configuration["EventBusRetryCount"]); + } + + return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); + }); + + services.AddSingleton(sp => + { + var rabbitMQPersistentConnection = sp.GetRequiredService(); + var iLifetimeScope = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); + + var retryCount = 5; + + if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) + { + retryCount = int.Parse(configuration["EventBusRetryCount"]); + } + + return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); + }); + } + + services.AddSingleton(); + + return services; + } + + public static ILoggingBuilder UseSerilog(this ILoggingBuilder builder, IConfiguration configuration) + { + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Verbose() + .Enrich.WithProperty("ApplicationContext", "BackgroundTasks") + .Enrich.FromLogContext() + .WriteTo.Console() + .ReadFrom.Configuration(configuration) + .CreateLogger(); + + return builder; + } + } +} diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj b/src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj new file mode 100644 index 000000000..94e44bd3e --- /dev/null +++ b/src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj @@ -0,0 +1,33 @@ + + + + $(NetCoreTargetVersion) + dotnet-Ordering.BackgroundTasks-9D3E1DD6-405B-447F-8AAB-1708B36D260E + Linux + $(LangVersion) + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Program.cs b/src/Services/Ordering/Ordering.BackgroundTasks/Program.cs new file mode 100644 index 000000000..ca42a4ebb --- /dev/null +++ b/src/Services/Ordering/Ordering.BackgroundTasks/Program.cs @@ -0,0 +1,40 @@ +using Autofac.Extensions.DependencyInjection; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Ordering.BackgroundTasks.Extensions; +using Ordering.BackgroundTasks.Tasks; +using Serilog; +using System.IO; + +namespace Ordering.BackgroundTasks +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureAppConfiguration((hostContext, builder) => + { + builder.SetBasePath(Directory.GetCurrentDirectory()); + builder.AddJsonFile("appsettings.json", optional: true); + builder.AddJsonFile($"appsettings.{hostContext.HostingEnvironment.EnvironmentName}.json", optional: true); + builder.AddEnvironmentVariables(); + builder.AddCommandLine(args); + }) + .ConfigureLogging((host, builder) => builder.UseSerilog(host.Configuration).AddSerilog()) + .ConfigureServices((host, services) => + { + services.AddCustomHealthCheck(host.Configuration); + services.Configure(host.Configuration); + services.AddOptions(); + services.AddHostedService(); + services.AddEventBus(host.Configuration); + services.AddAutofac(container => container.Populate(services)); + }); + } +} diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Properties/launchSettings.json b/src/Services/Ordering/Ordering.BackgroundTasks/Properties/launchSettings.json new file mode 100644 index 000000000..e2d987f50 --- /dev/null +++ b/src/Services/Ordering/Ordering.BackgroundTasks/Properties/launchSettings.json @@ -0,0 +1,13 @@ +{ + "profiles": { + "Ordering.BackgroundTasks": { + "commandName": "Project", + "environmentVariables": { + "DOTNET_ENVIRONMENT": "Development" + } + }, + "Docker": { + "commandName": "Docker" + } + } +} \ No newline at end of file diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/Tasks/GracePeriodManagerTask.cs b/src/Services/Ordering/Ordering.BackgroundTasks/Tasks/GracePeriodManagerTask.cs similarity index 87% rename from src/Services/Ordering/Ordering.API/Infrastructure/Tasks/GracePeriodManagerTask.cs rename to src/Services/Ordering/Ordering.BackgroundTasks/Tasks/GracePeriodManagerTask.cs index 740d1aa30..9d4c83c57 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/Tasks/GracePeriodManagerTask.cs +++ b/src/Services/Ordering/Ordering.BackgroundTasks/Tasks/GracePeriodManagerTask.cs @@ -1,25 +1,24 @@ using Dapper; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.eShopOnContainers.Services.Ordering.API; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Ordering.API.Application.IntegrationEvents.Events; +using Ordering.BackgroundTasks.Events; using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Threading; using System.Threading.Tasks; -namespace Ordering.API.Infrastructure.Tasks +namespace Ordering.BackgroundTasks.Tasks { public class GracePeriodManagerService : BackgroundService { private readonly ILogger _logger; - private readonly OrderingSettings _settings; + private readonly BackgroundTaskSettings _settings; private readonly IEventBus _eventBus; - public GracePeriodManagerService(IOptions settings, IEventBus eventBus, ILogger logger) + public GracePeriodManagerService(IOptions settings, IEventBus eventBus, ILogger logger) { _settings = settings?.Value ?? throw new ArgumentNullException(nameof(settings)); _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); @@ -56,7 +55,7 @@ namespace Ordering.API.Infrastructure.Tasks { var confirmGracePeriodEvent = new GracePeriodConfirmedIntegrationEvent(orderId); - _logger.LogInformation("----- Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", confirmGracePeriodEvent.Id, Program.AppName, confirmGracePeriodEvent); + _logger.LogInformation("----- Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", confirmGracePeriodEvent.Id, _settings.SubscriptionClientName, confirmGracePeriodEvent); _eventBus.Publish(confirmGracePeriodEvent); } diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/appsettings.Development.json b/src/Services/Ordering/Ordering.BackgroundTasks/appsettings.Development.json new file mode 100644 index 000000000..e203e9407 --- /dev/null +++ b/src/Services/Ordering/Ordering.BackgroundTasks/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/appsettings.json b/src/Services/Ordering/Ordering.BackgroundTasks/appsettings.json new file mode 100644 index 000000000..88e5d6858 --- /dev/null +++ b/src/Services/Ordering/Ordering.BackgroundTasks/appsettings.json @@ -0,0 +1,26 @@ +{ + "ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;", + "Serilog": { + "SeqServerUrl": null, + "LogstashgUrl": null, + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "Microsoft.eShopOnContainers": "Information", + "System": "Warning" + } + } + }, + "SubscriptionClientName": "BackgroundTasks", + "GracePeriodTime": "1", + "CheckUpdateTime": "1000", + "ApplicationInsights": { + "InstrumentationKey": "" + }, + "AzureServiceBusEnabled": false, + "EventBusRetryCount": 5, + "EventBusConnection": "", + "EventBusUserName": "", + "EventBusPassword": "" +} \ No newline at end of file diff --git a/src/Services/Webhooks/Webhooks.API/Startup.cs b/src/Services/Webhooks/Webhooks.API/Startup.cs index 5859e9e12..1d454a7ab 100644 --- a/src/Services/Webhooks/Webhooks.API/Startup.cs +++ b/src/Services/Webhooks/Webhooks.API/Startup.cs @@ -291,7 +291,7 @@ namespace Webhooks.API public static IServiceCollection AddIntegrationServices(this IServiceCollection services, IConfiguration configuration) { services.AddTransient>( - sp => (DbConnection c) => new IntegrationEventLogService(c)); + sp => (DbConnection c) => new IntegrationEventLogService(c, sp.GetService>())); if (configuration.GetValue("AzureServiceBusEnabled")) { diff --git a/src/Web/WebSPA/Startup.cs b/src/Web/WebSPA/Startup.cs index 7e6c374f7..6ea83baa5 100644 --- a/src/Web/WebSPA/Startup.cs +++ b/src/Web/WebSPA/Startup.cs @@ -118,6 +118,7 @@ namespace eShopConContainers.WebSPA app.UseRouting(); app.UseEndpoints(endpoints => { + endpoints.MapDefaultControllerRoute(); endpoints.MapControllers(); endpoints.MapHealthChecks("/liveness", new HealthCheckOptions { diff --git a/src/_build/dependencies.props b/src/_build/dependencies.props index 4de3340c6..dceb4edd2 100644 --- a/src/_build/dependencies.props +++ b/src/_build/dependencies.props @@ -20,6 +20,8 @@ 3.9.0-rc1 1.22.0 + 3.0.0-preview7.19362.4 + 1.7.9 2.2.0 2.2.0 2.2.0