From 93f045a75080b263fdca52c2944823e753323f9a Mon Sep 17 00:00:00 2001 From: Roberto Mancinelli Date: Thu, 19 Dec 2024 20:20:54 +0100 Subject: [PATCH] refactor refactor --- README.md | 54 +++++++++- .../MvcExample/Controllers/HomeController.cs | 3 +- .../Controllers/MerchantAccountsController.cs | 3 +- .../Controllers/PayoutController.cs | 3 +- .../Controllers/ProvidersController.cs | 3 +- examples/MvcExample/Startup.cs | 18 ++-- src/TrueLayer/Auth/AuthApiCacheDecorator.cs | 1 + .../Caching/AuthTokenCachingStrategies.cs | 27 +++++ .../{ => Caching}/IAuthTokenCache.cs | 2 +- .../{ => Caching}/InMemoryAuthTokenCache.cs | 2 +- .../{ => Caching}/NullMemoryCache.cs | 2 +- src/TrueLayer/TrueLayerClientFactory.cs | 34 +++++- .../TrueLayerServiceCollectionExtensions.cs | 102 ++++++++++++------ .../ApiTestFixture.cs | 10 +- test/TrueLayer.Tests/ApiClientTests.cs | 2 - .../Auth/AuthApiCacheDecoratorTests.cs | 1 - .../Mocks/InMemoryAuthTokenCacheMock.cs | 1 + test/TrueLayer.Tests/TrueLayerClientTests.cs | 44 +++++++- ...ueLayerServiceCollectionExtensionsTests.cs | 35 +++++- 19 files changed, 277 insertions(+), 70 deletions(-) create mode 100644 src/TrueLayer/Caching/AuthTokenCachingStrategies.cs rename src/TrueLayer/{ => Caching}/IAuthTokenCache.cs (97%) rename src/TrueLayer/{ => Caching}/InMemoryAuthTokenCache.cs (96%) rename src/TrueLayer/{ => Caching}/NullMemoryCache.cs (94%) diff --git a/README.md b/README.md index f583a402..bc5474e8 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ Add your Client ID, Secret and Signing Key ID to `appsettings.json` or any other ### Initialize TrueLayer.NET -Register the TrueLayer client in `Startup.cs` or `Program.cs` (.NET 6.0): +Register the TrueLayer client in `Startup.cs` or `Program.cs` (.NET 9.0/.NET 8.0/.NET 6.0): ```c# public IConfiguration Configuration { get; } @@ -115,13 +115,57 @@ public void ConfigureServices(IServiceCollection services) // For demo purposes only. Private key should be stored securely options.Payments.SigningKey.PrivateKey = File.ReadAllText("ec512-private-key.pem"); } - }) - // We advice to cache the auth token - .AddAuthTokenInMemoryCaching(); + }, + // For best performance and reliability we advice to cache the auth token + authCachingStrategy: AuthCachingStrategy.InMemory) + } ``` -Alternatively you can create a class that implements `IConfigureOptions` if you have more complex configuration requirements. +### Multiple TrueLayer Clients + +Use keyed version of TruLayer client (.NET 9.0/.NET 8.0): + +```c# +.AddKeyedTrueLayer(configuration, options => + { + // For demo purposes only. Private key should be stored securely + var privateKey = File.ReadAllText("ec512-private-key.pem"); + if (options.Payments?.SigningKey != null) + { + options.Payments.SigningKey.PrivateKey = privateKey; + } + }, + configurationSectionName: "TrueLayerGbp", + serviceKey: "TrueLayerGbp", + authTokenCachingStrategy: AuthTokenCachingStrategies.InMemory) +.AddKeyedTrueLayer(configuration, options => + { + // For demo purposes only. Private key should be stored securely + var privateKey = File.ReadAllText("ec512-private-key.pem"); + if (options.Payments?.SigningKey != null) + { + options.Payments.SigningKey.PrivateKey = privateKey; + } + }, + configurationSectionName: "TrueLayerEur", + serviceKey: "TrueLayerEur", + authTokenCachingStrategy: AuthTokenCachingStrategies.InMemory) +``` + +Use `[FromKeyedServices()]` attribute to retrieve keyed client + +```c# +public GbpController([FromKeyedServices("TrueLayerGbp")]ITrueLayerClient trueLayerClient, ... +public EurController([FromKeyedServices("TrueLayerEur")]ITrueLayerClient trueLayerClient, ... +``` + +Or `GetRequiredKeyedService` + +```c# +var GbpClient = ServiceProvider.GetRequiredKeyedService("TrueLayerGbp"); +var EurClient = ServiceProvider.GetRequiredKeyedService("TrueLayerEur"); +``` ### Make a payment diff --git a/examples/MvcExample/Controllers/HomeController.cs b/examples/MvcExample/Controllers/HomeController.cs index 92359144..507caee3 100644 --- a/examples/MvcExample/Controllers/HomeController.cs +++ b/examples/MvcExample/Controllers/HomeController.cs @@ -4,7 +4,6 @@ using System.Net; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using MvcExample.Models; using OneOf; @@ -20,7 +19,7 @@ public class HomeController : Controller private readonly ITrueLayerClient _trueLayerClient; private readonly ILogger _logger; - public HomeController([FromKeyedServices("TrueLayerClient")]ITrueLayerClient trueLayerClient, ILogger logger) + public HomeController(ITrueLayerClient trueLayerClient, ILogger logger) { _trueLayerClient = trueLayerClient; _logger = logger; diff --git a/examples/MvcExample/Controllers/MerchantAccountsController.cs b/examples/MvcExample/Controllers/MerchantAccountsController.cs index 667aba8f..049468e8 100644 --- a/examples/MvcExample/Controllers/MerchantAccountsController.cs +++ b/examples/MvcExample/Controllers/MerchantAccountsController.cs @@ -1,6 +1,5 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using TrueLayer; @@ -11,7 +10,7 @@ public class MerchantAccountsController : Controller private readonly ITrueLayerClient _trueLayerClient; private readonly ILogger _logger; - public MerchantAccountsController([FromKeyedServices("TrueLayerClient")]ITrueLayerClient trueLayerClient, ILogger logger) + public MerchantAccountsController(ITrueLayerClient trueLayerClient, ILogger logger) { _trueLayerClient = trueLayerClient; _logger = logger; diff --git a/examples/MvcExample/Controllers/PayoutController.cs b/examples/MvcExample/Controllers/PayoutController.cs index 1bc612e1..9b1b9738 100644 --- a/examples/MvcExample/Controllers/PayoutController.cs +++ b/examples/MvcExample/Controllers/PayoutController.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using MvcExample.Models; using TrueLayer; @@ -18,7 +17,7 @@ public class PayoutController : Controller private readonly ITrueLayerClient _trueLayerClient; private readonly ILogger _logger; - public PayoutController([FromKeyedServices("TrueLayerClient")]ITrueLayerClient trueLayerClient, ILogger logger) + public PayoutController(ITrueLayerClient trueLayerClient, ILogger logger) { _trueLayerClient = trueLayerClient; _logger = logger; diff --git a/examples/MvcExample/Controllers/ProvidersController.cs b/examples/MvcExample/Controllers/ProvidersController.cs index 8f97b60d..d21c66cf 100644 --- a/examples/MvcExample/Controllers/ProvidersController.cs +++ b/examples/MvcExample/Controllers/ProvidersController.cs @@ -1,7 +1,6 @@ using System.Net; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; using TrueLayer; using TrueLayer.PaymentsProviders.Model; @@ -11,7 +10,7 @@ public class ProvidersController : Controller { private readonly ITrueLayerClient _trueLayerClient; - public ProvidersController([FromKeyedServices("TrueLayerClient")]ITrueLayerClient trueLayerClient) + public ProvidersController(ITrueLayerClient trueLayerClient) { _trueLayerClient = trueLayerClient; } diff --git a/examples/MvcExample/Startup.cs b/examples/MvcExample/Startup.cs index 63f9e605..350c5453 100644 --- a/examples/MvcExample/Startup.cs +++ b/examples/MvcExample/Startup.cs @@ -4,6 +4,8 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using TrueLayer.Auth; +using TrueLayer.Caching; namespace MvcExample { @@ -21,17 +23,15 @@ public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); - const string serviceKey = "TrueLayerClient"; services.AddTrueLayer(Configuration, options => + { + var privateKey = File.ReadAllText("ec512-private-key.pem"); + if (options.Payments?.SigningKey != null) { - var privateKey = File.ReadAllText("ec512-private-key.pem"); - if (options.Payments?.SigningKey != null) - { - options.Payments.SigningKey.PrivateKey = privateKey; - } - }, - serviceKey: serviceKey) - .AddAuthTokenInMemoryCaching(serviceKey); + options.Payments.SigningKey.PrivateKey = privateKey; + } + }, + authTokenCachingStrategy: AuthTokenCachingStrategies.InMemory); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/src/TrueLayer/Auth/AuthApiCacheDecorator.cs b/src/TrueLayer/Auth/AuthApiCacheDecorator.cs index 4e885e7f..4078ffe7 100644 --- a/src/TrueLayer/Auth/AuthApiCacheDecorator.cs +++ b/src/TrueLayer/Auth/AuthApiCacheDecorator.cs @@ -1,6 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using TrueLayer.Caching; namespace TrueLayer.Auth { diff --git a/src/TrueLayer/Caching/AuthTokenCachingStrategies.cs b/src/TrueLayer/Caching/AuthTokenCachingStrategies.cs new file mode 100644 index 00000000..7690f85b --- /dev/null +++ b/src/TrueLayer/Caching/AuthTokenCachingStrategies.cs @@ -0,0 +1,27 @@ +namespace TrueLayer.Caching +{ + /// + /// Represents the different strategies for caching auth token + /// + public enum AuthTokenCachingStrategies + { + /// + /// No caching. + /// A new auth token is generated for each request. + /// + None, + + /// + /// Add a InMemory cache to store the auth token. + /// Recommended option as it improves latency and availability + /// + InMemory, + + /// + /// Add a custom caching to store the auth token. + /// To use this strategy it is required to implement interface + /// and add the custom caching class as a singleton in DI + /// + Custom + } +} diff --git a/src/TrueLayer/IAuthTokenCache.cs b/src/TrueLayer/Caching/IAuthTokenCache.cs similarity index 97% rename from src/TrueLayer/IAuthTokenCache.cs rename to src/TrueLayer/Caching/IAuthTokenCache.cs index 4fea8955..4460e18d 100644 --- a/src/TrueLayer/IAuthTokenCache.cs +++ b/src/TrueLayer/Caching/IAuthTokenCache.cs @@ -1,7 +1,7 @@ using System; using TrueLayer.Auth; -namespace TrueLayer; +namespace TrueLayer.Caching; /// /// IAuthTokenCache can be used to provide custom implementation of Auth token caching diff --git a/src/TrueLayer/InMemoryAuthTokenCache.cs b/src/TrueLayer/Caching/InMemoryAuthTokenCache.cs similarity index 96% rename from src/TrueLayer/InMemoryAuthTokenCache.cs rename to src/TrueLayer/Caching/InMemoryAuthTokenCache.cs index 8a3540ae..e701d7f8 100644 --- a/src/TrueLayer/InMemoryAuthTokenCache.cs +++ b/src/TrueLayer/Caching/InMemoryAuthTokenCache.cs @@ -2,7 +2,7 @@ using Microsoft.Extensions.Caching.Memory; using TrueLayer.Auth; -namespace TrueLayer +namespace TrueLayer.Caching { internal class InMemoryAuthTokenCache : IAuthTokenCache { diff --git a/src/TrueLayer/NullMemoryCache.cs b/src/TrueLayer/Caching/NullMemoryCache.cs similarity index 94% rename from src/TrueLayer/NullMemoryCache.cs rename to src/TrueLayer/Caching/NullMemoryCache.cs index 0ac7bf5f..d775e3d1 100644 --- a/src/TrueLayer/NullMemoryCache.cs +++ b/src/TrueLayer/Caching/NullMemoryCache.cs @@ -1,7 +1,7 @@ using System; using TrueLayer.Auth; -namespace TrueLayer +namespace TrueLayer.Caching { internal class NullMemoryCache : IAuthTokenCache { diff --git a/src/TrueLayer/TrueLayerClientFactory.cs b/src/TrueLayer/TrueLayerClientFactory.cs index 177c83c9..734ac21f 100644 --- a/src/TrueLayer/TrueLayerClientFactory.cs +++ b/src/TrueLayer/TrueLayerClientFactory.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using TrueLayer.Auth; +using TrueLayer.Caching; using TrueLayer.Mandates; using TrueLayer.MerchantAccounts; using TrueLayer.Payments; @@ -24,7 +25,36 @@ public TrueLayerClientFactory(IApiClient apiClient, IOptionsSnapshot(() => new PaymentsApi(_apiClient, auth, options)), + new Lazy(() => new PaymentsProvidersApi(_apiClient, auth, options)), + new Lazy(() => new PayoutsApi(_apiClient, auth, options)), + new Lazy(() => new MerchantAccountsApi(_apiClient, auth, options)), + new Lazy(() => new MandatesApi(_apiClient, auth, options))); + } + + public ITrueLayerClient CreateWithCache() + { + var options = _options.Value; + var authTokenCache = _serviceProvider.GetRequiredService(); + var decoratedAuthApi = new AuthApiCacheDecorator(new AuthApi(_apiClient, options), authTokenCache); + + return new TrueLayerClient( + decoratedAuthApi, + new Lazy(() => new PaymentsApi(_apiClient, decoratedAuthApi, options)), + new Lazy(() => new PaymentsProvidersApi(_apiClient, decoratedAuthApi, options)), + new Lazy(() => new PayoutsApi(_apiClient, decoratedAuthApi, options)), + new Lazy(() => new MerchantAccountsApi(_apiClient, decoratedAuthApi, options)), + new Lazy(() => new MandatesApi(_apiClient, decoratedAuthApi, options))); + } + + public ITrueLayerClient CreateKeyed(string serviceKey) { var options = _options.Get(serviceKey); var auth = new AuthApi(_apiClient, options); @@ -38,7 +68,7 @@ public ITrueLayerClient Create(string serviceKey) new Lazy(() => new MandatesApi(_apiClient, auth, options))); } - public ITrueLayerClient CreateWithCache(string serviceKey) + public ITrueLayerClient CreateWithCacheKeyed(string serviceKey) { var options = _options.Get(serviceKey); var authTokenCache = _serviceProvider.GetRequiredKeyedService(serviceKey); diff --git a/src/TrueLayer/TrueLayerServiceCollectionExtensions.cs b/src/TrueLayer/TrueLayerServiceCollectionExtensions.cs index ed3e9b62..49bca2a9 100644 --- a/src/TrueLayer/TrueLayerServiceCollectionExtensions.cs +++ b/src/TrueLayer/TrueLayerServiceCollectionExtensions.cs @@ -1,8 +1,8 @@ using System; -using System.Linq; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection.Extensions; using TrueLayer; +using TrueLayer.Auth; +using TrueLayer.Caching; namespace Microsoft.Extensions.DependencyInjection { @@ -20,7 +20,7 @@ public static class TrueLayerServiceCollectionExtensions /// Action to customise the TrueLayer options created from configuration. /// Action to override the HttpClientBuilder. /// Name of configuration section used to build the TrueLayer client - /// Key used to register TrueLayer client in DI + /// Caching strategy for auth token /// The service collection with registered TrueLayer SDK services. public static IServiceCollection AddTrueLayer( this IServiceCollection services, @@ -28,12 +28,12 @@ public static IServiceCollection AddTrueLayer( Action? configureOptions = null, Action? configureBuilder = null, string configurationSectionName = "TrueLayer", - string serviceKey = "TrueLayerClient") + AuthTokenCachingStrategies authTokenCachingStrategy = AuthTokenCachingStrategies.None) { if (services is null) throw new ArgumentNullException(nameof(services)); if (configuration is null) throw new ArgumentNullException(nameof(configuration)); - services.Configure(serviceKey, options => + services.Configure(options => { configuration.GetSection(configurationSectionName).Bind(options); configureOptions?.Invoke(options); @@ -43,47 +43,83 @@ public static IServiceCollection AddTrueLayer( IHttpClientBuilder httpClientBuilder = services.AddHttpClient(); configureBuilder?.Invoke(httpClientBuilder); - services.AddKeyedSingleton(serviceKey); - services.AddKeyedTransient(serviceKey); - services.AddKeyedTransient(serviceKey, - (x, _) => x.GetRequiredKeyedService(serviceKey).Create(serviceKey)); + services.AddTransient(); + + switch (authTokenCachingStrategy) + { + case AuthTokenCachingStrategies.None: + services.AddSingleton(); + services.AddTransient(s => s.GetRequiredService().Create()); + break; + case AuthTokenCachingStrategies.InMemory: + services.AddMemoryCache(); + services.AddSingleton(); + services.AddTransient(x => x.GetRequiredService().CreateWithCache()); + break; + case AuthTokenCachingStrategies.Custom: + services.AddTransient(x => x.GetRequiredService().CreateWithCache()); + break; + } return services; } /// - /// Registers the default caching mechanism for the auth token. + /// Registers the default TrueLayer SDK services to the provided . /// /// The service collection to add to. + /// The Microsoft configuration used to obtain the TrueLayer SDK configuration. + /// Action to customise the TrueLayer options created from configuration. + /// Action to override the HttpClientBuilder. + /// Name of configuration section used to build the TrueLayer client /// Key used to register TrueLayer client in DI - public static IServiceCollection AddAuthTokenInMemoryCaching( + /// Caching strategy for auth token + /// The service collection with registered TrueLayer SDK services. + public static IServiceCollection AddKeyedTrueLayer( this IServiceCollection services, - string serviceKey) + IConfiguration configuration, + Action? configureOptions = null, + Action? configureBuilder = null, + string configurationSectionName = "TrueLayer", + string serviceKey = "TrueLayerClient", + AuthTokenCachingStrategies authTokenCachingStrategy = AuthTokenCachingStrategies.None) { - services.RemoveAllKeyed(serviceKey); - services.RemoveAllKeyed(serviceKey); + if (services is null) throw new ArgumentNullException(nameof(services)); + if (configuration is null) throw new ArgumentNullException(nameof(configuration)); + + services.Configure(serviceKey, options => + { + configuration.GetSection(configurationSectionName).Bind(options); + configureOptions?.Invoke(options); + options.Validate(); + }); - services.AddMemoryCache(); - services.AddKeyedSingleton(serviceKey); - services.AddKeyedTransient(serviceKey, - (x, _) => x.GetRequiredKeyedService(serviceKey).CreateWithCache(serviceKey)); + IHttpClientBuilder httpClientBuilder = services.AddHttpClient(); + configureBuilder?.Invoke(httpClientBuilder); - return services; - } + services.AddKeyedTransient(serviceKey); - /// - /// Registers a custom caching mechanism for the auth token. - /// You need to register your own implementation of IAuthTokenCache before invoking this method - /// - /// The service collection to add to. - /// Key used to register TrueLayer client in DI - public static IServiceCollection AddAuthTokenCaching( - this IServiceCollection services, - string serviceKey) - { - services.RemoveAllKeyed(serviceKey); - services.AddKeyedTransient(serviceKey, - (s, _) => s.GetRequiredKeyedService(serviceKey).CreateWithCache(serviceKey)); + switch (authTokenCachingStrategy) + { + case AuthTokenCachingStrategies.None: + services.AddKeyedSingleton(serviceKey); + services.AddKeyedTransient(serviceKey, + (x, _) => x.GetRequiredKeyedService(serviceKey) + .CreateKeyed(serviceKey)); + break; + case AuthTokenCachingStrategies.InMemory: + services.AddMemoryCache(); + services.AddKeyedSingleton(serviceKey); + services.AddKeyedTransient(serviceKey, + (x, _) => x.GetRequiredKeyedService(serviceKey) + .CreateWithCacheKeyed(serviceKey)); + break; + case AuthTokenCachingStrategies.Custom: + services.AddKeyedTransient(serviceKey, + (x, _) => x.GetRequiredKeyedService(serviceKey) + .CreateWithCacheKeyed(serviceKey)); + break; + } return services; } diff --git a/test/TrueLayer.AcceptanceTests/ApiTestFixture.cs b/test/TrueLayer.AcceptanceTests/ApiTestFixture.cs index 988160b6..e48c9a5b 100644 --- a/test/TrueLayer.AcceptanceTests/ApiTestFixture.cs +++ b/test/TrueLayer.AcceptanceTests/ApiTestFixture.cs @@ -3,6 +3,8 @@ using System.Reflection; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using TrueLayer.Auth; +using TrueLayer.Caching; namespace TrueLayer.AcceptanceTests { @@ -18,7 +20,7 @@ public ApiTestFixture() const string serviceKey2 = "TrueLayerClient2"; ServiceProvider = new ServiceCollection() - .AddTrueLayer(configuration, options => + .AddKeyedTrueLayer(configuration, options => { var privateKey = File.ReadAllText("ec512-private-key.pem"); if (options.Payments?.SigningKey != null) @@ -28,7 +30,7 @@ public ApiTestFixture() }, configurationSectionName: configName1, serviceKey: serviceKey1) - .AddTrueLayer(configuration, options => + .AddKeyedTrueLayer(configuration, options => { var privateKey = File.ReadAllText("ec512-private-key.pem"); if (options.Payments?.SigningKey != null) @@ -37,8 +39,8 @@ public ApiTestFixture() } }, configurationSectionName: configName2, - serviceKey: serviceKey2) - .AddAuthTokenInMemoryCaching(configName2) + serviceKey: serviceKey2, + authTokenCachingStrategy: AuthTokenCachingStrategies.InMemory) .BuildServiceProvider(); Client = ServiceProvider.GetRequiredKeyedService(serviceKey1); diff --git a/test/TrueLayer.Tests/ApiClientTests.cs b/test/TrueLayer.Tests/ApiClientTests.cs index b6a3b7fb..4d225f32 100644 --- a/test/TrueLayer.Tests/ApiClientTests.cs +++ b/test/TrueLayer.Tests/ApiClientTests.cs @@ -7,9 +7,7 @@ using System.Text.Json; using System.Threading.Tasks; using FluentAssertions; -using Microsoft.Extensions.Options; using RichardSzalay.MockHttp; -using TrueLayer.Auth; using TrueLayer.Serialization; using TrueLayer.Tests.Mocks; using Xunit; diff --git a/test/TrueLayer.Tests/Auth/AuthApiCacheDecoratorTests.cs b/test/TrueLayer.Tests/Auth/AuthApiCacheDecoratorTests.cs index f1b17542..482ca93e 100644 --- a/test/TrueLayer.Tests/Auth/AuthApiCacheDecoratorTests.cs +++ b/test/TrueLayer.Tests/Auth/AuthApiCacheDecoratorTests.cs @@ -1,4 +1,3 @@ -using System; using System.Net; using System.Threading.Tasks; using FluentAssertions; diff --git a/test/TrueLayer.Tests/Mocks/InMemoryAuthTokenCacheMock.cs b/test/TrueLayer.Tests/Mocks/InMemoryAuthTokenCacheMock.cs index dc59de63..8b91d9ca 100644 --- a/test/TrueLayer.Tests/Mocks/InMemoryAuthTokenCacheMock.cs +++ b/test/TrueLayer.Tests/Mocks/InMemoryAuthTokenCacheMock.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using TrueLayer.Auth; +using TrueLayer.Caching; namespace TrueLayer.Tests.Mocks; diff --git a/test/TrueLayer.Tests/TrueLayerClientTests.cs b/test/TrueLayer.Tests/TrueLayerClientTests.cs index 25796b03..e802d5dc 100644 --- a/test/TrueLayer.Tests/TrueLayerClientTests.cs +++ b/test/TrueLayer.Tests/TrueLayerClientTests.cs @@ -13,7 +13,6 @@ public class TrueLayerClientTests [Fact] public void Can_Create_TrueLayer_Client_From_Options() { - var configName = "TrueLayer"; var options = new TrueLayerOptions { ClientId = "clientid", @@ -42,7 +41,48 @@ public void Can_Create_TrueLayer_Client_From_Options() new ApiClient(new HttpClient()), optionsMock, provider); - var client = factory.Create(configName); + var client = factory.Create(); + + + client.Auth.Should().NotBeNull(); + client.Payments.Should().NotBeNull(); + client.MerchantAccounts.Should().NotBeNull(); + client.Mandates.Should().NotBeNull(); + } + + [Fact] + public void Can_Create_Keyed_TrueLayer_Client_From_Options() + { + const string serviceKey = "TrueLayer"; + var options = new TrueLayerOptions + { + ClientId = "clientid", + ClientSecret = "secret", + UseSandbox = true, + Auth = new ApiOptions + { + Uri = new Uri("https://auth") + }, + Payments = new PaymentsOptions + { + Uri = new Uri("https://payments"), + HppUri = new Uri("https://hpp"), + SigningKey = new SigningKey + { + KeyId = "key-id", + PrivateKey = "--private--" + } + } + }; + var services = new ServiceCollection(); + var provider = services.BuildServiceProvider(); + + var optionsMock = new OptionSnapshotMock(options); + var factory = new TrueLayerClientFactory( + new ApiClient(new HttpClient()), + optionsMock, + provider); + var client = factory.CreateKeyed(serviceKey); client.Auth.Should().NotBeNull(); diff --git a/test/TrueLayer.Tests/TrueLayerServiceCollectionExtensionsTests.cs b/test/TrueLayer.Tests/TrueLayerServiceCollectionExtensionsTests.cs index 7b4e20dc..383b2398 100644 --- a/test/TrueLayer.Tests/TrueLayerServiceCollectionExtensionsTests.cs +++ b/test/TrueLayer.Tests/TrueLayerServiceCollectionExtensionsTests.cs @@ -32,8 +32,41 @@ public void Should_register_truelayer_client() .AddTrueLayer(configuration) .BuildServiceProvider(); - var client = services.GetRequiredKeyedService("TrueLayer"); + var client = services.GetRequiredService(); client.Should().NotBeNull(); } + + [Fact] + public void Should_register_keyed_truelayer_clients() + { + const string gbpServiceKey = "GbpClient"; + const string eurServiceKey = "EurClient"; + const string privateKey = @"-----BEGIN EC PRIVATE KEY----- +MIHcAgEBBEIALJ2sKM+8mVDfTIlk50rqB5lkxaLBt+OECvhXq3nEaB+V0nqljZ9c +5aHRN3qqxMzNLvxFQ+4twifa4ezkMK2/j5WgBwYFK4EEACOhgYkDgYYABADmhZbj +i8bgJRfMTdtzy+5VbS5ScMaKC1LQfhII+PTzGzOr+Ts7Qv8My5cmYU5qarGK3tWF +c3VMlcFZw7Y0iLjxAQFPvHqJ9vn3xWp+d3JREU1vQJ9daXswwbcoer88o1oVFmFf +WS1/11+TH1x/lgKckAws6sAzJLPtCUZLV4IZTb6ENg== +-----END EC PRIVATE KEY-----"; + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new[] + { + new KeyValuePair("TrueLayer:ClientId", "client_id"), + new KeyValuePair("TrueLayer:ClientSecret", "secret"), + new KeyValuePair("TrueLayer:Payments:SigningKey:KeyId", Guid.NewGuid().ToString()), + new KeyValuePair("TrueLayer:Payments:SigningKey:PrivateKey", privateKey) + }) + .Build(); + var services = new ServiceCollection() + .AddKeyedTrueLayer(configuration, serviceKey: gbpServiceKey) + .AddKeyedTrueLayer(configuration, serviceKey: eurServiceKey) + .BuildServiceProvider(); + + var gbpClient = services.GetRequiredKeyedService(gbpServiceKey); + var eurClient = services.GetRequiredKeyedService(eurServiceKey); + + gbpClient.Should().NotBeNull(); + eurClient.Should().NotBeNull(); + } } }