Merge pull request #2079 from erjain/update/ordering-api-webAppBuilder
Migrate Ordering.API to WebApplicationBuilder
This commit is contained in:
		
						commit
						ee673d9180
					
				
							
								
								
									
										132
									
								
								src/Services/Ordering/Ordering.API/.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								src/Services/Ordering/Ordering.API/.editorconfig
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,132 @@ | ||||
| ############################### | ||||
| # Core EditorConfig Options   # | ||||
| ############################### | ||||
| root = true | ||||
| # All files | ||||
| [*] | ||||
| indent_style = space | ||||
| 
 | ||||
| # XML project files | ||||
| [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] | ||||
| indent_size = 2 | ||||
| 
 | ||||
| # XML config files | ||||
| [*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] | ||||
| indent_size = 2 | ||||
| 
 | ||||
| # Code files | ||||
| [*.{cs,csx,vb,vbx}] | ||||
| indent_size = 4 | ||||
| insert_final_newline = true | ||||
| charset = utf-8-bom | ||||
| ############################### | ||||
| # .NET Coding Conventions     # | ||||
| ############################### | ||||
| [*.{cs,vb}] | ||||
| # Organize usings | ||||
| dotnet_sort_system_directives_first = true | ||||
| # this. preferences | ||||
| dotnet_style_qualification_for_field = false:silent | ||||
| dotnet_style_qualification_for_property = false:silent | ||||
| dotnet_style_qualification_for_method = false:silent | ||||
| dotnet_style_qualification_for_event = false:silent | ||||
| # Language keywords vs BCL types preferences | ||||
| dotnet_style_predefined_type_for_locals_parameters_members = true:silent | ||||
| dotnet_style_predefined_type_for_member_access = true:silent | ||||
| # Parentheses preferences | ||||
| dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent | ||||
| dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent | ||||
| dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent | ||||
| dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent | ||||
| # Modifier preferences | ||||
| dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent | ||||
| dotnet_style_readonly_field = true:suggestion | ||||
| # Expression-level preferences | ||||
| dotnet_style_object_initializer = true:suggestion | ||||
| dotnet_style_collection_initializer = true:suggestion | ||||
| dotnet_style_explicit_tuple_names = true:suggestion | ||||
| dotnet_style_null_propagation = true:suggestion | ||||
| dotnet_style_coalesce_expression = true:suggestion | ||||
| dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent | ||||
| dotnet_style_prefer_inferred_tuple_names = true:suggestion | ||||
| dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion | ||||
| dotnet_style_prefer_auto_properties = true:silent | ||||
| dotnet_style_prefer_conditional_expression_over_assignment = true:silent | ||||
| dotnet_style_prefer_conditional_expression_over_return = true:silent | ||||
| ############################### | ||||
| # Naming Conventions          # | ||||
| ############################### | ||||
| # Style Definitions | ||||
| dotnet_naming_style.pascal_case_style.capitalization             = pascal_case | ||||
| # Use PascalCase for constant fields | ||||
| dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion | ||||
| dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols  = constant_fields | ||||
| dotnet_naming_rule.constant_fields_should_be_pascal_case.style    = pascal_case_style | ||||
| dotnet_naming_symbols.constant_fields.applicable_kinds            = field | ||||
| dotnet_naming_symbols.constant_fields.applicable_accessibilities  = * | ||||
| dotnet_naming_symbols.constant_fields.required_modifiers          = const | ||||
| ############################### | ||||
| # C# Coding Conventions       # | ||||
| ############################### | ||||
| [*.cs] | ||||
| # var preferences | ||||
| csharp_style_var_for_built_in_types = true:silent | ||||
| csharp_style_var_when_type_is_apparent = true:silent | ||||
| csharp_style_var_elsewhere = true:silent | ||||
| # Expression-bodied members | ||||
| csharp_style_expression_bodied_methods = false:silent | ||||
| csharp_style_expression_bodied_constructors = false:silent | ||||
| csharp_style_expression_bodied_operators = false:silent | ||||
| csharp_style_expression_bodied_properties = true:silent | ||||
| csharp_style_expression_bodied_indexers = true:silent | ||||
| csharp_style_expression_bodied_accessors = true:silent | ||||
| # Pattern matching preferences | ||||
| csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion | ||||
| csharp_style_pattern_matching_over_as_with_null_check = true:suggestion | ||||
| # Null-checking preferences | ||||
| csharp_style_throw_expression = true:suggestion | ||||
| csharp_style_conditional_delegate_call = true:suggestion | ||||
| # Modifier preferences | ||||
| csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion | ||||
| # Expression-level preferences | ||||
| csharp_prefer_braces = true:silent | ||||
| csharp_style_deconstructed_variable_declaration = true:suggestion | ||||
| csharp_prefer_simple_default_expression = true:suggestion | ||||
| csharp_style_prefer_local_over_anonymous_function = true:suggestion | ||||
| csharp_style_inlined_variable_declaration = true:suggestion | ||||
| ############################### | ||||
| # C# Formatting Rules         # | ||||
| ############################### | ||||
| # New line preferences | ||||
| csharp_new_line_before_open_brace = all | ||||
| csharp_new_line_before_else = true | ||||
| csharp_new_line_before_catch = true | ||||
| csharp_new_line_before_finally = true | ||||
| csharp_new_line_before_members_in_object_initializers = true | ||||
| csharp_new_line_before_members_in_anonymous_types = true | ||||
| csharp_new_line_between_query_expression_clauses = true | ||||
| # Indentation preferences | ||||
| csharp_indent_case_contents = true | ||||
| csharp_indent_switch_labels = true | ||||
| csharp_indent_labels = flush_left | ||||
| # Space preferences | ||||
| csharp_space_after_cast = false | ||||
| csharp_space_after_keywords_in_control_flow_statements = true | ||||
| csharp_space_between_method_call_parameter_list_parentheses = false | ||||
| csharp_space_between_method_declaration_parameter_list_parentheses = false | ||||
| csharp_space_between_parentheses = false | ||||
| csharp_space_before_colon_in_inheritance_clause = true | ||||
| csharp_space_after_colon_in_inheritance_clause = true | ||||
| csharp_space_around_binary_operators = before_and_after | ||||
| csharp_space_between_method_declaration_empty_parameter_list_parentheses = false | ||||
| csharp_space_between_method_call_name_and_opening_parenthesis = false | ||||
| csharp_space_between_method_call_empty_parameter_list_parentheses = false | ||||
| # Wrapping preferences | ||||
| csharp_preserve_single_line_statements = true | ||||
| csharp_preserve_single_line_blocks = true | ||||
| ############################### | ||||
| # VB Coding Conventions       # | ||||
| ############################### | ||||
| [*.vb] | ||||
| # Modifier preferences | ||||
| visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion | ||||
| @ -76,4 +76,8 @@ | ||||
|     </None> | ||||
|   </ItemGroup> | ||||
| 
 | ||||
| <ItemGroup> | ||||
|     <None Include="\.editorconfig" /> | ||||
|   </ItemGroup> | ||||
| 
 | ||||
| </Project> | ||||
|  | ||||
| @ -1,27 +1,134 @@ | ||||
| var configuration = GetConfiguration(); | ||||
| using Autofac.Core; | ||||
| using Microsoft.Azure.Amqp.Framing; | ||||
| using Microsoft.Extensions.Configuration; | ||||
| using Microsoft.Extensions.Logging; | ||||
| 
 | ||||
| Log.Logger = CreateSerilogLogger(configuration); | ||||
| var appName = "Ordering.API"; | ||||
| var builder = WebApplication.CreateBuilder(new WebApplicationOptions | ||||
| { | ||||
|     Args = args, | ||||
|     ApplicationName = typeof(Program).Assembly.FullName, | ||||
|     ContentRootPath = Directory.GetCurrentDirectory() | ||||
| }); | ||||
| if (builder.Configuration.GetValue<bool>("UseVault", false)) | ||||
| { | ||||
|     TokenCredential credential = new ClientSecretCredential( | ||||
|         builder.Configuration["Vault:TenantId"], | ||||
|         builder.Configuration["Vault:ClientId"], | ||||
|         builder.Configuration["Vault:ClientSecret"]); | ||||
|     builder.Configuration.AddAzureKeyVault(new Uri($"https://{builder.Configuration["Vault:Name"]}.vault.azure.net/"), credential); | ||||
| } | ||||
| builder.Configuration.SetBasePath(Directory.GetCurrentDirectory()); | ||||
| builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); | ||||
| builder.Configuration.AddEnvironmentVariables(); | ||||
| builder.WebHost.ConfigureKestrel(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; | ||||
|     }); | ||||
| 
 | ||||
| }); | ||||
| builder.WebHost.CaptureStartupErrors(false); | ||||
| builder.Host.UseSerilog(CreateSerilogLogger(builder.Configuration)); | ||||
| builder.Services | ||||
|     .AddGrpc(options => | ||||
|     { | ||||
|         options.EnableDetailedErrors = true; | ||||
|     }) | ||||
|     .Services | ||||
|     .AddApplicationInsights(builder.Configuration) | ||||
|     .AddCustomMvc() | ||||
|     .AddHealthChecks(builder.Configuration) | ||||
|     .AddCustomDbContext(builder.Configuration) | ||||
|     .AddCustomSwagger(builder.Configuration)     | ||||
|     .AddCustomAuthentication(builder.Configuration) | ||||
|     .AddCustomAuthorization(builder.Configuration) | ||||
|     .AddCustomIntegrations(builder.Configuration) | ||||
|     .AddCustomConfiguration(builder.Configuration) | ||||
|     .AddEventBus(builder.Configuration); | ||||
| 
 | ||||
| builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); | ||||
| // Register your own things directly with Autofac here. Don't | ||||
| // call builder.Populate(), that happens in AutofacServiceProviderFactory | ||||
| // for you. | ||||
| builder.Host.ConfigureContainer<ContainerBuilder>(conbuilder => conbuilder.RegisterModule(new MediatorModule())); | ||||
| builder.Host.ConfigureContainer<ContainerBuilder>(conbuilder => conbuilder.RegisterModule(new ApplicationModule(builder.Configuration["ConnectionString"]))); | ||||
| 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); | ||||
| } | ||||
| app.UseSwagger() | ||||
|            .UseSwaggerUI(c => | ||||
|            { | ||||
|                c.SwaggerEndpoint($"{(!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty)}/swagger/v1/swagger.json", "Ordering.API V1"); | ||||
|                c.OAuthClientId("orderingswaggerui"); | ||||
|                c.OAuthAppName("Ordering Swagger UI"); | ||||
|            }); | ||||
| 
 | ||||
| app.UseRouting(); | ||||
| app.UseCors("CorsPolicy"); | ||||
| app.UseAuthentication(); | ||||
| app.UseAuthorization(); | ||||
| app.MapGrpcService<OrderingService>(); | ||||
| app.MapDefaultControllerRoute(); | ||||
| app.MapControllers(); | ||||
| app.MapGet("/_proto/", async ctx => | ||||
| { | ||||
|     ctx.Response.ContentType = "text/plain"; | ||||
|     using var fs = new FileStream(Path.Combine(app.Environment.ContentRootPath, "Proto", "basket.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.MapHealthChecks("/hc", new HealthCheckOptions() | ||||
| { | ||||
|     Predicate = _ => true, | ||||
|     ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse | ||||
| }); | ||||
| app.MapHealthChecks("/liveness", new HealthCheckOptions | ||||
| { | ||||
|     Predicate = r => r.Name.Contains("self") | ||||
| }); | ||||
| ConfigureEventBus(app); | ||||
| try | ||||
| { | ||||
|     Log.Information("Configuring web host ({ApplicationContext})...", Program.AppName); | ||||
|     var host = BuildWebHost(configuration, args); | ||||
| 
 | ||||
|     Log.Information("Applying migrations ({ApplicationContext})...", Program.AppName); | ||||
|     host.MigrateDbContext<OrderingContext>((context, services) => | ||||
|     { | ||||
|         var env = services.GetService<IWebHostEnvironment>(); | ||||
|         var settings = services.GetService<IOptions<OrderingSettings>>(); | ||||
|         var logger = services.GetService<ILogger<OrderingContextSeed>>(); | ||||
|     using var scope = app.Services.CreateScope(); | ||||
|     var context = scope.ServiceProvider.GetRequiredService<OrderingContext>(); | ||||
|     var env = app.Services.GetService<IWebHostEnvironment>(); | ||||
|     var settings = app.Services.GetService<IOptions<OrderingSettings>>(); | ||||
|     var logger = app.Services.GetService<ILogger<OrderingContextSeed>>(); | ||||
|     await context.Database.MigrateAsync(); | ||||
| 
 | ||||
|         new OrderingContextSeed() | ||||
|             .SeedAsync(context, env, settings, logger) | ||||
|             .Wait(); | ||||
|     }) | ||||
|     .MigrateDbContext<IntegrationEventLogContext>((_, __) => { }); | ||||
|     await new OrderingContextSeed().SeedAsync(context, env, settings, logger); | ||||
|     var integEventContext = scope.ServiceProvider.GetRequiredService<IntegrationEventLogContext>(); | ||||
|     await integEventContext.Database.MigrateAsync(); | ||||
| 
 | ||||
|     Log.Information("Starting web host ({ApplicationContext})...", Program.AppName); | ||||
|     host.Run(); | ||||
|     await app.RunAsync(); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| @ -34,30 +141,17 @@ finally | ||||
| { | ||||
|     Log.CloseAndFlush(); | ||||
| } | ||||
| void ConfigureEventBus(IApplicationBuilder app) | ||||
| { | ||||
|     var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>(); | ||||
| 
 | ||||
| IWebHost BuildWebHost(IConfiguration configuration, string[] args) => | ||||
|     WebHost.CreateDefaultBuilder(args) | ||||
|         .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; | ||||
|             }); | ||||
| 
 | ||||
|         }) | ||||
|         .ConfigureAppConfiguration(x => x.AddConfiguration(configuration)) | ||||
|         .UseStartup<Startup>() | ||||
|         .UseContentRoot(Directory.GetCurrentDirectory()) | ||||
|         .UseSerilog() | ||||
|         .Build(); | ||||
| 
 | ||||
|     eventBus.Subscribe<UserCheckoutAcceptedIntegrationEvent, IIntegrationEventHandler<UserCheckoutAcceptedIntegrationEvent>>(); | ||||
|     eventBus.Subscribe<GracePeriodConfirmedIntegrationEvent, IIntegrationEventHandler<GracePeriodConfirmedIntegrationEvent>>(); | ||||
|     eventBus.Subscribe<OrderStockConfirmedIntegrationEvent, IIntegrationEventHandler<OrderStockConfirmedIntegrationEvent>>(); | ||||
|     eventBus.Subscribe<OrderStockRejectedIntegrationEvent, IIntegrationEventHandler<OrderStockRejectedIntegrationEvent>>(); | ||||
|     eventBus.Subscribe<OrderPaymentFailedIntegrationEvent, IIntegrationEventHandler<OrderPaymentFailedIntegrationEvent>>(); | ||||
|     eventBus.Subscribe<OrderPaymentSucceededIntegrationEvent, IIntegrationEventHandler<OrderPaymentSucceededIntegrationEvent>>(); | ||||
| } | ||||
| Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) | ||||
| { | ||||
|     var seqServerUrl = configuration["Serilog:SeqServerUrl"]; | ||||
| @ -68,42 +162,295 @@ Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) | ||||
|         .Enrich.FromLogContext() | ||||
|         .WriteTo.Console() | ||||
|         .WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl) | ||||
|         .WriteTo.Http(string.IsNullOrWhiteSpace(logstashUrl) ? "http://logstash:8080" : logstashUrl,null) | ||||
|         .WriteTo.Http(string.IsNullOrWhiteSpace(logstashUrl) ? "http://logstash:8080" : logstashUrl, null) | ||||
|         .ReadFrom.Configuration(configuration) | ||||
|         .CreateLogger(); | ||||
| } | ||||
| 
 | ||||
| IConfiguration GetConfiguration() | ||||
| { | ||||
|     var builder = new ConfigurationBuilder() | ||||
|         .SetBasePath(Directory.GetCurrentDirectory()) | ||||
|         .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) | ||||
|         .AddEnvironmentVariables(); | ||||
| 
 | ||||
|     var config = builder.Build(); | ||||
| 
 | ||||
|     if (config.GetValue<bool>("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); | ||||
|     } | ||||
| 
 | ||||
|     return builder.Build(); | ||||
| } | ||||
| 
 | ||||
| (int httpPort, int grpcPort) GetDefinedPorts(IConfiguration config) | ||||
| { | ||||
|     var grpcPort = config.GetValue("GRPC_PORT", 5001); | ||||
|     var port = config.GetValue("PORT", 80); | ||||
|     return (port, grpcPort); | ||||
| } | ||||
| 
 | ||||
| public partial class Program | ||||
| { | ||||
| 
 | ||||
|     public static string Namespace = typeof(Startup).Namespace; | ||||
|     public static string Namespace = typeof(Program).Assembly.GetName().Name; | ||||
|     public static string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1); | ||||
| } | ||||
| static class CustomExtensionsMethods | ||||
| { | ||||
|     public static IServiceCollection AddApplicationInsights(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         services.AddApplicationInsightsTelemetry(configuration); | ||||
|         services.AddApplicationInsightsKubernetesEnricher(); | ||||
| 
 | ||||
|         return services; | ||||
|     } | ||||
| 
 | ||||
|     public static IServiceCollection AddCustomMvc(this IServiceCollection services) | ||||
|     { | ||||
|         // Add framework services. | ||||
|         services.AddControllers(options => | ||||
|         { | ||||
|             options.Filters.Add(typeof(HttpGlobalExceptionFilter)); | ||||
|         }) | ||||
|             // Added for functional tests | ||||
|             .AddApplicationPart(typeof(OrdersController).Assembly) | ||||
|             .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 AddHealthChecks(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         var hcBuilder = services.AddHealthChecks(); | ||||
| 
 | ||||
|         hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy()); | ||||
| 
 | ||||
|         hcBuilder | ||||
|             .AddSqlServer( | ||||
|                 configuration["ConnectionString"], | ||||
|                 name: "OrderingDB-check", | ||||
|                 tags: new string[] { "orderingdb" }); | ||||
| 
 | ||||
|         if (configuration.GetValue<bool>("AzureServiceBusEnabled")) | ||||
|         { | ||||
|             hcBuilder | ||||
|                 .AddAzureServiceBusTopic( | ||||
|                     configuration["EventBusConnection"], | ||||
|                     topicName: "eshop_event_bus", | ||||
|                     name: "ordering-servicebus-check", | ||||
|                     tags: new string[] { "servicebus" }); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             hcBuilder | ||||
|                 .AddRabbitMQ( | ||||
|                     $"amqp://{configuration["EventBusConnection"]}", | ||||
|                     name: "ordering-rabbitmqbus-check", | ||||
|                     tags: new string[] { "rabbitmqbus" }); | ||||
|         } | ||||
| 
 | ||||
|         return services; | ||||
|     } | ||||
| 
 | ||||
|     public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         services.AddDbContext<OrderingContext>(options => | ||||
|         { | ||||
|             options.UseSqlServer(configuration["ConnectionString"], | ||||
|                 sqlServerOptionsAction: sqlOptions => | ||||
|                 { | ||||
|                     sqlOptions.MigrationsAssembly(typeof(Program).GetTypeInfo().Assembly.GetName().Name); | ||||
|                     sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); | ||||
|                 }); | ||||
|         }, | ||||
|                     ServiceLifetime.Scoped  //Showing explicitly that the DbContext is shared across the HTTP request scope (graph of objects started in the HTTP request) | ||||
|                 ); | ||||
| 
 | ||||
|         services.AddDbContext<IntegrationEventLogContext>(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 AddCustomSwagger(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         services.AddSwaggerGen(options => | ||||
|         { | ||||
|             options.SwaggerDoc("v1", new OpenApiInfo | ||||
|             { | ||||
|                 Title = "eShopOnContainers - Ordering HTTP API", | ||||
|                 Version = "v1", | ||||
|                 Description = "The Ordering Service HTTP API" | ||||
|             }); | ||||
|             options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme | ||||
|             { | ||||
|                 Type = SecuritySchemeType.OAuth2, | ||||
|                 Flows = new OpenApiOAuthFlows() | ||||
|                 { | ||||
|                     Implicit = new OpenApiOAuthFlow() | ||||
|                     { | ||||
|                         AuthorizationUrl = new Uri($"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize"), | ||||
|                         TokenUrl = new Uri($"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/token"), | ||||
|                         Scopes = new Dictionary<string, string>() | ||||
|                         { | ||||
|                             { "orders", "Ordering API" } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|             options.OperationFilter<AuthorizeCheckOperationFilter>(); | ||||
|         }); | ||||
|         return services; | ||||
|     } | ||||
| 
 | ||||
|     public static IServiceCollection AddCustomIntegrations(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); | ||||
|         services.AddTransient<IIdentityService, IdentityService>(); | ||||
|         services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>( | ||||
|             sp => (DbConnection c) => new IntegrationEventLogService(c)); | ||||
| 
 | ||||
|         services.AddTransient<IOrderingIntegrationEventService, OrderingIntegrationEventService>(); | ||||
| 
 | ||||
|         if (configuration.GetValue<bool>("AzureServiceBusEnabled")) | ||||
|         { | ||||
|             services.AddSingleton<IServiceBusPersisterConnection>(sp => | ||||
|             { | ||||
|                 var serviceBusConnectionString = configuration["EventBusConnection"]; | ||||
| 
 | ||||
|                 var subscriptionClientName = configuration["SubscriptionClientName"]; | ||||
| 
 | ||||
|                 return new DefaultServiceBusPersisterConnection(serviceBusConnectionString); | ||||
|             }); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             services.AddSingleton<IRabbitMQPersistentConnection>(sp => | ||||
|             { | ||||
|                 var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>(); | ||||
| 
 | ||||
| 
 | ||||
|                 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 AddCustomConfiguration(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         services.AddOptions(); | ||||
|         services.Configure<OrderingSettings>(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 AddEventBus(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         if (configuration.GetValue<bool>("AzureServiceBusEnabled")) | ||||
|         { | ||||
|             services.AddSingleton<IEventBus, EventBusServiceBus>(sp => | ||||
|             { | ||||
|                 var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>(); | ||||
|                 var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>(); | ||||
|                 var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | ||||
|                 string subscriptionName = configuration["SubscriptionClientName"]; | ||||
| 
 | ||||
|                 return new EventBusServiceBus(serviceBusPersisterConnection, logger, | ||||
|                     eventBusSubcriptionsManager, sp, subscriptionName); | ||||
|             }); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             services.AddSingleton<IEventBus, EventBusRabbitMQ>(sp => | ||||
|             { | ||||
|                 var subscriptionClientName = configuration["SubscriptionClientName"]; | ||||
|                 var rabbitMQPersistentConnection = sp.GetRequiredService<IRabbitMQPersistentConnection>(); | ||||
|                 var logger = sp.GetRequiredService<ILogger<EventBusRabbitMQ>>(); | ||||
|                 var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | ||||
| 
 | ||||
|                 var retryCount = 5; | ||||
|                 if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) | ||||
|                 { | ||||
|                     retryCount = int.Parse(configuration["EventBusRetryCount"]); | ||||
|                 } | ||||
| 
 | ||||
|                 return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, sp, eventBusSubcriptionsManager, subscriptionClientName, retryCount); | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>(); | ||||
| 
 | ||||
|         return services; | ||||
|     } | ||||
| 
 | ||||
|     public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         // prevent from mapping "sub" claim to nameidentifier. | ||||
|         JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); | ||||
| 
 | ||||
|         var identityUrl = configuration.GetValue<string>("IdentityUrl"); | ||||
| 
 | ||||
|         services.AddAuthentication("Bearer").AddJwtBearer(options => | ||||
|         { | ||||
|             options.Authority = identityUrl; | ||||
|             options.RequireHttpsMetadata = false; | ||||
|             options.Audience = "orders"; | ||||
|             options.TokenValidationParameters.ValidateAudience = false; | ||||
|         }); | ||||
| 
 | ||||
|         return services; | ||||
|     } | ||||
|     public static IServiceCollection AddCustomAuthorization(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         services.AddAuthorization(options => | ||||
|         { | ||||
|             options.AddPolicy("ApiScope", policy => | ||||
|             { | ||||
|                 policy.RequireAuthenticatedUser(); | ||||
|                 policy.RequireClaim("scope", "orders"); | ||||
|             }); | ||||
|         }); | ||||
|         return services; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,396 +0,0 @@ | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API; | ||||
| 
 | ||||
| public class Startup | ||||
| { | ||||
|     public Startup(IConfiguration configuration) | ||||
|     { | ||||
|         Configuration = configuration; | ||||
|     } | ||||
| 
 | ||||
|     public IConfiguration Configuration { get; } | ||||
| 
 | ||||
|     public virtual IServiceProvider ConfigureServices(IServiceCollection services) | ||||
|     { | ||||
|         services | ||||
|             .AddGrpc(options => | ||||
|             { | ||||
|                 options.EnableDetailedErrors = true; | ||||
|             }) | ||||
|             .Services | ||||
|             .AddApplicationInsights(Configuration) | ||||
|             .AddCustomMvc() | ||||
|             .AddHealthChecks(Configuration) | ||||
|             .AddCustomDbContext(Configuration) | ||||
|             .AddCustomSwagger(Configuration) | ||||
|             .AddCustomAuthentication(Configuration) | ||||
|             .AddCustomAuthorization(Configuration) | ||||
|             .AddCustomIntegrations(Configuration) | ||||
|             .AddCustomConfiguration(Configuration) | ||||
|             .AddEventBus(Configuration); | ||||
|         //configure autofac | ||||
| 
 | ||||
|         var container = new ContainerBuilder(); | ||||
|         container.Populate(services); | ||||
| 
 | ||||
|         container.RegisterModule(new MediatorModule()); | ||||
|         container.RegisterModule(new ApplicationModule(Configuration["ConnectionString"])); | ||||
| 
 | ||||
|         return new AutofacServiceProvider(container.Build()); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) | ||||
|     { | ||||
|         //loggerFactory.AddAzureWebAppDiagnostics(); | ||||
|         //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); | ||||
| 
 | ||||
|         var pathBase = Configuration["PATH_BASE"]; | ||||
|         if (!string.IsNullOrEmpty(pathBase)) | ||||
|         { | ||||
|             loggerFactory.CreateLogger<Startup>().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", "Ordering.API V1"); | ||||
|                 c.OAuthClientId("orderingswaggerui"); | ||||
|                 c.OAuthAppName("Ordering Swagger UI"); | ||||
|             }); | ||||
| 
 | ||||
|         app.UseRouting(); | ||||
|         app.UseCors("CorsPolicy"); | ||||
|         ConfigureAuth(app); | ||||
| 
 | ||||
|         app.UseEndpoints(endpoints => | ||||
|         { | ||||
|             endpoints.MapGrpcService<OrderingService>(); | ||||
|             endpoints.MapDefaultControllerRoute(); | ||||
|             endpoints.MapControllers(); | ||||
|             endpoints.MapGet("/_proto/", async ctx => | ||||
|             { | ||||
|                 ctx.Response.ContentType = "text/plain"; | ||||
|                 using var fs = new FileStream(Path.Combine(env.ContentRootPath, "Proto", "basket.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.MapHealthChecks("/hc", new HealthCheckOptions() | ||||
|             { | ||||
|                 Predicate = _ => true, | ||||
|                 ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse | ||||
|             }); | ||||
|             endpoints.MapHealthChecks("/liveness", new HealthCheckOptions | ||||
|             { | ||||
|                 Predicate = r => r.Name.Contains("self") | ||||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         ConfigureEventBus(app); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     private void ConfigureEventBus(IApplicationBuilder app) | ||||
|     { | ||||
|         var eventBus = app.ApplicationServices.GetRequiredService<BuildingBlocks.EventBus.Abstractions.IEventBus>(); | ||||
| 
 | ||||
|         eventBus.Subscribe<UserCheckoutAcceptedIntegrationEvent, IIntegrationEventHandler<UserCheckoutAcceptedIntegrationEvent>>(); | ||||
|         eventBus.Subscribe<GracePeriodConfirmedIntegrationEvent, IIntegrationEventHandler<GracePeriodConfirmedIntegrationEvent>>(); | ||||
|         eventBus.Subscribe<OrderStockConfirmedIntegrationEvent, IIntegrationEventHandler<OrderStockConfirmedIntegrationEvent>>(); | ||||
|         eventBus.Subscribe<OrderStockRejectedIntegrationEvent, IIntegrationEventHandler<OrderStockRejectedIntegrationEvent>>(); | ||||
|         eventBus.Subscribe<OrderPaymentFailedIntegrationEvent, IIntegrationEventHandler<OrderPaymentFailedIntegrationEvent>>(); | ||||
|         eventBus.Subscribe<OrderPaymentSucceededIntegrationEvent, IIntegrationEventHandler<OrderPaymentSucceededIntegrationEvent>>(); | ||||
|     } | ||||
| 
 | ||||
|     protected virtual void ConfigureAuth(IApplicationBuilder app) | ||||
|     { | ||||
|         app.UseAuthentication(); | ||||
|         app.UseAuthorization(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static class CustomExtensionsMethods | ||||
| { | ||||
|     public static IServiceCollection AddApplicationInsights(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         services.AddApplicationInsightsTelemetry(configuration); | ||||
|         services.AddApplicationInsightsKubernetesEnricher(); | ||||
| 
 | ||||
|         return services; | ||||
|     } | ||||
| 
 | ||||
|     public static IServiceCollection AddCustomMvc(this IServiceCollection services) | ||||
|     { | ||||
|         // Add framework services. | ||||
|         services.AddControllers(options => | ||||
|             { | ||||
|                 options.Filters.Add(typeof(HttpGlobalExceptionFilter)); | ||||
|             }) | ||||
|             // Added for functional tests | ||||
|             .AddApplicationPart(typeof(OrdersController).Assembly) | ||||
|             .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 AddHealthChecks(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         var hcBuilder = services.AddHealthChecks(); | ||||
| 
 | ||||
|         hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy()); | ||||
| 
 | ||||
|         hcBuilder | ||||
|             .AddSqlServer( | ||||
|                 configuration["ConnectionString"], | ||||
|                 name: "OrderingDB-check", | ||||
|                 tags: new string[] { "orderingdb" }); | ||||
| 
 | ||||
|         if (configuration.GetValue<bool>("AzureServiceBusEnabled")) | ||||
|         { | ||||
|             hcBuilder | ||||
|                 .AddAzureServiceBusTopic( | ||||
|                     configuration["EventBusConnection"], | ||||
|                     topicName: "eshop_event_bus", | ||||
|                     name: "ordering-servicebus-check", | ||||
|                     tags: new string[] { "servicebus" }); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             hcBuilder | ||||
|                 .AddRabbitMQ( | ||||
|                     $"amqp://{configuration["EventBusConnection"]}", | ||||
|                     name: "ordering-rabbitmqbus-check", | ||||
|                     tags: new string[] { "rabbitmqbus" }); | ||||
|         } | ||||
| 
 | ||||
|         return services; | ||||
|     } | ||||
| 
 | ||||
|     public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         services.AddDbContext<OrderingContext>(options => | ||||
|                 { | ||||
|                     options.UseSqlServer(configuration["ConnectionString"], | ||||
|                         sqlServerOptionsAction: sqlOptions => | ||||
|                         { | ||||
|                             sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); | ||||
|                             sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); | ||||
|                         }); | ||||
|                 }, | ||||
|                     ServiceLifetime.Scoped  //Showing explicitly that the DbContext is shared across the HTTP request scope (graph of objects started in the HTTP request) | ||||
|                 ); | ||||
| 
 | ||||
|         services.AddDbContext<IntegrationEventLogContext>(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 AddCustomSwagger(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         services.AddSwaggerGen(options => | ||||
|         {             | ||||
|             options.SwaggerDoc("v1", new OpenApiInfo | ||||
|             { | ||||
|                 Title = "eShopOnContainers - Ordering HTTP API", | ||||
|                 Version = "v1", | ||||
|                 Description = "The Ordering Service HTTP API" | ||||
|             }); | ||||
|             options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme | ||||
|             { | ||||
|                 Type = SecuritySchemeType.OAuth2, | ||||
|                 Flows = new OpenApiOAuthFlows() | ||||
|                 { | ||||
|                     Implicit = new OpenApiOAuthFlow() | ||||
|                     { | ||||
|                         AuthorizationUrl = new Uri($"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize"), | ||||
|                         TokenUrl = new Uri($"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/token"), | ||||
|                         Scopes = new Dictionary<string, string>() | ||||
|                         { | ||||
|                             { "orders", "Ordering API" } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
| 
 | ||||
|             options.OperationFilter<AuthorizeCheckOperationFilter>(); | ||||
|         }); | ||||
| 
 | ||||
|         return services; | ||||
|     } | ||||
| 
 | ||||
|     public static IServiceCollection AddCustomIntegrations(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); | ||||
|         services.AddTransient<IIdentityService, IdentityService>(); | ||||
|         services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>( | ||||
|             sp => (DbConnection c) => new IntegrationEventLogService(c)); | ||||
| 
 | ||||
|         services.AddTransient<IOrderingIntegrationEventService, OrderingIntegrationEventService>(); | ||||
| 
 | ||||
|         if (configuration.GetValue<bool>("AzureServiceBusEnabled")) | ||||
|         { | ||||
|             services.AddSingleton<IServiceBusPersisterConnection>(sp => | ||||
|             { | ||||
|                 var serviceBusConnectionString = configuration["EventBusConnection"]; | ||||
| 
 | ||||
|                 var subscriptionClientName = configuration["SubscriptionClientName"]; | ||||
| 
 | ||||
|                 return new DefaultServiceBusPersisterConnection(serviceBusConnectionString); | ||||
|             }); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             services.AddSingleton<IRabbitMQPersistentConnection>(sp => | ||||
|             { | ||||
|                 var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>(); | ||||
| 
 | ||||
| 
 | ||||
|                 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 AddCustomConfiguration(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         services.AddOptions(); | ||||
|         services.Configure<OrderingSettings>(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 AddEventBus(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         if (configuration.GetValue<bool>("AzureServiceBusEnabled")) | ||||
|         { | ||||
|             services.AddSingleton<IEventBus, EventBusServiceBus>(sp => | ||||
|             { | ||||
|                 var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>(); | ||||
|                 var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>(); | ||||
|                 var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | ||||
|                 string subscriptionName = configuration["SubscriptionClientName"]; | ||||
| 
 | ||||
|                 return new EventBusServiceBus(serviceBusPersisterConnection, logger, | ||||
|                     eventBusSubcriptionsManager, sp, subscriptionName); | ||||
|             }); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             services.AddSingleton<IEventBus, EventBusRabbitMQ>(sp => | ||||
|             { | ||||
|                 var subscriptionClientName = configuration["SubscriptionClientName"]; | ||||
|                 var rabbitMQPersistentConnection = sp.GetRequiredService<IRabbitMQPersistentConnection>(); | ||||
|                 var logger = sp.GetRequiredService<ILogger<EventBusRabbitMQ>>(); | ||||
|                 var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | ||||
| 
 | ||||
|                 var retryCount = 5; | ||||
|                 if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) | ||||
|                 { | ||||
|                     retryCount = int.Parse(configuration["EventBusRetryCount"]); | ||||
|                 } | ||||
| 
 | ||||
|                 return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, sp, eventBusSubcriptionsManager, subscriptionClientName, retryCount); | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>(); | ||||
| 
 | ||||
|         return services; | ||||
|     } | ||||
| 
 | ||||
|     public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         // prevent from mapping "sub" claim to nameidentifier. | ||||
|         JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); | ||||
| 
 | ||||
|         var identityUrl = configuration.GetValue<string>("IdentityUrl"); | ||||
| 
 | ||||
|         services.AddAuthentication("Bearer").AddJwtBearer(options => | ||||
|         { | ||||
|             options.Authority = identityUrl; | ||||
|             options.RequireHttpsMetadata = false; | ||||
|             options.Audience = "orders"; | ||||
|             options.TokenValidationParameters.ValidateAudience = false; | ||||
|         }); | ||||
| 
 | ||||
|         return services; | ||||
|     } | ||||
|     public static IServiceCollection AddCustomAuthorization(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         services.AddAuthorization(options => | ||||
|         { | ||||
|             options.AddPolicy("ApiScope", policy => | ||||
|             { | ||||
|                 policy.RequireAuthenticatedUser(); | ||||
|                 policy.RequireClaim("scope", "orders"); | ||||
|             }); | ||||
|         }); | ||||
|         return services; | ||||
|     } | ||||
| } | ||||
| @ -13,7 +13,7 @@ public class OrderingScenarioBase | ||||
|             { | ||||
|                 cb.AddJsonFile("appsettings.json", optional: false) | ||||
|                 .AddEnvironmentVariables(); | ||||
|             }).UseStartup<OrderingTestsStartup>(); | ||||
|             }); | ||||
| 
 | ||||
|         var testServer = new TestServer(hostBuilder); | ||||
| 
 | ||||
|  | ||||
| @ -1,27 +0,0 @@ | ||||
| namespace Ordering.FunctionalTests; | ||||
| 
 | ||||
| public class OrderingTestsStartup : Startup | ||||
| { | ||||
|     public OrderingTestsStartup(IConfiguration env) : base(env) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     public override IServiceProvider ConfigureServices(IServiceCollection services) | ||||
|     { | ||||
|         // Added to avoid the Authorize data annotation in test environment.  | ||||
|         // Property "SuppressCheckForUnhandledSecurityMetadata" in appsettings.json | ||||
|         services.Configure<RouteOptions>(Configuration); | ||||
|         return base.ConfigureServices(services); | ||||
|     } | ||||
|     protected override void ConfigureAuth(IApplicationBuilder app) | ||||
|     { | ||||
|         if (Configuration["isTest"] == bool.TrueString.ToLowerInvariant()) | ||||
|         { | ||||
|             app.UseMiddleware<AutoAuthorizeMiddleware>(); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             base.ConfigureAuth(app); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -16,7 +16,7 @@ public class BasketScenariosBase | ||||
|             { | ||||
|                 cb.AddJsonFile("Services/Basket/appsettings.json", optional: false) | ||||
|                 .AddEnvironmentVariables(); | ||||
|             }).UseStartup<BasketTestsStartup>(); | ||||
|             }); | ||||
| 
 | ||||
|         return new TestServer(hostBuilder); | ||||
|     } | ||||
|  | ||||
| @ -1,21 +0,0 @@ | ||||
| namespace FunctionalTests.Services.Basket; | ||||
| using Microsoft.eShopOnContainers.Services.Basket.API; | ||||
| class BasketTestsStartup : Startup | ||||
| { | ||||
|     public BasketTestsStartup(IConfiguration env) : base(env) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     protected override void ConfigureAuth(IApplicationBuilder app) | ||||
|     { | ||||
|         if (Configuration["isTest"] == bool.TrueString.ToLowerInvariant()) | ||||
|         { | ||||
|             app.UseMiddleware<AutoAuthorizeMiddleware>(); | ||||
|             app.UseAuthorization(); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             base.ConfigureAuth(app); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -14,7 +14,7 @@ public class CatalogScenariosBase | ||||
|             { | ||||
|                 cb.AddJsonFile("Services/Catalog/appsettings.json", optional: false) | ||||
|                 .AddEnvironmentVariables(); | ||||
|             }).UseStartup<Startup>(); | ||||
|             }); | ||||
| 
 | ||||
|         var testServer = new TestServer(hostBuilder); | ||||
| 
 | ||||
|  | ||||
| @ -13,7 +13,7 @@ public class OrderingScenariosBase | ||||
|             { | ||||
|                 cb.AddJsonFile("Services/Ordering/appsettings.json", optional: false) | ||||
|                 .AddEnvironmentVariables(); | ||||
|             }).UseStartup<OrderingTestsStartup>(); | ||||
|             }); | ||||
| 
 | ||||
|         var testServer = new TestServer(hostBuilder); | ||||
| 
 | ||||
|  | ||||
| @ -1,22 +0,0 @@ | ||||
| namespace FunctionalTests.Services.Ordering; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.API; | ||||
| 
 | ||||
| public class OrderingTestsStartup : Startup | ||||
| { | ||||
|     public OrderingTestsStartup(IConfiguration env) : base(env) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     protected override void ConfigureAuth(IApplicationBuilder app) | ||||
|     { | ||||
|         if (Configuration["isTest"] == bool.TrueString.ToLowerInvariant()) | ||||
|         { | ||||
|             app.UseMiddleware<AutoAuthorizeMiddleware>(); | ||||
|             app.UseAuthorization(); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             base.ConfigureAuth(app); | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user