From c0cc12ad0c07b6afb98c9804f7ac00c361d3265b Mon Sep 17 00:00:00 2001 From: Tarun Jain Date: Wed, 12 Apr 2023 23:32:11 +0530 Subject: [PATCH] Commit to migrate payment api to WebApplicationBuilder --- .../Payment/Payment.API/.editorconfig | 132 +++++++++++++ .../Payment/Payment.API/GlobalUsings.cs | 3 +- .../Payment/Payment.API/Payment.API.csproj | 8 + src/Services/Payment/Payment.API/Program.cs | 179 ++++++++++++++++-- 4 files changed, 305 insertions(+), 17 deletions(-) create mode 100644 src/Services/Payment/Payment.API/.editorconfig diff --git a/src/Services/Payment/Payment.API/.editorconfig b/src/Services/Payment/Payment.API/.editorconfig new file mode 100644 index 000000000..a2816621e --- /dev/null +++ b/src/Services/Payment/Payment.API/.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/Payment/Payment.API/GlobalUsings.cs b/src/Services/Payment/Payment.API/GlobalUsings.cs index fb2a84e1b..01ba3682b 100644 --- a/src/Services/Payment/Payment.API/GlobalUsings.cs +++ b/src/Services/Payment/Payment.API/GlobalUsings.cs @@ -25,4 +25,5 @@ global using Serilog; global using System.Threading.Tasks; global using System; global using System.IO; -global using Microsoft.AspNetCore.Hosting; \ No newline at end of file +global using Microsoft.AspNetCore.Hosting; +global using Microsoft.Extensions.Hosting; diff --git a/src/Services/Payment/Payment.API/Payment.API.csproj b/src/Services/Payment/Payment.API/Payment.API.csproj index 61cfd2050..cd40ebdca 100644 --- a/src/Services/Payment/Payment.API/Payment.API.csproj +++ b/src/Services/Payment/Payment.API/Payment.API.csproj @@ -35,4 +35,12 @@ + + + + + + + + diff --git a/src/Services/Payment/Payment.API/Program.cs b/src/Services/Payment/Payment.API/Program.cs index 3e49d1ee5..acfc73217 100644 --- a/src/Services/Payment/Payment.API/Program.cs +++ b/src/Services/Payment/Payment.API/Program.cs @@ -1,14 +1,98 @@ -var configuration = GetConfiguration(); +var appName = "Payment.API"; +var builder = WebApplication.CreateBuilder(new WebApplicationOptions +{ + Args = args, + ApplicationName = typeof(Program).Assembly.FullName, + ContentRootPath = Directory.GetCurrentDirectory() +}); +if (builder.Configuration.GetValue("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.CaptureStartupErrors(false); +builder.Host.UseSerilog(CreateSerilogLogger(builder.Configuration)); +builder.Services + .AddCustomHealthCheck(builder.Configuration); +builder.Services.Configure(builder.Configuration); +builder.Services.AddApplicationInsightsTelemetry(builder.Configuration); +if (builder.Configuration.GetValue("AzureServiceBusEnabled")) +{ + builder.Services.AddSingleton(sp => + { + var serviceBusConnectionString = builder.Configuration["EventBusConnection"]; + var subscriptionClientName = builder.Configuration["SubscriptionClientName"]; -Log.Logger = CreateSerilogLogger(configuration); + return new DefaultServiceBusPersisterConnection(serviceBusConnectionString); + }); +} +else +{ + builder.Services.AddSingleton(sp => + { + var logger = sp.GetRequiredService>(); + var factory = new ConnectionFactory() + { + HostName = builder.Configuration["EventBusConnection"], + DispatchConsumersAsync = true + }; -try + 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); + }); +} +RegisterEventBus(builder.Services); +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)) { - Log.Information("Configuring web host ({ApplicationContext})...", Program.AppName); - var host = BuildWebHost(configuration, args); + app.UsePathBase(pathBase); +} +ConfigureEventBus(app); +app.UseRouting(); +app.MapHealthChecks("/hc", new HealthCheckOptions() +{ + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse +}); +app.MapHealthChecks("/liveness", new HealthCheckOptions +{ + Predicate = r => r.Name.Contains("self") +}); +try +{ Log.Information("Starting web host ({ApplicationContext})...", Program.AppName); - host.Run(); + await app.RunAsync(); return 0; } @@ -22,15 +106,49 @@ finally Log.CloseAndFlush(); } +void RegisterEventBus(IServiceCollection services) +{ + if (builder.Configuration.GetValue("AzureServiceBusEnabled")) + { + services.AddSingleton(sp => + { + var serviceBusPersisterConnection = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); + string subscriptionName = builder.Configuration["SubscriptionClientName"]; + + return new EventBusServiceBus(serviceBusPersisterConnection, logger, + eventBusSubcriptionsManager, sp, subscriptionName); + }); + } + else + { + services.AddSingleton(sp => + { + var subscriptionClientName = builder.Configuration["SubscriptionClientName"]; + var rabbitMQPersistentConnection = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); -IWebHost BuildWebHost(IConfiguration configuration, string[] args) => - WebHost.CreateDefaultBuilder(args) - .CaptureStartupErrors(false) - .ConfigureAppConfiguration(x => x.AddConfiguration(configuration)) - .UseStartup() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseSerilog() - .Build(); + var retryCount = 5; + if (!string.IsNullOrEmpty(builder.Configuration["EventBusRetryCount"])) + { + retryCount = int.Parse(builder.Configuration["EventBusRetryCount"]); + } + + return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, sp, eventBusSubcriptionsManager, subscriptionClientName, retryCount); + }); + } + + services.AddTransient(); + services.AddSingleton(); +} + +void ConfigureEventBus(IApplicationBuilder app) +{ + var eventBus = app.ApplicationServices.GetRequiredService(); + eventBus.Subscribe(); +} Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) { @@ -70,6 +188,35 @@ IConfiguration GetConfiguration() 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: "payment-servicebus-check", + tags: new string[] { "servicebus" }); + } + else + { + hcBuilder + .AddRabbitMQ( + $"amqp://{configuration["EventBusConnection"]}", + name: "payment-rabbitmqbus-check", + tags: new string[] { "rabbitmqbus" }); + } + + return services; + } +}