2017-07-07 11:56:57 +02:00
namespace Microsoft.eShopOnContainers.Services.Marketing.API
2017-06-01 10:10:00 +02:00
{
2017-06-13 17:31:37 +02:00
using Autofac ;
using Autofac.Extensions.DependencyInjection ;
2017-07-07 11:56:57 +02:00
using IntegrationEvents.Events ;
using AspNetCore.Builder ;
using AspNetCore.Hosting ;
using AspNetCore.Http ;
using Azure.ServiceBus ;
using EntityFrameworkCore ;
using EntityFrameworkCore.Infrastructure ;
using BuildingBlocks.EventBus ;
using BuildingBlocks.EventBus.Abstractions ;
using BuildingBlocks.EventBusRabbitMQ ;
using BuildingBlocks.EventBusServiceBus ;
using Infrastructure ;
using Infrastructure.Filters ;
using Infrastructure.Repositories ;
2017-07-10 14:49:26 -07:00
using Infrastructure.Services ;
2017-07-07 11:56:57 +02:00
using Extensions.Configuration ;
using Extensions.DependencyInjection ;
using Extensions.Logging ;
2017-06-15 19:26:24 +02:00
using Polly ;
2017-06-30 08:59:26 +02:00
using RabbitMQ.Client ;
using System ;
2017-06-15 19:26:24 +02:00
using System.Data.SqlClient ;
2017-06-30 08:59:26 +02:00
using System.Reflection ;
using System.Threading.Tasks ;
2017-07-07 11:56:57 +02:00
using Extensions.HealthChecks ;
using Marketing.API.IntegrationEvents.Handlers ;
2017-06-01 10:10:00 +02:00
public class Startup
{
public Startup ( IHostingEnvironment env )
{
var builder = new ConfigurationBuilder ( )
. SetBasePath ( env . ContentRootPath )
. AddJsonFile ( "appsettings.json" , optional : false , reloadOnChange : true )
. AddJsonFile ( $"appsettings.{env.EnvironmentName}.json" , optional : true )
. AddEnvironmentVariables ( ) ;
if ( env . IsDevelopment ( ) )
{
builder . AddUserSecrets ( typeof ( Startup ) . GetTypeInfo ( ) . Assembly ) ;
}
builder . AddEnvironmentVariables ( ) ;
Configuration = builder . Build ( ) ;
}
public IConfigurationRoot Configuration { get ; }
// This method gets called by the runtime. Use this method to add services to the container.
2017-06-13 17:31:37 +02:00
public IServiceProvider ConfigureServices ( IServiceCollection services )
2017-06-01 10:10:00 +02:00
{
// Add framework services.
2017-06-02 16:31:03 +02:00
services . AddMvc ( options = >
{
options . Filters . Add ( typeof ( HttpGlobalExceptionFilter ) ) ;
} ) . AddControllersAsServices ( ) ; //Injecting Controllers themselves thru DIFor further info see: http://docs.autofac.org/en/latest/integration/aspnetcore.html#controllers-as-services
2017-06-01 10:10:00 +02:00
2017-06-13 17:31:37 +02:00
services . Configure < MarketingSettings > ( Configuration ) ;
2017-07-04 11:27:16 +02:00
services . AddHealthChecks ( checks = >
{
checks . AddValueTaskCheck ( "HTTP Endpoint" , ( ) = > new ValueTask < IHealthCheckResult > ( HealthCheckResult . Healthy ( "Ok" ) ) ) ;
2017-07-07 14:05:52 +02:00
var accountName = Configuration . GetValue < string > ( "AzureStorageAccountName" ) ;
var accountKey = Configuration . GetValue < string > ( "AzureStorageAccountKey" ) ;
if ( ! string . IsNullOrEmpty ( accountName ) & & ! string . IsNullOrEmpty ( accountKey ) )
{
checks . AddAzureBlobStorageCheck ( accountName , accountKey ) ;
}
2017-07-04 11:27:16 +02:00
} ) ;
2017-06-01 10:10:00 +02:00
services . AddDbContext < MarketingContext > ( 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 : 5 , maxRetryDelay : TimeSpan . FromSeconds ( 30 ) , errorNumbersToAdd : null ) ;
} ) ;
// Changing default behavior when client evaluation occurs to throw.
// Default in EF Core would be to log a warning when client evaluation is performed.
options . ConfigureWarnings ( warnings = > warnings . Throw ( RelationalEventId . QueryClientEvaluationWarning ) ) ;
//Check Client vs. Server evaluation: https://docs.microsoft.com/en-us/ef/core/querying/client-eval
} ) ;
2017-06-30 08:59:26 +02:00
if ( Configuration . GetValue < bool > ( "AzureServiceBusEnabled" ) )
2017-06-13 17:31:37 +02:00
{
2017-06-30 08:59:26 +02:00
services . AddSingleton < IServiceBusPersisterConnection > ( sp = >
{
var logger = sp . GetRequiredService < ILogger < DefaultServiceBusPersisterConnection > > ( ) ;
2017-06-13 17:31:37 +02:00
2017-06-30 08:59:26 +02:00
var serviceBusConnectionString = Configuration [ "EventBusConnection" ] ;
var serviceBusConnection = new ServiceBusConnectionStringBuilder ( serviceBusConnectionString ) ;
return new DefaultServiceBusPersisterConnection ( serviceBusConnection , logger ) ;
} ) ;
}
else
{
services . AddSingleton < IRabbitMQPersistentConnection > ( sp = >
2017-06-13 17:31:37 +02:00
{
2017-06-30 08:59:26 +02:00
var logger = sp . GetRequiredService < ILogger < DefaultRabbitMQPersistentConnection > > ( ) ;
2017-06-13 17:31:37 +02:00
2017-06-30 08:59:26 +02:00
var factory = new ConnectionFactory ( )
{
HostName = Configuration [ "EventBusConnection" ]
} ;
2017-06-13 17:31:37 +02:00
2017-06-30 08:59:26 +02:00
return new DefaultRabbitMQPersistentConnection ( factory , logger ) ;
} ) ;
}
2017-06-13 17:31:37 +02:00
2017-06-01 10:10:00 +02:00
// Add framework services.
services . AddSwaggerGen ( options = >
{
options . DescribeAllEnumsAsStrings ( ) ;
options . SwaggerDoc ( "v1" , new Swashbuckle . AspNetCore . Swagger . Info
{
Title = "Marketing HTTP API" ,
Version = "v1" ,
Description = "The Marketing Service HTTP API" ,
TermsOfService = "Terms Of Service"
} ) ;
} ) ;
services . AddCors ( options = >
{
options . AddPolicy ( "CorsPolicy" ,
builder = > builder . AllowAnyOrigin ( )
. AllowAnyMethod ( )
. AllowAnyHeader ( )
. AllowCredentials ( ) ) ;
} ) ;
2017-06-13 17:31:37 +02:00
2017-06-30 08:59:26 +02:00
RegisterEventBus ( services ) ;
2017-06-13 17:31:37 +02:00
services . AddTransient < IMarketingDataRepository , MarketingDataRepository > ( ) ;
2017-06-26 18:31:04 +02:00
services . AddSingleton < IHttpContextAccessor , HttpContextAccessor > ( ) ;
services . AddTransient < IIdentityService , IdentityService > ( ) ;
2017-06-13 17:31:37 +02:00
2017-06-30 08:59:26 +02:00
services . AddOptions ( ) ;
2017-06-13 17:31:37 +02:00
//configure autofac
var container = new ContainerBuilder ( ) ;
container . Populate ( services ) ;
return new AutofacServiceProvider ( container . Build ( ) ) ;
2017-06-01 10:10:00 +02:00
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure ( IApplicationBuilder app , IHostingEnvironment env , ILoggerFactory loggerFactory )
{
loggerFactory . AddConsole ( Configuration . GetSection ( "Logging" ) ) ;
loggerFactory . AddDebug ( ) ;
app . UseCors ( "CorsPolicy" ) ;
ConfigureAuth ( app ) ;
app . UseMvcWithDefaultRoute ( ) ;
app . UseSwagger ( )
. UseSwaggerUI ( c = >
{
c . SwaggerEndpoint ( "/swagger/v1/swagger.json" , "My API V1" ) ;
2017-06-30 08:59:26 +02:00
} ) ;
2017-06-01 10:10:00 +02:00
2017-06-15 19:26:24 +02:00
var context = ( MarketingContext ) app
. ApplicationServices . GetService ( typeof ( MarketingContext ) ) ;
WaitForSqlAvailabilityAsync ( context , loggerFactory , app ) . Wait ( ) ;
2017-06-13 17:31:37 +02:00
2017-06-15 19:26:24 +02:00
ConfigureEventBus ( app ) ;
2017-06-01 10:10:00 +02:00
}
protected virtual void ConfigureAuth ( IApplicationBuilder app )
{
var identityUrl = Configuration . GetValue < string > ( "IdentityUrl" ) ;
app . UseIdentityServerAuthentication ( new IdentityServerAuthenticationOptions
{
Authority = identityUrl . ToString ( ) ,
ApiName = "marketing" ,
RequireHttpsMetadata = false
} ) ;
}
2017-06-13 17:31:37 +02:00
2017-06-30 08:59:26 +02:00
private void RegisterEventBus ( IServiceCollection services )
2017-06-13 17:31:37 +02:00
{
2017-06-30 08:59:26 +02:00
if ( Configuration . GetValue < bool > ( "AzureServiceBusEnabled" ) )
{
services . AddSingleton < IEventBus , EventBusServiceBus > ( sp = >
{
var serviceBusPersisterConnection = sp . GetRequiredService < IServiceBusPersisterConnection > ( ) ;
var iLifetimeScope = sp . GetRequiredService < ILifetimeScope > ( ) ;
var logger = sp . GetRequiredService < ILogger < EventBusServiceBus > > ( ) ;
var eventBusSubcriptionsManager = sp . GetRequiredService < IEventBusSubscriptionsManager > ( ) ;
var subscriptionClientName = Configuration [ "SubscriptionClientName" ] ;
return new EventBusServiceBus ( serviceBusPersisterConnection , logger ,
eventBusSubcriptionsManager , subscriptionClientName , iLifetimeScope ) ;
} ) ;
}
else
{
services . AddSingleton < IEventBus , EventBusRabbitMQ > ( ) ;
}
2017-06-13 17:31:37 +02:00
2017-06-30 08:59:26 +02:00
services . AddSingleton < IEventBusSubscriptionsManager , InMemoryEventBusSubscriptionsManager > ( ) ;
services . AddTransient < UserLocationUpdatedIntegrationEventHandler > ( ) ;
2017-06-13 17:31:37 +02:00
}
private void ConfigureEventBus ( IApplicationBuilder app )
{
var eventBus = app . ApplicationServices . GetRequiredService < IEventBus > ( ) ;
2017-06-30 08:59:26 +02:00
eventBus . Subscribe < UserLocationUpdatedIntegrationEvent , UserLocationUpdatedIntegrationEventHandler > ( ) ;
2017-06-13 17:31:37 +02:00
}
2017-06-15 19:26:24 +02:00
private async Task WaitForSqlAvailabilityAsync ( MarketingContext ctx , ILoggerFactory loggerFactory , IApplicationBuilder app , int retries = 0 )
{
var logger = loggerFactory . CreateLogger ( nameof ( Startup ) ) ;
var policy = CreatePolicy ( retries , logger , nameof ( WaitForSqlAvailabilityAsync ) ) ;
await policy . ExecuteAsync ( async ( ) = >
{
await MarketingContextSeed . SeedAsync ( app , loggerFactory ) ;
} ) ;
}
private Policy CreatePolicy ( int retries , ILogger logger , string prefix )
{
return Policy . Handle < SqlException > ( ) .
WaitAndRetryAsync (
retryCount : retries ,
sleepDurationProvider : retry = > TimeSpan . FromSeconds ( 5 ) ,
onRetry : ( exception , timeSpan , retry , ctx ) = >
{
logger . LogTrace ( $"[{prefix}] Exception {exception.GetType().Name} with message ${exception.Message} detected on attempt {retry} of {retries}" ) ;
}
) ;
}
2017-06-01 10:10:00 +02:00
}
}