143 lines
5.5 KiB
C#
143 lines
5.5 KiB
C#
using Elastic.Clients.Elasticsearch;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Serilog;
|
|
using StackExchange.Redis;
|
|
using System.Text.Json.Serialization;
|
|
|
|
Log.Logger = new LoggerConfiguration()
|
|
.WriteTo.Console()
|
|
.CreateBootstrapLogger();
|
|
|
|
try
|
|
{
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
// Serilog's reloadable logger can only be frozen once per process; skip in
|
|
// integration tests where WebApplicationFactory may build multiple hosts.
|
|
if (!builder.Environment.IsEnvironment("Testing"))
|
|
{
|
|
builder.Host.UseSerilog((ctx, services, config) =>
|
|
config.ReadFrom.Configuration(ctx.Configuration)
|
|
.ReadFrom.Services(services)
|
|
.Enrich.FromLogContext());
|
|
}
|
|
|
|
builder.Services.AddDbContext<AppDbContext>(opts =>
|
|
opts.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
|
|
|
|
builder.Services.AddSingleton<IConnectionMultiplexer>(sp =>
|
|
ConnectionMultiplexer.Connect(sp.GetRequiredService<IConfiguration>()["Redis:ConnectionString"]!));
|
|
|
|
builder.Services.Configure<KafkaOptions>(
|
|
builder.Configuration.GetSection(KafkaOptions.Section));
|
|
|
|
builder.Services.Configure<ElasticsearchOptions>(
|
|
builder.Configuration.GetSection(ElasticsearchOptions.Section));
|
|
|
|
var esOptions = builder.Configuration
|
|
.GetSection(ElasticsearchOptions.Section)
|
|
.Get<ElasticsearchOptions>()!;
|
|
|
|
builder.Services.AddSingleton(
|
|
new ElasticsearchClient(new Uri(esOptions.Uri)));
|
|
|
|
builder.Services.Configure<RabbitMqOptions>(
|
|
builder.Configuration.GetSection(RabbitMqOptions.Section));
|
|
builder.Services.Configure<MinioOptions>(
|
|
builder.Configuration.GetSection(MinioOptions.Section));
|
|
|
|
builder.Services.AddSingleton<RabbitMqTopologyProvisioner>();
|
|
builder.Services.AddHostedService(sp => sp.GetRequiredService<RabbitMqTopologyProvisioner>());
|
|
|
|
builder.Services.Configure<ReconciliationJobOptions>(
|
|
builder.Configuration.GetSection(ReconciliationJobOptions.Section));
|
|
|
|
builder.Services.AddScoped<IPatientService, PatientService>();
|
|
builder.Services.AddScoped<IEncounterService, EncounterService>();
|
|
builder.Services.AddScoped<IAlertThresholdService, AlertThresholdService>();
|
|
builder.Services.AddScoped<IObservationService, ObservationService>();
|
|
builder.Services.AddScoped<IObservationQueryService, ObservationQueryService>();
|
|
builder.Services.AddScoped<IAlertService, AlertService>();
|
|
builder.Services.AddScoped<IAnalyticsService, AnalyticsService>();
|
|
builder.Services.AddScoped<SirsDetector>();
|
|
builder.Services.AddScoped<UnacknowledgedAlertsCheck>();
|
|
builder.Services.AddScoped<PendingOrdersCheck>();
|
|
builder.Services.AddScoped<DisconnectedMonitorsCheck>();
|
|
builder.Services.AddScoped<ReconciliationPublisher>();
|
|
|
|
|
|
builder.Services.AddHostedService<ThresholdCacheLoader>();
|
|
builder.Services.AddHostedService<KafkaTopicProvisioner>();
|
|
builder.Services.AddHostedService<OutboxRelayService>();
|
|
builder.Services.AddHostedService<ElasticIndexProvisioner>();
|
|
builder.Services.AddHostedService<EsIndexerService>();
|
|
builder.Services.AddHostedService<SepsisEngineService>();
|
|
builder.Services.AddHostedService<NotificationPublisherService>();
|
|
builder.Services.AddHostedService<PagingWorkerService>();
|
|
builder.Services.AddHostedService<EscalationWorkerService>();
|
|
builder.Services.AddHostedService<DischargeSummaryWorkerService>();
|
|
builder.Services.AddHostedService<ReconciliationScheduler>();
|
|
|
|
builder.Services.AddControllers()
|
|
.AddJsonOptions(opts =>
|
|
{
|
|
opts.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
|
|
opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
|
|
opts.JsonSerializerOptions.Converters.Add(new ObservationSourceJsonConverter());
|
|
opts.JsonSerializerOptions.Converters.Add(new DepartmentJsonConverter());
|
|
});
|
|
builder.Services.AddEndpointsApiExplorer();
|
|
builder.Services.AddSwaggerGen();
|
|
|
|
var app = builder.Build();
|
|
|
|
if (!app.Environment.IsEnvironment("Testing"))
|
|
{
|
|
using var scope = app.Services.CreateScope();
|
|
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
|
var redis = scope.ServiceProvider.GetRequiredService<IConnectionMultiplexer>();
|
|
await DataSeeder.SeedAsync(db, redis);
|
|
}
|
|
|
|
if (!app.Environment.IsEnvironment("Testing"))
|
|
{
|
|
app.UseSerilogRequestLogging(options =>
|
|
{
|
|
options.MessageTemplate =
|
|
"HTTP {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.000}ms";
|
|
});
|
|
}
|
|
|
|
app.UseMiddleware<CorrelationIdMiddleware>();
|
|
app.UseMiddleware<ExceptionHandlerMiddleware>();
|
|
|
|
// Configure the HTTP request pipeline.
|
|
if (app.Environment.IsDevelopment())
|
|
{
|
|
app.UseSwagger();
|
|
app.UseSwaggerUI();
|
|
}
|
|
|
|
app.MapControllers();
|
|
app.Run();
|
|
|
|
}
|
|
/***
|
|
dotnet ef tools use HostFactoryResolver which throws HostAbortedException internally as a control-flow mechanism to stop the host after discovering the DbContext.
|
|
Your generic catch was swallowing it instead of letting it propagate, so EF saw the process exit abnormally.
|
|
***/
|
|
catch (HostAbortedException)
|
|
{
|
|
throw;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Fatal(ex, "Application failed to start.");
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
Log.CloseAndFlush();
|
|
}
|
|
|
|
public partial class Program { } |