diff --git a/Readme.md b/Readme.md index d4c9ec6..dee8c8b 100644 --- a/Readme.md +++ b/Readme.md @@ -23,6 +23,7 @@ This package currently supports: - **Cors Configuration** with easy configuration options. - **Resilience Pipelines** for `HttpClient` operations. - **Controller Extensions** for mapping old-style MVC controllers. +- **SignalR Extensions** for adding simple SignalR or distributed SignalR backed with Redis. - **OpenTelemetry Integration** for tracking metrics, traces, and logging. - **Health Checks** with default endpoints and startup validation. - Various **Extensions and Utilities**, including enumerable, string, and queryable extensions. @@ -121,6 +122,7 @@ Follow this example to set up your project with all the features provided by thi }, "RepositoryName": "be-lib-sharedkernel", "ConnectionStrings": { + "Redis": "localhost:6379", "PersistentStorage": "/persistence" }, "Security": { @@ -143,14 +145,15 @@ builder .AddResponseCrafter(NamingConvention.ToSnakeCase) .AddOpenApi() .AddOpenTelemetry() - .AddEndpoints(AssemblyRegistry.ToArray()) + .AddMapMinimalApis(AssemblyRegistry.ToArray()) .AddControllers(AssemblyRegistry.ToArray()) .AddMediatrWithBehaviors(AssemblyRegistry.ToArray()) .AddResilienceDefaultPipeline() .MapDefaultTimeZone() - .AddCors(); - -builder.Services.AddHealthChecks(); + .AddRedis(KeyPrefix.AssemblyNamePrefix) + .AddDistributedSignalR("DistributedSignalR") // or .AddSignalR() + .AddCors() + .AddHealthChecks(); var app = builder.Build(); @@ -159,11 +162,12 @@ app .UseRequestResponseLogging() .UseResponseCrafter() .UseCors() - .MapEndpoints() + .MapMinimalApis() .MapDefaultEndpoints() .EnsureHealthy() .ClearAssemblyRegistry() - .UseOpenApi(); + .UseOpenApi() + .MapControllers(); app.LogStartSuccess(); app.Run(); @@ -531,11 +535,16 @@ The default resilience pipeline includes the following policies: For mapping old style MVC controllers, use `builder.AddControllers()`. The `AddControllers()` method can also accept assembly names as parameters to scan for controllers. +The `MapControllers()` method maps the controllers to the application. Example: ```csharp +var builder = WebApplication.CreateBuilder(args); builder.AddControllers([typeof(Program).Assembly]); +var app = builder.Build(); +app.MapControllers(); +app.Run(); ``` ## OpenTelemetry @@ -598,6 +607,7 @@ This package includes various extensions and utilities to aid development: - **Pandatech.FluentMinimalApiMapper:** Simplifies mapping in minimal APIs. - **Pandatech.RegexBox:** A collection of useful regular expressions. - **Pandatech.ResponseCrafter:** A utility for crafting consistent API responses. +- **Pandatech.DistributedCache:** A distributed cache provider for Redis. ## License diff --git a/src/SharedKernel/Extensions/ConfigurationExtensions.cs b/src/SharedKernel/Extensions/ConfigurationExtensions.cs index 5163066..018734c 100644 --- a/src/SharedKernel/Extensions/ConfigurationExtensions.cs +++ b/src/SharedKernel/Extensions/ConfigurationExtensions.cs @@ -8,6 +8,7 @@ internal static class ConfigurationExtensions private const string PersistentConfigurationPath = "PersistentStorage"; private const string RepositoryNameConfigurationPath = "RepositoryName"; private const string TimeZoneConfigurationPath = "DefaultTimeZone"; + private const string RedisConfigurationPath = "Redis"; internal static string GetAllowedCorsOrigins(this IConfiguration configuration) { @@ -52,4 +53,15 @@ public static string GetDefaultTimeZone(this IConfiguration configuration) return timeZone; } + + public static string GetRedisUrl(this IConfiguration configuration) + { + var redisConnectionString = configuration.GetConnectionString(RedisConfigurationPath); + if (redisConnectionString is null) + { + throw new InvalidOperationException("Redis connection string is not configured."); + } + + return redisConnectionString; + } } \ No newline at end of file diff --git a/src/SharedKernel/Extensions/DistributedCacheExtension.cs b/src/SharedKernel/Extensions/DistributedCacheExtension.cs new file mode 100644 index 0000000..5094cba --- /dev/null +++ b/src/SharedKernel/Extensions/DistributedCacheExtension.cs @@ -0,0 +1,19 @@ +using DistributedCache.Extensions; +using DistributedCache.Options; +using Microsoft.AspNetCore.Builder; + +namespace SharedKernel.Extensions; + +public static class DistributedCacheExtension +{ + public static WebApplicationBuilder AddRedis(this WebApplicationBuilder builder, KeyPrefix keyPrefix) + { + builder.AddDistributedCache(options => + { + options.RedisConnectionString = builder.Configuration.GetRedisUrl(); + options.KeyPrefixForIsolation = keyPrefix; + }); + + return builder; + } +} \ No newline at end of file diff --git a/src/SharedKernel/Extensions/HealthCheckRunnerExtension.cs b/src/SharedKernel/Extensions/HealthCheckExtensions.cs similarity index 87% rename from src/SharedKernel/Extensions/HealthCheckRunnerExtension.cs rename to src/SharedKernel/Extensions/HealthCheckExtensions.cs index 4eeaa4b..fc66850 100644 --- a/src/SharedKernel/Extensions/HealthCheckRunnerExtension.cs +++ b/src/SharedKernel/Extensions/HealthCheckExtensions.cs @@ -5,7 +5,7 @@ namespace SharedKernel.Extensions; -public static class HealthCheckRunnerExtension +public static class HealthCheckExtensions { public static WebApplication EnsureHealthy(this WebApplication app) { @@ -42,4 +42,10 @@ public static WebApplication EnsureHealthy(this WebApplication app) var message = $"Unhealthy services detected: {string.Join(", ", unhealthyChecks)}"; throw new ServiceUnavailableException(message); } + + public static WebApplicationBuilder AddHealthChecks(this WebApplicationBuilder builder) + { + builder.Services.AddHealthChecks(); + return builder; + } } \ No newline at end of file diff --git a/src/SharedKernel/Extensions/SignalRExtensions.cs b/src/SharedKernel/Extensions/SignalRExtensions.cs new file mode 100644 index 0000000..7c856f5 --- /dev/null +++ b/src/SharedKernel/Extensions/SignalRExtensions.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using StackExchange.Redis; + +namespace SharedKernel.Extensions; + +public static class SignalRExtensions +{ + + public static WebApplicationBuilder AddSignalR(this WebApplicationBuilder builder) + { + builder + .Services + .AddSignalR() + .AddMessagePackProtocol(); + + return builder; + } + + public static WebApplicationBuilder AddDistributedSignalR(this WebApplicationBuilder builder, string redisChannelName) + { + builder + .Services + .AddSignalR() + .AddMessagePackProtocol() + .AddStackExchangeRedis(builder.Configuration.GetRedisUrl(), + options => + { + options.Configuration.ChannelPrefix = RedisChannel.Literal("FinHub:SignalR:"); + }); + + + return builder; + } +} \ No newline at end of file diff --git a/src/SharedKernel/Logging/StartupLoggerExtensions.cs b/src/SharedKernel/Logging/StartupLoggerExtensions.cs index a052ed9..a3fb2c5 100644 --- a/src/SharedKernel/Logging/StartupLoggerExtensions.cs +++ b/src/SharedKernel/Logging/StartupLoggerExtensions.cs @@ -1,6 +1,6 @@ using System.Globalization; +using System.Text.Json; using Microsoft.AspNetCore.Builder; -using Newtonsoft.Json; namespace SharedKernel.Logging; @@ -11,7 +11,7 @@ public static class StartupLoggerExtensions public static WebApplicationBuilder LogStartAttempt(this WebApplicationBuilder builder) { var now = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture); - Console.WriteLine(JsonConvert.SerializeObject(new + Console.WriteLine(JsonSerializer.Serialize(new { Timestamp = now, Event = "ApplicationStartAttempt", @@ -27,7 +27,7 @@ public static WebApplication LogStartSuccess(this WebApplication app) .TotalMilliseconds; var deltaInSeconds = Math.Round(delta / 1000, 2); var now = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture); - Console.WriteLine(JsonConvert.SerializeObject(new + Console.WriteLine(JsonSerializer.Serialize(new { Timestamp = now, Event = "ApplicationStartSuccess", @@ -40,7 +40,7 @@ public static WebApplicationBuilder LogModuleRegistrationSuccess(this WebApplica string moduleName) { var now = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture); - Console.WriteLine(JsonConvert.SerializeObject(new + Console.WriteLine(JsonSerializer.Serialize(new { Timestamp = now, Event = "ModuleRegistrationSuccess", @@ -52,7 +52,7 @@ public static WebApplicationBuilder LogModuleRegistrationSuccess(this WebApplica public static WebApplication LogModuleUseSuccess(this WebApplication app, string moduleName) { var now = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture); - Console.WriteLine(JsonConvert.SerializeObject(new + Console.WriteLine(JsonSerializer.Serialize(new { Timestamp = now, Event = "ModuleUseSuccess", @@ -60,4 +60,4 @@ public static WebApplication LogModuleUseSuccess(this WebApplication app, string })); return app; } -} \ No newline at end of file +} diff --git a/src/SharedKernel/SharedKernel.csproj b/src/SharedKernel/SharedKernel.csproj index 7909080..bc7222a 100644 --- a/src/SharedKernel/SharedKernel.csproj +++ b/src/SharedKernel/SharedKernel.csproj @@ -8,13 +8,13 @@ Readme.md Pandatech MIT - 1.0.1 + 1.0.2 Pandatech.SharedKernel Pandatech Shared Kernel Library Pandatech, shared kernel, library, OpenAPI, Swagger, utilities, scalar Pandatech.SharedKernel provides centralized configurations, utilities, and extensions for ASP.NET Core projects. For more information refere to readme.md document. https://github.com/PandaTechAM/be-lib-sharedkernel - Nuget updates + Added redis and signalR utils @@ -26,10 +26,14 @@ + + + + @@ -38,18 +42,14 @@ - + + - + - - - - - diff --git a/test/SharedKernel.Demo/Program.cs b/test/SharedKernel.Demo/Program.cs index 4ac9fb9..bf7af46 100644 --- a/test/SharedKernel.Demo/Program.cs +++ b/test/SharedKernel.Demo/Program.cs @@ -1,3 +1,5 @@ +using DistributedCache.Extensions; +using DistributedCache.Options; using FluentMinimalApiMapper; using Microsoft.AspNetCore.Mvc; using SharedKernel.Demo; @@ -21,14 +23,15 @@ .AddResponseCrafter(NamingConvention.ToSnakeCase) .AddOpenApi() .AddOpenTelemetry() - .AddEndpoints(AssemblyRegistry.ToArray()) + .AddMinimalApis(AssemblyRegistry.ToArray()) .AddControllers(AssemblyRegistry.ToArray()) .AddMediatrWithBehaviors(AssemblyRegistry.ToArray()) .AddResilienceDefaultPipeline() + .AddRedis(KeyPrefix.AssemblyNamePrefix) + .AddDistributedSignalR("DistributedSignalR") // or .AddSignalR() .MapDefaultTimeZone() - .AddCors(); - -builder.Services.AddHealthChecks(); + .AddCors() + .AddHealthChecks(); var app = builder.Build(); @@ -37,11 +40,12 @@ .UseRequestResponseLogging() .UseResponseCrafter() .UseCors() - .MapEndpoints() + .MapMinimalApis() .MapDefaultEndpoints() .EnsureHealthy() .ClearAssemblyRegistry() - .UseOpenApi(); + .UseOpenApi() + .MapControllers(); app.MapPost("/params", ([AsParameters] TestTypes testTypes) => TypedResults.Ok(testTypes)); app.MapPost("/body", ([FromBody] TestTypes testTypes) => TypedResults.Ok(testTypes)); diff --git a/test/SharedKernel.Demo/appsettings.Development.json b/test/SharedKernel.Demo/appsettings.Development.json index ca14bbd..c7efd58 100644 --- a/test/SharedKernel.Demo/appsettings.Development.json +++ b/test/SharedKernel.Demo/appsettings.Development.json @@ -12,6 +12,7 @@ "DefaultTimeZone": "Caucasus Standard Time", "RepositoryName": "be-lib-sharedkernel", "ConnectionStrings": { + "Redis": "localhost:6379", "PersistentStorage": "/persistence" } }