diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/.editorconfig b/src/Services/Ordering/Ordering.BackgroundTasks/.editorconfig new file mode 100644 index 000000000..a2816621e --- /dev/null +++ b/src/Services/Ordering/Ordering.BackgroundTasks/.editorconfig @@ -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 \ No newline at end of file diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/.editorconfig.bak b/src/Services/Ordering/Ordering.BackgroundTasks/.editorconfig.bak new file mode 100644 index 000000000..e69de29bb diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/GlobalUsings.cs b/src/Services/Ordering/Ordering.BackgroundTasks/GlobalUsings.cs new file mode 100644 index 000000000..43b0981b8 --- /dev/null +++ b/src/Services/Ordering/Ordering.BackgroundTasks/GlobalUsings.cs @@ -0,0 +1,17 @@ +global using Autofac.Extensions.DependencyInjection; +global using Microsoft.AspNetCore.Hosting; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.Hosting; +global using Ordering.BackgroundTasks.Extensions; +global using Serilog; +global using System.IO; +global using HealthChecks.UI.Client; +global using Microsoft.AspNetCore.Builder; +global using Microsoft.AspNetCore.Diagnostics.HealthChecks; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Logging; +global using Ordering.BackgroundTasks.Extensions; +global using Ordering.BackgroundTasks.Services; +global using System; +global using Ordering.BackgroundTasks; \ No newline at end of file diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj b/src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj index 0b81c1cfa..5aa2a26aa 100644 --- a/src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj +++ b/src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj @@ -32,4 +32,12 @@ + + + + + + + + diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Program.cs b/src/Services/Ordering/Ordering.BackgroundTasks/Program.cs index ab30ba2d9..ef12360db 100644 --- a/src/Services/Ordering/Ordering.BackgroundTasks/Program.cs +++ b/src/Services/Ordering/Ordering.BackgroundTasks/Program.cs @@ -1,35 +1,74 @@ -using Autofac.Extensions.DependencyInjection; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; -using Ordering.BackgroundTasks.Extensions; -using Serilog; -using System.IO; +var appName = "Ordering.BackgroundTasks"; +var builder = WebApplication.CreateBuilder(new WebApplicationOptions +{ + Args = args, + ApplicationName = typeof(Program).Assembly.FullName +}); +builder.Configuration.SetBasePath(Directory.GetCurrentDirectory()); +builder.Configuration.AddJsonFile("appsettings.json", optional: true); +builder.Configuration.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true); +builder.Configuration.AddEnvironmentVariables(); +builder.Host.UseSerilog(CreateSerilogLogger(builder.Configuration)); +builder.Services.AddCustomHealthCheck(builder.Configuration) + .Configure(builder.Configuration) + .AddOptions() + .AddHostedService() + .AddEventBus(builder.Configuration); +var app = builder.Build(); +if (app.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); +} +else +{ + app.UseExceptionHandler("/Home/Error"); +} +app.UseRouting(); -namespace Ordering.BackgroundTasks +app.MapHealthChecks("/hc", new HealthCheckOptions() { - public class Program - { - public static readonly string AppName = typeof(Program).Assembly.GetName().Name; + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse +}); +app.MapHealthChecks("/liveness", new HealthCheckOptions +{ + Predicate = r => r.Name.Contains("self") +}); - public static void Main(string[] args) - { - CreateHostBuilder(args).Run(); - } +try +{ + Log.Information("Starting web host ({ApplicationContext})...", Program.AppName); + await app.RunAsync(); - public static IHost CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .UseServiceProviderFactory(new AutofacServiceProviderFactory()) - .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup()) - .ConfigureAppConfiguration((host, builder) => - { - builder.SetBasePath(Directory.GetCurrentDirectory()); - builder.AddJsonFile("appsettings.json", optional: true); - builder.AddJsonFile($"appsettings.{host.HostingEnvironment.EnvironmentName}.json", optional: true); - builder.AddEnvironmentVariables(); - builder.AddCommandLine(args); - }) - .ConfigureLogging((host, builder) => builder.UseSerilog(host.Configuration).AddSerilog()) - .Build(); - } + return 0; +} +catch (Exception ex) +{ + Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", Program.AppName); + return 1; +} +finally +{ + Log.CloseAndFlush(); +} + +Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) +{ + var seqServerUrl = configuration["Serilog:SeqServerUrl"]; + var logstashUrl = configuration["Serilog:LogstashgUrl"]; + return new LoggerConfiguration() + .MinimumLevel.Verbose() + .Enrich.WithProperty("ApplicationContext", Program.AppName) + .Enrich.FromLogContext() + .WriteTo.Console() + .WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl) + .WriteTo.Http(string.IsNullOrWhiteSpace(logstashUrl) ? "http://logstash:8080" : logstashUrl, null) + .ReadFrom.Configuration(configuration) + .CreateLogger(); +} + +public partial class Program +{ + public static string Namespace = typeof(Program).Assembly.GetName().Name; + public static string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1); } diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Startup.cs b/src/Services/Ordering/Ordering.BackgroundTasks/Startup.cs deleted file mode 100644 index 403844e73..000000000 --- a/src/Services/Ordering/Ordering.BackgroundTasks/Startup.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace Ordering.BackgroundTasks -{ - using HealthChecks.UI.Client; - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Diagnostics.HealthChecks; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Logging; - using Ordering.BackgroundTasks.Extensions; - using Ordering.BackgroundTasks.Services; - - public class Startup - { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - public virtual void ConfigureServices(IServiceCollection services) - { - services.AddCustomHealthCheck(this.Configuration) - .Configure(this.Configuration) - .AddOptions() - .AddHostedService() - .AddEventBus(this.Configuration); - } - - - public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) - { - app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapHealthChecks("/hc", new HealthCheckOptions() - { - Predicate = _ => true, - ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse - }); - endpoints.MapHealthChecks("/liveness", new HealthCheckOptions - { - Predicate = r => r.Name.Contains("self") - }); - }); - } - } -} diff --git a/src/Services/Ordering/Ordering.SignalrHub/.editorconfig b/src/Services/Ordering/Ordering.SignalrHub/.editorconfig new file mode 100644 index 000000000..a2816621e --- /dev/null +++ b/src/Services/Ordering/Ordering.SignalrHub/.editorconfig @@ -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 \ No newline at end of file diff --git a/src/Services/Ordering/Ordering.SignalrHub/GlobalUsings.cs b/src/Services/Ordering/Ordering.SignalrHub/GlobalUsings.cs index 61640da79..b913f56bf 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/GlobalUsings.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/GlobalUsings.cs @@ -30,3 +30,4 @@ global using System.IO; global using System.Reflection; global using System.Threading.Tasks; global using System; +global using Microsoft.Extensions.Hosting; diff --git a/src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj b/src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj index 92e65dd6b..77fbbb1c2 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj +++ b/src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj @@ -37,4 +37,8 @@ + + + + diff --git a/src/Services/Ordering/Ordering.SignalrHub/Program.cs b/src/Services/Ordering/Ordering.SignalrHub/Program.cs index ed1edf59e..ffbb5b8dc 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/Program.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/Program.cs @@ -1,14 +1,120 @@ -var configuration = GetConfiguration(); +var appName = "Ordering.SignalrHub"; +var builder = WebApplication.CreateBuilder(new WebApplicationOptions +{ + Args = args, + ApplicationName = typeof(Program).Assembly.FullName, + ContentRootPath = Directory.GetCurrentDirectory() +}); +builder.Configuration.SetBasePath(Directory.GetCurrentDirectory()); +builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); +builder.Configuration.AddEnvironmentVariables(); +builder.WebHost.CaptureStartupErrors(false); +builder.Host.UseSerilog(CreateSerilogLogger(builder.Configuration)); -Log.Logger = CreateSerilogLogger(configuration); +builder.Services + .AddCustomHealthCheck(builder.Configuration) + .AddCors(options => + { + options.AddPolicy("CorsPolicy", + builder => builder + .AllowAnyMethod() + .AllowAnyHeader() + .SetIsOriginAllowed((host) => true) + .AllowCredentials()); + }); +if (builder.Configuration.GetValue("IsClusterEnv") == bool.TrueString) +{ + builder.Services + .AddSignalR() + .AddStackExchangeRedis(builder.Configuration["SignalrStoreConnectionString"]); +} +else +{ + builder.Services.AddSignalR(); +} -try +if (builder.Configuration.GetValue("AzureServiceBusEnabled")) +{ + builder.Services.AddSingleton(sp => + { + var serviceBusConnectionString = builder.Configuration["EventBusConnection"]; + + var subscriptionClientName = builder.Configuration["SubscriptionClientName"]; + + return new DefaultServiceBusPersisterConnection(serviceBusConnectionString); + }); +} +else +{ + builder.Services.AddSingleton(sp => + { + var logger = sp.GetRequiredService>(); + + + var factory = new ConnectionFactory() + { + HostName = builder.Configuration["EventBusConnection"], + DispatchConsumersAsync = true + }; + + if (!string.IsNullOrEmpty(builder.Configuration["EventBusUserName"])) + { + factory.UserName = builder.Configuration["EventBusUserName"]; + } + + if (!string.IsNullOrEmpty(builder.Configuration["EventBusPassword"])) + { + factory.Password = builder.Configuration["EventBusPassword"]; + } + + var retryCount = 5; + if (!string.IsNullOrEmpty(builder.Configuration["EventBusRetryCount"])) + { + retryCount = int.Parse(builder.Configuration["EventBusRetryCount"]); + } + + return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); + }); +} +ConfigureAuthService(builder.Services, builder.Configuration); +RegisterEventBus(builder.Services, builder.Configuration); +builder.Services.AddOptions(); +builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); +builder.Host.ConfigureContainer(conBuilder => conBuilder.RegisterModule(new ApplicationModule())); +var app = builder.Build(); +if (app.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); +} +else +{ + app.UseExceptionHandler("/Home/Error"); +} +var pathBase = builder.Configuration["PATH_BASE"]; +if (!string.IsNullOrEmpty(pathBase)) +{ + app.UsePathBase(pathBase); +} +app.UseRouting(); +app.UseCors("CorsPolicy"); +app.UseAuthentication(); +app.UseAuthorization(); +app.MapHealthChecks("/hc", new HealthCheckOptions() +{ + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse +}); +app.MapHealthChecks("/liveness", new HealthCheckOptions { - Log.Information("Configuring web host ({ApplicationContext})...", Program.AppName); - var host = BuildWebHost(configuration, args); + Predicate = r => r.Name.Contains("self") +}); +app.MapHub("/hub/notificationhub"); +ConfigureEventBus(app); +try +{ Log.Information("Starting web host ({ApplicationContext})...", Program.AppName); - host.Run(); + await app.RunAsync(); return 0; } @@ -21,16 +127,90 @@ finally { Log.CloseAndFlush(); } +void ConfigureEventBus(IApplicationBuilder app) +{ + var eventBus = app.ApplicationServices.GetRequiredService(); + + eventBus.Subscribe(); + eventBus.Subscribe(); + eventBus.Subscribe(); + eventBus.Subscribe(); + eventBus.Subscribe(); + eventBus.Subscribe(); +} +void ConfigureAuthService(IServiceCollection services, IConfiguration configuration) +{ + // prevent from mapping "sub" claim to nameidentifier. + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); + + var identityUrl = configuration.GetValue("IdentityUrl"); + + services.AddAuthentication("Bearer").AddJwtBearer(options => + { + options.Authority = identityUrl; + options.RequireHttpsMetadata = false; + options.Audience = "orders.signalrhub"; + options.TokenValidationParameters.ValidateAudience = false; + options.Events = new JwtBearerEvents + { + OnMessageReceived = context => + { + var accessToken = context.Request.Query["access_token"]; + + var path = context.HttpContext.Request.Path; + if (!string.IsNullOrEmpty(accessToken) && (path.StartsWithSegments("/hub/notificationhub"))) + { + context.Token = accessToken; + } + return Task.CompletedTask; + } + }; + }); + services.AddAuthorization(options => + { + options.AddPolicy("ApiScope", policy => + { + policy.RequireAuthenticatedUser(); + policy.RequireClaim("scope", "orders.signalrhub"); + }); + }); +} +void RegisterEventBus(IServiceCollection services, IConfiguration configuration) +{ + if (configuration.GetValue("AzureServiceBusEnabled")) + { + services.AddSingleton(sp => + { + var serviceBusPersisterConnection = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); + string subscriptionName = configuration["SubscriptionClientName"]; + + return new EventBusServiceBus(serviceBusPersisterConnection, logger, + eventBusSubcriptionsManager, sp, subscriptionName); + }); + } + else + { + services.AddSingleton(sp => + { + var subscriptionClientName = configuration["SubscriptionClientName"]; + var rabbitMQPersistentConnection = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); + var retryCount = 5; + if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) + { + retryCount = int.Parse(configuration["EventBusRetryCount"]); + } -static IWebHost BuildWebHost(IConfiguration configuration, string[] args) => - WebHost.CreateDefaultBuilder(args) - .CaptureStartupErrors(false) - .ConfigureAppConfiguration(x => x.AddConfiguration(configuration)) - .UseStartup() - .UseSerilog() - .Build(); + return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, sp, eventBusSubcriptionsManager, subscriptionClientName, retryCount); + }); + } + services.AddSingleton(); +} static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) { var seqServerUrl = configuration["Serilog:SeqServerUrl"]; @@ -45,19 +225,37 @@ static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) .ReadFrom.Configuration(configuration) .CreateLogger(); } - -static IConfiguration GetConfiguration() -{ - var builder = new ConfigurationBuilder() - .SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddEnvironmentVariables(); - - return builder.Build(); -} - 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); -} \ No newline at end of file +} +public static class CustomExtensionMethods +{ + public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) + { + var hcBuilder = services.AddHealthChecks(); + + hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy()); + + if (configuration.GetValue("AzureServiceBusEnabled")) + { + hcBuilder + .AddAzureServiceBusTopic( + configuration["EventBusConnection"], + topicName: "eshop_event_bus", + name: "signalr-servicebus-check", + tags: new string[] { "servicebus" }); + } + else + { + hcBuilder + .AddRabbitMQ( + $"amqp://{configuration["EventBusConnection"]}", + name: "signalr-rabbitmqbus-check", + tags: new string[] { "rabbitmqbus" }); + } + + return services; + } +} diff --git a/src/Services/Ordering/Ordering.SignalrHub/Startup.cs b/src/Services/Ordering/Ordering.SignalrHub/Startup.cs deleted file mode 100644 index bc017d218..000000000 --- a/src/Services/Ordering/Ordering.SignalrHub/Startup.cs +++ /dev/null @@ -1,251 +0,0 @@ -namespace Microsoft.eShopOnContainers.Services.Ordering.SignalrHub; - -public class Startup -{ - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - // This method gets called by the runtime. Use this method to add services to the container. - // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 - public IServiceProvider ConfigureServices(IServiceCollection services) - { - services - .AddCustomHealthCheck(Configuration) - .AddCors(options => - { - options.AddPolicy("CorsPolicy", - builder => builder - .AllowAnyMethod() - .AllowAnyHeader() - .SetIsOriginAllowed((host) => true) - .AllowCredentials()); - }); - - if (Configuration.GetValue("IsClusterEnv") == bool.TrueString) - { - services - .AddSignalR() - .AddStackExchangeRedis(Configuration["SignalrStoreConnectionString"]); - } - else - { - services.AddSignalR(); - } - - if (Configuration.GetValue("AzureServiceBusEnabled")) - { - services.AddSingleton(sp => - { - var serviceBusConnectionString = Configuration["EventBusConnection"]; - - var subscriptionClientName = Configuration["SubscriptionClientName"]; - - return new DefaultServiceBusPersisterConnection(serviceBusConnectionString); - }); - } - else - { - services.AddSingleton(sp => - { - var logger = sp.GetRequiredService>(); - - - 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); - }); - } - - ConfigureAuthService(services); - - RegisterEventBus(services); - - services.AddOptions(); - - //configure autofac - var container = new ContainerBuilder(); - container.RegisterModule(new ApplicationModule()); - container.Populate(services); - - return new AutofacServiceProvider(container.Build()); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) - { - //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().LogDebug("Using PATH BASE '{pathBase}'", pathBase); - app.UsePathBase(pathBase); - } - - app.UseRouting(); - app.UseCors("CorsPolicy"); - app.UseAuthentication(); - app.UseAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapHealthChecks("/hc", new HealthCheckOptions() - { - Predicate = _ => true, - ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse - }); - endpoints.MapHealthChecks("/liveness", new HealthCheckOptions - { - Predicate = r => r.Name.Contains("self") - }); - endpoints.MapHub("/hub/notificationhub"); - }); - - ConfigureEventBus(app); - } - - private void ConfigureEventBus(IApplicationBuilder app) - { - var eventBus = app.ApplicationServices.GetRequiredService(); - - eventBus.Subscribe(); - eventBus.Subscribe(); - eventBus.Subscribe(); - eventBus.Subscribe(); - eventBus.Subscribe(); - eventBus.Subscribe(); - } - - private void ConfigureAuthService(IServiceCollection services) - { - // prevent from mapping "sub" claim to nameidentifier. - JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); - - var identityUrl = Configuration.GetValue("IdentityUrl"); - - services.AddAuthentication("Bearer").AddJwtBearer(options => - { - options.Authority = identityUrl; - options.RequireHttpsMetadata = false; - options.Audience = "orders.signalrhub"; - options.TokenValidationParameters.ValidateAudience = false; - options.Events = new JwtBearerEvents - { - OnMessageReceived = context => - { - var accessToken = context.Request.Query["access_token"]; - - var path = context.HttpContext.Request.Path; - if (!string.IsNullOrEmpty(accessToken) && (path.StartsWithSegments("/hub/notificationhub"))) - { - context.Token = accessToken; - } - return Task.CompletedTask; - } - }; - }); - services.AddAuthorization(options => - { - options.AddPolicy("ApiScope", policy => - { - policy.RequireAuthenticatedUser(); - policy.RequireClaim("scope", "orders.signalrhub"); - }); - }); - } - - private void RegisterEventBus(IServiceCollection services) - { - if (Configuration.GetValue("AzureServiceBusEnabled")) - { - services.AddSingleton(sp => - { - var serviceBusPersisterConnection = sp.GetRequiredService(); - var logger = sp.GetRequiredService>(); - var eventBusSubcriptionsManager = sp.GetRequiredService(); - string subscriptionName = Configuration["SubscriptionClientName"]; - - return new EventBusServiceBus(serviceBusPersisterConnection, logger, - eventBusSubcriptionsManager, sp, subscriptionName); - }); - } - else - { - services.AddSingleton(sp => - { - var subscriptionClientName = Configuration["SubscriptionClientName"]; - var rabbitMQPersistentConnection = sp.GetRequiredService(); - var logger = sp.GetRequiredService>(); - var eventBusSubcriptionsManager = sp.GetRequiredService(); - - var retryCount = 5; - if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"])) - { - retryCount = int.Parse(Configuration["EventBusRetryCount"]); - } - - return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, sp, eventBusSubcriptionsManager, subscriptionClientName, retryCount); - }); - } - - services.AddSingleton(); - } -} - -public static class CustomExtensionMethods -{ - public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) - { - var hcBuilder = services.AddHealthChecks(); - - hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy()); - - if (configuration.GetValue("AzureServiceBusEnabled")) - { - hcBuilder - .AddAzureServiceBusTopic( - configuration["EventBusConnection"], - topicName: "eshop_event_bus", - name: "signalr-servicebus-check", - tags: new string[] { "servicebus" }); - } - else - { - hcBuilder - .AddRabbitMQ( - $"amqp://{configuration["EventBusConnection"]}", - name: "signalr-rabbitmqbus-check", - tags: new string[] { "rabbitmqbus" }); - } - - return services; - } -}