From 528d59c2871893491d2c8c02d3e4fd0cf7ed35f5 Mon Sep 17 00:00:00 2001 From: Kenneth Mugo Date: Wed, 10 Jul 2024 16:05:17 +0300 Subject: [PATCH 1/5] Upload files using the files.falu.io host --- .../IServiceCollectionExtensions.cs | 8 ++++++- src/Falu/FilesUploadHandler.cs | 21 +++++++++++++++++++ .../Clients/BaseServiceClientTests.cs | 8 ++++++- .../Clients/FilesServiceClientTests.cs | 2 +- ...IdentityVerificationsServiceClientTests.cs | 2 ++ .../MessageBatchesServiceClientTests.cs | 3 +++ .../MessageStreamsServiceClientTests.cs | 2 ++ .../MessageTemplatesServiceClientTests.cs | 1 + .../Clients/MessagesServiceClientTests.cs | 2 ++ .../MoneyBalancesServiceClientTests.cs | 1 + ...PaymentAuthorizationsServiceClientTests.cs | 2 ++ 11 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 src/Falu/FilesUploadHandler.cs diff --git a/src/Falu/Extensions/IServiceCollectionExtensions.cs b/src/Falu/Extensions/IServiceCollectionExtensions.cs index 26ad029..cdc3b0f 100644 --- a/src/Falu/Extensions/IServiceCollectionExtensions.cs +++ b/src/Falu/Extensions/IServiceCollectionExtensions.cs @@ -1,11 +1,13 @@ using Falu; using Falu.Core; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; using Polly; using Polly.Contrib.WaitAndRetry; using Polly.Retry; using System.Diagnostics.CodeAnalysis; using System.Net; +using System.Net.Http; using System.Net.Http.Headers; namespace Microsoft.Extensions.DependencyInjection; @@ -77,6 +79,9 @@ public static IHttpClientBuilder AddFalu< // get the version from the assembly var productVersion = typeof(TClient).Assembly.GetName().Version!.ToString(3); + // register a custom delegating handler to upload files using files.falu.io host + services.AddTransient(); + // setup client var builder = services.AddHttpClient() .ConfigureHttpClient((provider, client) => @@ -86,7 +91,8 @@ public static IHttpClientBuilder AddFalu< // populate the User-Agent value for the SDK/library client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("falu-dotnet", productVersion)); - }); + }) + .AddHttpMessageHandler(); // setup retries builder.AddPolicyHandler((sp, request) => diff --git a/src/Falu/FilesUploadHandler.cs b/src/Falu/FilesUploadHandler.cs new file mode 100644 index 0000000..bbddac9 --- /dev/null +++ b/src/Falu/FilesUploadHandler.cs @@ -0,0 +1,21 @@ +namespace Falu; + +/// +/// Modifies the request uri for file uploads to use files.falu.io host +/// +internal class FilesUploadHandler : DelegatingHandler +{ + private readonly Uri FilesUploadUri = new("https://files.falu.io/v1/files"); + + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var path = request.RequestUri!.AbsolutePath; + if (request.Method == HttpMethod.Post && FilesUploadUri.AbsolutePath.Equals(path)) + { + // Send the request to files.falu.io + request.RequestUri = FilesUploadUri; + } + + return base.SendAsync(request, cancellationToken); + } +} diff --git a/tests/Falu.Tests/Clients/BaseServiceClientTests.cs b/tests/Falu.Tests/Clients/BaseServiceClientTests.cs index 7337f96..8771fec 100644 --- a/tests/Falu.Tests/Clients/BaseServiceClientTests.cs +++ b/tests/Falu.Tests/Clients/BaseServiceClientTests.cs @@ -27,6 +27,7 @@ protected DynamicHttpMessageHandler GetAsync_Handler(RequestOptions? requestOpti var handler = new DynamicHttpMessageHandler((req, ct) => { Assert.Equal(HttpMethod.Get, req.Method); + Assert.Equal(ApiHost, req.RequestUri!.Host); Assert.Equal($"{BasePath}/{Data.Id!}", req.RequestUri!.AbsolutePath); AssertRequestHeaders(req, requestOptions); @@ -49,6 +50,7 @@ protected DynamicHttpMessageHandler ListAsync_Handler(bool? hasContinuationToken var handler = new DynamicHttpMessageHandler((req, ct) => { Assert.Equal(HttpMethod.Get, req.Method); + Assert.Equal(ApiHost, req.RequestUri!.Host); Assert.Equal($"{BasePath}", req.RequestUri!.AbsolutePath); AssertRequestHeaders(req, requestOptions); @@ -74,11 +76,12 @@ protected DynamicHttpMessageHandler ListAsync_Handler(bool? hasContinuationToken return handler; } - protected DynamicHttpMessageHandler CreateAsync_Handler(RequestOptions? requestOptions = null) + protected DynamicHttpMessageHandler CreateAsync_Handler(RequestOptions? requestOptions = null, string? host = ApiHost) { var handler = new DynamicHttpMessageHandler((req, ct) => { Assert.Equal(HttpMethod.Post, req.Method); + Assert.Equal(host, req.RequestUri!.Host); Assert.Equal($"{BasePath}", req.RequestUri!.AbsolutePath); AssertRequestHeaders(req, requestOptions); @@ -102,6 +105,7 @@ protected DynamicHttpMessageHandler UpdateAsync_Handler(RequestOptions? requestO var handler = new DynamicHttpMessageHandler((req, ct) => { Assert.Equal(HttpMethod.Patch, req.Method); + Assert.Equal(ApiHost, req.RequestUri!.Host); Assert.Equal($"{BasePath}/{Data.Id!}", req.RequestUri!.AbsolutePath); AssertRequestHeaders(req, requestOptions); @@ -123,6 +127,7 @@ protected DynamicHttpMessageHandler DeleteAsync_Handler(RequestOptions? requestO var handler = new DynamicHttpMessageHandler((req, ct) => { Assert.Equal(HttpMethod.Delete, req.Method); + Assert.Equal(ApiHost, req.RequestUri!.Host); Assert.Equal($"{BasePath}/{Data.Id!}", req.RequestUri!.AbsolutePath); AssertRequestHeaders(req, requestOptions); @@ -138,6 +143,7 @@ public class BaseServiceClientTests { private const string TestKey = "test"; + protected const string ApiHost = "api.falu.io"; protected const string WorkspaceId = "wksp_602a8dd0a54847479a874de4"; protected const string IdempotencyKey = "05bc69eb-218d-46f2-8812-5bede8592abf"; diff --git a/tests/Falu.Tests/Clients/FilesServiceClientTests.cs b/tests/Falu.Tests/Clients/FilesServiceClientTests.cs index e8bf319..21726bc 100644 --- a/tests/Falu.Tests/Clients/FilesServiceClientTests.cs +++ b/tests/Falu.Tests/Clients/FilesServiceClientTests.cs @@ -92,7 +92,7 @@ await TestAsync(handler, async (client) => [ClassData(typeof(RequestOptionsData))] public async Task CreateAsync_Works(RequestOptions requestOptions) { - var handler = CreateAsync_Handler(requestOptions); + var handler = CreateAsync_Handler(requestOptions, "files.falu.io"); await TestAsync(handler, async (client) => { diff --git a/tests/Falu.Tests/Clients/IdentityVerificationsServiceClientTests.cs b/tests/Falu.Tests/Clients/IdentityVerificationsServiceClientTests.cs index 823b0c8..57cade8 100644 --- a/tests/Falu.Tests/Clients/IdentityVerificationsServiceClientTests.cs +++ b/tests/Falu.Tests/Clients/IdentityVerificationsServiceClientTests.cs @@ -128,6 +128,7 @@ public async Task CancelAsync_Works(RequestOptions requestOptions) var handler = new DynamicHttpMessageHandler((req, ct) => { Assert.Equal(HttpMethod.Post, req.Method); + Assert.Equal(ApiHost, req.RequestUri!.Host); Assert.Equal($"{BasePath}/{Data!.Id}/cancel", req.RequestUri!.AbsolutePath); AssertRequestHeaders(req, requestOptions); @@ -156,6 +157,7 @@ public async Task RedactAsync_Works(RequestOptions requestOptions) var handler = new DynamicHttpMessageHandler((req, ct) => { Assert.Equal(HttpMethod.Post, req.Method); + Assert.Equal(ApiHost, req.RequestUri!.Host); Assert.Equal($"{BasePath}/{Data!.Id}/redact", req.RequestUri!.AbsolutePath); AssertRequestHeaders(req, requestOptions); diff --git a/tests/Falu.Tests/Clients/MessageBatchesServiceClientTests.cs b/tests/Falu.Tests/Clients/MessageBatchesServiceClientTests.cs index 541a0be..f6a28e1 100644 --- a/tests/Falu.Tests/Clients/MessageBatchesServiceClientTests.cs +++ b/tests/Falu.Tests/Clients/MessageBatchesServiceClientTests.cs @@ -137,6 +137,7 @@ public async Task StatusAsync_Works(RequestOptions requestOptions) var handler = new DynamicHttpMessageHandler((req, ct) => { Assert.Equal(HttpMethod.Get, req.Method); + Assert.Equal(ApiHost, req.RequestUri!.Host); Assert.Equal($"{BasePath}/{Data!.Id}/status", req.RequestUri!.AbsolutePath); var response = new HttpResponseMessage(HttpStatusCode.OK) @@ -163,6 +164,7 @@ public async Task CancelAsync_Works(RequestOptions requestOptions) var handler = new DynamicHttpMessageHandler((req, ct) => { Assert.Equal(HttpMethod.Post, req.Method); + Assert.Equal(ApiHost, req.RequestUri!.Host); Assert.Equal($"{BasePath}/{Data!.Id}/cancel", req.RequestUri!.AbsolutePath); AssertRequestHeaders(req, requestOptions); @@ -191,6 +193,7 @@ public async Task RedactAsync_Works(RequestOptions requestOptions) var handler = new DynamicHttpMessageHandler((req, ct) => { Assert.Equal(HttpMethod.Post, req.Method); + Assert.Equal(ApiHost, req.RequestUri!.Host); Assert.Equal($"{BasePath}/{Data!.Id}/redact", req.RequestUri!.AbsolutePath); AssertRequestHeaders(req, requestOptions); diff --git a/tests/Falu.Tests/Clients/MessageStreamsServiceClientTests.cs b/tests/Falu.Tests/Clients/MessageStreamsServiceClientTests.cs index 53b2ea9..e138295 100644 --- a/tests/Falu.Tests/Clients/MessageStreamsServiceClientTests.cs +++ b/tests/Falu.Tests/Clients/MessageStreamsServiceClientTests.cs @@ -115,6 +115,7 @@ public async Task ArchiveAsync_Works(RequestOptions requestOptions) var handler = new DynamicHttpMessageHandler((req, ct) => { Assert.Equal(HttpMethod.Post, req.Method); + Assert.Equal(ApiHost, req.RequestUri!.Host); Assert.Equal($"{BasePath}/{Data!.Id}/archive", req.RequestUri!.AbsolutePath); AssertRequestHeaders(req, requestOptions); @@ -145,6 +146,7 @@ public async Task UnarchiveAsync_Works(RequestOptions requestOptions) var handler = new DynamicHttpMessageHandler((req, ct) => { Assert.Equal(HttpMethod.Post, req.Method); + Assert.Equal(ApiHost, req.RequestUri!.Host); Assert.Equal($"{BasePath}/{Data!.Id!}/unarchive", req.RequestUri!.AbsolutePath); AssertRequestHeaders(req, requestOptions); diff --git a/tests/Falu.Tests/Clients/MessageTemplatesServiceClientTests.cs b/tests/Falu.Tests/Clients/MessageTemplatesServiceClientTests.cs index 592cc5e..c567ebf 100644 --- a/tests/Falu.Tests/Clients/MessageTemplatesServiceClientTests.cs +++ b/tests/Falu.Tests/Clients/MessageTemplatesServiceClientTests.cs @@ -150,6 +150,7 @@ public async Task ValidateAsync_Works(RequestOptions requestOptions) var handler = new DynamicHttpMessageHandler((req, ct) => { Assert.Equal(HttpMethod.Post, req.Method); + Assert.Equal(ApiHost, req.RequestUri!.Host); Assert.Equal($"{BasePath}/validate", req.RequestUri!.AbsolutePath); AssertRequestHeaders(req, requestOptions); diff --git a/tests/Falu.Tests/Clients/MessagesServiceClientTests.cs b/tests/Falu.Tests/Clients/MessagesServiceClientTests.cs index a4035b0..1208035 100644 --- a/tests/Falu.Tests/Clients/MessagesServiceClientTests.cs +++ b/tests/Falu.Tests/Clients/MessagesServiceClientTests.cs @@ -161,6 +161,7 @@ public async Task CancelAsync_Works(RequestOptions requestOptions) var handler = new DynamicHttpMessageHandler((req, ct) => { Assert.Equal(HttpMethod.Post, req.Method); + Assert.Equal(ApiHost, req.RequestUri!.Host); Assert.Equal($"{BasePath}/{Data!.Id}/cancel", req.RequestUri!.AbsolutePath); AssertRequestHeaders(req, requestOptions); @@ -189,6 +190,7 @@ public async Task RedactAsync_Works(RequestOptions requestOptions) var handler = new DynamicHttpMessageHandler((req, ct) => { Assert.Equal(HttpMethod.Post, req.Method); + Assert.Equal(ApiHost, req.RequestUri!.Host); Assert.Equal($"{BasePath}/{Data!.Id}/redact", req.RequestUri!.AbsolutePath); AssertRequestHeaders(req, requestOptions); diff --git a/tests/Falu.Tests/Clients/MoneyBalancesServiceClientTests.cs b/tests/Falu.Tests/Clients/MoneyBalancesServiceClientTests.cs index 3c4296e..5e4af6c 100644 --- a/tests/Falu.Tests/Clients/MoneyBalancesServiceClientTests.cs +++ b/tests/Falu.Tests/Clients/MoneyBalancesServiceClientTests.cs @@ -58,6 +58,7 @@ public async Task RefreshAsync_Works(RequestOptions requestOptions) var handler = new DynamicHttpMessageHandler((req, ct) => { Assert.Equal(HttpMethod.Post, req.Method); + Assert.Equal(ApiHost, req.RequestUri!.Host); Assert.Equal($"{BasePath}/refresh", req.RequestUri!.AbsolutePath); AssertRequestHeaders(req, requestOptions); diff --git a/tests/Falu.Tests/Clients/PaymentAuthorizationsServiceClientTests.cs b/tests/Falu.Tests/Clients/PaymentAuthorizationsServiceClientTests.cs index 4397421..a96409d 100644 --- a/tests/Falu.Tests/Clients/PaymentAuthorizationsServiceClientTests.cs +++ b/tests/Falu.Tests/Clients/PaymentAuthorizationsServiceClientTests.cs @@ -103,6 +103,7 @@ public async Task ApproveAsync_Works(RequestOptions requestOptions) var handler = new DynamicHttpMessageHandler((req, ct) => { Assert.Equal(HttpMethod.Post, req.Method); + Assert.Equal(ApiHost, req.RequestUri!.Host); Assert.Equal($"{BasePath}/{Data!.Id}/approve", req.RequestUri!.AbsolutePath); AssertRequestHeaders(req, requestOptions); @@ -135,6 +136,7 @@ public async Task DeclineAsync_Works(RequestOptions requestOptions) var handler = new DynamicHttpMessageHandler((req, ct) => { Assert.Equal(HttpMethod.Post, req.Method); + Assert.Equal(ApiHost, req.RequestUri!.Host); Assert.Equal($"{BasePath}/{Data!.Id}/decline", req.RequestUri!.AbsolutePath); AssertRequestHeaders(req, requestOptions); From 7f00a5b9a065024a2a698eac93d323ed5d0a831c Mon Sep 17 00:00:00 2001 From: Kenneth Mugo Date: Wed, 10 Jul 2024 17:14:11 +0300 Subject: [PATCH 2/5] Added the files upload host name to FaluClientOptions --- src/Falu/FaluClientOptions.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Falu/FaluClientOptions.cs b/src/Falu/FaluClientOptions.cs index 14d8369..88e3a44 100644 --- a/src/Falu/FaluClientOptions.cs +++ b/src/Falu/FaluClientOptions.cs @@ -19,6 +19,11 @@ public class FaluClientOptions /// public int Retries { get; set; } = 2; + /// + /// Host to use when uploading files + /// + public string FilesHost { get; set; } = "files.falu.io"; + /// /// Information about the application. /// It is recommended for use only with third party services for identification purposes. From 3e0b037c2823b1308fc79703458213b612ad4ad8 Mon Sep 17 00:00:00 2001 From: Kenneth Mugo Date: Wed, 10 Jul 2024 17:15:11 +0300 Subject: [PATCH 3/5] Removed FilesUploadHandler --- .../IServiceCollectionExtensions.cs | 9 +------- src/Falu/FilesUploadHandler.cs | 21 ------------------- 2 files changed, 1 insertion(+), 29 deletions(-) delete mode 100644 src/Falu/FilesUploadHandler.cs diff --git a/src/Falu/Extensions/IServiceCollectionExtensions.cs b/src/Falu/Extensions/IServiceCollectionExtensions.cs index cdc3b0f..b3c8a94 100644 --- a/src/Falu/Extensions/IServiceCollectionExtensions.cs +++ b/src/Falu/Extensions/IServiceCollectionExtensions.cs @@ -73,15 +73,9 @@ public static IHttpClientBuilder AddFalu< services.Configure(configure); } - // register post configuration for validation purposes - services.ConfigureOptions>(); - // get the version from the assembly var productVersion = typeof(TClient).Assembly.GetName().Version!.ToString(3); - // register a custom delegating handler to upload files using files.falu.io host - services.AddTransient(); - // setup client var builder = services.AddHttpClient() .ConfigureHttpClient((provider, client) => @@ -91,8 +85,7 @@ public static IHttpClientBuilder AddFalu< // populate the User-Agent value for the SDK/library client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("falu-dotnet", productVersion)); - }) - .AddHttpMessageHandler(); + }); // setup retries builder.AddPolicyHandler((sp, request) => diff --git a/src/Falu/FilesUploadHandler.cs b/src/Falu/FilesUploadHandler.cs deleted file mode 100644 index bbddac9..0000000 --- a/src/Falu/FilesUploadHandler.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Falu; - -/// -/// Modifies the request uri for file uploads to use files.falu.io host -/// -internal class FilesUploadHandler : DelegatingHandler -{ - private readonly Uri FilesUploadUri = new("https://files.falu.io/v1/files"); - - protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - var path = request.RequestUri!.AbsolutePath; - if (request.Method == HttpMethod.Post && FilesUploadUri.AbsolutePath.Equals(path)) - { - // Send the request to files.falu.io - request.RequestUri = FilesUploadUri; - } - - return base.SendAsync(request, cancellationToken); - } -} From cf8708f684d8ddcba21bc3cfde985a76805ee495 Mon Sep 17 00:00:00 2001 From: Kenneth Mugo Date: Wed, 10 Jul 2024 17:19:36 +0300 Subject: [PATCH 4/5] Use the FilesHost defined in the config to construct the uri for uploading files --- src/Falu/Files/FilesServiceClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Falu/Files/FilesServiceClient.cs b/src/Falu/Files/FilesServiceClient.cs index ff628cf..a7a9baa 100644 --- a/src/Falu/Files/FilesServiceClient.cs +++ b/src/Falu/Files/FilesServiceClient.cs @@ -79,7 +79,7 @@ public virtual Task> CreateAsync(FileCreateOptions option content.Add(new StringContent(options.Expires!.Value.ToString("O")), "expires"); } - var uri = MakePath(); + var uri = new UriBuilder(BackChannel.BaseAddress!) { Host = Options.FilesHost, Path = MakePath() }.ToString(); return RequestResourceAsync(uri, HttpMethod.Post, content, requestOptions, cancellationToken); } } From 07671ed03c094d279bfecc870faa27638447a3b6 Mon Sep 17 00:00:00 2001 From: Kenneth Mugo Date: Wed, 10 Jul 2024 17:30:50 +0300 Subject: [PATCH 5/5] Restored accidentally removed code block --- src/Falu/Extensions/IServiceCollectionExtensions.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Falu/Extensions/IServiceCollectionExtensions.cs b/src/Falu/Extensions/IServiceCollectionExtensions.cs index b3c8a94..26ad029 100644 --- a/src/Falu/Extensions/IServiceCollectionExtensions.cs +++ b/src/Falu/Extensions/IServiceCollectionExtensions.cs @@ -1,13 +1,11 @@ using Falu; using Falu.Core; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; using Polly; using Polly.Contrib.WaitAndRetry; using Polly.Retry; using System.Diagnostics.CodeAnalysis; using System.Net; -using System.Net.Http; using System.Net.Http.Headers; namespace Microsoft.Extensions.DependencyInjection; @@ -73,6 +71,9 @@ public static IHttpClientBuilder AddFalu< services.Configure(configure); } + // register post configuration for validation purposes + services.ConfigureOptions>(); + // get the version from the assembly var productVersion = typeof(TClient).Assembly.GetName().Version!.ToString(3);