using System.IO; using System.Reflection; using Microsoft.AspNetCore.Builder; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.OpenApi.Models; using Newtonsoft.Json; using MassTransit; using LCU.Library.Internal.Configuration; using LCU.Library.Internal.LcuMassTransit; using LCU.Library.Internal.LcuMassTransitModels; using LCU.Library.Internal.Logging; using LCU.Library.Internal.Logging.Middleware; using LCU.Library.Internal.Logging.Models; using LCU.Api.Internal.Dispatcher.Configurations; using LCU.Api.Internal.Dispatcher.Consumers; using LCU.Api.Internal.Dispatcher.HostedServices; using LCU.Api.Internal.Dispatcher.Models; using LCU.Api.Internal.Dispatcher.Services; using LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices; using LCU.Api.Internal.Dispatcher.Services.QueueServices; using LCU.Api.Internal.DispatcherLib.Persistence; using LCU.Api.Internal.DispatcherLib.Repositories; using LCU.Api.Internal.DispatcherSharedLib; using System.Collections.Generic; using LCU.Library.Internal.ApiSecurity.Constants; using LCU.Library.Internal.ApiSecurity; using LCU.Library.Internal.LcuHttpClient; using LCU.Api.Internal.Dispatcher.DataAccess; string MyAllowSpecificOrigins = "_myAllowSpecificOrigins"; ApiConfiguration _apiConfiguration; var builder = BuildBuilder(args); BuildServicesAndConfigurations(builder); BuildAppAndRun(builder); WebApplicationBuilder BuildBuilder(string[] args) { var builder = WebApplication.CreateBuilder(args); builder.Configuration.AddJsonFile("/app/config/environment-appsettings/appsettings.json", optional: true, reloadOnChange: true); builder.Configuration.AddJsonFile("/app/config/workload-appsettings/appsettings.json", optional: true, reloadOnChange: true); builder.Configuration.AddJsonFile("/vault/secrets/dispatcher-api-database-connection.json", optional: true, reloadOnChange: true); builder.Configuration.AddJsonFile("/vault/secrets/dispatcher-api-seq-api-key.json", optional: true, reloadOnChange: true); builder.Configuration.AddJsonFile("/vault/secrets/dispatcher-api-rabbitmq-username.json", optional: true, reloadOnChange: true); builder.Configuration.AddJsonFile("/vault/secrets/dispatcher-api-rabbitmq-password.json", optional: true, reloadOnChange: true); builder.Configuration.AddJsonFile("/vault/secrets/dispatcher-api-external-api-key-set.json", optional: true, reloadOnChange: true); builder.Host.UseSerilog(); return builder; } void BuildServicesAndConfigurations(WebApplicationBuilder builder) { var services = builder.Services; var configuration = builder.Configuration; services.AddControllers(); services.AddEndpointsApiExplorer(); services.AddSwaggerGen(); services.AddHttpClient(); _apiConfiguration = configuration.Bind(services, "Primary"); var connectionsConfiguration = configuration.Bind(services); var loggerConfiguration = new LoggingConfiguration(_apiConfiguration.AppName, _apiConfiguration.SeqApiKey, _apiConfiguration.SeqUrl); var lcuLogging = new LcuLogging(loggerConfiguration); lcuLogging.UseLcuLogging(); var connectionConfiguration = MassTransitConfigurationSetUp.AddConnectionConfiguration(ref services, configuration); //debugging Console.WriteLine(JsonConvert.SerializeObject(connectionConfiguration)); var receiveEndpointConfigurations = new ReceiveEndpointConfigurations() { ExecutionReceiveEndpoint = MassTransitConfigurationSetUp.GetReceiveEndpointConfiguration(configuration, "ExecutionReceiveEndpoint"), CompletionReceiveEndpoint = MassTransitConfigurationSetUp.GetReceiveEndpointConfiguration(configuration, "CompletionReceiveEndpoint"), DetermineOperationReceiveEndpoint = MassTransitConfigurationSetUp.GetReceiveEndpointConfiguration(configuration, "DetermineOperationReceiveEndpoint"), JobFailureReceiveEndpoint = MassTransitConfigurationSetUp.GetReceiveEndpointConfiguration(configuration, "JobFailureReceiveEndpoint"), SendErroredJobAlertReceiveEndpoint = MassTransitConfigurationSetUp.GetReceiveEndpointConfiguration(configuration, "SendErroredJobAlertReceiveEndpoint") }; services.AddSingleton(receiveEndpointConfigurations); //debugging Console.WriteLine(JsonConvert.SerializeObject(receiveEndpointConfigurations)); services.AddCors(options => { options.AddPolicy(MyAllowSpecificOrigins, builder => { builder.WithOrigins( _apiConfiguration.CorsAllowedOrigins ).AllowAnyHeader().AllowAnyMethod(); }); }); services.AddDbContextPool(options => { options.UseSqlServer( connectionsConfiguration.Sql.DispatcherDb, sqlServerOptionsAction: sqlOptions => { sqlOptions.EnableRetryOnFailure( maxRetryCount: connectionsConfiguration.Sql.SQLOptions.MaxRetryCount, maxRetryDelay: connectionsConfiguration.Sql.SQLOptions.MaxRetryDelay, errorNumbersToAdd: connectionsConfiguration.Sql.SQLOptions.ErrorNumbersToAdd ); sqlOptions.CommandTimeout(connectionsConfiguration.Sql.SQLOptions.CommandTimeout); } ); options.EnableDetailedErrors(connectionsConfiguration.Sql.SQLOptions.EnableDetailedErrors); }); AddMassTransitServices(ref services, connectionConfiguration, receiveEndpointConfigurations, _apiConfiguration); services.AddDispatcherServices(configuration); services.AddLCUHttpServices(); AddServices(services); AddRepositories(services); AddHostedServices(services); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = $"{_apiConfiguration.AppName}", Version = "v1" }); //Add Security Entry Fields In Swagger c.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Id = HeaderKeys.ApiKey, // Must match the "AddSecurityDefinition" Type = ReferenceType.SecurityScheme } }, new List() } }); c.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Id = HeaderKeys.Client, // Must match the "AddSecurityDefinition" Type = ReferenceType.SecurityScheme } }, new List() } }); c.AddSecurityDefinition(HeaderKeys.ApiKey, new OpenApiSecurityScheme { Description = HeaderKeys.ApiKey, In = ParameterLocation.Header, Name = HeaderKeys.ApiKey, Type = SecuritySchemeType.ApiKey, Scheme = "apikey" }); c.AddSecurityDefinition(HeaderKeys.Client, new OpenApiSecurityScheme { Description = HeaderKeys.Client, In = ParameterLocation.Header, Name = HeaderKeys.Client, Type = SecuritySchemeType.ApiKey, Scheme = "apikey" }); }); builder.Services.AddApiKeySecurity(builder.Configuration); } void AddMassTransitServices(ref IServiceCollection services, ConnectionConfiguration connectionConfiguration, ReceiveEndpointConfigurations receiveEndpointConfigurations, ApiConfiguration apiConfiguration) { services.AddMassTransit(x => { x.AddConsumer(); x.AddConsumer(); x.AddConsumer(); x.AddConsumer(); x.AddConsumer(); x.UsingRabbitMq((context, cfg) => { cfg.Host(connectionConfiguration.HostUrl, "/", host => { host.Username(connectionConfiguration.UserName); host.Password(connectionConfiguration.Password); }); if (apiConfiguration.FeatureFlags.RunExecutionConsumer) { cfg.ReceiveEndpoint(receiveEndpointConfigurations.ExecutionReceiveEndpoint.ReceiveEndpoint, e => { e.ConfigureConsumer(context); e.SetQuorumQueue(); e.PrefetchCount = receiveEndpointConfigurations.CompletionReceiveEndpoint.PrefetchLimit; }); } if (apiConfiguration.FeatureFlags.RunCompletionConsumer) { cfg.ReceiveEndpoint(receiveEndpointConfigurations.CompletionReceiveEndpoint.ReceiveEndpoint, e => { e.ConfigureConsumer(context); e.SetQuorumQueue(); e.PrefetchCount = receiveEndpointConfigurations.CompletionReceiveEndpoint.PrefetchLimit; }); } if (apiConfiguration.FeatureFlags.RunDetermineOperationConsumer) { cfg.ReceiveEndpoint(receiveEndpointConfigurations.DetermineOperationReceiveEndpoint.ReceiveEndpoint, e => { e.ConfigureConsumer(context); e.SetQuorumQueue(); e.PrefetchCount = receiveEndpointConfigurations.DetermineOperationReceiveEndpoint.PrefetchLimit; }); } if (apiConfiguration.FeatureFlags.RunJobFailureConsumer) { cfg.ReceiveEndpoint(receiveEndpointConfigurations.JobFailureReceiveEndpoint.ReceiveEndpoint, e => { e.ConfigureConsumer(context); e.SetQuorumQueue(); e.PrefetchCount = receiveEndpointConfigurations.JobFailureReceiveEndpoint.PrefetchLimit; }); } if (apiConfiguration.FeatureFlags.RunSendErroredJobAlertConsumer) { cfg.ReceiveEndpoint(receiveEndpointConfigurations.SendErroredJobAlertReceiveEndpoint.ReceiveEndpoint, e => { e.ConfigureConsumer(context, cons => { cons.Options(options => options .SetMessageLimit(200) .SetTimeLimit(5000) .SetConcurrencyLimit(1)); }); e.SetQuorumQueue(); e.PrefetchCount = receiveEndpointConfigurations.SendErroredJobAlertReceiveEndpoint.PrefetchLimit; }); } }); }); services.AddMassTransitHostedService(); } void AddServices(IServiceCollection services) { services.AddScoped(); AddDispatcherOperationServices(services); AddQueueServices(services); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); } void AddDispatcherOperationServices(IServiceCollection services) { services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); } void AddQueueServices(IServiceCollection services) { services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); } void AddRepositories(IServiceCollection services) { services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); } void AddHostedServices(IServiceCollection services) { services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); } void BuildAppAndRun(WebApplicationBuilder builder) { var app = builder.Build(); if (_apiConfiguration.FeatureFlags.RunCleanUpJobsHostedService) { var service = app.Services.GetRequiredService(); service.StartAsync().Wait(); } if (_apiConfiguration.FeatureFlags.RunJobSchedulerHostedService) { var service = app.Services.GetRequiredService(); service.StartAsync().Wait(); } if (_apiConfiguration.FeatureFlags.RunBatchErroredJobsHostedService) { var service = app.Services.GetRequiredService(); service.StartAsync().Wait(); } // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseSwagger(c => { c.PreSerializeFilters.Add((swaggerDoc, request) => { const string prefixHeader = "X-Forwarded-Prefix"; if (request.Headers.ContainsKey(prefixHeader)) return; var serverUrl = request.Headers[prefixHeader]; if (!String.IsNullOrWhiteSpace(serverUrl)) { swaggerDoc.Servers = new List() { new() { Description = serverUrl, Url = serverUrl } }; } }); }); app.UseSwaggerUI(c => { c.RoutePrefix = "swagger"; c.SwaggerEndpoint("v1/swagger.json", "LCU.Api.Internal.Dispatcher v1"); }); app.UseMiddleware(); app.UseSerilogRequestLogging(options => { options.EnrichDiagnosticContext = RequestResponseLoggingMiddleware.RequestDataParser.EnrichFromRequest; }); app.UseRouting(); app.UseCors(MyAllowSpecificOrigins); app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); app.Run(); }