diff --git a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs index 2721bf09f..7628f1db7 100644 --- a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs +++ b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs @@ -1,4 +1,5 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; +using Microsoft.Extensions.DependencyInjection; public class EventBusRabbitMQ : IEventBus, IDisposable { @@ -8,21 +9,21 @@ public class EventBusRabbitMQ : IEventBus, IDisposable private readonly IRabbitMQPersistentConnection _persistentConnection; private readonly ILogger _logger; private readonly IEventBusSubscriptionsManager _subsManager; - private readonly ILifetimeScope _autofac; + private readonly IServiceProvider _serviceProvider; private readonly int _retryCount; private IModel _consumerChannel; private string _queueName; public EventBusRabbitMQ(IRabbitMQPersistentConnection persistentConnection, ILogger logger, - ILifetimeScope autofac, IEventBusSubscriptionsManager subsManager, string queueName = null, int retryCount = 5) + IServiceProvider serviceProvider, IEventBusSubscriptionsManager subsManager, string queueName = null, int retryCount = 5) { _persistentConnection = persistentConnection ?? throw new ArgumentNullException(nameof(persistentConnection)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _subsManager = subsManager ?? new InMemoryEventBusSubscriptionsManager(); _queueName = queueName; _consumerChannel = CreateConsumerChannel(); - _autofac = autofac; + _serviceProvider = serviceProvider; _retryCount = retryCount; _subsManager.OnEventRemoved += SubsManager_OnEventRemoved; } @@ -240,20 +241,20 @@ public class EventBusRabbitMQ : IEventBus, IDisposable if (_subsManager.HasSubscriptionsForEvent(eventName)) { - await using var scope = _autofac.BeginLifetimeScope(AUTOFAC_SCOPE_NAME); + using var scope = _serviceProvider.CreateScope(); var subscriptions = _subsManager.GetHandlersForEvent(eventName); foreach (var subscription in subscriptions) { if (subscription.IsDynamic) { - if (scope.ResolveOptional(subscription.HandlerType) is not IDynamicIntegrationEventHandler handler) continue; + if (scope.ServiceProvider.GetService(subscription.HandlerType) is not IDynamicIntegrationEventHandler handler) continue; using dynamic eventData = JsonDocument.Parse(message); await Task.Yield(); await handler.Handle(eventData); } else { - var handler = scope.ResolveOptional(subscription.HandlerType); + var handler = scope.ServiceProvider.GetService(subscription.HandlerType); if (handler == null) continue; var eventType = _subsManager.GetEventTypeByName(eventName); var integrationEvent = JsonSerializer.Deserialize(message, eventType, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }); diff --git a/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs b/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs index b1dce9917..46a5a1c64 100644 --- a/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs +++ b/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs @@ -1,3 +1,5 @@ +using Microsoft.Extensions.DependencyInjection; + namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; public class EventBusServiceBus : IEventBus, IAsyncDisposable @@ -5,7 +7,7 @@ public class EventBusServiceBus : IEventBus, IAsyncDisposable private readonly IServiceBusPersisterConnection _serviceBusPersisterConnection; private readonly ILogger _logger; private readonly IEventBusSubscriptionsManager _subsManager; - private readonly ILifetimeScope _autofac; + private readonly IServiceProvider _serviceProvider; private readonly string _topicName = "eshop_event_bus"; private readonly string _subscriptionName; private readonly ServiceBusSender _sender; @@ -14,12 +16,12 @@ public class EventBusServiceBus : IEventBus, IAsyncDisposable private const string INTEGRATION_EVENT_SUFFIX = "IntegrationEvent"; public EventBusServiceBus(IServiceBusPersisterConnection serviceBusPersisterConnection, - ILogger logger, IEventBusSubscriptionsManager subsManager, ILifetimeScope autofac, string subscriptionClientName) + ILogger logger, IEventBusSubscriptionsManager subsManager, IServiceProvider serviceProvider, string subscriptionClientName) { _serviceBusPersisterConnection = serviceBusPersisterConnection; _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _subsManager = subsManager ?? new InMemoryEventBusSubscriptionsManager(); - _autofac = autofac; + _serviceProvider = serviceProvider; _subscriptionName = subscriptionClientName; _sender = _serviceBusPersisterConnection.TopicClient.CreateSender(_topicName); ServiceBusProcessorOptions options = new ServiceBusProcessorOptions { MaxConcurrentCalls = 10, AutoCompleteMessages = false }; @@ -149,20 +151,20 @@ public class EventBusServiceBus : IEventBus, IAsyncDisposable var processed = false; if (_subsManager.HasSubscriptionsForEvent(eventName)) { - var scope = _autofac.BeginLifetimeScope(AUTOFAC_SCOPE_NAME); + var scope = _serviceProvider.CreateScope(); var subscriptions = _subsManager.GetHandlersForEvent(eventName); foreach (var subscription in subscriptions) { if (subscription.IsDynamic) { - if (scope.ResolveOptional(subscription.HandlerType) is not IDynamicIntegrationEventHandler handler) continue; + if (scope.ServiceProvider.GetService(subscription.HandlerType) is not IDynamicIntegrationEventHandler handler) continue; using dynamic eventData = JsonDocument.Parse(message); await handler.Handle(eventData); } else { - var handler = scope.ResolveOptional(subscription.HandlerType); + var handler = scope.ServiceProvider.GetService(subscription.HandlerType); if (handler == null) continue; var eventType = _subsManager.GetEventTypeByName(eventName); var integrationEvent = JsonSerializer.Deserialize(message, eventType); diff --git a/src/Services/Basket/Basket.API/Startup.cs b/src/Services/Basket/Basket.API/Startup.cs index a03ebda9a..8c2631e5c 100644 --- a/src/Services/Basket/Basket.API/Startup.cs +++ b/src/Services/Basket/Basket.API/Startup.cs @@ -244,13 +244,12 @@ public class Startup services.AddSingleton(sp => { var serviceBusPersisterConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); var logger = sp.GetRequiredService>(); var eventBusSubscriptionsManager = sp.GetRequiredService(); string subscriptionName = Configuration["SubscriptionClientName"]; return new EventBusServiceBus(serviceBusPersisterConnection, logger, - eventBusSubscriptionsManager, iLifetimeScope, subscriptionName); + eventBusSubscriptionsManager, sp, subscriptionName); }); } else @@ -259,7 +258,6 @@ public class Startup { var subscriptionClientName = Configuration["SubscriptionClientName"]; var rabbitMQPersistentConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); var logger = sp.GetRequiredService>(); var eventBusSubscriptionsManager = sp.GetRequiredService(); @@ -269,7 +267,7 @@ public class Startup retryCount = int.Parse(Configuration["EventBusRetryCount"]); } - return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubscriptionsManager, subscriptionClientName, retryCount); + return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, sp, eventBusSubscriptionsManager, subscriptionClientName, retryCount); }); } diff --git a/src/Services/Catalog/Catalog.API/GlobalUsings.cs b/src/Services/Catalog/Catalog.API/GlobalUsings.cs index 278e9408a..277288adc 100644 --- a/src/Services/Catalog/Catalog.API/GlobalUsings.cs +++ b/src/Services/Catalog/Catalog.API/GlobalUsings.cs @@ -59,4 +59,5 @@ global using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents. global using Microsoft.Extensions.Diagnostics.HealthChecks; global using Microsoft.OpenApi.Models; global using RabbitMQ.Client; -global using System.Reflection; \ No newline at end of file +global using System.Reflection; +global using Microsoft.Extensions.FileProviders; \ No newline at end of file diff --git a/src/Services/Catalog/Catalog.API/Program.cs b/src/Services/Catalog/Catalog.API/Program.cs index 0e51b9a25..aa594b1fc 100644 --- a/src/Services/Catalog/Catalog.API/Program.cs +++ b/src/Services/Catalog/Catalog.API/Program.cs @@ -1,106 +1,332 @@ -var configuration = GetConfiguration(); +var appName = "Catalog.API"; +var builder = WebApplication.CreateBuilder(new WebApplicationOptions { + Args = args, + ApplicationName = typeof(Program).Assembly.FullName, + ContentRootPath = Directory.GetCurrentDirectory(), + WebRootPath = "Pics", +}); +if (builder.Configuration.GetValue("UseVault", false)) { + TokenCredential credential = new ClientSecretCredential( + builder.Configuration["Vault:TenantId"], + builder.Configuration["Vault:ClientId"], + builder.Configuration["Vault:ClientSecret"]); + //builder.AddAzureKeyVault(new Uri($"https://{builder.Configuration["Vault:Name"]}.vault.azure.net/"), credential); +} +builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); +builder.WebHost.UseKestrel(options => { + var ports = GetDefinedPorts(builder.Configuration); + options.Listen(IPAddress.Any, ports.httpPort, listenOptions => { + listenOptions.Protocols = HttpProtocols.Http1AndHttp2; + }); + options.Listen(IPAddress.Any, ports.grpcPort, listenOptions => { + listenOptions.Protocols = HttpProtocols.Http2; + }); -Log.Logger = CreateSerilogLogger(configuration); +}); +builder.Services.AddAppInsight(builder.Configuration); +builder.Services.AddGrpc().Services + .AddCustomMVC(builder.Configuration) + .AddCustomDbContext(builder.Configuration) + .AddCustomOptions(builder.Configuration) + .AddCustomHealthCheck(builder.Configuration) + .AddIntegrationServices(builder.Configuration) + .AddEventBus(builder.Configuration) + .AddSwagger(builder.Configuration); -try -{ - Log.Information("Configuring web host ({ApplicationContext})...", Program.AppName); - var host = CreateHostBuilder(configuration, args); +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) { + app.UseDeveloperExceptionPage(); +} +else { + app.UseExceptionHandler("/Home/Error"); +} + +var pathBase = app.Configuration["PATH_BASE"]; +if (!string.IsNullOrEmpty(pathBase)) { + app.UsePathBase(pathBase); +} - Log.Information("Applying migrations ({ApplicationContext})...", Program.AppName); - host.MigrateDbContext((context, services) => - { - var env = services.GetService(); - var settings = services.GetService>(); - var logger = services.GetService>(); +app.UseSwagger() + .UseSwaggerUI(c => { + c.SwaggerEndpoint($"{(!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty)}/swagger/v1/swagger.json", "Catalog.API V1"); + }); - new CatalogContextSeed().SeedAsync(context, env, settings, logger).Wait(); - }) - .MigrateDbContext((_, __) => { }); +app.UseRouting(); +app.UseCors("CorsPolicy"); +app.MapDefaultControllerRoute(); +app.MapControllers(); +app.UseFileServer(new FileServerOptions { + FileProvider = new PhysicalFileProvider(Path.Combine(app.Environment.ContentRootPath, "Pics")), + RequestPath = "/pics" +}); +app.UseStaticFiles(new StaticFileOptions { + FileProvider = new PhysicalFileProvider(Path.Combine(app.Environment.ContentRootPath, "Pics")), + RequestPath = "/pics" +}); +app.MapGet("/_proto/", async ctx => { + ctx.Response.ContentType = "text/plain"; + using var fs = new FileStream(Path.Combine(app.Environment.ContentRootPath, "Proto", "catalog.proto"), FileMode.Open, FileAccess.Read); + using var sr = new StreamReader(fs); + while (!sr.EndOfStream) { + var line = await sr.ReadLineAsync(); + if (line != "/* >>" || line != "<< */") { + await ctx.Response.WriteAsync(line); + } + } +}); +app.MapGrpcService(); +app.MapHealthChecks("/hc", new HealthCheckOptions() { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse +}); +app.MapHealthChecks("/liveness", new HealthCheckOptions { + Predicate = r => r.Name.Contains("self") +}); - Log.Information("Starting web host ({ApplicationContext})...", Program.AppName); - host.Run(); +ConfigureEventBus(app); + + + +try { + Log.Information("Configuring web host ({ApplicationContext})...", Program.AppName); + using var scope = app.Services.CreateScope(); + var context = scope.ServiceProvider.GetRequiredService(); + var env = app.Services.GetService(); + var settings = app.Services.GetService>(); + var logger = app.Services.GetService>(); + await context.Database.MigrateAsync(); + + await new CatalogContextSeed().SeedAsync(context, env, settings, logger); + var integEventContext = scope.ServiceProvider.GetRequiredService(); + await integEventContext.Database.MigrateAsync(); + app.Logger.LogInformation("Starting web host ({ApplicationName})...", appName); + await app.RunAsync(); return 0; } -catch (Exception ex) -{ +catch (Exception ex) { Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", Program.AppName); return 1; } -finally -{ +finally { Log.CloseAndFlush(); } - -IWebHost CreateHostBuilder(IConfiguration configuration, string[] args) => - WebHost.CreateDefaultBuilder(args) - .ConfigureAppConfiguration(x => x.AddConfiguration(configuration)) - .CaptureStartupErrors(false) - .ConfigureKestrel(options => - { - var ports = GetDefinedPorts(configuration); - options.Listen(IPAddress.Any, ports.httpPort, listenOptions => - { - listenOptions.Protocols = HttpProtocols.Http1AndHttp2; - }); - options.Listen(IPAddress.Any, ports.grpcPort, listenOptions => - { - listenOptions.Protocols = HttpProtocols.Http2; - }); - - }) - .UseStartup() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseWebRoot("Pics") - .UseSerilog() - .Build(); - -Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) -{ - var seqServerUrl = configuration["Serilog:SeqServerUrl"]; - var logstashUrl = configuration["Serilog:LogstashgUrl"]; - return new LoggerConfiguration() - .MinimumLevel.Verbose() - .Enrich.WithProperty("ApplicationContext", Program.AppName) - .Enrich.FromLogContext() - .WriteTo.Console() - .WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl) - .WriteTo.Http(string.IsNullOrWhiteSpace(logstashUrl) ? "http://logstash:8080" : logstashUrl,null) - .ReadFrom.Configuration(configuration) - .CreateLogger(); +void ConfigureEventBus(IApplicationBuilder app) { + var eventBus = app.ApplicationServices.GetRequiredService(); + eventBus.Subscribe(); + eventBus.Subscribe(); } -(int httpPort, int grpcPort) GetDefinedPorts(IConfiguration config) -{ +(int httpPort, int grpcPort) GetDefinedPorts(IConfiguration config) { var grpcPort = config.GetValue("GRPC_PORT", 81); var port = config.GetValue("PORT", 80); return (port, grpcPort); } -IConfiguration GetConfiguration() -{ - var builder = new ConfigurationBuilder() - .SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddEnvironmentVariables(); - - var config = builder.Build(); - - if (config.GetValue("UseVault", false)) - { - TokenCredential credential = new ClientSecretCredential( - config["Vault:TenantId"], - config["Vault:ClientId"], - config["Vault:ClientSecret"]); - //builder.AddAzureKeyVault(new Uri($"https://{config["Vault:Name"]}.vault.azure.net/"), credential); +public partial class Program { + public static string Namespace = typeof(Program).Assembly.GetName().Name; + public static string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1); +} + +public static class CustomExtensionMethods { + public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration) { + services.AddApplicationInsightsTelemetry(configuration); + services.AddApplicationInsightsKubernetesEnricher(); + + return services; } - return builder.Build(); -} + public static IServiceCollection AddCustomMVC(this IServiceCollection services, IConfiguration configuration) { + services.AddControllers(options => { + options.Filters.Add(typeof(HttpGlobalExceptionFilter)); + }) + .AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true); -public partial class Program -{ - public static string Namespace = typeof(Startup).Namespace; - public static string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1); + services.AddCors(options => { + options.AddPolicy("CorsPolicy", + builder => builder + .SetIsOriginAllowed((host) => true) + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials()); + }); + + return services; + } + + public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) { + var accountName = configuration.GetValue("AzureStorageAccountName"); + var accountKey = configuration.GetValue("AzureStorageAccountKey"); + + var hcBuilder = services.AddHealthChecks(); + + hcBuilder + .AddCheck("self", () => HealthCheckResult.Healthy()) + .AddSqlServer( + configuration["ConnectionString"], + name: "CatalogDB-check", + tags: new string[] { "catalogdb" }); + + if (!string.IsNullOrEmpty(accountName) && !string.IsNullOrEmpty(accountKey)) { + hcBuilder + .AddAzureBlobStorage( + $"DefaultEndpointsProtocol=https;AccountName={accountName};AccountKey={accountKey};EndpointSuffix=core.windows.net", + name: "catalog-storage-check", + tags: new string[] { "catalogstorage" }); + } + + if (configuration.GetValue("AzureServiceBusEnabled")) { + hcBuilder + .AddAzureServiceBusTopic( + configuration["EventBusConnection"], + topicName: "eshop_event_bus", + name: "catalog-servicebus-check", + tags: new string[] { "servicebus" }); + } + else { + hcBuilder + .AddRabbitMQ( + $"amqp://{configuration["EventBusConnection"]}", + name: "catalog-rabbitmqbus-check", + tags: new string[] { "rabbitmqbus" }); + } + + return services; + } + + public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration) { + services.AddEntityFrameworkSqlServer() + .AddDbContext(options => { + options.UseSqlServer(configuration["ConnectionString"], + sqlServerOptionsAction: sqlOptions => { + sqlOptions.MigrationsAssembly(typeof(Program).GetTypeInfo().Assembly.GetName().Name); + //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency + sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); + }); + }); + + services.AddDbContext(options => { + options.UseSqlServer(configuration["ConnectionString"], + sqlServerOptionsAction: sqlOptions => { + sqlOptions.MigrationsAssembly(typeof(Program).GetTypeInfo().Assembly.GetName().Name); + //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency + sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); + }); + }); + + return services; + } + + public static IServiceCollection AddCustomOptions(this IServiceCollection services, IConfiguration configuration) { + services.Configure(configuration); + services.Configure(options => { + options.InvalidModelStateResponseFactory = context => { + var problemDetails = new ValidationProblemDetails(context.ModelState) { + Instance = context.HttpContext.Request.Path, + Status = StatusCodes.Status400BadRequest, + Detail = "Please refer to the errors property for additional details." + }; + + return new BadRequestObjectResult(problemDetails) { + ContentTypes = { "application/problem+json", "application/problem+xml" } + }; + }; + }); + + return services; + } + + public static IServiceCollection AddSwagger(this IServiceCollection services, IConfiguration configuration) { + services.AddSwaggerGen(options => { + options.SwaggerDoc("v1", new OpenApiInfo { + Title = "eShopOnContainers - Catalog HTTP API", + Version = "v1", + Description = "The Catalog Microservice HTTP API. This is a Data-Driven/CRUD microservice sample" + }); + }); + + return services; + + } + + public static IServiceCollection AddIntegrationServices(this IServiceCollection services, IConfiguration configuration) { + services.AddTransient>( + sp => (DbConnection c) => new IntegrationEventLogService(c)); + + services.AddTransient(); + + if (configuration.GetValue("AzureServiceBusEnabled")) { + services.AddSingleton(sp => { + var settings = sp.GetRequiredService>().Value; + var serviceBusConnection = settings.EventBusConnection; + + return new DefaultServiceBusPersisterConnection(serviceBusConnection); + }); + } + else { + services.AddSingleton(sp => { + var settings = sp.GetRequiredService>().Value; + 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); + }); + } + + return services; + } + + public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration) { + if (configuration.GetValue("AzureServiceBusEnabled")) { + services.AddSingleton(sp => { + var serviceBusPersisterConnection = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); + string subscriptionName = configuration["SubscriptionClientName"]; + + return new EventBusServiceBus(serviceBusPersisterConnection, logger, + eventBusSubcriptionsManager, sp, subscriptionName); + }); + + } + else { + services.AddSingleton(sp => { + var subscriptionClientName = configuration["SubscriptionClientName"]; + var rabbitMQPersistentConnection = 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, sp, eventBusSubcriptionsManager, subscriptionClientName, retryCount); + }); + } + + services.AddSingleton(); + services.AddTransient(); + services.AddTransient(); + + return services; + } } \ No newline at end of file diff --git a/src/Services/Catalog/Catalog.API/Startup.cs b/src/Services/Catalog/Catalog.API/Startup.cs deleted file mode 100644 index f7b46cb6f..000000000 --- a/src/Services/Catalog/Catalog.API/Startup.cs +++ /dev/null @@ -1,333 +0,0 @@ -namespace Microsoft.eShopOnContainers.Services.Catalog.API; - -public class Startup -{ - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - public IServiceProvider ConfigureServices(IServiceCollection services) - { - services.AddAppInsight(Configuration) - .AddGrpc().Services - .AddCustomMVC(Configuration) - .AddCustomDbContext(Configuration) - .AddCustomOptions(Configuration) - .AddIntegrationServices(Configuration) - .AddEventBus(Configuration) - .AddSwagger(Configuration) - .AddCustomHealthCheck(Configuration); - - var container = new ContainerBuilder(); - container.Populate(services); - - return new AutofacServiceProvider(container.Build()); - } - - public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) - { - //Configure logs - - //loggerFactory.AddAzureWebAppDiagnostics(); - //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); - - var pathBase = Configuration["PATH_BASE"]; - - if (!string.IsNullOrEmpty(pathBase)) - { - loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{pathBase}'", pathBase); - app.UsePathBase(pathBase); - } - - app.UseSwagger() - .UseSwaggerUI(c => - { - c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Catalog.API V1"); - }); - - app.UseRouting(); - app.UseCors("CorsPolicy"); - app.UseEndpoints(endpoints => - { - endpoints.MapDefaultControllerRoute(); - endpoints.MapControllers(); - endpoints.MapGet("/_proto/", async ctx => - { - ctx.Response.ContentType = "text/plain"; - using var fs = new FileStream(Path.Combine(env.ContentRootPath, "Proto", "catalog.proto"), FileMode.Open, FileAccess.Read); - using var sr = new StreamReader(fs); - while (!sr.EndOfStream) - { - var line = await sr.ReadLineAsync(); - if (line != "/* >>" || line != "<< */") - { - await ctx.Response.WriteAsync(line); - } - } - }); - endpoints.MapGrpcService(); - endpoints.MapHealthChecks("/hc", new HealthCheckOptions() - { - Predicate = _ => true, - ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse - }); - endpoints.MapHealthChecks("/liveness", new HealthCheckOptions - { - Predicate = r => r.Name.Contains("self") - }); - }); - - ConfigureEventBus(app); - } - - protected virtual void ConfigureEventBus(IApplicationBuilder app) - { - var eventBus = app.ApplicationServices.GetRequiredService(); - eventBus.Subscribe(); - eventBus.Subscribe(); - } -} - -public static class CustomExtensionMethods -{ - public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration) - { - services.AddApplicationInsightsTelemetry(configuration); - services.AddApplicationInsightsKubernetesEnricher(); - - return services; - } - - public static IServiceCollection AddCustomMVC(this IServiceCollection services, IConfiguration configuration) - { - services.AddControllers(options => - { - options.Filters.Add(typeof(HttpGlobalExceptionFilter)); - }) - .AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true); - - services.AddCors(options => - { - options.AddPolicy("CorsPolicy", - builder => builder - .SetIsOriginAllowed((host) => true) - .AllowAnyMethod() - .AllowAnyHeader() - .AllowCredentials()); - }); - - return services; - } - - public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) - { - var accountName = configuration.GetValue("AzureStorageAccountName"); - var accountKey = configuration.GetValue("AzureStorageAccountKey"); - - var hcBuilder = services.AddHealthChecks(); - - hcBuilder - .AddCheck("self", () => HealthCheckResult.Healthy()) - .AddSqlServer( - configuration["ConnectionString"], - name: "CatalogDB-check", - tags: new string[] { "catalogdb" }); - - if (!string.IsNullOrEmpty(accountName) && !string.IsNullOrEmpty(accountKey)) - { - hcBuilder - .AddAzureBlobStorage( - $"DefaultEndpointsProtocol=https;AccountName={accountName};AccountKey={accountKey};EndpointSuffix=core.windows.net", - name: "catalog-storage-check", - tags: new string[] { "catalogstorage" }); - } - - if (configuration.GetValue("AzureServiceBusEnabled")) - { - hcBuilder - .AddAzureServiceBusTopic( - configuration["EventBusConnection"], - topicName: "eshop_event_bus", - name: "catalog-servicebus-check", - tags: new string[] { "servicebus" }); - } - else - { - hcBuilder - .AddRabbitMQ( - $"amqp://{configuration["EventBusConnection"]}", - name: "catalog-rabbitmqbus-check", - tags: new string[] { "rabbitmqbus" }); - } - - return services; - } - - public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration) - { - services.AddEntityFrameworkSqlServer() - .AddDbContext(options => - { - options.UseSqlServer(configuration["ConnectionString"], - sqlServerOptionsAction: sqlOptions => - { - sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); - //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency - sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); - }); - }); - - services.AddDbContext(options => - { - options.UseSqlServer(configuration["ConnectionString"], - sqlServerOptionsAction: sqlOptions => - { - sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); - //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency - sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); - }); - }); - - return services; - } - - public static IServiceCollection AddCustomOptions(this IServiceCollection services, IConfiguration configuration) - { - services.Configure(configuration); - services.Configure(options => - { - options.InvalidModelStateResponseFactory = context => - { - var problemDetails = new ValidationProblemDetails(context.ModelState) - { - Instance = context.HttpContext.Request.Path, - Status = StatusCodes.Status400BadRequest, - Detail = "Please refer to the errors property for additional details." - }; - - return new BadRequestObjectResult(problemDetails) - { - ContentTypes = { "application/problem+json", "application/problem+xml" } - }; - }; - }); - - return services; - } - - public static IServiceCollection AddSwagger(this IServiceCollection services, IConfiguration configuration) - { - services.AddSwaggerGen(options => - { - options.SwaggerDoc("v1", new OpenApiInfo - { - Title = "eShopOnContainers - Catalog HTTP API", - Version = "v1", - Description = "The Catalog Microservice HTTP API. This is a Data-Driven/CRUD microservice sample" - }); - }); - - return services; - - } - - public static IServiceCollection AddIntegrationServices(this IServiceCollection services, IConfiguration configuration) - { - services.AddTransient>( - sp => (DbConnection c) => new IntegrationEventLogService(c)); - - services.AddTransient(); - - if (configuration.GetValue("AzureServiceBusEnabled")) - { - services.AddSingleton(sp => - { - var settings = sp.GetRequiredService>().Value; - var serviceBusConnection = settings.EventBusConnection; - - return new DefaultServiceBusPersisterConnection(serviceBusConnection); - }); - } - else - { - services.AddSingleton(sp => - { - var settings = sp.GetRequiredService>().Value; - 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); - }); - } - - return services; - } - - public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration) - { - if (configuration.GetValue("AzureServiceBusEnabled")) - { - services.AddSingleton(sp => - { - var serviceBusPersisterConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); - var logger = sp.GetRequiredService>(); - var eventBusSubcriptionsManager = sp.GetRequiredService(); - string subscriptionName = configuration["SubscriptionClientName"]; - - return new EventBusServiceBus(serviceBusPersisterConnection, logger, - eventBusSubcriptionsManager, iLifetimeScope, subscriptionName); - }); - - } - else - { - services.AddSingleton(sp => - { - var subscriptionClientName = configuration["SubscriptionClientName"]; - 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(); - services.AddTransient(); - services.AddTransient(); - - return services; - } -} \ No newline at end of file diff --git a/src/Services/Catalog/Catalog.FunctionalTests/CatalogScenarioBase.cs b/src/Services/Catalog/Catalog.FunctionalTests/CatalogScenarioBase.cs index 793d170ae..562dc000d 100644 --- a/src/Services/Catalog/Catalog.FunctionalTests/CatalogScenarioBase.cs +++ b/src/Services/Catalog/Catalog.FunctionalTests/CatalogScenarioBase.cs @@ -13,8 +13,7 @@ public class CatalogScenariosBase { cb.AddJsonFile("appsettings.json", optional: false) .AddEnvironmentVariables(); - }) - .UseStartup(); + }); var testServer = new TestServer(hostBuilder); diff --git a/src/Services/Ordering/Ordering.API/Startup.cs b/src/Services/Ordering/Ordering.API/Startup.cs index 2a932dabd..8c8ecb117 100644 --- a/src/Services/Ordering/Ordering.API/Startup.cs +++ b/src/Services/Ordering/Ordering.API/Startup.cs @@ -332,13 +332,12 @@ static class CustomExtensionsMethods services.AddSingleton(sp => { var serviceBusPersisterConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); var logger = sp.GetRequiredService>(); var eventBusSubcriptionsManager = sp.GetRequiredService(); string subscriptionName = configuration["SubscriptionClientName"]; return new EventBusServiceBus(serviceBusPersisterConnection, logger, - eventBusSubcriptionsManager, iLifetimeScope, subscriptionName); + eventBusSubcriptionsManager, sp, subscriptionName); }); } else @@ -347,7 +346,6 @@ static class CustomExtensionsMethods { var subscriptionClientName = configuration["SubscriptionClientName"]; var rabbitMQPersistentConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); var logger = sp.GetRequiredService>(); var eventBusSubcriptionsManager = sp.GetRequiredService(); @@ -357,7 +355,7 @@ static class CustomExtensionsMethods retryCount = int.Parse(configuration["EventBusRetryCount"]); } - return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); + return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, sp, eventBusSubcriptionsManager, subscriptionClientName, retryCount); }); } diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Extensions/CustomExtensionMethods.cs b/src/Services/Ordering/Ordering.BackgroundTasks/Extensions/CustomExtensionMethods.cs index bf35a61dc..83fd4dc0d 100644 --- a/src/Services/Ordering/Ordering.BackgroundTasks/Extensions/CustomExtensionMethods.cs +++ b/src/Services/Ordering/Ordering.BackgroundTasks/Extensions/CustomExtensionMethods.cs @@ -60,12 +60,11 @@ namespace Ordering.BackgroundTasks.Extensions services.AddSingleton(sp => { var serviceBusPersisterConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); var logger = sp.GetRequiredService>(); var eventBusSubcriptionsManager = sp.GetRequiredService(); string subscriptionName = configuration["SubscriptionClientName"]; - return new EventBusServiceBus(serviceBusPersisterConnection, logger, eventBusSubcriptionsManager, iLifetimeScope, subscriptionName); + return new EventBusServiceBus(serviceBusPersisterConnection, logger, eventBusSubcriptionsManager, sp, subscriptionName); }); } else @@ -103,7 +102,6 @@ namespace Ordering.BackgroundTasks.Extensions services.AddSingleton(sp => { var rabbitMQPersistentConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); var logger = sp.GetRequiredService>(); var eventBusSubcriptionsManager = sp.GetRequiredService(); @@ -114,7 +112,7 @@ namespace Ordering.BackgroundTasks.Extensions retryCount = int.Parse(configuration["EventBusRetryCount"]); } - return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); + return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, sp, eventBusSubcriptionsManager, subscriptionClientName, retryCount); }); } diff --git a/src/Services/Ordering/Ordering.SignalrHub/Startup.cs b/src/Services/Ordering/Ordering.SignalrHub/Startup.cs index b91350eb6..bc017d218 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/Startup.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/Startup.cs @@ -189,13 +189,12 @@ public class Startup services.AddSingleton(sp => { var serviceBusPersisterConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); var logger = sp.GetRequiredService>(); var eventBusSubcriptionsManager = sp.GetRequiredService(); string subscriptionName = Configuration["SubscriptionClientName"]; return new EventBusServiceBus(serviceBusPersisterConnection, logger, - eventBusSubcriptionsManager, iLifetimeScope, subscriptionName); + eventBusSubcriptionsManager, sp, subscriptionName); }); } else @@ -204,7 +203,6 @@ public class Startup { var subscriptionClientName = Configuration["SubscriptionClientName"]; var rabbitMQPersistentConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); var logger = sp.GetRequiredService>(); var eventBusSubcriptionsManager = sp.GetRequiredService(); @@ -214,7 +212,7 @@ public class Startup retryCount = int.Parse(Configuration["EventBusRetryCount"]); } - return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); + return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, sp, eventBusSubcriptionsManager, subscriptionClientName, retryCount); }); } diff --git a/src/Services/Payment/Payment.API/Startup.cs b/src/Services/Payment/Payment.API/Startup.cs index c880f2a75..d55b9a3e3 100644 --- a/src/Services/Payment/Payment.API/Startup.cs +++ b/src/Services/Payment/Payment.API/Startup.cs @@ -107,13 +107,12 @@ public class Startup services.AddSingleton(sp => { var serviceBusPersisterConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); var logger = sp.GetRequiredService>(); var eventBusSubcriptionsManager = sp.GetRequiredService(); string subscriptionName = Configuration["SubscriptionClientName"]; return new EventBusServiceBus(serviceBusPersisterConnection, logger, - eventBusSubcriptionsManager, iLifetimeScope, subscriptionName); + eventBusSubcriptionsManager, sp, subscriptionName); }); } else @@ -122,7 +121,6 @@ public class Startup { var subscriptionClientName = Configuration["SubscriptionClientName"]; var rabbitMQPersistentConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); var logger = sp.GetRequiredService>(); var eventBusSubcriptionsManager = sp.GetRequiredService(); @@ -132,7 +130,7 @@ public class Startup retryCount = int.Parse(Configuration["EventBusRetryCount"]); } - return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); + return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, sp, eventBusSubcriptionsManager, subscriptionClientName, retryCount); }); } diff --git a/src/Services/Webhooks/Webhooks.API/Startup.cs b/src/Services/Webhooks/Webhooks.API/Startup.cs index 14fd7a8e9..05d760b7b 100644 --- a/src/Services/Webhooks/Webhooks.API/Startup.cs +++ b/src/Services/Webhooks/Webhooks.API/Startup.cs @@ -174,13 +174,12 @@ internal static class CustomExtensionMethods services.AddSingleton(sp => { var serviceBusPersisterConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); var logger = sp.GetRequiredService>(); var eventBusSubscriptionManager = sp.GetRequiredService(); string subscriptionName = configuration["SubscriptionClientName"]; return new EventBusServiceBus(serviceBusPersisterConnection, logger, - eventBusSubscriptionManager, iLifetimeScope, subscriptionName); + eventBusSubscriptionManager, sp, subscriptionName); }); } @@ -190,7 +189,6 @@ internal static class CustomExtensionMethods { var subscriptionClientName = configuration["SubscriptionClientName"]; var rabbitMQPersistentConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); var logger = sp.GetRequiredService>(); var eventBusSubscriptionManager = sp.GetRequiredService(); @@ -200,7 +198,7 @@ internal static class CustomExtensionMethods retryCount = int.Parse(configuration["EventBusRetryCount"]); } - return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubscriptionManager, subscriptionClientName, retryCount); + return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, sp, eventBusSubscriptionManager, subscriptionClientName, retryCount); }); }