From 63674cd3a116d1c24c4027b43d7c8cf361580192 Mon Sep 17 00:00:00 2001 From: Chase Miller Date: Mon, 14 Apr 2025 09:05:53 -0400 Subject: [PATCH 1/2] Expose [Try]AddAWSCredentialsFactory methods --- .../AWSOptions.cs | 2 +- .../ClientFactory.cs | 60 ++++--------- .../ServiceCollectionExtensions.cs | 89 ++++++++++++++++--- 3 files changed, 97 insertions(+), 54 deletions(-) diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptions.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptions.cs index 3b20c866457d..dbf4d2feb1dd 100644 --- a/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptions.cs +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptions.cs @@ -106,7 +106,7 @@ internal set /// The service client that implements the service interface. public T CreateServiceClient() where T : class, IAmazonService { - return new ClientFactory(this).CreateServiceClient((ILogger)null, this) as T; + return new ClientFactory(this, CredentialsFactory, (ILogger)null).CreateServiceClient() as T; } /// diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ClientFactory.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ClientFactory.cs index d1f84c78d2cb..47bd3f1666d3 100644 --- a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ClientFactory.cs +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ClientFactory.cs @@ -37,69 +37,45 @@ internal class ClientFactory private static readonly Type[] EMPTY_TYPES = Array.Empty(); private static readonly object[] EMPTY_PARAMETERS = Array.Empty(); - private AWSOptions _awsOptions; + private readonly AWSOptions _options; + private readonly IAWSCredentialsFactory _credentialsFactory; + private readonly ILogger _logger; /// /// Constructs an instance of the ClientFactory /// /// The AWS options used for creating service clients. - internal ClientFactory(AWSOptions awsOptions) - { - _awsOptions = awsOptions; - } - - /// - /// Creates the AWS service client that implements the service client interface. The AWSOptions object - /// will be searched for in the IServiceProvider. - /// - /// The dependency injection provider. - /// The AWS service client - internal object CreateServiceClient(IServiceProvider provider) + /// + /// + internal ClientFactory(AWSOptions awsOptions, IAWSCredentialsFactory credentialsFactory, ILogger logger) { - var loggerFactory = provider.GetService(); - var logger = loggerFactory?.CreateLogger("AWSSDK"); - - var options = _awsOptions ?? provider.GetService(); - if(options == null) - { - var configuration = provider.GetService(); - if(configuration != null) - { - options = configuration.GetAWSOptions(); - if (options != null) - logger?.LogInformation("Found AWS options in IConfiguration"); - } - } - - return CreateServiceClient(logger, options); + _options = awsOptions ?? throw new ArgumentNullException(nameof(awsOptions)); + _credentialsFactory = credentialsFactory ?? throw new ArgumentNullException(nameof(credentialsFactory)); + _logger = logger; } /// - /// Creates the AWS service client that implements the service client interface. The AWSOptions object - /// will be searched for in the IServiceProvider. + /// Creates the AWS service client that implements the service client interface. /// - /// Logger instance for writing diagnostic logs. - /// The AWS options used for creating the service client. /// The AWS service client - internal IAmazonService CreateServiceClient(ILogger logger, AWSOptions options) + internal IAmazonService CreateServiceClient() { - PerformGlobalConfig(logger, options); - var credentialsFactory = options.CredentialsFactory ?? new DefaultAWSCredentialsFactory(options, logger); - var credentials = credentialsFactory.Create(); + PerformGlobalConfig(_logger, _options); + var credentials = _credentialsFactory.Create(); - if (!string.IsNullOrEmpty(options?.SessionRoleArn)) + if (!string.IsNullOrEmpty(_options?.SessionRoleArn)) { - if (string.IsNullOrEmpty(options?.ExternalId)) + if (string.IsNullOrEmpty(_options?.ExternalId)) { - credentials = new AssumeRoleAWSCredentials(credentials, options.SessionRoleArn, options.SessionName); + credentials = new AssumeRoleAWSCredentials(credentials, _options.SessionRoleArn, _options.SessionName); } else { - credentials = new AssumeRoleAWSCredentials(credentials, options.SessionRoleArn, options.SessionName, new AssumeRoleAWSCredentialsOptions() { ExternalId = options.ExternalId }); + credentials = new AssumeRoleAWSCredentials(credentials, _options.SessionRoleArn, _options.SessionName, new AssumeRoleAWSCredentialsOptions() { ExternalId = _options.ExternalId }); } } - var config = CreateConfig(options); + var config = CreateConfig(_options); var client = CreateClient(credentials, config); return client as IAmazonService; } diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs index c0cf329b5104..959cf9639e59 100644 --- a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs @@ -21,7 +21,9 @@ using Amazon.Runtime; using Amazon.Extensions.NETCore.Setup; +using AWSSDK.Extensions.NETCore.Setup; using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Logging; namespace Microsoft.Extensions.DependencyInjection { @@ -63,6 +65,66 @@ public static IServiceCollection AddDefaultAWSOptions( return collection; } + /// + /// + /// + /// + /// + /// + /// + public static IServiceCollection AddAWSCredentialsFactory( + this IServiceCollection collection, + Func implementationFactory, + ServiceLifetime lifetime = ServiceLifetime.Singleton) + { + collection.Add(new ServiceDescriptor(typeof(IAWSCredentialsFactory), implementationFactory, lifetime)); + return collection; + } + + /// + /// + /// + /// + /// + /// + public static IServiceCollection AddAWSCredentialsFactory( + this IServiceCollection collection, + IAWSCredentialsFactory credentials) + { + collection.Add(new ServiceDescriptor(typeof(IAWSCredentialsFactory), credentials)); + return collection; + } + + /// + /// + /// + /// + /// + /// + /// + public static IServiceCollection TryAddAWSCredentialsFactory( + this IServiceCollection collection, + Func implementationFactory, + ServiceLifetime lifetime = ServiceLifetime.Singleton) + { + collection.TryAdd(new ServiceDescriptor(typeof(IAWSCredentialsFactory), implementationFactory, lifetime)); + return collection; + } + + /// + /// + /// + /// + /// + /// + public static IServiceCollection TryAddAWSCredentialsFactory( + this IServiceCollection collection, + IAWSCredentialsFactory credentials) + { + collection.TryAdd(new ServiceDescriptor(typeof(IAWSCredentialsFactory), credentials)); + return collection; + } + /// /// Adds the AWSOptions object to the dependency injection framework providing information /// that will be used to construct Amazon service clients if they haven't already been registered. @@ -121,10 +183,7 @@ public static IServiceCollection AddAWSService(this IServiceCollection collec /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. public static IServiceCollection AddAWSService(this IServiceCollection collection, AWSOptions options, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService { - Func factory = - new ClientFactory(options).CreateServiceClient; - - var descriptor = new ServiceDescriptor(typeof(T), factory, lifetime); + var descriptor = new ServiceDescriptor(typeof(T), sp => CreateServiceClient(options, sp), lifetime); collection.Add(descriptor); return collection; } @@ -155,14 +214,11 @@ public static IServiceCollection TryAddAWSService(this IServiceCollection col /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. public static IServiceCollection TryAddAWSService(this IServiceCollection collection, AWSOptions options, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService { - Func factory = - new ClientFactory(options).CreateServiceClient; - - var descriptor = new ServiceDescriptor(typeof(T), factory, lifetime); + var descriptor = new ServiceDescriptor(typeof(T), sp => CreateServiceClient(options, sp), lifetime); collection.TryAdd(descriptor); return collection; } - + #if NET8_0_OR_GREATER /// @@ -193,7 +249,7 @@ public static IServiceCollection AddKeyedAWSService(this IServiceCollection c /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. public static IServiceCollection AddKeyedAWSService(this IServiceCollection collection, object serviceKey, AWSOptions options, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService { - object Factory(IServiceProvider sp, object key) => new ClientFactory(options).CreateServiceClient(sp); + object Factory(IServiceProvider sp, object key) => new ClientFactory(options, sp.GetService(), sp.GetService()).CreateServiceClient(); var descriptor = new ServiceDescriptor(typeof(T), serviceKey, Factory, lifetime); collection.Add(descriptor); @@ -228,12 +284,23 @@ public static IServiceCollection TryAddKeyedAWSService(this IServiceCollectio /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. public static IServiceCollection TryAddKeyedAWSService(this IServiceCollection collection, object serviceKey, AWSOptions options, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService { - object Factory(IServiceProvider sp, object key) => new ClientFactory(options).CreateServiceClient(sp); + object Factory(IServiceProvider sp, object key) => new ClientFactory(options, sp.GetService(), sp.GetService()).CreateServiceClient(); var descriptor = new ServiceDescriptor(typeof(T), serviceKey, Factory, lifetime); collection.TryAdd(descriptor); return collection; } #endif + + private static object CreateServiceClient(AWSOptions options, IServiceProvider sp) where T : IAmazonService + { + var logger = sp.GetService(); + var awsOptions = options ?? sp.GetService() ?? new AWSOptions(); + var credentialsFactory = awsOptions.CredentialsFactory ?? sp.GetService() ?? new DefaultAWSCredentialsFactory(awsOptions, logger); + + var factory = new ClientFactory(awsOptions, credentialsFactory, logger); + + return factory.CreateServiceClient(); + } } } From 5990d5c32a2efab8e31e5bed3d186dbbeab88a22 Mon Sep 17 00:00:00 2001 From: Chase Miller Date: Mon, 14 Apr 2025 11:02:57 -0400 Subject: [PATCH 2/2] Missed some spots --- .../ServiceCollectionExtensions.cs | 42 ++++++++++++++++--- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs index 959cf9639e59..1808316a2359 100644 --- a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs @@ -65,6 +65,20 @@ public static IServiceCollection AddDefaultAWSOptions( return collection; } + /// + /// + /// + /// + /// + /// + public static IServiceCollection AddAWSCredentialsFactory( + this IServiceCollection collection, + ServiceLifetime lifetime = ServiceLifetime.Singleton) + { + collection.Add(new ServiceDescriptor(typeof(IAWSCredentialsFactory), CreateDefaultCredentialsFactory, lifetime)); + return collection; + } + /// /// /// @@ -95,6 +109,20 @@ public static IServiceCollection AddAWSCredentialsFactory( return collection; } + /// + /// + /// + /// + /// + /// + public static IServiceCollection TryAddAWSCredentialsFactory( + this IServiceCollection collection, + ServiceLifetime lifetime = ServiceLifetime.Singleton) + { + collection.TryAdd(new ServiceDescriptor(typeof(IAWSCredentialsFactory), CreateDefaultCredentialsFactory, lifetime)); + return collection; + } + /// /// /// @@ -249,9 +277,7 @@ public static IServiceCollection AddKeyedAWSService(this IServiceCollection c /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. public static IServiceCollection AddKeyedAWSService(this IServiceCollection collection, object serviceKey, AWSOptions options, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService { - object Factory(IServiceProvider sp, object key) => new ClientFactory(options, sp.GetService(), sp.GetService()).CreateServiceClient(); - - var descriptor = new ServiceDescriptor(typeof(T), serviceKey, Factory, lifetime); + var descriptor = new ServiceDescriptor(typeof(T), serviceKey, (sp, _) => CreateServiceClient(options, sp), lifetime); collection.Add(descriptor); return collection; } @@ -284,9 +310,7 @@ public static IServiceCollection TryAddKeyedAWSService(this IServiceCollectio /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. public static IServiceCollection TryAddKeyedAWSService(this IServiceCollection collection, object serviceKey, AWSOptions options, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService { - object Factory(IServiceProvider sp, object key) => new ClientFactory(options, sp.GetService(), sp.GetService()).CreateServiceClient(); - - var descriptor = new ServiceDescriptor(typeof(T), serviceKey, Factory, lifetime); + var descriptor = new ServiceDescriptor(typeof(T), serviceKey, (sp, _) => CreateServiceClient(options, sp), lifetime); collection.TryAdd(descriptor); return collection; } @@ -302,5 +326,11 @@ private static object CreateServiceClient(AWSOptions options, IServiceProvide return factory.CreateServiceClient(); } + + private static IAWSCredentialsFactory CreateDefaultCredentialsFactory(IServiceProvider sp) + { + var options = sp.GetService() ?? new AWSOptions(); + return new DefaultAWSCredentialsFactory(options, sp.GetService()); + } } }