From 2f4e0d47da241c111b8bf3c66dbf1936b6226018 Mon Sep 17 00:00:00 2001 From: Arne Dumarey Date: Tue, 14 Jan 2025 15:28:55 +0100 Subject: [PATCH] refactor(producers): convert to consoleapp --- paket.dependencies | 2 +- paket.lock | 2 +- .../Infrastructure/Modules/LoggingModule.cs | 36 ---- .../{ApiModule.cs => ProducerModule.cs} | 67 +++++-- .../Infrastructure/Program.cs | 160 ++++++++++++++--- .../Infrastructure/Startup.cs | 165 ++--------------- .../ProducerModule.cs | 71 -------- .../Projections/ProjectionsController.cs | 24 --- .../SnapshotProducer.cs | 41 +++++ ...NameRegistry.Producer.Snapshot.Oslo.csproj | 2 - .../Infrastructure/Modules/LoggingModule.cs | 36 ---- .../{ApiModule.cs => ProducerModule.cs} | 64 +++++-- .../Infrastructure/Program.cs | 167 +++++++++++++++--- .../Infrastructure/Startup.cs | 163 ++--------------- .../ProducerModule.cs | 70 -------- .../Projections/ProjectionsController.cs | 24 --- .../StreetNameProducer.cs | 40 +++++ .../StreetNameRegistry.Producer.csproj | 2 - 18 files changed, 493 insertions(+), 643 deletions(-) delete mode 100644 src/StreetNameRegistry.Producer.Snapshot.Oslo/Infrastructure/Modules/LoggingModule.cs rename src/StreetNameRegistry.Producer.Snapshot.Oslo/Infrastructure/Modules/{ApiModule.cs => ProducerModule.cs} (66%) delete mode 100644 src/StreetNameRegistry.Producer.Snapshot.Oslo/ProducerModule.cs delete mode 100644 src/StreetNameRegistry.Producer.Snapshot.Oslo/Projections/ProjectionsController.cs create mode 100644 src/StreetNameRegistry.Producer.Snapshot.Oslo/SnapshotProducer.cs delete mode 100644 src/StreetNameRegistry.Producer/Infrastructure/Modules/LoggingModule.cs rename src/StreetNameRegistry.Producer/Infrastructure/Modules/{ApiModule.cs => ProducerModule.cs} (64%) delete mode 100644 src/StreetNameRegistry.Producer/ProducerModule.cs delete mode 100644 src/StreetNameRegistry.Producer/Projections/ProjectionsController.cs create mode 100644 src/StreetNameRegistry.Producer/StreetNameProducer.cs diff --git a/paket.dependencies b/paket.dependencies index bf05b7411..ae7f95518 100644 --- a/paket.dependencies +++ b/paket.dependencies @@ -70,7 +70,7 @@ nuget Be.Vlaanderen.Basisregisters.ProjectionHandling.Connector.Testing 14.0.0 nuget Be.Vlaanderen.Basisregisters.ProjectionHandling.Testing.Xunit 14.0.0 nuget Be.Vlaanderen.Basisregisters.ProjectionHandling.Syndication 14.0.0 -nuget Be.Vlaanderen.Basisregisters.Projector 15.1.0 +nuget Be.Vlaanderen.Basisregisters.Projector 15.2.0 nuget Be.Vlaanderen.Basisregisters.Crab 4.0.0 diff --git a/paket.lock b/paket.lock index e1224e44b..b745a881d 100644 --- a/paket.lock +++ b/paket.lock @@ -376,7 +376,7 @@ NUGET Microsoft.EntityFrameworkCore (>= 8.0.2) Microsoft.Extensions.Logging (>= 8.0) xunit (>= 2.7) - Be.Vlaanderen.Basisregisters.Projector (15.1) + Be.Vlaanderen.Basisregisters.Projector (15.2) Autofac (>= 8.0) Autofac.Extensions.DependencyInjection (>= 9.0) Be.Vlaanderen.Basisregisters.ProjectionHandling.Connector (>= 14.0) diff --git a/src/StreetNameRegistry.Producer.Snapshot.Oslo/Infrastructure/Modules/LoggingModule.cs b/src/StreetNameRegistry.Producer.Snapshot.Oslo/Infrastructure/Modules/LoggingModule.cs deleted file mode 100644 index 439ca4127..000000000 --- a/src/StreetNameRegistry.Producer.Snapshot.Oslo/Infrastructure/Modules/LoggingModule.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace StreetNameRegistry.Producer.Snapshot.Oslo.Infrastructure.Modules -{ - using System; - using Autofac; - using Destructurama; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Logging; - using Serilog; - using Serilog.Debugging; - - public class LoggingModule : Module - { - public LoggingModule( - IConfiguration configuration, - IServiceCollection services) - { - SelfLog.Enable(Console.WriteLine); - - Log.Logger = new LoggerConfiguration() - .ReadFrom.Configuration(configuration) - .Enrich.FromLogContext() - .Enrich.WithMachineName() - .Enrich.WithThreadId() - .Enrich.WithEnvironmentUserName() - .Destructure.JsonNetTypes() - .CreateLogger(); - - services.AddLogging(l => - { - l.ClearProviders(); - l.AddSerilog(Log.Logger); - }); - } - } -} diff --git a/src/StreetNameRegistry.Producer.Snapshot.Oslo/Infrastructure/Modules/ApiModule.cs b/src/StreetNameRegistry.Producer.Snapshot.Oslo/Infrastructure/Modules/ProducerModule.cs similarity index 66% rename from src/StreetNameRegistry.Producer.Snapshot.Oslo/Infrastructure/Modules/ApiModule.cs rename to src/StreetNameRegistry.Producer.Snapshot.Oslo/Infrastructure/Modules/ProducerModule.cs index cc1c643ee..d81f5ede3 100644 --- a/src/StreetNameRegistry.Producer.Snapshot.Oslo/Infrastructure/Modules/ApiModule.cs +++ b/src/StreetNameRegistry.Producer.Snapshot.Oslo/Infrastructure/Modules/ProducerModule.cs @@ -9,22 +9,24 @@ namespace StreetNameRegistry.Producer.Snapshot.Oslo.Infrastructure.Modules using Be.Vlaanderen.Basisregisters.GrAr.Oslo.SnapshotProducer; using Be.Vlaanderen.Basisregisters.MessageHandling.Kafka; using Be.Vlaanderen.Basisregisters.MessageHandling.Kafka.Producer; + using Be.Vlaanderen.Basisregisters.ProjectionHandling.Runner.SqlServer.MigrationExtensions; using Be.Vlaanderen.Basisregisters.ProjectionHandling.SqlStreamStore.Autofac; using Be.Vlaanderen.Basisregisters.Projector; using Be.Vlaanderen.Basisregisters.Projector.ConnectedProjections; using Be.Vlaanderen.Basisregisters.Projector.Modules; + using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using StreetNameRegistry.Infrastructure; - public class ApiModule : Module + public class ProducerModule : Module { private readonly IConfiguration _configuration; private readonly IServiceCollection _services; private readonly ILoggerFactory _loggerFactory; - public ApiModule( + public ProducerModule( IConfiguration configuration, IServiceCollection services, ILoggerFactory loggerFactory) @@ -52,11 +54,8 @@ private void RegisterProjectionSetup(ContainerBuilder builder) new EventHandlingModule( typeof(DomainAssemblyMarker).Assembly, EventsJsonSerializerSettingsProvider.CreateSerializerSettings())) - .RegisterModule() - .RegisterEventstreamModule(_configuration) - .RegisterModule(new ProjectorModule(_configuration)); RegisterProjections(builder); @@ -64,12 +63,26 @@ private void RegisterProjectionSetup(ContainerBuilder builder) private void RegisterProjections(ContainerBuilder builder) { - builder - .RegisterModule( - new ProducerModule( - _configuration, - _services, - _loggerFactory)); + var logger = _loggerFactory.CreateLogger(); + var connectionString = _configuration.GetConnectionString("ProducerProjections"); + + var hasConnectionString = !string.IsNullOrWhiteSpace(connectionString); + if (hasConnectionString) + { + RunOnSqlServer(_services, _loggerFactory, connectionString); + } + else + { + RunInMemoryDb(_services, _loggerFactory, logger); + } + + logger.LogInformation( + "Added {Context} to services:" + + Environment.NewLine + + "\tSchema: {Schema}" + + Environment.NewLine + + "\tTableName: {TableName}", + nameof(ProducerContext), Schema.ProducerSnapshotOslo, MigrationTables.ProducerSnapshotOslo); var connectedProjectionSettings = ConnectedProjectionSettings.Configure(x => { @@ -88,7 +101,8 @@ private void RegisterProjections(ContainerBuilder builder) //osloNamespace = osloNamespace.TrimEnd('/'); var bootstrapServers = _configuration["Kafka:BootstrapServers"]!; - var topic = $"{_configuration[ProducerProjections.StreetNameTopicKey]}" ?? throw new ArgumentException($"Configuration has no value for {ProducerProjections.StreetNameTopicKey}"); + var topic = $"{_configuration[ProducerProjections.StreetNameTopicKey]}" ?? + throw new ArgumentException($"Configuration has no value for {ProducerProjections.StreetNameTopicKey}"); var producerOptions = new ProducerOptions( new BootstrapServers(bootstrapServers), new Topic(topic), @@ -117,5 +131,34 @@ private void RegisterProjections(ContainerBuilder builder) }, connectedProjectionSettings); } + + private static void RunOnSqlServer( + IServiceCollection services, + ILoggerFactory loggerFactory, + string backofficeProjectionsConnectionString) + { + services + .AddDbContext((_, options) => options + .UseLoggerFactory(loggerFactory) + .UseSqlServer(backofficeProjectionsConnectionString, sqlServerOptions => + { + sqlServerOptions.EnableRetryOnFailure(); + sqlServerOptions.MigrationsHistoryTable(MigrationTables.ProducerSnapshotOslo, Schema.ProducerSnapshotOslo); + }) + .UseExtendedSqlServerMigrations()); + } + + private static void RunInMemoryDb( + IServiceCollection services, + ILoggerFactory loggerFactory, + ILogger logger) + { + services + .AddDbContext(options => options + .UseLoggerFactory(loggerFactory) + .UseInMemoryDatabase(Guid.NewGuid().ToString(), sqlServerOptions => { })); + + logger.LogWarning("Running InMemory for {Context}!", nameof(ProducerContext)); + } } } diff --git a/src/StreetNameRegistry.Producer.Snapshot.Oslo/Infrastructure/Program.cs b/src/StreetNameRegistry.Producer.Snapshot.Oslo/Infrastructure/Program.cs index c5422d51f..9fe868aae 100644 --- a/src/StreetNameRegistry.Producer.Snapshot.Oslo/Infrastructure/Program.cs +++ b/src/StreetNameRegistry.Producer.Snapshot.Oslo/Infrastructure/Program.cs @@ -1,39 +1,157 @@ namespace StreetNameRegistry.Producer.Snapshot.Oslo.Infrastructure { + using System; + using System.IO; + using System.Linq; + using System.Threading.Tasks; + using Autofac; + using Autofac.Extensions.DependencyInjection; using Be.Vlaanderen.Basisregisters.Api; using Be.Vlaanderen.Basisregisters.Aws.DistributedMutex; + using Destructurama; using Microsoft.AspNetCore.Hosting; + using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Hosting; + using Microsoft.Extensions.Logging; + using Modules; + using Serilog; + using Serilog.Debugging; + using Serilog.Extensions.Logging; public sealed class Program { private Program() { } - public static void Main(string[] args) - => Run(new ProgramOptions + public static async Task Main(string[] args) + { + AppDomain.CurrentDomain.FirstChanceException += (_, eventArgs) => + Log.Debug( + eventArgs.Exception, + "FirstChanceException event raised in {AppDomain}.", + AppDomain.CurrentDomain.FriendlyName); + + AppDomain.CurrentDomain.UnhandledException += (_, eventArgs) => + Log.Fatal((Exception)eventArgs.ExceptionObject, "Encountered a fatal exception, exiting program."); + + Log.Information("Initializing StreetNameRegistry.Producer.Snapshot.Oslo"); + + var host = new HostBuilder() + .ConfigureAppConfiguration((_, configurationBuilder) => { - Hosting = - { - HttpPort = 4016 - }, - Logging = - { - WriteTextToConsole = false, - WriteJsonToConsole = false - }, - Runtime = + configurationBuilder + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: false) + .AddJsonFile($"appsettings.{Environment.MachineName.ToLowerInvariant()}.json", optional: true, reloadOnChange: false) + .AddEnvironmentVariables() + .AddCommandLine(args); + }) + .ConfigureLogging((hostContext, loggingBuilder) => + { + SelfLog.Enable(Console.WriteLine); + Log.Logger = new LoggerConfiguration() //NOSONAR logging configuration is safe + .ReadFrom.Configuration(hostContext.Configuration) + .Enrich.FromLogContext() + .Enrich.WithMachineName() + .Enrich.WithThreadId() + .Enrich.WithEnvironmentUserName() + .Destructure.JsonNetTypes() + .CreateLogger(); + loggingBuilder.ClearProviders(); + loggingBuilder.AddSerilog(Log.Logger); + }) + .ConfigureServices((hostContext, services) => + { + var healthChecksBuilder = services.AddHealthChecks(); + var connectionStrings = hostContext.Configuration + .GetSection("ConnectionStrings") + .GetChildren(); + + foreach (var connectionString in connectionStrings) { - CommandLineArgs = args - }, - MiddlewareHooks = + healthChecksBuilder.AddSqlServer( + connectionString.Value, + name: $"sqlserver-{connectionString.Key.ToLowerInvariant()}", + tags: new[] { "db", "sql", "sqlserver" }); + } + + healthChecksBuilder.AddDbContextCheck( + $"dbcontext-{nameof(ProducerContext).ToLowerInvariant()}", + tags: new[] { "db", "sql", "sqlserver" }); + + var origins = hostContext.Configuration + .GetSection("Cors") + .GetChildren() + .Select(c => c.Value) + .ToArray(); + + foreach (var origin in origins) { - ConfigureDistributedLock = DistributedLockOptions.LoadFromConfiguration + services.AddCors(options => + { + options.AddDefaultPolicy(builder => + { + builder.WithOrigins(origin); + }); + }); } - }); + }) + .UseServiceProviderFactory(new AutofacServiceProviderFactory()) + .ConfigureContainer((hostContext, builder) => + { + var services = new ServiceCollection(); + var loggerFactory = new SerilogLoggerFactory(Log.Logger); + + builder.RegisterModule(new ProducerModule(hostContext.Configuration, services, loggerFactory)); + + builder + .RegisterType() + .As() + .SingleInstance(); + + builder.Populate(services); + }) + .ConfigureWebHostDefaults(webHostBuilder => + webHostBuilder + .UseStartup() + .UseKestrel()) + .UseConsoleLifetime() + .Build(); + + Log.Information("Starting StreetNameRegistry.Producer.Snapshot.Oslo"); + + var logger = host.Services.GetRequiredService>(); + var configuration = host.Services.GetRequiredService(); + + try + { + await DistributedLock.RunAsync( + async () => { await host.RunAsync().ConfigureAwait(false); }, + DistributedLockOptions.LoadFromConfiguration(configuration), + logger) + .ConfigureAwait(false); + } + catch (AggregateException aggregateException) + { + foreach (var innerException in aggregateException.InnerExceptions) + { + logger.LogCritical(innerException, "Encountered a fatal exception, exiting program."); + } + } + catch (Exception e) + { + logger.LogCritical(e, "Encountered a fatal exception, exiting program."); + Log.CloseAndFlush(); - private static void Run(ProgramOptions options) - => new WebHostBuilder() - .UseDefaultForApi(options) - .RunWithLock(); + // Allow some time for flushing before shutdown. + await Task.Delay(500, default); + throw; + } + finally + { + logger.LogInformation("Stopping..."); + } + } } } diff --git a/src/StreetNameRegistry.Producer.Snapshot.Oslo/Infrastructure/Startup.cs b/src/StreetNameRegistry.Producer.Snapshot.Oslo/Infrastructure/Startup.cs index 78d3a891d..ee362a757 100644 --- a/src/StreetNameRegistry.Producer.Snapshot.Oslo/Infrastructure/Startup.cs +++ b/src/StreetNameRegistry.Producer.Snapshot.Oslo/Infrastructure/Startup.cs @@ -1,169 +1,24 @@ namespace StreetNameRegistry.Producer.Snapshot.Oslo.Infrastructure { - using System; - using System.Linq; - using System.Reflection; - using Asp.Versioning.ApiExplorer; - using Autofac; - using Autofac.Extensions.DependencyInjection; - using Be.Vlaanderen.Basisregisters.Api; - using Be.Vlaanderen.Basisregisters.GrAr.Oslo.SnapshotProducer; + using Be.Vlaanderen.Basisregisters.AspNetCore.Mvc.Formatters.Json; using Be.Vlaanderen.Basisregisters.Projector; - using Configuration; using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Diagnostics.HealthChecks; - using Microsoft.Extensions.Hosting; - using Microsoft.Extensions.Logging; - using Microsoft.OpenApi.Models; - using Modules; + using Newtonsoft.Json; - /// Represents the startup process for the application. - public class Startup + public sealed class Startup { - private const string DatabaseTag = "db"; - - private IContainer _applicationContainer; - - private readonly IConfiguration _configuration; - private readonly ILoggerFactory _loggerFactory; - - public Startup( - IConfiguration configuration, - ILoggerFactory loggerFactory) - { - _configuration = configuration; - _loggerFactory = loggerFactory; - } - - /// Configures services for the application. - /// The collection of services to configure the application with. - public IServiceProvider ConfigureServices(IServiceCollection services) - { - var baseUrl = _configuration.GetValue("BaseUrl"); - var baseUrlForExceptions = baseUrl.EndsWith("/") - ? baseUrl.Substring(0, baseUrl.Length - 1) - : baseUrl; - - services - .ConfigureDefaultForApi( - new StartupConfigureOptions - { - Cors = - { - Origins = _configuration - .GetSection("Cors") - .GetChildren() - .Select(c => c.Value) - .ToArray() - }, - Server = - { - BaseUrl = baseUrlForExceptions - }, - Swagger = - { - ApiInfo = (provider, description) => new OpenApiInfo - { - Version = description.ApiVersion.ToString(), - Title = "Basisregisters Vlaanderen StreetName Registry API", - Description = GetApiLeadingText(description), - Contact = new OpenApiContact - { - Name = "Digitaal Vlaanderen", - Email = "digitaal.vlaanderen@vlaanderen.be", - Url = new Uri("https://legacy.basisregisters.vlaanderen") - } - }, - XmlCommentPaths = new[] { typeof(Startup).GetTypeInfo().Assembly.GetName().Name } - }, - MiddlewareHooks = - { - FluentValidation = fv => fv.RegisterValidatorsFromAssemblyContaining(), - - AfterHealthChecks = health => - { - var connectionStrings = _configuration - .GetSection("ConnectionStrings") - .GetChildren(); - - foreach (var connectionString in connectionStrings) - health.AddSqlServer( - connectionString.Value, - name: $"sqlserver-{connectionString.Key.ToLowerInvariant()}", - tags: new[] { DatabaseTag, "sql", "sqlserver" }); - - health.AddDbContextCheck( - $"dbcontext-{nameof(ProducerContext).ToLowerInvariant()}", - tags: new[] { DatabaseTag, "sql", "sqlserver" }); - } - } - }) - .AddOsloProxy(_configuration["OsloApiUrl"]); - - var containerBuilder = new ContainerBuilder(); - containerBuilder.RegisterModule(new LoggingModule(_configuration, services)); - containerBuilder.RegisterModule(new ApiModule(_configuration, services, _loggerFactory)); - _applicationContainer = containerBuilder.Build(); - - return new AutofacServiceProvider(_applicationContainer); - } - - public void Configure( - IServiceProvider serviceProvider, - IApplicationBuilder app, - IWebHostEnvironment env, - IHostApplicationLifetime appLifetime, - ILoggerFactory loggerFactory, - IApiVersionDescriptionProvider apiVersionProvider, - HealthCheckService healthCheckService) + public void Configure(IApplicationBuilder app) { - StartupHelpers.CheckDatabases(healthCheckService, DatabaseTag, loggerFactory).GetAwaiter().GetResult(); + app.UseRouting(); + app.UseCors(); - app - .UseDefaultForApi(new StartupUseOptions - { - Common = - { - ApplicationContainer = _applicationContainer, - ServiceProvider = serviceProvider, - HostingEnvironment = env, - ApplicationLifetime = appLifetime, - LoggerFactory = loggerFactory - }, - Api = - { - VersionProvider = apiVersionProvider, - Info = groupName => $"Basisregisters Vlaanderen - StreetName Registry API {groupName}", - CSharpClientOptions = - { - ClassName = "StreetNameRegistryProducer", - Namespace = "Be.Vlaanderen.Basisregisters" - }, - TypeScriptClientOptions = - { - ClassName = "StreetNameRegistryProducer" - } - }, - MiddlewareHooks = - { - AfterMiddleware = x => x.UseMiddleware(), - } - }) + app.UseHealthChecks("/health"); - .UseProjectionsManager(new ProjectionsManagerOptions - { - Common = - { - ServiceProvider = serviceProvider, - ApplicationLifetime = appLifetime - } - }); + var configuration = app.ApplicationServices.GetRequiredService(); + var baseUri = configuration.GetValue("BaseUrl").TrimEnd('/'); + app.UseProjectorEndpoints(baseUri, new JsonSerializerSettings().ConfigureDefaultForApi()); } - - private static string GetApiLeadingText(ApiVersionDescription description) - => $"Momenteel leest u de documentatie voor versie {description.ApiVersion} van de Basisregisters Vlaanderen StreetName Registry Producer Snapshot Oslo API{string.Format(description.IsDeprecated ? ", **deze API versie is niet meer ondersteund * *." : ".")}"; } } diff --git a/src/StreetNameRegistry.Producer.Snapshot.Oslo/ProducerModule.cs b/src/StreetNameRegistry.Producer.Snapshot.Oslo/ProducerModule.cs deleted file mode 100644 index 129920f26..000000000 --- a/src/StreetNameRegistry.Producer.Snapshot.Oslo/ProducerModule.cs +++ /dev/null @@ -1,71 +0,0 @@ -namespace StreetNameRegistry.Producer.Snapshot.Oslo -{ - using System; - using Autofac; - using Be.Vlaanderen.Basisregisters.ProjectionHandling.Runner.SqlServer.MigrationExtensions; - using Microsoft.EntityFrameworkCore; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Logging; - using StreetNameRegistry.Infrastructure; - - public class ProducerModule : Module - { - public ProducerModule( - IConfiguration configuration, - IServiceCollection services, - ILoggerFactory loggerFactory) - { - var logger = loggerFactory.CreateLogger(); - var connectionString = configuration.GetConnectionString("ProducerProjections"); - - var hasConnectionString = !string.IsNullOrWhiteSpace(connectionString); - if (hasConnectionString) - { - RunOnSqlServer(configuration, services, loggerFactory, connectionString); - } - else - { - RunInMemoryDb(services, loggerFactory, logger); - } - - logger.LogInformation( - "Added {Context} to services:" + - Environment.NewLine + - "\tSchema: {Schema}" + - Environment.NewLine + - "\tTableName: {TableName}", - nameof(ProducerContext), Schema.ProducerSnapshotOslo, MigrationTables.ProducerSnapshotOslo); - } - - private static void RunOnSqlServer( - IConfiguration configuration, - IServiceCollection services, - ILoggerFactory loggerFactory, - string backofficeProjectionsConnectionString) - { - services - .AddDbContext((_, options) => options - .UseLoggerFactory(loggerFactory) - .UseSqlServer(backofficeProjectionsConnectionString, sqlServerOptions => - { - sqlServerOptions.EnableRetryOnFailure(); - sqlServerOptions.MigrationsHistoryTable(MigrationTables.ProducerSnapshotOslo, Schema.ProducerSnapshotOslo); - }) - .UseExtendedSqlServerMigrations()); - } - - private static void RunInMemoryDb( - IServiceCollection services, - ILoggerFactory loggerFactory, - ILogger logger) - { - services - .AddDbContext(options => options - .UseLoggerFactory(loggerFactory) - .UseInMemoryDatabase(Guid.NewGuid().ToString(), sqlServerOptions => { })); - - logger.LogWarning("Running InMemory for {Context}!", nameof(ProducerContext)); - } - } -} diff --git a/src/StreetNameRegistry.Producer.Snapshot.Oslo/Projections/ProjectionsController.cs b/src/StreetNameRegistry.Producer.Snapshot.Oslo/Projections/ProjectionsController.cs deleted file mode 100644 index 0550664f6..000000000 --- a/src/StreetNameRegistry.Producer.Snapshot.Oslo/Projections/ProjectionsController.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace StreetNameRegistry.Producer.Snapshot.Oslo.Projections -{ - using Asp.Versioning; - using Be.Vlaanderen.Basisregisters.Api; - using Be.Vlaanderen.Basisregisters.Projector.ConnectedProjections; - using Be.Vlaanderen.Basisregisters.Projector.Controllers; - using Microsoft.Extensions.Configuration; - using StreetNameRegistry.Infrastructure; - - [ApiVersion("1.0")] - [ApiRoute("projections")] - public class ProjectionsController : DefaultProjectorController - { - public ProjectionsController( - IConnectedProjectionsManager connectedProjectionsManager, - IConfiguration configuration) - : base( - connectedProjectionsManager, - configuration.GetValue("BaseUrl")) - { - RegisterConnectionString(Schema.ProducerSnapshotOslo, configuration.GetConnectionString("ProducerProjections")); - } - } -} diff --git a/src/StreetNameRegistry.Producer.Snapshot.Oslo/SnapshotProducer.cs b/src/StreetNameRegistry.Producer.Snapshot.Oslo/SnapshotProducer.cs new file mode 100644 index 000000000..f01b2fa19 --- /dev/null +++ b/src/StreetNameRegistry.Producer.Snapshot.Oslo/SnapshotProducer.cs @@ -0,0 +1,41 @@ +namespace StreetNameRegistry.Producer.Snapshot.Oslo +{ + using System; + using System.Threading; + using System.Threading.Tasks; + using Be.Vlaanderen.Basisregisters.Projector.ConnectedProjections; + using Microsoft.Extensions.Hosting; + using Microsoft.Extensions.Logging; + + public sealed class SnapshotProducer : BackgroundService + { + private readonly IConnectedProjectionsManager _projectionsManager; + private readonly IHostApplicationLifetime _hostApplicationLifetime; + private readonly ILogger _logger; + + public SnapshotProducer( + IConnectedProjectionsManager projectionsManager, + IHostApplicationLifetime hostApplicationLifetime, + ILoggerFactory loggerFactory) + { + _projectionsManager = projectionsManager; + _hostApplicationLifetime = hostApplicationLifetime; + _logger = loggerFactory.CreateLogger(); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + try + { + _logger.LogInformation("Starting snapshot projections"); + await _projectionsManager.Start(stoppingToken); + } + catch (Exception exception) + { + _logger.LogCritical(exception, $"An error occurred while starting the {nameof(SnapshotProducer)}."); + _hostApplicationLifetime.StopApplication(); + throw; + } + } + } +} diff --git a/src/StreetNameRegistry.Producer.Snapshot.Oslo/StreetNameRegistry.Producer.Snapshot.Oslo.csproj b/src/StreetNameRegistry.Producer.Snapshot.Oslo/StreetNameRegistry.Producer.Snapshot.Oslo.csproj index 9a6ceecb0..678998beb 100644 --- a/src/StreetNameRegistry.Producer.Snapshot.Oslo/StreetNameRegistry.Producer.Snapshot.Oslo.csproj +++ b/src/StreetNameRegistry.Producer.Snapshot.Oslo/StreetNameRegistry.Producer.Snapshot.Oslo.csproj @@ -2,8 +2,6 @@ - - false true false diff --git a/src/StreetNameRegistry.Producer/Infrastructure/Modules/LoggingModule.cs b/src/StreetNameRegistry.Producer/Infrastructure/Modules/LoggingModule.cs deleted file mode 100644 index cf127d0be..000000000 --- a/src/StreetNameRegistry.Producer/Infrastructure/Modules/LoggingModule.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace StreetNameRegistry.Producer.Infrastructure.Modules -{ - using System; - using Autofac; - using Destructurama; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Logging; - using Serilog; - using Serilog.Debugging; - - public class LoggingModule : Module - { - public LoggingModule( - IConfiguration configuration, - IServiceCollection services) - { - SelfLog.Enable(Console.WriteLine); - - Log.Logger = new LoggerConfiguration() - .ReadFrom.Configuration(configuration) - .Enrich.FromLogContext() - .Enrich.WithMachineName() - .Enrich.WithThreadId() - .Enrich.WithEnvironmentUserName() - .Destructure.JsonNetTypes() - .CreateLogger(); - - services.AddLogging(l => - { - l.ClearProviders(); - l.AddSerilog(Log.Logger); - }); - } - } -} diff --git a/src/StreetNameRegistry.Producer/Infrastructure/Modules/ApiModule.cs b/src/StreetNameRegistry.Producer/Infrastructure/Modules/ProducerModule.cs similarity index 64% rename from src/StreetNameRegistry.Producer/Infrastructure/Modules/ApiModule.cs rename to src/StreetNameRegistry.Producer/Infrastructure/Modules/ProducerModule.cs index abd4427ee..7715d053d 100644 --- a/src/StreetNameRegistry.Producer/Infrastructure/Modules/ApiModule.cs +++ b/src/StreetNameRegistry.Producer/Infrastructure/Modules/ProducerModule.cs @@ -8,22 +8,24 @@ namespace StreetNameRegistry.Producer.Infrastructure.Modules using Be.Vlaanderen.Basisregisters.EventHandling.Autofac; using Be.Vlaanderen.Basisregisters.MessageHandling.Kafka; using Be.Vlaanderen.Basisregisters.MessageHandling.Kafka.Producer; + using Be.Vlaanderen.Basisregisters.ProjectionHandling.Runner.SqlServer.MigrationExtensions; using Be.Vlaanderen.Basisregisters.ProjectionHandling.SqlStreamStore.Autofac; using Be.Vlaanderen.Basisregisters.Projector; using Be.Vlaanderen.Basisregisters.Projector.ConnectedProjections; using Be.Vlaanderen.Basisregisters.Projector.Modules; + using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using StreetNameRegistry.Infrastructure; - public class ApiModule : Module + public class ProducerModule : Module { private readonly IConfiguration _configuration; private readonly IServiceCollection _services; private readonly ILoggerFactory _loggerFactory; - public ApiModule( + public ProducerModule( IConfiguration configuration, IServiceCollection services, ILoggerFactory loggerFactory) @@ -51,11 +53,8 @@ private void RegisterProjectionSetup(ContainerBuilder builder) new EventHandlingModule( typeof(DomainAssemblyMarker).Assembly, EventsJsonSerializerSettingsProvider.CreateSerializerSettings())) - .RegisterModule() - .RegisterEventstreamModule(_configuration) - .RegisterModule(new ProjectorModule(_configuration)); RegisterProjections(builder); @@ -63,12 +62,26 @@ private void RegisterProjectionSetup(ContainerBuilder builder) private void RegisterProjections(ContainerBuilder builder) { - builder - .RegisterModule( - new ProducerModule( - _configuration, - _services, - _loggerFactory)); + var logger = _loggerFactory.CreateLogger(); + var connectionString = _configuration.GetConnectionString("ProducerProjections"); + + var hasConnectionString = !string.IsNullOrWhiteSpace(connectionString); + if (hasConnectionString) + { + RunOnSqlServer(_services, _loggerFactory, connectionString); + } + else + { + RunInMemoryDb(_services, _loggerFactory, logger); + } + + logger.LogInformation( + "Added {Context} to services:" + + Environment.NewLine + + "\tSchema: {Schema}" + + Environment.NewLine + + "\tTableName: {TableName}", + nameof(ProducerContext), Schema.Producer, MigrationTables.Producer); var connectedProjectionSettings = ConnectedProjectionSettings.Configure(x => { @@ -102,5 +115,34 @@ private void RegisterProjections(ContainerBuilder builder) return new ProducerMigrateProjections(new Producer(producerOptions)); }, connectedProjectionSettings); } + + private static void RunOnSqlServer( + IServiceCollection services, + ILoggerFactory loggerFactory, + string backofficeProjectionsConnectionString) + { + services + .AddDbContext((_, options) => options + .UseLoggerFactory(loggerFactory) + .UseSqlServer(backofficeProjectionsConnectionString, sqlServerOptions => + { + sqlServerOptions.EnableRetryOnFailure(); + sqlServerOptions.MigrationsHistoryTable(MigrationTables.Producer, Schema.Producer); + }) + .UseExtendedSqlServerMigrations()); + } + + private static void RunInMemoryDb( + IServiceCollection services, + ILoggerFactory loggerFactory, + ILogger logger) + { + services + .AddDbContext(options => options + .UseLoggerFactory(loggerFactory) + .UseInMemoryDatabase(Guid.NewGuid().ToString(), sqlServerOptions => { })); + + logger.LogWarning("Running InMemory for {Context}!", nameof(ProducerContext)); + } } } diff --git a/src/StreetNameRegistry.Producer/Infrastructure/Program.cs b/src/StreetNameRegistry.Producer/Infrastructure/Program.cs index 7e5154cb2..e0f3c930b 100644 --- a/src/StreetNameRegistry.Producer/Infrastructure/Program.cs +++ b/src/StreetNameRegistry.Producer/Infrastructure/Program.cs @@ -1,39 +1,158 @@ namespace StreetNameRegistry.Producer.Infrastructure { - using Be.Vlaanderen.Basisregisters.Api; + using System; + using System.IO; + using System.Linq; + using System.Threading.Tasks; + using Autofac; + using Autofac.Extensions.DependencyInjection; using Be.Vlaanderen.Basisregisters.Aws.DistributedMutex; + using Destructurama; using Microsoft.AspNetCore.Hosting; + using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Hosting; + using Microsoft.Extensions.Logging; + using Modules; + using Serilog; + using Serilog.Debugging; + using Serilog.Extensions.Logging; - public class Program + public sealed class Program { - protected Program() + private Program() { } - public static void Main(string[] args) - => Run(new ProgramOptions + public static async Task Main(string[] args) + { + AppDomain.CurrentDomain.FirstChanceException += (_, eventArgs) => + Log.Debug( + eventArgs.Exception, + "FirstChanceException event raised in {AppDomain}.", + AppDomain.CurrentDomain.FriendlyName); + + AppDomain.CurrentDomain.UnhandledException += (_, eventArgs) => + Log.Fatal((Exception)eventArgs.ExceptionObject, "Encountered a fatal exception, exiting program."); + + Log.Information("Initializing StreetNameRegistry.Producer"); + + var host = new HostBuilder() + .ConfigureAppConfiguration((_, configurationBuilder) => { - Hosting = - { - HttpPort = 4014 - }, - Logging = - { - WriteTextToConsole = false, - WriteJsonToConsole = false - }, - Runtime = + configurationBuilder + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: false) + .AddJsonFile($"appsettings.{Environment.MachineName.ToLowerInvariant()}.json", optional: true, reloadOnChange: false) + .AddEnvironmentVariables() + .AddCommandLine(args); + }) + .ConfigureLogging((hostContext, loggingBuilder) => + { + SelfLog.Enable(Console.WriteLine); + + Log.Logger = new LoggerConfiguration() //NOSONAR logging configuration is safe + .ReadFrom.Configuration(hostContext.Configuration) + .Enrich.FromLogContext() + .Enrich.WithMachineName() + .Enrich.WithThreadId() + .Enrich.WithEnvironmentUserName() + .Destructure.JsonNetTypes() + .CreateLogger(); + + loggingBuilder.ClearProviders(); + loggingBuilder.AddSerilog(Log.Logger); + }) + .ConfigureServices((hostContext, services) => + { + var healthChecksBuilder = services.AddHealthChecks(); + var connectionStrings = hostContext.Configuration + .GetSection("ConnectionStrings") + .GetChildren(); + + foreach (var connectionString in connectionStrings) { - CommandLineArgs = args - }, - MiddlewareHooks = + healthChecksBuilder.AddSqlServer( + connectionString.Value, + name: $"sqlserver-{connectionString.Key.ToLowerInvariant()}", + tags: new[] { "db", "sql", "sqlserver" }); + } + + healthChecksBuilder.AddDbContextCheck( + $"dbcontext-{nameof(ProducerContext).ToLowerInvariant()}", + tags: new[] { "db", "sql", "sqlserver" }); + + var origins = hostContext.Configuration + .GetSection("Cors") + .GetChildren() + .Select(c => c.Value) + .ToArray(); + + foreach (var origin in origins) { - ConfigureDistributedLock = DistributedLockOptions.LoadFromConfiguration + services.AddCors(options => + { + options.AddDefaultPolicy(builder => + { + builder.WithOrigins(origin); + }); + }); } - }); + }) + .UseServiceProviderFactory(new AutofacServiceProviderFactory()) + .ConfigureContainer((hostContext, builder) => + { + var services = new ServiceCollection(); + var loggerFactory = new SerilogLoggerFactory(Log.Logger); + + builder.RegisterModule(new ProducerModule(hostContext.Configuration, services, loggerFactory)); + + builder + .RegisterType() + .As() + .SingleInstance(); + + builder.Populate(services); + }) + .ConfigureWebHostDefaults(webHostBuilder => + webHostBuilder + .UseStartup() + .UseKestrel()) + .UseConsoleLifetime() + .Build(); + + Log.Information("Starting StreetNameRegistry.Producer"); + + var logger = host.Services.GetRequiredService>(); + var configuration = host.Services.GetRequiredService(); + + try + { + await DistributedLock.RunAsync( + async () => { await host.RunAsync().ConfigureAwait(false); }, + DistributedLockOptions.LoadFromConfiguration(configuration), + logger) + .ConfigureAwait(false); + } + catch (AggregateException aggregateException) + { + foreach (var innerException in aggregateException.InnerExceptions) + { + logger.LogCritical(innerException, "Encountered a fatal exception, exiting program."); + } + } + catch (Exception e) + { + logger.LogCritical(e, "Encountered a fatal exception, exiting program."); + Log.CloseAndFlush(); - private static void Run(ProgramOptions options) - => new WebHostBuilder() - .UseDefaultForApi(options) - .RunWithLock(); + // Allow some time for flushing before shutdown. + await Task.Delay(500, default); + throw; + } + finally + { + logger.LogInformation("Stopping..."); + } + } } } diff --git a/src/StreetNameRegistry.Producer/Infrastructure/Startup.cs b/src/StreetNameRegistry.Producer/Infrastructure/Startup.cs index 5e4d1c4c0..eb5b7b5d7 100644 --- a/src/StreetNameRegistry.Producer/Infrastructure/Startup.cs +++ b/src/StreetNameRegistry.Producer/Infrastructure/Startup.cs @@ -1,167 +1,24 @@ namespace StreetNameRegistry.Producer.Infrastructure { - using System; - using System.Linq; - using System.Reflection; - using Asp.Versioning.ApiExplorer; - using Autofac; - using Autofac.Extensions.DependencyInjection; - using Be.Vlaanderen.Basisregisters.Api; + using Be.Vlaanderen.Basisregisters.AspNetCore.Mvc.Formatters.Json; using Be.Vlaanderen.Basisregisters.Projector; - using Configuration; using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Diagnostics.HealthChecks; - using Microsoft.Extensions.Hosting; - using Microsoft.Extensions.Logging; - using Microsoft.OpenApi.Models; - using Modules; + using Newtonsoft.Json; - /// Represents the startup process for the application. - public class Startup + public sealed class Startup { - private const string DatabaseTag = "db"; - - private IContainer _applicationContainer; - - private readonly IConfiguration _configuration; - private readonly ILoggerFactory _loggerFactory; - - public Startup( - IConfiguration configuration, - ILoggerFactory loggerFactory) - { - _configuration = configuration; - _loggerFactory = loggerFactory; - } - - /// Configures services for the application. - /// The collection of services to configure the application with. - public IServiceProvider ConfigureServices(IServiceCollection services) - { - var baseUrl = _configuration.GetValue("BaseUrl"); - var baseUrlForExceptions = baseUrl.EndsWith("/") - ? baseUrl.Substring(0, baseUrl.Length - 1) - : baseUrl; - - services - .ConfigureDefaultForApi( - new StartupConfigureOptions - { - Cors = - { - Origins = _configuration - .GetSection("Cors") - .GetChildren() - .Select(c => c.Value) - .ToArray() - }, - Server = - { - BaseUrl = baseUrlForExceptions - }, - Swagger = - { - ApiInfo = (provider, description) => new OpenApiInfo - { - Version = description.ApiVersion.ToString(), - Title = "Basisregisters Vlaanderen StreetName Registry API", - Description = GetApiLeadingText(description), - Contact = new OpenApiContact - { - Name = "Digitaal Vlaanderen", - Email = "digitaal.vlaanderen@vlaanderen.be", - Url = new Uri("https://legacy.basisregisters.vlaanderen") - } - }, - XmlCommentPaths = new[] { typeof(Startup).GetTypeInfo().Assembly.GetName().Name } - }, - MiddlewareHooks = - { - FluentValidation = fv => fv.RegisterValidatorsFromAssemblyContaining(), - - AfterHealthChecks = health => - { - var connectionStrings = _configuration - .GetSection("ConnectionStrings") - .GetChildren(); - - foreach (var connectionString in connectionStrings) - health.AddSqlServer( - connectionString.Value, - name: $"sqlserver-{connectionString.Key.ToLowerInvariant()}", - tags: new[] { DatabaseTag, "sql", "sqlserver" }); - - health.AddDbContextCheck( - $"dbcontext-{nameof(ProducerContext).ToLowerInvariant()}", - tags: new[] { DatabaseTag, "sql", "sqlserver" }); - } - } - }); - - var containerBuilder = new ContainerBuilder(); - containerBuilder.RegisterModule(new LoggingModule(_configuration, services)); - containerBuilder.RegisterModule(new ApiModule(_configuration, services, _loggerFactory)); - _applicationContainer = containerBuilder.Build(); - - return new AutofacServiceProvider(_applicationContainer); - } - - public void Configure( - IServiceProvider serviceProvider, - IApplicationBuilder app, - IWebHostEnvironment env, - IHostApplicationLifetime appLifetime, - ILoggerFactory loggerFactory, - IApiVersionDescriptionProvider apiVersionProvider, - HealthCheckService healthCheckService) + public void Configure(IApplicationBuilder app) { - StartupHelpers.CheckDatabases(healthCheckService, DatabaseTag, loggerFactory).GetAwaiter().GetResult(); + app.UseRouting(); + app.UseCors(); - app - .UseDefaultForApi(new StartupUseOptions - { - Common = - { - ApplicationContainer = _applicationContainer, - ServiceProvider = serviceProvider, - HostingEnvironment = env, - ApplicationLifetime = appLifetime, - LoggerFactory = loggerFactory - }, - Api = - { - VersionProvider = apiVersionProvider, - Info = groupName => $"Basisregisters Vlaanderen - StreetName Registry API {groupName}", - CSharpClientOptions = - { - ClassName = "StreetNameRegistryProducer", - Namespace = "Be.Vlaanderen.Basisregisters" - }, - TypeScriptClientOptions = - { - ClassName = "StreetNameRegistryProducer" - } - }, - MiddlewareHooks = - { - AfterMiddleware = x => x.UseMiddleware(), - } - }) + app.UseHealthChecks("/health"); - .UseProjectionsManager(new ProjectionsManagerOptions - { - Common = - { - ServiceProvider = serviceProvider, - ApplicationLifetime = appLifetime - } - }); + var configuration = app.ApplicationServices.GetRequiredService(); + var baseUri = configuration.GetValue("BaseUrl").TrimEnd('/'); + app.UseProjectorEndpoints(baseUri, new JsonSerializerSettings().ConfigureDefaultForApi()); } - - private static string GetApiLeadingText(ApiVersionDescription description) - => $"Momenteel leest u de documentatie voor versie {description.ApiVersion} van de Basisregisters Vlaanderen StreetName Registry Producer API{string.Format(description.IsDeprecated ? ", **deze API versie is niet meer ondersteund * *." : ".")}"; } } diff --git a/src/StreetNameRegistry.Producer/ProducerModule.cs b/src/StreetNameRegistry.Producer/ProducerModule.cs deleted file mode 100644 index 02a8414fd..000000000 --- a/src/StreetNameRegistry.Producer/ProducerModule.cs +++ /dev/null @@ -1,70 +0,0 @@ -namespace StreetNameRegistry.Producer -{ - using System; - using Autofac; - using Be.Vlaanderen.Basisregisters.ProjectionHandling.Runner.SqlServer.MigrationExtensions; - using Microsoft.EntityFrameworkCore; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Logging; - using StreetNameRegistry.Infrastructure; - - public class ProducerModule : Module - { - public ProducerModule( - IConfiguration configuration, - IServiceCollection services, - ILoggerFactory loggerFactory) - { - var logger = loggerFactory.CreateLogger(); - var connectionString = configuration.GetConnectionString("ProducerProjections"); - - var hasConnectionString = !string.IsNullOrWhiteSpace(connectionString); - if (hasConnectionString) - { - RunOnSqlServer(services, loggerFactory, connectionString); - } - else - { - RunInMemoryDb(services, loggerFactory, logger); - } - - logger.LogInformation( - "Added {Context} to services:" + - Environment.NewLine + - "\tSchema: {Schema}" + - Environment.NewLine + - "\tTableName: {TableName}", - nameof(ProducerContext), Schema.Producer, MigrationTables.Producer); - } - - private static void RunOnSqlServer( - IServiceCollection services, - ILoggerFactory loggerFactory, - string backofficeProjectionsConnectionString) - { - services - .AddDbContext((_, options) => options - .UseLoggerFactory(loggerFactory) - .UseSqlServer(backofficeProjectionsConnectionString, sqlServerOptions => - { - sqlServerOptions.EnableRetryOnFailure(); - sqlServerOptions.MigrationsHistoryTable(MigrationTables.Producer, Schema.Producer); - }) - .UseExtendedSqlServerMigrations()); - } - - private static void RunInMemoryDb( - IServiceCollection services, - ILoggerFactory loggerFactory, - ILogger logger) - { - services - .AddDbContext(options => options - .UseLoggerFactory(loggerFactory) - .UseInMemoryDatabase(Guid.NewGuid().ToString(), sqlServerOptions => { })); - - logger.LogWarning("Running InMemory for {Context}!", nameof(ProducerContext)); - } - } -} diff --git a/src/StreetNameRegistry.Producer/Projections/ProjectionsController.cs b/src/StreetNameRegistry.Producer/Projections/ProjectionsController.cs deleted file mode 100644 index fc861237c..000000000 --- a/src/StreetNameRegistry.Producer/Projections/ProjectionsController.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace StreetNameRegistry.Producer.Projections -{ - using Asp.Versioning; - using Be.Vlaanderen.Basisregisters.Api; - using Be.Vlaanderen.Basisregisters.Projector.ConnectedProjections; - using Be.Vlaanderen.Basisregisters.Projector.Controllers; - using Microsoft.Extensions.Configuration; - using StreetNameRegistry.Infrastructure; - - [ApiVersion("1.0")] - [ApiRoute("projections")] - public class ProjectionsController : DefaultProjectorController - { - public ProjectionsController( - IConnectedProjectionsManager connectedProjectionsManager, - IConfiguration configuration) - : base( - connectedProjectionsManager, - configuration.GetValue("BaseUrl")) - { - RegisterConnectionString(Schema.Producer, configuration.GetConnectionString("ProducerProjections")); - } - } -} diff --git a/src/StreetNameRegistry.Producer/StreetNameProducer.cs b/src/StreetNameRegistry.Producer/StreetNameProducer.cs new file mode 100644 index 000000000..b22cb2bb3 --- /dev/null +++ b/src/StreetNameRegistry.Producer/StreetNameProducer.cs @@ -0,0 +1,40 @@ +namespace StreetNameRegistry.Producer +{ + using System; + using System.Threading; + using System.Threading.Tasks; + using Be.Vlaanderen.Basisregisters.Projector.ConnectedProjections; + using Microsoft.Extensions.Hosting; + using Microsoft.Extensions.Logging; + + public sealed class StreetNameProducer : BackgroundService + { + private readonly IHostApplicationLifetime _hostApplicationLifetime; + private readonly ILogger _logger; + private readonly IConnectedProjectionsManager _projectionManager; + + public StreetNameProducer( + IHostApplicationLifetime hostApplicationLifetime, + IConnectedProjectionsManager projectionManager, + ILogger logger) + { + _hostApplicationLifetime = hostApplicationLifetime; + _projectionManager = projectionManager; + _logger = logger; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + try + { + await _projectionManager.Start(stoppingToken); + } + catch (Exception exception) + { + _logger.LogCritical(exception, $"Critical error occured in {nameof(StreetNameProducer)}."); + _hostApplicationLifetime.StopApplication(); + throw; + } + } + } +} diff --git a/src/StreetNameRegistry.Producer/StreetNameRegistry.Producer.csproj b/src/StreetNameRegistry.Producer/StreetNameRegistry.Producer.csproj index 9a6ceecb0..678998beb 100644 --- a/src/StreetNameRegistry.Producer/StreetNameRegistry.Producer.csproj +++ b/src/StreetNameRegistry.Producer/StreetNameRegistry.Producer.csproj @@ -2,8 +2,6 @@ - - false true false