From cd8070e30e9b47249d61fd989ce819681c8b6e8c Mon Sep 17 00:00:00 2001 From: Peter Nylander Date: Wed, 1 Oct 2025 21:19:00 +0200 Subject: [PATCH 1/3] Update Azure AppConfig integration and package versions - Updated `Scalar.AspNetCore` to v2.8.8 and `Microsoft.Azure.AppConfiguration.AspNetCore` to v8.4.0. - Replaced `AzureCliCredential` with `ChainedTokenCredential` in `Program.cs`. - Added `app.UseAzureAppConfiguration()` middleware for better integration. - Changed `GlobalKeyFilter` in `appsettings.json` to an empty string. - Refactored `TokenCredential` initialization in `AzureAppConfigSettings`. - Enhanced `ConfigureAzureAppConfiguration` to improve endpoint resolution and key filtering logic. --- samples/AzureSample/AzureSample.csproj | 3 +- samples/AzureSample/Program.cs | 9 ++++- samples/AzureSample/appsettings.json | 2 +- .../ConfigurationBuilderExtensions.cs | 33 +++++++++++++------ .../Infinity.Toolkit.Azure.csproj | 5 ++- 5 files changed, 35 insertions(+), 17 deletions(-) diff --git a/samples/AzureSample/AzureSample.csproj b/samples/AzureSample/AzureSample.csproj index 4469668..b68a92f 100644 --- a/samples/AzureSample/AzureSample.csproj +++ b/samples/AzureSample/AzureSample.csproj @@ -10,8 +10,7 @@ - - + diff --git a/samples/AzureSample/Program.cs b/samples/AzureSample/Program.cs index bb79964..0ddd0c6 100644 --- a/samples/AzureSample/Program.cs +++ b/samples/AzureSample/Program.cs @@ -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/src/Infinity.Toolkit.Azure/Configuration/ConfigurationBuilderExtensions.cs b/src/Infinity.Toolkit.Azure/Configuration/ConfigurationBuilderExtensions.cs index f8a94ae..ab345ef 100644 --- a/src/Infinity.Toolkit.Azure/Configuration/ConfigurationBuilderExtensions.cs +++ b/src/Infinity.Toolkit.Azure/Configuration/ConfigurationBuilderExtensions.cs @@ -36,7 +36,7 @@ public class AzureAppConfigSettings public bool UseKeyVault { get; set; } = true; [JsonIgnore] - public TokenCredential? TokenCredential { get; set; } = Identity.TokenCredentialHelper.GetTokenCredential(); + public TokenCredential? TokenCredential { get; set; } internal void ParseConnectionString(string? connectionString) { @@ -97,39 +97,52 @@ public static IHostApplicationBuilder ConfigureAzureAppConfiguration(this IHostA 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 +151,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 + ":"); } diff --git a/src/Infinity.Toolkit.Azure/Infinity.Toolkit.Azure.csproj b/src/Infinity.Toolkit.Azure/Infinity.Toolkit.Azure.csproj index 377ceb0..6bbd4eb 100644 --- a/src/Infinity.Toolkit.Azure/Infinity.Toolkit.Azure.csproj +++ b/src/Infinity.Toolkit.Azure/Infinity.Toolkit.Azure.csproj @@ -5,8 +5,7 @@ enable enable Infinity.Toolkit.Azure - 0.1.3 - false + 1.0.0 @@ -16,7 +15,7 @@ - + From f305e07509976516a19373948c5cda3e5a034ef3 Mon Sep 17 00:00:00 2001 From: Peter Nylander Date: Tue, 21 Oct 2025 17:34:45 +0200 Subject: [PATCH 2/3] WIP --- .../ConfigurationBuilderExtensions.cs | 19 ++++++++++--------- .../Infinity.Toolkit.OpenApi.csproj | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/Infinity.Toolkit.Azure/Configuration/ConfigurationBuilderExtensions.cs b/src/Infinity.Toolkit.Azure/Configuration/ConfigurationBuilderExtensions.cs index ab345ef..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,7 +33,7 @@ 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; } @@ -84,15 +84,13 @@ 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() @@ -175,7 +173,10 @@ configuration section or 'AZURE_APP_CONFIG_ENDPOINT' environment variable. }); } - options.ConfigureRefresh(refreshOptions); + if (refreshOptions != null) + { + options.ConfigureRefresh(refreshOptions); + } }); return builder; diff --git a/src/Infinity.Toolkit.OpenApi/Infinity.Toolkit.OpenApi.csproj b/src/Infinity.Toolkit.OpenApi/Infinity.Toolkit.OpenApi.csproj index 290eb55..57285e2 100644 --- a/src/Infinity.Toolkit.OpenApi/Infinity.Toolkit.OpenApi.csproj +++ b/src/Infinity.Toolkit.OpenApi/Infinity.Toolkit.OpenApi.csproj @@ -14,7 +14,7 @@ - + From fb440b0570895c186bc4073bf71209caa1513429 Mon Sep 17 00:00:00 2001 From: Peter Nylander Date: Tue, 21 Oct 2025 21:21:45 +0200 Subject: [PATCH 3/3] Upgrade to .NET 10 and enhance Azure compatibility Updated .NET version to 10.0.x in build pipeline and added multi-targeting for net10.0 across projects. Enhanced EnvironmentHelper and TokenCredentialHelper for better Azure environment detection and credential management. Updated NuGet package versions and repository URLs. Refactored feature modules and improved logging. Removed legacy ServiceCollectionExtensions and cleaned up code. Upgraded test dependencies and SDK. --- .github/workflows/nuget-publish.yml | 9 +- Directory.Build.props | 6 +- samples/AzureSample/AzureSample.csproj | 4 +- samples/AzureSample/Program.cs | 2 +- .../AzureServiceBusMessagingSample.csproj | 4 +- .../FeatureModulesSample.Module1.csproj | 4 +- .../FeatureModulesSample.csproj | 6 +- samples/MediatorSample/MediatorSample.csproj | 2 +- .../MessagingSample/MessagingSample.csproj | 4 +- samples/PipelineSample/PipelineSample.csproj | 2 +- .../Infinity.Toolkit.AspNetCore.csproj | 6 +- .../EnvironmentHelper.cs | 14 +++ .../Identity/TokenCredentialHelper.cs | 83 ++++++++------ .../Infinity.Toolkit.Azure.csproj | 4 +- .../Infinity.Toolkit.EntityFramework.csproj | 4 +- .../Infinity.Toolkit.Experimental.csproj | 4 +- .../FeatureModule.cs | 6 +- .../IFeatureModule.cs | 2 +- .../Infinity.Toolkit.FeatureModules.csproj | 6 +- .../ServiceCollectionExtensions.cs | 104 ------------------ .../WebApplicationBuilderExtensions.cs | 11 +- .../Infinity.Toolkit.IntegrationTests.csproj | 12 +- .../Infinity.Toolkit.LogFormatter.csproj | 8 +- ...y.Toolkit.Messaging.AzureServiceBus.csproj | 6 +- .../Infinity.Toolkit.Messaging.csproj | 25 ++--- .../Infinity.Toolkit.OpenApi.csproj | 8 +- .../Infinity.Toolkit.TestUtils.csproj | 14 +-- src/Infinity.Toolkit/EnvironmentHelper.cs | 4 +- src/Infinity.Toolkit/Infinity.Toolkit.csproj | 6 +- .../Infinity.Toolkit.Tests.csproj | 8 +- 30 files changed, 155 insertions(+), 223 deletions(-) create mode 100644 src/Infinity.Toolkit.Azure/EnvironmentHelper.cs delete mode 100644 src/Infinity.Toolkit.FeatureModules/ServiceCollectionExtensions.cs 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 b68a92f..3abbf72 100644 --- a/samples/AzureSample/AzureSample.csproj +++ b/samples/AzureSample/AzureSample.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/samples/AzureSample/Program.cs b/samples/AzureSample/Program.cs index 0ddd0c6..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; 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/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 6bbd4eb..3d5245b 100644 --- a/src/Infinity.Toolkit.Azure/Infinity.Toolkit.Azure.csproj +++ b/src/Infinity.Toolkit.Azure/Infinity.Toolkit.Azure.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0;net9.0 enable enable Infinity.Toolkit.Azure @@ -14,7 +14,7 @@ - + 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 57285e2..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 @@ - - - -