|
|
@ -1,35 +1,37 @@ |
|
|
|
namespace Microsoft.eShopOnContainers.Services.Catalog.API |
|
|
|
using Autofac; |
|
|
|
using Autofac.Extensions.DependencyInjection; |
|
|
|
using global::Catalog.API.Infrastructure.Filters; |
|
|
|
using global::Catalog.API.IntegrationEvents; |
|
|
|
using Microsoft.ApplicationInsights.Extensibility; |
|
|
|
using Microsoft.ApplicationInsights.ServiceFabric; |
|
|
|
using Microsoft.AspNetCore.Builder; |
|
|
|
using Microsoft.AspNetCore.Hosting; |
|
|
|
using Microsoft.AspNetCore.Http; |
|
|
|
using Microsoft.AspNetCore.Mvc; |
|
|
|
using Microsoft.Azure.ServiceBus; |
|
|
|
using Microsoft.EntityFrameworkCore; |
|
|
|
using Microsoft.EntityFrameworkCore.Diagnostics; |
|
|
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; |
|
|
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; |
|
|
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; |
|
|
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; |
|
|
|
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; |
|
|
|
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; |
|
|
|
using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; |
|
|
|
using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.EventHandling; |
|
|
|
using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events; |
|
|
|
using Microsoft.Extensions.Configuration; |
|
|
|
using Microsoft.Extensions.DependencyInjection; |
|
|
|
using Microsoft.Extensions.HealthChecks; |
|
|
|
using Microsoft.Extensions.Logging; |
|
|
|
using Microsoft.Extensions.Options; |
|
|
|
using RabbitMQ.Client; |
|
|
|
using System; |
|
|
|
using System.Data.Common; |
|
|
|
using System.Reflection; |
|
|
|
|
|
|
|
namespace Microsoft.eShopOnContainers.Services.Catalog.API |
|
|
|
{ |
|
|
|
using Autofac; |
|
|
|
using Autofac.Extensions.DependencyInjection; |
|
|
|
using global::Catalog.API.Infrastructure.Filters; |
|
|
|
using global::Catalog.API.IntegrationEvents; |
|
|
|
using Microsoft.ApplicationInsights.Extensibility; |
|
|
|
using Microsoft.ApplicationInsights.ServiceFabric; |
|
|
|
using Microsoft.AspNetCore.Builder; |
|
|
|
using Microsoft.AspNetCore.Hosting; |
|
|
|
using Microsoft.Azure.ServiceBus; |
|
|
|
using Microsoft.EntityFrameworkCore; |
|
|
|
using Microsoft.EntityFrameworkCore.Diagnostics; |
|
|
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; |
|
|
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; |
|
|
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; |
|
|
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; |
|
|
|
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; |
|
|
|
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; |
|
|
|
using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; |
|
|
|
using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.EventHandling; |
|
|
|
using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events; |
|
|
|
using Microsoft.Extensions.Configuration; |
|
|
|
using Microsoft.Extensions.DependencyInjection; |
|
|
|
using Microsoft.Extensions.HealthChecks; |
|
|
|
using Microsoft.Extensions.Logging; |
|
|
|
using Microsoft.Extensions.Options; |
|
|
|
using RabbitMQ.Client; |
|
|
|
using System; |
|
|
|
using System.Data.Common; |
|
|
|
using System.Reflection; |
|
|
|
|
|
|
|
public class Startup |
|
|
|
{ |
|
|
|
public Startup(IConfiguration configuration) |
|
|
@ -41,21 +43,94 @@ |
|
|
|
|
|
|
|
public IServiceProvider ConfigureServices(IServiceCollection services) |
|
|
|
{ |
|
|
|
// Add framework services.
|
|
|
|
services.AddAppInsight(Configuration) |
|
|
|
.AddCustomMVC(Configuration) |
|
|
|
.AddCustomDbContext(Configuration) |
|
|
|
.AddCustomOptions(Configuration) |
|
|
|
.AddEventBus(Configuration) |
|
|
|
.AddSwagger(); |
|
|
|
|
|
|
|
var container = new ContainerBuilder(); |
|
|
|
container.Populate(services); |
|
|
|
return new AutofacServiceProvider(container.Build()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) |
|
|
|
{ |
|
|
|
//Configure logs
|
|
|
|
|
|
|
|
loggerFactory.AddAzureWebAppDiagnostics(); |
|
|
|
loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); |
|
|
|
|
|
|
|
var pathBase = Configuration["PATH_BASE"]; |
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(pathBase)) |
|
|
|
{ |
|
|
|
loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); |
|
|
|
app.UsePathBase(pathBase); |
|
|
|
} |
|
|
|
|
|
|
|
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
|
|
|
app.Map("/liveness", lapp => lapp.Run(async ctx => ctx.Response.StatusCode = 200)); |
|
|
|
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
|
|
|
|
|
|
|
app.UseCors("CorsPolicy"); |
|
|
|
|
|
|
|
app.UseMvcWithDefaultRoute(); |
|
|
|
|
|
|
|
RegisterAppInsights(services); |
|
|
|
app.UseSwagger() |
|
|
|
.UseSwaggerUI(c => |
|
|
|
{ |
|
|
|
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Catalog.API V1"); |
|
|
|
}); |
|
|
|
|
|
|
|
ConfigureEventBus(app); |
|
|
|
} |
|
|
|
|
|
|
|
protected virtual void ConfigureEventBus(IApplicationBuilder app) |
|
|
|
{ |
|
|
|
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>(); |
|
|
|
eventBus.Subscribe<OrderStatusChangedToAwaitingValidationIntegrationEvent, OrderStatusChangedToAwaitingValidationIntegrationEventHandler>(); |
|
|
|
eventBus.Subscribe<OrderStatusChangedToPaidIntegrationEvent, OrderStatusChangedToPaidIntegrationEventHandler>(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public static class CustomExtensionMethods |
|
|
|
{ |
|
|
|
public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration) |
|
|
|
{ |
|
|
|
services.AddApplicationInsightsTelemetry(configuration); |
|
|
|
var orchestratorType = configuration.GetValue<string>("OrchestratorType"); |
|
|
|
|
|
|
|
if (orchestratorType?.ToUpper() == "K8S") |
|
|
|
{ |
|
|
|
// Enable K8s telemetry initializer
|
|
|
|
services.EnableKubernetes(); |
|
|
|
} |
|
|
|
if (orchestratorType?.ToUpper() == "SF") |
|
|
|
{ |
|
|
|
// Enable SF telemetry initializer
|
|
|
|
services.AddSingleton<ITelemetryInitializer>((serviceProvider) => |
|
|
|
new FabricTelemetryInitializer()); |
|
|
|
} |
|
|
|
|
|
|
|
return services; |
|
|
|
} |
|
|
|
|
|
|
|
public static IServiceCollection AddCustomMVC(this IServiceCollection services, IConfiguration configuration) |
|
|
|
{ |
|
|
|
services.AddHealthChecks(checks => |
|
|
|
{ |
|
|
|
var minutes = 1; |
|
|
|
if (int.TryParse(Configuration["HealthCheck:Timeout"], out var minutesParsed)) |
|
|
|
if (int.TryParse(configuration["HealthCheck:Timeout"], out var minutesParsed)) |
|
|
|
{ |
|
|
|
minutes = minutesParsed; |
|
|
|
} |
|
|
|
checks.AddSqlCheck("CatalogDb", Configuration["ConnectionString"], TimeSpan.FromMinutes(minutes)); |
|
|
|
checks.AddSqlCheck("CatalogDb", configuration["ConnectionString"], TimeSpan.FromMinutes(minutes)); |
|
|
|
|
|
|
|
var accountName = Configuration.GetValue<string>("AzureStorageAccountName"); |
|
|
|
var accountKey = Configuration.GetValue<string>("AzureStorageAccountKey"); |
|
|
|
var accountName = configuration.GetValue<string>("AzureStorageAccountName"); |
|
|
|
var accountKey = configuration.GetValue<string>("AzureStorageAccountKey"); |
|
|
|
if (!string.IsNullOrEmpty(accountName) && !string.IsNullOrEmpty(accountKey)) |
|
|
|
{ |
|
|
|
checks.AddAzureBlobStorageCheck(accountName, accountKey); |
|
|
@ -67,9 +142,23 @@ |
|
|
|
options.Filters.Add(typeof(HttpGlobalExceptionFilter)); |
|
|
|
}).AddControllersAsServices(); |
|
|
|
|
|
|
|
services.AddCors(options => |
|
|
|
{ |
|
|
|
options.AddPolicy("CorsPolicy", |
|
|
|
builder => builder.AllowAnyOrigin() |
|
|
|
.AllowAnyMethod() |
|
|
|
.AllowAnyHeader() |
|
|
|
.AllowCredentials()); |
|
|
|
}); |
|
|
|
|
|
|
|
return services; |
|
|
|
} |
|
|
|
|
|
|
|
public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration) |
|
|
|
{ |
|
|
|
services.AddDbContext<CatalogContext>(options => |
|
|
|
{ |
|
|
|
options.UseSqlServer(Configuration["ConnectionString"], |
|
|
|
options.UseSqlServer(configuration["ConnectionString"], |
|
|
|
sqlServerOptionsAction: sqlOptions => |
|
|
|
{ |
|
|
|
sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); |
|
|
@ -85,7 +174,7 @@ |
|
|
|
|
|
|
|
services.AddDbContext<IntegrationEventLogContext>(options => |
|
|
|
{ |
|
|
|
options.UseSqlServer(Configuration["ConnectionString"], |
|
|
|
options.UseSqlServer(configuration["ConnectionString"], |
|
|
|
sqlServerOptionsAction: sqlOptions => |
|
|
|
{ |
|
|
|
sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); |
|
|
@ -94,9 +183,35 @@ |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
services.Configure<CatalogSettings>(Configuration); |
|
|
|
return services; |
|
|
|
} |
|
|
|
|
|
|
|
// Add framework services.
|
|
|
|
public static IServiceCollection AddCustomOptions(this IServiceCollection services, IConfiguration configuration) |
|
|
|
{ |
|
|
|
services.Configure<CatalogSettings>(configuration); |
|
|
|
services.Configure<ApiBehaviorOptions>(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) |
|
|
|
{ |
|
|
|
services.AddSwaggerGen(options => |
|
|
|
{ |
|
|
|
options.DescribeAllEnumsAsStrings(); |
|
|
@ -109,21 +224,18 @@ |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
services.AddCors(options => |
|
|
|
{ |
|
|
|
options.AddPolicy("CorsPolicy", |
|
|
|
builder => builder.AllowAnyOrigin() |
|
|
|
.AllowAnyMethod() |
|
|
|
.AllowAnyHeader() |
|
|
|
.AllowCredentials()); |
|
|
|
}); |
|
|
|
return services; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
public static IServiceCollection AddIntegrationServices(IServiceCollection services, IConfiguration configuration) |
|
|
|
{ |
|
|
|
services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>( |
|
|
|
sp => (DbConnection c) => new IntegrationEventLogService(c)); |
|
|
|
sp => (DbConnection c) => new IntegrationEventLogService(c)); |
|
|
|
|
|
|
|
services.AddTransient<ICatalogIntegrationEventService, CatalogIntegrationEventService>(); |
|
|
|
|
|
|
|
if (Configuration.GetValue<bool>("AzureServiceBusEnabled")) |
|
|
|
if (configuration.GetValue<bool>("AzureServiceBusEnabled")) |
|
|
|
{ |
|
|
|
services.AddSingleton<IServiceBusPersisterConnection>(sp => |
|
|
|
{ |
|
|
@ -144,93 +256,37 @@ |
|
|
|
|
|
|
|
var factory = new ConnectionFactory() |
|
|
|
{ |
|
|
|
HostName = Configuration["EventBusConnection"] |
|
|
|
HostName = configuration["EventBusConnection"] |
|
|
|
}; |
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(Configuration["EventBusUserName"])) |
|
|
|
if (!string.IsNullOrEmpty(configuration["EventBusUserName"])) |
|
|
|
{ |
|
|
|
factory.UserName = Configuration["EventBusUserName"]; |
|
|
|
factory.UserName = configuration["EventBusUserName"]; |
|
|
|
} |
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(Configuration["EventBusPassword"])) |
|
|
|
if (!string.IsNullOrEmpty(configuration["EventBusPassword"])) |
|
|
|
{ |
|
|
|
factory.Password = Configuration["EventBusPassword"]; |
|
|
|
factory.Password = configuration["EventBusPassword"]; |
|
|
|
} |
|
|
|
|
|
|
|
var retryCount = 5; |
|
|
|
if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"])) |
|
|
|
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) |
|
|
|
{ |
|
|
|
retryCount = int.Parse(Configuration["EventBusRetryCount"]); |
|
|
|
retryCount = int.Parse(configuration["EventBusRetryCount"]); |
|
|
|
} |
|
|
|
|
|
|
|
return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
RegisterEventBus(services); |
|
|
|
|
|
|
|
var container = new ContainerBuilder(); |
|
|
|
container.Populate(services); |
|
|
|
return new AutofacServiceProvider(container.Build()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) |
|
|
|
{ |
|
|
|
//Configure logs
|
|
|
|
|
|
|
|
loggerFactory.AddConsole(Configuration.GetSection("Logging")); |
|
|
|
loggerFactory.AddDebug(); |
|
|
|
loggerFactory.AddAzureWebAppDiagnostics(); |
|
|
|
loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); |
|
|
|
|
|
|
|
var pathBase = Configuration["PATH_BASE"]; |
|
|
|
if (!string.IsNullOrEmpty(pathBase)) |
|
|
|
{ |
|
|
|
loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); |
|
|
|
app.UsePathBase(pathBase); |
|
|
|
} |
|
|
|
|
|
|
|
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
|
|
|
app.Map("/liveness", lapp => lapp.Run(async ctx => ctx.Response.StatusCode = 200)); |
|
|
|
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
|
|
|
|
|
|
|
app.UseCors("CorsPolicy"); |
|
|
|
|
|
|
|
app.UseMvcWithDefaultRoute(); |
|
|
|
|
|
|
|
app.UseSwagger() |
|
|
|
.UseSwaggerUI(c => |
|
|
|
{ |
|
|
|
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Catalog.API V1"); |
|
|
|
}); |
|
|
|
|
|
|
|
ConfigureEventBus(app); |
|
|
|
return services; |
|
|
|
} |
|
|
|
|
|
|
|
private void RegisterAppInsights(IServiceCollection services) |
|
|
|
public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration) |
|
|
|
{ |
|
|
|
services.AddApplicationInsightsTelemetry(Configuration); |
|
|
|
var orchestratorType = Configuration.GetValue<string>("OrchestratorType"); |
|
|
|
var subscriptionClientName = configuration["SubscriptionClientName"]; |
|
|
|
|
|
|
|
if (orchestratorType?.ToUpper() == "K8S") |
|
|
|
{ |
|
|
|
// Enable K8s telemetry initializer
|
|
|
|
services.EnableKubernetes(); |
|
|
|
} |
|
|
|
if (orchestratorType?.ToUpper() == "SF") |
|
|
|
{ |
|
|
|
// Enable SF telemetry initializer
|
|
|
|
services.AddSingleton<ITelemetryInitializer>((serviceProvider) => |
|
|
|
new FabricTelemetryInitializer()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void RegisterEventBus(IServiceCollection services) |
|
|
|
{ |
|
|
|
var subscriptionClientName = Configuration["SubscriptionClientName"]; |
|
|
|
|
|
|
|
if (Configuration.GetValue<bool>("AzureServiceBusEnabled")) |
|
|
|
if (configuration.GetValue<bool>("AzureServiceBusEnabled")) |
|
|
|
{ |
|
|
|
services.AddSingleton<IEventBus, EventBusServiceBus>(sp => |
|
|
|
{ |
|
|
@ -254,9 +310,9 @@ |
|
|
|
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); |
|
|
|
|
|
|
|
var retryCount = 5; |
|
|
|
if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"])) |
|
|
|
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) |
|
|
|
{ |
|
|
|
retryCount = int.Parse(Configuration["EventBusRetryCount"]); |
|
|
|
retryCount = int.Parse(configuration["EventBusRetryCount"]); |
|
|
|
} |
|
|
|
|
|
|
|
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); |
|
|
@ -266,12 +322,8 @@ |
|
|
|
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>(); |
|
|
|
services.AddTransient<OrderStatusChangedToAwaitingValidationIntegrationEventHandler>(); |
|
|
|
services.AddTransient<OrderStatusChangedToPaidIntegrationEventHandler>(); |
|
|
|
} |
|
|
|
protected virtual void ConfigureEventBus(IApplicationBuilder app) |
|
|
|
{ |
|
|
|
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>(); |
|
|
|
eventBus.Subscribe<OrderStatusChangedToAwaitingValidationIntegrationEvent, OrderStatusChangedToAwaitingValidationIntegrationEventHandler>(); |
|
|
|
eventBus.Subscribe<OrderStatusChangedToPaidIntegrationEvent, OrderStatusChangedToPaidIntegrationEventHandler>(); |
|
|
|
|
|
|
|
return services; |
|
|
|
} |
|
|
|
} |
|
|
|
} |