diff --git a/.github/workflows/nuget-publish.yml b/.github/workflows/nuget-publish.yml index 2b4a141..b36a76d 100644 --- a/.github/workflows/nuget-publish.yml +++ b/.github/workflows/nuget-publish.yml @@ -31,6 +31,11 @@ jobs: with: dotnet-version: "9.0.x" + - name: Setup .NET 10 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: "10.0.x" + - name: Restore dependencies run: dotnet restore @@ -41,7 +46,7 @@ jobs: run: dotnet test --configuration Release --no-build --verbosity normal - name: Pack - run: dotnet pack --configuration Release --no-build --output ./nupkgs -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg + run: dotnet pack --configuration Release --output ./nupkgs -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg - name: Publish uses: actions/upload-artifact@v4 @@ -67,7 +72,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: "9.0.x" + dotnet-version: "10.0.x" - name: Publish to NuGet run: | diff --git a/Directory.Build.props b/Directory.Build.props index 28b43e0..b1b6c7e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -12,7 +12,7 @@ Peter Nylander LICENSE README.md - Copyright © 2025 Peter Nylander + Copyright © $([System.DateTime]::Now.Year) Peter Nylander @@ -49,8 +49,8 @@ git true - https://github.com/penyland/infinity-tomahawk - https://github.com/penyland/infinity-tomahawk + https://github.com/penyland/infinity-toolkit + https://github.com/penyland/infinity-toolkit diff --git a/samples/AzureSample/AzureSample.csproj b/samples/AzureSample/AzureSample.csproj index 4469668..3abbf72 100644 --- a/samples/AzureSample/AzureSample.csproj +++ b/samples/AzureSample/AzureSample.csproj @@ -9,9 +9,8 @@ - - - + + diff --git a/samples/AzureSample/Program.cs b/samples/AzureSample/Program.cs index bb79964..10f8aa1 100644 --- a/samples/AzureSample/Program.cs +++ b/samples/AzureSample/Program.cs @@ -1,5 +1,5 @@ using Azure.Identity; -using Infinity.Toolkit.Azure.Configuration; +using Infinity.Toolkit.Azure; using Infinity.Toolkit.OpenApi; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; @@ -9,7 +9,13 @@ builder.ConfigureAzureAppConfiguration(configureSettings: options => { - options.TokenCredential = new AzureCliCredential(); + //options.TokenCredential = new VisualStudioCredential(new() { TenantId = "cf4e6228-1dc7-45f6-aa1c-71e47553e4ac" }); + //options.TokenCredential = new AzureCliCredential(new() { TenantId = "cf4e6228-1dc7-45f6-aa1c-71e47553e4ac" }); + //options.TokenCredential = new DefaultAzureCredential(); + options.TokenCredential = new ChainedTokenCredential( + new AzureCliCredential(), + new VisualStudioCredential() + ); }, refreshOptions: refreshOptions => { refreshOptions.SetRefreshInterval(TimeSpan.FromSeconds(15)); @@ -43,6 +49,7 @@ } app.UseHttpsRedirection(); +app.UseAzureAppConfiguration(); app.MapGet("/config", ([FromServices] IConfiguration configuration) => { diff --git a/samples/AzureSample/appsettings.json b/samples/AzureSample/appsettings.json index 2a3eb48..49d8b97 100644 --- a/samples/AzureSample/appsettings.json +++ b/samples/AzureSample/appsettings.json @@ -22,7 +22,7 @@ "AppConfig": { "ApplicationName": "", "Endpoint": "", - "GlobalKeyFilter": "Global", + "GlobalKeyFilter": "", "UseFeatureFlags": true } } diff --git a/samples/AzureServiceBusMessagingSample/AzureServiceBusMessagingSample.csproj b/samples/AzureServiceBusMessagingSample/AzureServiceBusMessagingSample.csproj index 9b9856c..30b168d 100644 --- a/samples/AzureServiceBusMessagingSample/AzureServiceBusMessagingSample.csproj +++ b/samples/AzureServiceBusMessagingSample/AzureServiceBusMessagingSample.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/samples/FeatureModulesSample.Module1/FeatureModulesSample.Module1.csproj b/samples/FeatureModulesSample.Module1/FeatureModulesSample.Module1.csproj index 8d39c89..dbb5777 100644 --- a/samples/FeatureModulesSample.Module1/FeatureModulesSample.Module1.csproj +++ b/samples/FeatureModulesSample.Module1/FeatureModulesSample.Module1.csproj @@ -13,8 +13,8 @@ - - + + diff --git a/samples/FeatureModulesSample/FeatureModulesSample.csproj b/samples/FeatureModulesSample/FeatureModulesSample.csproj index 5186432..3625f5e 100644 --- a/samples/FeatureModulesSample/FeatureModulesSample.csproj +++ b/samples/FeatureModulesSample/FeatureModulesSample.csproj @@ -9,9 +9,9 @@ - - - + + + diff --git a/samples/MediatorSample/MediatorSample.csproj b/samples/MediatorSample/MediatorSample.csproj index b13950d..cc5f3d8 100644 --- a/samples/MediatorSample/MediatorSample.csproj +++ b/samples/MediatorSample/MediatorSample.csproj @@ -9,7 +9,7 @@ - + diff --git a/samples/MessagingSample/MessagingSample.csproj b/samples/MessagingSample/MessagingSample.csproj index 2c52571..446bec9 100644 --- a/samples/MessagingSample/MessagingSample.csproj +++ b/samples/MessagingSample/MessagingSample.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/samples/PipelineSample/PipelineSample.csproj b/samples/PipelineSample/PipelineSample.csproj index 40a0c07..f21be1d 100644 --- a/samples/PipelineSample/PipelineSample.csproj +++ b/samples/PipelineSample/PipelineSample.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Infinity.Toolkit.AspNetCore/Infinity.Toolkit.AspNetCore.csproj b/src/Infinity.Toolkit.AspNetCore/Infinity.Toolkit.AspNetCore.csproj index 02e7886..17ace32 100644 --- a/src/Infinity.Toolkit.AspNetCore/Infinity.Toolkit.AspNetCore.csproj +++ b/src/Infinity.Toolkit.AspNetCore/Infinity.Toolkit.AspNetCore.csproj @@ -1,11 +1,11 @@  - net9.0 + net10.0;net9.0 enable enable Infinity.Toolkit.AspNetCore - 1.0.0 + 1.1.0 @@ -17,7 +17,7 @@ - + diff --git a/src/Infinity.Toolkit.Azure/Configuration/ConfigurationBuilderExtensions.cs b/src/Infinity.Toolkit.Azure/Configuration/ConfigurationBuilderExtensions.cs index f8a94ae..866fc53 100644 --- a/src/Infinity.Toolkit.Azure/Configuration/ConfigurationBuilderExtensions.cs +++ b/src/Infinity.Toolkit.Azure/Configuration/ConfigurationBuilderExtensions.cs @@ -6,7 +6,7 @@ using System.Data.Common; using System.Text.Json.Serialization; -namespace Infinity.Toolkit.Azure.Configuration; +namespace Infinity.Toolkit.Azure; // Config section specifications //"Infinity": { @@ -23,7 +23,7 @@ namespace Infinity.Toolkit.Azure.Configuration; public class AzureAppConfigSettings { - internal const string DefaultConfigSectionName = "Infinity:Azure:AppConfig"; + internal const string DefaultConfigSectionName = "AzureAppConfiguration"; public string ApplicationName { get; set; } @@ -33,10 +33,10 @@ public class AzureAppConfigSettings public bool UseFeatureFlags { get; set; } = true; - public bool UseKeyVault { get; set; } = true; + public bool UseKeyVault { get; set; } = false; [JsonIgnore] - public TokenCredential? TokenCredential { get; set; } = Identity.TokenCredentialHelper.GetTokenCredential(); + public TokenCredential? TokenCredential { get; set; } internal void ParseConnectionString(string? connectionString) { @@ -84,52 +84,63 @@ configuration section. public static class ConfigurationBuilderExtensions { - private const string DefaultConfigSectionName = "Infinity:Azure:AppConfig"; - public static IHostApplicationBuilder ConfigureAzureAppConfiguration(this IHostApplicationBuilder builder) - => builder.ConfigureAzureAppConfiguration(DefaultConfigSectionName, null, null); + => builder.ConfigureAzureAppConfiguration(AzureAppConfigSettings.DefaultConfigSectionName, null, null); public static IHostApplicationBuilder ConfigureAzureAppConfiguration(this IHostApplicationBuilder app, Action? configure = null, Action? refreshOptions = null) - => app.ConfigureAzureAppConfiguration(DefaultConfigSectionName, configure, refreshOptions); + => app.ConfigureAzureAppConfiguration(AzureAppConfigSettings.DefaultConfigSectionName, configure, refreshOptions); - public static IHostApplicationBuilder ConfigureAzureAppConfiguration(this IHostApplicationBuilder builder, string configSectionName = DefaultConfigSectionName, Action? configureSettings = null, Action? refreshOptions = null) + public static IHostApplicationBuilder ConfigureAzureAppConfiguration(this IHostApplicationBuilder builder, string configSectionName = AzureAppConfigSettings.DefaultConfigSectionName, Action? configureSettings = null, Action? refreshOptions = null) { ArgumentNullException.ThrowIfNull(builder, nameof(builder)); var settings = new AzureAppConfigSettings() { - ApplicationName = builder.Environment.ApplicationName + ApplicationName = builder.Environment.ApplicationName, + TokenCredential = Identity.TokenCredentialHelper.GetTokenCredential() }; builder.Configuration.GetSection(configSectionName).Bind(settings); configureSettings?.Invoke(settings); + builder.Services.AddAzureAppConfiguration(); + builder.Configuration.AddAzureAppConfiguration(options => { if (builder.Configuration.GetConnectionString("AzureAppConfig") is string connectionString) { settings.ParseConnectionString(connectionString); - options.Connect(builder.Configuration.GetConnectionString("AzureAppConfig")); + options.Connect(connectionString); } else { - if (Uri.TryCreate(settings.Endpoint.ToString(), UriKind.Absolute, out var endpointUri)) + // Try to use configured endpoint + if (settings.Endpoint != null && Uri.TryCreate(settings.Endpoint.ToString(), UriKind.Absolute, out var endpointUri)) { - options.Connect(endpointUri, settings.TokenCredential); + // Endpoint from settings is valid } else { - throw new InvalidOperationException($""" - The 'Endpoint' key in - '{configSectionName}') isn't a valid URI. + // Try to get endpoint from environment variable + var envEndpoint = Environment.GetEnvironmentVariable("AZURE_APP_CONFIG_ENDPOINT"); + if (!Uri.TryCreate(envEndpoint, UriKind.Absolute, out endpointUri)) + { + throw new InvalidOperationException($""" + ConnectionString is missing. + It should be provided in 'ConnectionStrings:AzureAppConfig' + or '{configSectionName}:Endpoint' key + configuration section or 'AZURE_APP_CONFIG_ENDPOINT' environment variable. """); + } } + + options.Connect(endpointUri, settings.TokenCredential); } if (!string.IsNullOrEmpty(settings.GlobalKeyFilter)) { // Filter by global key filter options - .Select($"{settings.GlobalKeyFilter}*") + .Select($"{settings.GlobalKeyFilter}*", LabelFilter.Null) .Select($"{settings.GlobalKeyFilter}*", builder.Environment.EnvironmentName) .TrimKeyPrefix($"{settings.GlobalKeyFilter}:"); } @@ -138,7 +149,7 @@ public static IHostApplicationBuilder ConfigureAzureAppConfiguration(this IHostA { // Filter by application name options - .Select($"{settings.ApplicationName}*") + .Select($"{settings.ApplicationName}*", LabelFilter.Null) .Select($"{settings.ApplicationName}*", builder.Environment.EnvironmentName) .TrimKeyPrefix(settings.ApplicationName + ":"); } @@ -162,7 +173,10 @@ public static IHostApplicationBuilder ConfigureAzureAppConfiguration(this IHostA }); } - options.ConfigureRefresh(refreshOptions); + if (refreshOptions != null) + { + options.ConfigureRefresh(refreshOptions); + } }); return builder; diff --git a/src/Infinity.Toolkit.Azure/EnvironmentHelper.cs b/src/Infinity.Toolkit.Azure/EnvironmentHelper.cs new file mode 100644 index 0000000..c45f849 --- /dev/null +++ b/src/Infinity.Toolkit.Azure/EnvironmentHelper.cs @@ -0,0 +1,14 @@ +namespace Infinity.Toolkit.Azure; + +internal static class EnvironmentHelper +{ + public static bool IsRunningInContainer => Equals(Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER"), "true"); + + public static bool IsRunningInAzureAppService => Equals(Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME"), "true"); + + public static bool IsRunningInAzureContainerApps => IsRunningInContainer && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("CONTAINER_APP_NAME")); + + public static bool IsRunningInAzureFunctions => !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("FUNCTIONS_WORKER_RUNTIME")); + + public static bool IsRunningInAzure => IsRunningInAzureAppService || IsRunningInAzureContainerApps || IsRunningInAzureFunctions; +} diff --git a/src/Infinity.Toolkit.Azure/Identity/TokenCredentialHelper.cs b/src/Infinity.Toolkit.Azure/Identity/TokenCredentialHelper.cs index c46c97b..7666c2f 100644 --- a/src/Infinity.Toolkit.Azure/Identity/TokenCredentialHelper.cs +++ b/src/Infinity.Toolkit.Azure/Identity/TokenCredentialHelper.cs @@ -7,11 +7,13 @@ public static class TokenCredentialHelper { /// /// Helps creating a ChainedTokenCredential used to authenticate with Azure services. - /// By default, the following credential types are included: + /// By default if the application is running in Azure (App Service, Functions, Container Apps), the following credential types are included: /// EnvironmentCredential /// ManagedIdentityCredential /// - /// To include additional credential types, set the corresponding environment variable to "true". + /// If the application is not running in Azure, only the EnvironmentCredential is included by default. + /// + /// To include additional credential types, set the value of the corresponding environment variable to "true". /// INCLUDE_VISUAL_STUDIO_CREDENTIAL /// INCLUDE_VISUAL_STUDIO_CODE_CREDENTIAL /// INCLUDE_INTERACTIVE_BROWSER_CREDENTIAL @@ -19,52 +21,65 @@ public static class TokenCredentialHelper /// INCLUDE_AZURE_POWER_SHELL_CREDENTIAL /// INCLUDE_AZURE_CLI_CREDENTIAL /// INCLUDE_WORKLOAD_IDENTITY_CREDENTIAL - /// /// - /// A ChainedTokenCredential with the specified credential types. - public static TokenCredential GetTokenCredential() + /// The client ID of the user-assigned managed identity. If null, the AZURE_CLIENT_ID environment variable will be used. + /// A ChainedTokenCredential with the specified credential types or DefaultAzureCredentials if no credentials are specified. + public static TokenCredential GetTokenCredential(string? clientId = null) { TokenCredential[] tokenCredentials =[]; - if (Equals(Environment.GetEnvironmentVariable("INCLUDE_AZURE_CLI_CREDENTIAL"), "true")) + // First check if the app is running in Azure + if (EnvironmentHelper.IsRunningInAzure) { - tokenCredentials = [.. tokenCredentials, new AzureCliCredential()]; - } + tokenCredentials = [ + new EnvironmentCredential(), + new ManagedIdentityCredential(ManagedIdentityId.FromUserAssignedClientId(clientId ?? Environment.GetEnvironmentVariable("AZURE_CLIENT_ID") ?? string.Empty))]; - if (Equals(Environment.GetEnvironmentVariable("INCLUDE_WORKLOAD_IDENTITY_CREDENTIAL"), "true")) - { - tokenCredentials = [.. tokenCredentials, new WorkloadIdentityCredential()]; + var tokenCredential = new ChainedTokenCredential(tokenCredentials); + return tokenCredential; } - - if (Equals(Environment.GetEnvironmentVariable("INCLUDE_AZURE_DEVELOPER_CLI_CREDENTIAL"), "true")) + else { - tokenCredentials = [.. tokenCredentials, new AzureDeveloperCliCredential()]; - } + if (Equals(Environment.GetEnvironmentVariable("INCLUDE_AZURE_CLI_CREDENTIAL"), "true")) + { + tokenCredentials = [.. tokenCredentials, new AzureCliCredential()]; + } - if (Equals(Environment.GetEnvironmentVariable("INCLUDE_VISUAL_STUDIO_CREDENTIAL"), "true")) - { - tokenCredentials = [.. tokenCredentials, new VisualStudioCredential()]; - } + if (Equals(Environment.GetEnvironmentVariable("INCLUDE_WORKLOAD_IDENTITY_CREDENTIAL"), "true")) + { + tokenCredentials = [.. tokenCredentials, new WorkloadIdentityCredential()]; + } - if (Equals(Environment.GetEnvironmentVariable("INCLUDE_VISUAL_STUDIO_CODE_CREDENTIAL"), "true")) - { - tokenCredentials = [.. tokenCredentials, new VisualStudioCodeCredential()]; - } + if (Equals(Environment.GetEnvironmentVariable("INCLUDE_AZURE_DEVELOPER_CLI_CREDENTIAL"), "true")) + { + tokenCredentials = [.. tokenCredentials, new AzureDeveloperCliCredential()]; + } - if (Equals(Environment.GetEnvironmentVariable("INCLUDE_AZURE_POWER_SHELL_CREDENTIAL"), "true")) - { - tokenCredentials = [.. tokenCredentials, new AzurePowerShellCredential()]; - } + if (Equals(Environment.GetEnvironmentVariable("INCLUDE_VISUAL_STUDIO_CREDENTIAL"), "true")) + { + tokenCredentials = [.. tokenCredentials, new VisualStudioCredential()]; + } - if (Equals(Environment.GetEnvironmentVariable("INCLUDE_INTERACTIVE_BROWSER_CREDENTIAL"), "true")) - { - tokenCredentials = [.. tokenCredentials, new InteractiveBrowserCredential()]; - } + if (Equals(Environment.GetEnvironmentVariable("INCLUDE_VISUAL_STUDIO_CODE_CREDENTIAL"), "true")) + { + tokenCredentials = [.. tokenCredentials, new VisualStudioCodeCredential()]; + } - tokenCredentials = [.. tokenCredentials, new EnvironmentCredential(), new ManagedIdentityCredential()]; + if (Equals(Environment.GetEnvironmentVariable("INCLUDE_AZURE_POWER_SHELL_CREDENTIAL"), "true")) + { + tokenCredentials = [.. tokenCredentials, new AzurePowerShellCredential()]; + } - var tokenCredential = new ChainedTokenCredential(tokenCredentials); + if (Equals(Environment.GetEnvironmentVariable("INCLUDE_INTERACTIVE_BROWSER_CREDENTIAL"), "true")) + { + tokenCredentials = [.. tokenCredentials, new InteractiveBrowserCredential()]; + } - return tokenCredential; + return tokenCredentials.Length switch + { + 0 => new DefaultAzureCredential(), + _ => new ChainedTokenCredential(tokenCredentials) + }; + } } } diff --git a/src/Infinity.Toolkit.Azure/Infinity.Toolkit.Azure.csproj b/src/Infinity.Toolkit.Azure/Infinity.Toolkit.Azure.csproj index 377ceb0..3d5245b 100644 --- a/src/Infinity.Toolkit.Azure/Infinity.Toolkit.Azure.csproj +++ b/src/Infinity.Toolkit.Azure/Infinity.Toolkit.Azure.csproj @@ -1,12 +1,11 @@  - net9.0 + net10.0;net9.0 enable enable Infinity.Toolkit.Azure - 0.1.3 - false + 1.0.0 @@ -15,8 +14,8 @@ - - + + diff --git a/src/Infinity.Toolkit.EntityFramework/Infinity.Toolkit.EntityFramework.csproj b/src/Infinity.Toolkit.EntityFramework/Infinity.Toolkit.EntityFramework.csproj index 25ead20..3a704a6 100644 --- a/src/Infinity.Toolkit.EntityFramework/Infinity.Toolkit.EntityFramework.csproj +++ b/src/Infinity.Toolkit.EntityFramework/Infinity.Toolkit.EntityFramework.csproj @@ -5,7 +5,7 @@ enable enable Infinity.Toolkit.EntityFramework - 1.0.0 + 1.0.1 false @@ -14,7 +14,7 @@ - + diff --git a/src/Infinity.Toolkit.Experimental/Infinity.Toolkit.Experimental.csproj b/src/Infinity.Toolkit.Experimental/Infinity.Toolkit.Experimental.csproj index 2b9be59..a5322ce 100644 --- a/src/Infinity.Toolkit.Experimental/Infinity.Toolkit.Experimental.csproj +++ b/src/Infinity.Toolkit.Experimental/Infinity.Toolkit.Experimental.csproj @@ -18,9 +18,9 @@ - + - + diff --git a/src/Infinity.Toolkit.FeatureModules/FeatureModule.cs b/src/Infinity.Toolkit.FeatureModules/FeatureModule.cs index 05e1fbe..251a724 100644 --- a/src/Infinity.Toolkit.FeatureModules/FeatureModule.cs +++ b/src/Infinity.Toolkit.FeatureModules/FeatureModule.cs @@ -5,7 +5,7 @@ /// public abstract class FeatureModule : IFeatureModule { - public virtual IModuleInfo? ModuleInfo => new FeatureModuleInfo(nameof(FeatureModule), Assembly.GetExecutingAssembly()?.GetName()?.Version?.ToString() ?? "1.0.0"); + public abstract IModuleInfo? ModuleInfo { get; } public virtual ModuleContext RegisterModule(ModuleContext moduleContext) => moduleContext; } @@ -16,9 +16,9 @@ public abstract class FeatureModule : IFeatureModule /// public abstract class WebFeatureModule : IWebFeatureModule { - public virtual IModuleInfo? ModuleInfo => new FeatureModuleInfo(nameof(WebFeatureModule), Assembly.GetExecutingAssembly()?.GetName()?.Version?.ToString() ?? "1.0.0"); + public abstract IModuleInfo? ModuleInfo { get; } - public virtual void RegisterModule(WebApplicationBuilder builder) { } + public virtual void RegisterModule(IHostApplicationBuilder builder) { } public virtual void MapEndpoints(WebApplication app) { } } diff --git a/src/Infinity.Toolkit.FeatureModules/IFeatureModule.cs b/src/Infinity.Toolkit.FeatureModules/IFeatureModule.cs index ce36b73..b97420f 100644 --- a/src/Infinity.Toolkit.FeatureModules/IFeatureModule.cs +++ b/src/Infinity.Toolkit.FeatureModules/IFeatureModule.cs @@ -43,5 +43,5 @@ public interface IWebFeatureModule : IFeatureModuleBase /// /// Register all dependencies needed by a web module in the DI-container. /// - void RegisterModule(WebApplicationBuilder builder); + void RegisterModule(IHostApplicationBuilder builder); } diff --git a/src/Infinity.Toolkit.FeatureModules/Infinity.Toolkit.FeatureModules.csproj b/src/Infinity.Toolkit.FeatureModules/Infinity.Toolkit.FeatureModules.csproj index 25ad52d..12ca6b8 100644 --- a/src/Infinity.Toolkit.FeatureModules/Infinity.Toolkit.FeatureModules.csproj +++ b/src/Infinity.Toolkit.FeatureModules/Infinity.Toolkit.FeatureModules.csproj @@ -1,10 +1,10 @@  - net9.0;net8.0 + net10.0;net9.0;net8.0 enable enable - 1.2.1 + 1.3.0 Infinity.Toolkit.FeatureModules Infinity Toolkit Feature Modules let's you automatically register dependencies and endpoints in modules which simplifies development when you are working with vertical feature slices. FeatureModules;VerticalSliceArchitecture;ModularMonoliths;MinimalApis @@ -19,7 +19,7 @@ - + diff --git a/src/Infinity.Toolkit.FeatureModules/ServiceCollectionExtensions.cs b/src/Infinity.Toolkit.FeatureModules/ServiceCollectionExtensions.cs deleted file mode 100644 index 47e7836..0000000 --- a/src/Infinity.Toolkit.FeatureModules/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,104 +0,0 @@ -using Infinity.Toolkit.LogFormatter; -using Microsoft.Extensions.DependencyInjection.Extensions; - -namespace Infinity.Toolkit.FeatureModules; - -public static class ServiceCollectionExtensions -{ - /// - /// Add all feature modules that are found in all assemblies. - /// - public static IServiceCollection AddFeatureModules(this IServiceCollection services, HostBuilderContext hostBuilderContext, ILoggerFactory? loggerFactory) - { - return services.AddFeatureModules(hostBuilderContext, options => { }, loggerFactory); - } - - /// - /// Add all feature modules that are found in all assemblies. - /// - public static IServiceCollection AddFeatureModules(this IServiceCollection services, HostBuilderContext hostBuilderContext, Action configure, ILoggerFactory? loggerFactory) - { - var options = new FeatureModuleOptions(); - services.TryAddSingleton(options); - configure(options); - - return services.RegisterFeatureModules(hostBuilderContext.Configuration, hostBuilderContext.HostingEnvironment, options, loggerFactory); - } - - internal static IServiceCollection RegisterFeatureModules(this IServiceCollection services, IConfiguration configuration, IHostEnvironment hostEnvironment, FeatureModuleOptions options, ILoggerFactory? loggerFactory) - { - loggerFactory ??= LoggerFactory.Create(builder => - { - builder - .AddConfiguration(configuration.GetSection("Logging")) -#if DEBUG - .AddDebug() -#endif - .AddConsole(options => options.FormatterName = "CodeThemeConsoleFormatter").AddConsoleFormatter(); - }); - var logger = loggerFactory.CreateLogger("Infinity.Toolkit.FeatureModules"); - - try - { - logger?.LogDebug(new EventId(1000, "Scanning"), "Scanning assemblies for feature modules..."); - - var discoveredModules = ModuleUtilities.DiscoverModules(options, logger); - RegisterModules(discoveredModules, services, configuration, hostEnvironment, logger); - - logger?.LogDebug(new EventId(1003, "ScanningComplete"), "Registering feature modules completed."); - - return services; - } - catch (Exception ex) - { - logger.LogError(new EventId(5000, "ScanningFailed"), "Failed to register feature modules. {ex}", ex.Message); - return services; - } - } - - /// - /// Register all classes implementing IFeatureModule while scanning the project to IServiceCollection. - /// - /// List of found feature modules. - /// The . - /// The . - /// The . - /// The . - /// Thrown if no modules are found while scanning. - private static void RegisterModules(IEnumerable discoveredModules, IServiceCollection services, IConfiguration configuration, IHostEnvironment hostEnvironment, ILogger? logger) - { - ArgumentNullException.ThrowIfNull(discoveredModules, nameof(discoveredModules)); - ArgumentNullException.ThrowIfNull(services, nameof(services)); - - Dictionary registeredFeatureModules = []; - - var serviceDescriptors = discoveredModules - .Select(type => ServiceDescriptor.Transient(typeof(IFeatureModuleBase), type)); - services.TryAddEnumerable(serviceDescriptors); - - var modules = discoveredModules - .Select(Activator.CreateInstance) - .Cast(); - - foreach (var module in modules) - { - logger?.LogDebug(new EventId(1002, "RegisteringModules"), "Registering feature module: {module} - v{version}", module.GetType().FullName, module.ModuleInfo?.Version); - registeredFeatureModules.Add(module.GetType(), module); - - if (module is IFeatureModule featureModule) - { - featureModule.RegisterModule(new() - { - Configuration = configuration, - Environment = hostEnvironment, - Services = services - }); - } - } - - services.Configure(options => - { - options.AdditionalAssemblies.AddRange([.. registeredFeatureModules.Keys.Select(x => x.Assembly)]); - }); - } -} diff --git a/src/Infinity.Toolkit.FeatureModules/WebApplicationBuilderExtensions.cs b/src/Infinity.Toolkit.FeatureModules/WebApplicationBuilderExtensions.cs index 5903088..b078c22 100644 --- a/src/Infinity.Toolkit.FeatureModules/WebApplicationBuilderExtensions.cs +++ b/src/Infinity.Toolkit.FeatureModules/WebApplicationBuilderExtensions.cs @@ -80,7 +80,7 @@ internal static WebApplicationBuilder RegisterFeatureModules(this WebApplication /// The . /// The . /// Thrown if no modules are found while scanning. - private static void RegisterModules(IEnumerable discoveredModules, WebApplicationBuilder builder, ILogger? logger) + private static void RegisterModules(IEnumerable discoveredModules, IHostApplicationBuilder builder, ILogger? logger) { ArgumentNullException.ThrowIfNull(discoveredModules, nameof(discoveredModules)); ArgumentNullException.ThrowIfNull(builder, nameof(builder)); @@ -97,15 +97,22 @@ private static void RegisterModules(IEnumerable discoveredModules, Web foreach (var module in modules) { - logger?.LogDebug(new EventId(1002, "RegisteringModules"), "Registering feature module: {module} - Version: {version}", module.GetType().FullName, module.ModuleInfo?.Version); + if (registeredFeatureModules.ContainsKey(module.GetType())) + { + logger?.LogWarning(new EventId(1001, "RegisteringModules"), "Module {module} is already registered. Skipping duplicate registration.", module.GetType().FullName); + continue; + } + registeredFeatureModules.Add(module.GetType(), module); if (module is IWebFeatureModule webModule) { + logger?.LogInformation(new EventId(1002, "RegisteringModules"), "Registering web feature module: {module}", module.GetType().FullName); webModule.RegisterModule(builder); } else if (module is IFeatureModule featureModule) { + logger?.LogInformation(new EventId(1002, "RegisteringModules"), "Registering feature module: {module}", module.GetType().FullName); featureModule?.RegisterModule(new() { Configuration = builder.Configuration, diff --git a/src/Infinity.Toolkit.IntegrationTests/Infinity.Toolkit.IntegrationTests.csproj b/src/Infinity.Toolkit.IntegrationTests/Infinity.Toolkit.IntegrationTests.csproj index 99b32b8..e8641d5 100644 --- a/src/Infinity.Toolkit.IntegrationTests/Infinity.Toolkit.IntegrationTests.csproj +++ b/src/Infinity.Toolkit.IntegrationTests/Infinity.Toolkit.IntegrationTests.csproj @@ -1,19 +1,19 @@  - net9.0 + net10.0;net9.0 enable enable Infinity.Toolkit.IntegrationTests - 1.0.0 + 1.1.0 false - - - - + + + + diff --git a/src/Infinity.Toolkit.LogFormatter/Infinity.Toolkit.LogFormatter.csproj b/src/Infinity.Toolkit.LogFormatter/Infinity.Toolkit.LogFormatter.csproj index f1adbbe..8ec6412 100644 --- a/src/Infinity.Toolkit.LogFormatter/Infinity.Toolkit.LogFormatter.csproj +++ b/src/Infinity.Toolkit.LogFormatter/Infinity.Toolkit.LogFormatter.csproj @@ -1,10 +1,10 @@  - net9.0;net8.0 + net10.0;net9.0;net8.0 enable enable - 1.0.7 + 1.1.0 Infinity.Toolkit.LogFormatter A logging formatter that formats log messages with a Visual Studio Code inspired theme and Serilog like formatting. Logging formatter @@ -15,8 +15,8 @@ - - + + diff --git a/src/Infinity.Toolkit.Messaging.AzureServiceBus/Infinity.Toolkit.Messaging.AzureServiceBus.csproj b/src/Infinity.Toolkit.Messaging.AzureServiceBus/Infinity.Toolkit.Messaging.AzureServiceBus.csproj index 8704c24..2c36ee3 100644 --- a/src/Infinity.Toolkit.Messaging.AzureServiceBus/Infinity.Toolkit.Messaging.AzureServiceBus.csproj +++ b/src/Infinity.Toolkit.Messaging.AzureServiceBus/Infinity.Toolkit.Messaging.AzureServiceBus.csproj @@ -1,10 +1,10 @@  - net9.0 + net10.0;net9.0 enable enable - 1.0.4 + 1.1.0 Azure Service Bus Integration for Infinity.Toolkit.Messaging. Infinity.Toolkit.Messaging.AzureServiceBus @@ -18,7 +18,7 @@ - + diff --git a/src/Infinity.Toolkit.Messaging/Infinity.Toolkit.Messaging.csproj b/src/Infinity.Toolkit.Messaging/Infinity.Toolkit.Messaging.csproj index e12021f..8301f35 100644 --- a/src/Infinity.Toolkit.Messaging/Infinity.Toolkit.Messaging.csproj +++ b/src/Infinity.Toolkit.Messaging/Infinity.Toolkit.Messaging.csproj @@ -1,9 +1,9 @@  - net9.0 + net10.0;net9.0 Infinity.Toolkit.Messaging - 1.0.5 + 1.1.0 @@ -16,18 +16,17 @@ - - - - - - - - - + + + + + + + + + - - + diff --git a/src/Infinity.Toolkit.OpenApi/Infinity.Toolkit.OpenApi.csproj b/src/Infinity.Toolkit.OpenApi/Infinity.Toolkit.OpenApi.csproj index 290eb55..889fe2f 100644 --- a/src/Infinity.Toolkit.OpenApi/Infinity.Toolkit.OpenApi.csproj +++ b/src/Infinity.Toolkit.OpenApi/Infinity.Toolkit.OpenApi.csproj @@ -5,7 +5,7 @@ enable enable Infinity.Toolkit.OpenApi - 0.1.3 + 0.1.4 false @@ -14,9 +14,9 @@ - - - + + + diff --git a/src/Infinity.Toolkit.TestUtils/Infinity.Toolkit.TestUtils.csproj b/src/Infinity.Toolkit.TestUtils/Infinity.Toolkit.TestUtils.csproj index c46ff90..920ef10 100644 --- a/src/Infinity.Toolkit.TestUtils/Infinity.Toolkit.TestUtils.csproj +++ b/src/Infinity.Toolkit.TestUtils/Infinity.Toolkit.TestUtils.csproj @@ -14,13 +14,13 @@ - - - - - - - + + + + + + + diff --git a/src/Infinity.Toolkit/EnvironmentHelper.cs b/src/Infinity.Toolkit/EnvironmentHelper.cs index 6a2e10d..a1ca90a 100644 --- a/src/Infinity.Toolkit/EnvironmentHelper.cs +++ b/src/Infinity.Toolkit/EnvironmentHelper.cs @@ -4,7 +4,7 @@ public static partial class EnvironmentHelper { public static bool IsRunningInContainer => Equals(Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER"), "true"); - public static bool IsRunningInAzureAppService => Equals(Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME"), "true"); + public static bool IsRunningInKubernetes => !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("KUBERNETES_SERVICE_HOST")); - public static bool IsRunningInAzureContainerApps => IsRunningInContainer && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("CONTAINER_APP_NAME")); + public static bool IsRunningInDockerDesktop => IsRunningInContainer && Equals(Environment.GetEnvironmentVariable("DOCKER_DESKTOP_ENVIRONMENT"), "true"); } diff --git a/src/Infinity.Toolkit/Infinity.Toolkit.csproj b/src/Infinity.Toolkit/Infinity.Toolkit.csproj index 9748f39..1ea7f69 100644 --- a/src/Infinity.Toolkit/Infinity.Toolkit.csproj +++ b/src/Infinity.Toolkit/Infinity.Toolkit.csproj @@ -5,7 +5,7 @@ enable enable Infinity.Toolkit - 1.1.0 + 1.2.0 @@ -13,10 +13,10 @@ - + - + diff --git a/tests/Infinity.Toolkit.Tests/Infinity.Toolkit.Tests.csproj b/tests/Infinity.Toolkit.Tests/Infinity.Toolkit.Tests.csproj index adbcfbe..e55709d 100644 --- a/tests/Infinity.Toolkit.Tests/Infinity.Toolkit.Tests.csproj +++ b/tests/Infinity.Toolkit.Tests/Infinity.Toolkit.Tests.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 enable enable false @@ -11,7 +11,7 @@ - + @@ -32,8 +32,4 @@ - - - -