From f517fd469b23a121fd059a9590c82e57b4039492 Mon Sep 17 00:00:00 2001 From: "davidovrelid.com" Date: Wed, 8 Jan 2025 12:49:45 +0100 Subject: [PATCH 1/2] added DelegationHandler for AzureDevOps response --- .../AzureDevOpsTokenDelegatingHandler.cs | 24 +++++++ .../TypedHttpClientRegistration.cs | 2 +- .../AzureDevOpsTokenDelegatingHandlerTests.cs | 70 +++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 backend/src/Designer/TypedHttpClients/DelegatingHandlers/AzureDevOpsTokenDelegatingHandler.cs create mode 100644 backend/tests/Designer.Tests/TypedHttpClients/DelegatingHandlers/AzureDevOpsTokenDelegatingHandlerTests.cs diff --git a/backend/src/Designer/TypedHttpClients/DelegatingHandlers/AzureDevOpsTokenDelegatingHandler.cs b/backend/src/Designer/TypedHttpClients/DelegatingHandlers/AzureDevOpsTokenDelegatingHandler.cs new file mode 100644 index 00000000000..6832d5d11a6 --- /dev/null +++ b/backend/src/Designer/TypedHttpClients/DelegatingHandlers/AzureDevOpsTokenDelegatingHandler.cs @@ -0,0 +1,24 @@ +using System.Net; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace Altinn.Studio.Designer.TypedHttpClients.DelegatingHandlers; + +public class AzureDevOpsTokenDelegatingHandler : DelegatingHandler +{ + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + HttpResponseMessage response = await base.SendAsync(request, cancellationToken); + + if (response.StatusCode == HttpStatusCode.Unauthorized) + { + return new HttpResponseMessage(HttpStatusCode.InternalServerError) + { + Content = new StringContent("Failed to interact with Azure DevOps. Contact system support.") + }; + } + + return response; + } +} diff --git a/backend/src/Designer/TypedHttpClients/TypedHttpClientRegistration.cs b/backend/src/Designer/TypedHttpClients/TypedHttpClientRegistration.cs index 7b387507409..75fd6512be7 100644 --- a/backend/src/Designer/TypedHttpClients/TypedHttpClientRegistration.cs +++ b/backend/src/Designer/TypedHttpClients/TypedHttpClientRegistration.cs @@ -70,7 +70,7 @@ private static IHttpClientBuilder AddAzureDevOpsTypedHttpClient(this IServiceCol client.BaseAddress = new Uri($"{azureDevOpsSettings.BaseUri}build/builds/"); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", token); - }).AddHttpMessageHandler(); + }).AddHttpMessageHandler().AddHttpMessageHandler(); } private static IHttpClientBuilder AddKubernetesWrapperTypedHttpClient(this IServiceCollection services) diff --git a/backend/tests/Designer.Tests/TypedHttpClients/DelegatingHandlers/AzureDevOpsTokenDelegatingHandlerTests.cs b/backend/tests/Designer.Tests/TypedHttpClients/DelegatingHandlers/AzureDevOpsTokenDelegatingHandlerTests.cs new file mode 100644 index 00000000000..ee54efa5b68 --- /dev/null +++ b/backend/tests/Designer.Tests/TypedHttpClients/DelegatingHandlers/AzureDevOpsTokenDelegatingHandlerTests.cs @@ -0,0 +1,70 @@ +using System.Net; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Altinn.Studio.Designer.TypedHttpClients.DelegatingHandlers; +using Moq; +using Moq.Protected; +using Xunit; + +public class AzureDevOpsTokenDelegatingHandlerTests +{ + [Fact] + public async Task SendAsync_WhenUnauthorized_ThrowsHttpRequestException() + { + // Arrange + var mockInnerHandler = new Mock(); + + mockInnerHandler.Protected() + .Setup>("SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny()) + .ReturnsAsync(new HttpResponseMessage + { + StatusCode = HttpStatusCode.Unauthorized + }); + + var handler = new AzureDevOpsTokenDelegatingHandler + { + InnerHandler = mockInnerHandler.Object + }; + + HttpClient httpClient = new HttpClient(handler); + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://dev.azure.com"); + + HttpResponseMessage response = await httpClient.SendAsync(request); + string content = await response.Content.ReadAsStringAsync(); + + Assert.Equal("Failed to interact with Azure DevOps. Contact system support.", content); + Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); + } + + [Fact] + public async Task SendAsync_WhenSuccessful_ReturnsResponse() + { + var mockInnerHandler = new Mock(); + + mockInnerHandler.Protected() + .Setup>("SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny()) + .ReturnsAsync(new HttpResponseMessage + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent("Success") + }); + + var handler = new AzureDevOpsTokenDelegatingHandler + { + InnerHandler = mockInnerHandler.Object + }; + + HttpClient httpClient = new HttpClient(handler); + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://dev.azure.com"); + + HttpResponseMessage response = await httpClient.SendAsync(request); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal("Success", await response.Content.ReadAsStringAsync()); + } +} From 24d6f7dff8cb82c1a563645f9eae807f73806fa1 Mon Sep 17 00:00:00 2001 From: "davidovrelid.com" Date: Wed, 8 Jan 2025 15:01:11 +0100 Subject: [PATCH 2/2] register correctly --- .../src/Designer/TypedHttpClients/TypedHttpClientRegistration.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/Designer/TypedHttpClients/TypedHttpClientRegistration.cs b/backend/src/Designer/TypedHttpClients/TypedHttpClientRegistration.cs index 75fd6512be7..b20fe66cad4 100644 --- a/backend/src/Designer/TypedHttpClients/TypedHttpClientRegistration.cs +++ b/backend/src/Designer/TypedHttpClients/TypedHttpClientRegistration.cs @@ -38,6 +38,7 @@ public static class TypedHttpClientRegistration public static IServiceCollection RegisterTypedHttpClients(this IServiceCollection services, IConfiguration config) { services.AddHttpClient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddAzureDevOpsTypedHttpClient(config);