2017-06-01 10:10:00 +02:00
namespace Microsoft.eShopOnContainers.Services.Marketing.API
{
2017-07-07 11:56:57 +02:00
using AspNetCore.Builder ;
using AspNetCore.Hosting ;
using AspNetCore.Http ;
2017-08-29 11:01:27 +02:00
using Autofac ;
using Autofac.Extensions.DependencyInjection ;
2017-07-07 11:56:57 +02:00
using Azure.ServiceBus ;
using BuildingBlocks.EventBus ;
using BuildingBlocks.EventBus.Abstractions ;
using BuildingBlocks.EventBusRabbitMQ ;
using BuildingBlocks.EventBusServiceBus ;
2017-08-29 11:01:27 +02:00
using EntityFrameworkCore ;
using Extensions.Configuration ;
using Extensions.DependencyInjection ;
using Extensions.HealthChecks ;
using Extensions.Logging ;
2017-07-07 11:56:57 +02:00
using Infrastructure ;
using Infrastructure.Filters ;
using Infrastructure.Repositories ;
2017-07-10 14:49:26 -07:00
using Infrastructure.Services ;
2017-08-29 11:01:27 +02:00
using IntegrationEvents.Events ;
using Marketing.API.IntegrationEvents.Handlers ;
using Microsoft.EntityFrameworkCore.Diagnostics ;
2017-06-15 19:26:24 +02:00
using Polly ;
2017-06-30 08:59:26 +02:00
using RabbitMQ.Client ;
2017-08-29 11:01:27 +02:00
using Swashbuckle.AspNetCore.Swagger ;
2017-06-30 08:59:26 +02:00
using System ;
2017-08-29 11:01:27 +02:00
using System.Collections.Generic ;
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-06-01 10:10:00 +02:00
public class Startup
{
2017-08-29 11:01:27 +02:00
public Startup ( IConfiguration configuration )
2017-06-01 10:10:00 +02:00
{
2017-08-29 11:01:27 +02:00
Configuration = configuration ;
2017-06-01 10:10:00 +02:00
}
2017-08-29 11:01:27 +02:00
public IConfiguration Configuration { get ; }
2017-06-01 10:10:00 +02:00
// 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-08-29 11:01:27 +02:00
services . AddAuthentication ( )
. AddJwtBearer ( options = >
{
options . Authority = Configuration . GetValue < string > ( "IdentityUrl" ) ;
options . Audience = "marketing" ;
options . RequireHttpsMetadata = false ;
} ) ;
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 ) ;
2017-06-13 17:31:37 +02:00
2017-06-30 08:59:26 +02:00
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"
} ) ;
2017-07-12 12:10:10 +02:00
options . AddSecurityDefinition ( "oauth2" , new OAuth2Scheme
{
Type = "oauth2" ,
Flow = "implicit" ,
AuthorizationUrl = $"{Configuration.GetValue<string>(" IdentityUrlExternal ")}/connect/authorize" ,
TokenUrl = $"{Configuration.GetValue<string>(" IdentityUrlExternal ")}/connect/token" ,
Scopes = new Dictionary < string , string > ( )
{
{ "marketing" , "Marketing API" }
}
} ) ;
options . OperationFilter < AuthorizeCheckOperationFilter > ( ) ;
2017-06-01 10:10:00 +02:00
} ) ;
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" ) ;
2017-08-29 11:01:27 +02:00
app . UseAuthentication ( ) ;
2017-06-01 10:10:00 +02:00
app . UseMvcWithDefaultRoute ( ) ;
app . UseSwagger ( )
. UseSwaggerUI ( c = >
{
c . SwaggerEndpoint ( "/swagger/v1/swagger.json" , "My API V1" ) ;
2017-07-12 12:10:10 +02:00
c . ConfigureOAuth2 ( "marketingswaggerui" , "" , "" , "Marketing Swagger UI" ) ;
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
}
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
}
}