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 ;
2017-10-13 11:35:26 +02:00
using Microsoft.ApplicationInsights.Extensibility ;
using Microsoft.ApplicationInsights.ServiceFabric ;
2017-09-14 18:52:29 +02:00
using Microsoft.AspNetCore.Authentication.JwtBearer ;
2017-08-29 11:01:27 +02:00
using Microsoft.EntityFrameworkCore.Diagnostics ;
2017-10-23 17:39:45 +02:00
using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Middlewares ;
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-08-29 18:11:30 +02:00
using System.IdentityModel.Tokens.Jwt ;
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
{
2017-10-13 11:35:26 +02:00
RegisterAppInsights ( services ) ;
2017-10-11 18:53:26 +02:00
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 18:11:30 +02:00
ConfigureAuthService ( services ) ;
2017-08-29 11:01:27 +02:00
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
2017-10-10 19:56:34 -07:00
sqlOptions . EnableRetryOnFailure ( maxRetryCount : 10 , maxRetryDelay : TimeSpan . FromSeconds ( 30 ) , errorNumbersToAdd : null ) ;
2017-06-01 10:10:00 +02:00
} ) ;
// 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-09-05 15:55:17 +02:00
if ( ! string . IsNullOrEmpty ( Configuration [ "EventBusUserName" ] ) )
{
factory . UserName = Configuration [ "EventBusUserName" ] ;
}
if ( ! string . IsNullOrEmpty ( Configuration [ "EventBusPassword" ] ) )
{
factory . Password = Configuration [ "EventBusPassword" ] ;
}
2017-10-12 09:25:01 +01:00
var retryCount = 5 ;
if ( ! string . IsNullOrEmpty ( Configuration [ "EventBusRetryCount" ] ) )
{
retryCount = int . Parse ( Configuration [ "EventBusRetryCount" ] ) ;
}
return new DefaultRabbitMQPersistentConnection ( factory , logger , retryCount ) ;
2017-06-30 08:59:26 +02:00
} ) ;
}
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.
2017-08-29 11:17:09 +02:00
public void Configure ( IApplicationBuilder app , IHostingEnvironment env , ILoggerFactory loggerFactory )
2017-06-01 10:10:00 +02:00
{
2017-10-11 16:26:44 +02:00
loggerFactory . AddAzureWebAppDiagnostics ( ) ;
loggerFactory . AddApplicationInsights ( app . ApplicationServices , LogLevel . Trace ) ;
2017-09-07 19:18:53 +02:00
var pathBase = Configuration [ "PATH_BASE" ] ;
2017-09-13 14:53:06 +02:00
2017-09-07 19:18:53 +02:00
if ( ! string . IsNullOrEmpty ( pathBase ) )
{
app . UsePathBase ( pathBase ) ;
2018-01-22 11:46:18 +01:00
}
#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
2017-06-01 10:10:00 +02:00
app . UseCors ( "CorsPolicy" ) ;
2017-08-29 18:11:30 +02:00
ConfigureAuth ( app ) ;
2017-06-01 10:10:00 +02:00
app . UseMvcWithDefaultRoute ( ) ;
app . UseSwagger ( )
. UseSwaggerUI ( c = >
{
2017-10-27 14:28:27 -07:00
c . SwaggerEndpoint ( $"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json" , "Marketing.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
ConfigureEventBus ( app ) ;
2017-06-01 10:10:00 +02:00
}
2017-10-13 11:35:26 +02:00
private void RegisterAppInsights ( IServiceCollection services )
{
services . AddApplicationInsightsTelemetry ( Configuration ) ;
2017-10-16 16:10:40 +02:00
var orchestratorType = Configuration . GetValue < string > ( "OrchestratorType" ) ;
2017-10-23 17:39:45 +02:00
if ( orchestratorType ? . ToUpper ( ) = = "K8S" )
2017-10-13 11:35:26 +02:00
{
// Enable K8s telemetry initializer
services . EnableKubernetes ( ) ;
}
2017-10-23 17:39:45 +02:00
if ( orchestratorType ? . ToUpper ( ) = = "SF" )
2017-10-13 11:35:26 +02:00
{
// Enable SF telemetry initializer
services . AddSingleton < ITelemetryInitializer > ( ( serviceProvider ) = >
new FabricTelemetryInitializer ( ) ) ;
}
}
2017-10-11 11:33:35 -07:00
private void ConfigureAuthService ( IServiceCollection services )
2017-08-29 18:11:30 +02:00
{
// prevent from mapping "sub" claim to nameidentifier.
JwtSecurityTokenHandler . DefaultInboundClaimTypeMap . Clear ( ) ;
2017-09-14 18:52:29 +02:00
services . AddAuthentication ( options = >
{
options . DefaultAuthenticateScheme = JwtBearerDefaults . AuthenticationScheme ;
options . DefaultChallengeScheme = JwtBearerDefaults . AuthenticationScheme ;
} ) . AddJwtBearer ( options = >
2017-08-29 18:11:30 +02:00
{
options . Authority = Configuration . GetValue < string > ( "IdentityUrl" ) ;
options . Audience = "marketing" ;
options . RequireHttpsMetadata = false ;
} ) ;
}
2017-11-10 17:37:18 +01:00
private void RegisterEventBus ( IServiceCollection services )
2017-06-13 17:31:37 +02:00
{
2017-11-10 17:37:18 +01:00
var subscriptionClientName = Configuration [ "SubscriptionClientName" ] ;
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 > > ( ) ;
2017-11-10 17:37:18 +01:00
var eventBusSubcriptionsManager = sp . GetRequiredService < IEventBusSubscriptionsManager > ( ) ;
2017-06-30 08:59:26 +02:00
return new EventBusServiceBus ( serviceBusPersisterConnection , logger ,
eventBusSubcriptionsManager , subscriptionClientName , iLifetimeScope ) ;
} ) ;
}
else
{
2017-10-12 09:25:01 +01:00
services . AddSingleton < IEventBus , EventBusRabbitMQ > ( sp = >
{
var rabbitMQPersistentConnection = sp . GetRequiredService < IRabbitMQPersistentConnection > ( ) ;
var iLifetimeScope = sp . GetRequiredService < ILifetimeScope > ( ) ;
var logger = sp . GetRequiredService < ILogger < EventBusRabbitMQ > > ( ) ;
var eventBusSubcriptionsManager = sp . GetRequiredService < IEventBusSubscriptionsManager > ( ) ;
var retryCount = 5 ;
if ( ! string . IsNullOrEmpty ( Configuration [ "EventBusRetryCount" ] ) )
{
retryCount = int . Parse ( Configuration [ "EventBusRetryCount" ] ) ;
}
2017-11-10 17:37:18 +01:00
return new EventBusRabbitMQ ( rabbitMQPersistentConnection , logger , iLifetimeScope , eventBusSubcriptionsManager , subscriptionClientName , retryCount ) ;
2017-10-12 09:25:01 +01:00
} ) ;
2017-06-30 08:59:26 +02:00
}
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
2017-08-29 18:11:30 +02:00
protected virtual void ConfigureAuth ( IApplicationBuilder app )
{
2017-10-23 17:39:45 +02:00
if ( Configuration . GetValue < bool > ( "UseLoadTest" ) )
{
app . UseMiddleware < ByPassAuthMiddleware > ( ) ;
}
2017-08-29 18:11:30 +02:00
app . UseAuthentication ( ) ;
}
2017-06-01 10:10:00 +02:00
}
}