From 33f0813f45d902d494651ea01e1d29302e69b153 Mon Sep 17 00:00:00 2001 From: Terence Fan Date: Wed, 11 Sep 2024 16:05:13 +0800 Subject: [PATCH] Move Rest classes to Management project --- .../MicrosoftEntraAccessKey.Rest.cs | 117 ++++++++++++++++++ .../MicrosoftEntra/MicrosoftEntraAccessKey.cs | 4 +- .../Rest/BinaryPayloadContentBuilder.cs | 2 +- .../Rest/BinaryPayloadMessageContent.cs | 3 +- .../Rest/IPayloadContentBuilder.cs | 2 +- .../Rest/JsonPayloadContentBuilder.cs | 2 +- .../Rest/JsonPayloadMessageContent.cs | 2 +- .../Rest/PayloadMessage.cs | 2 +- .../Rest}/RestApiAccessTokenGenerator.cs | 2 +- .../Rest}/RestApiEndpoint.cs | 2 +- .../Rest}/RestClient.cs | 2 +- .../Rest/BinaryPayloadMessageContentTest.cs | 3 +- .../Rest/JsonPayloadMessageContentTest.cs | 3 +- .../{ => Rest}/RestApiProviderFacts.cs | 8 +- .../Rest}/RestClientFacts.cs | 3 +- 15 files changed, 137 insertions(+), 20 deletions(-) create mode 100644 src/Microsoft.Azure.SignalR.Common/Auth/MicrosoftEntra/MicrosoftEntraAccessKey.Rest.cs rename src/{Microsoft.Azure.SignalR.Common/Utilities => Microsoft.Azure.SignalR.Management}/Rest/BinaryPayloadContentBuilder.cs (94%) rename src/{Microsoft.Azure.SignalR.Common/Utilities => Microsoft.Azure.SignalR.Management}/Rest/BinaryPayloadMessageContent.cs (97%) rename src/{Microsoft.Azure.SignalR.Common/Utilities => Microsoft.Azure.SignalR.Management}/Rest/IPayloadContentBuilder.cs (87%) rename src/{Microsoft.Azure.SignalR.Common/Utilities => Microsoft.Azure.SignalR.Management}/Rest/JsonPayloadContentBuilder.cs (94%) rename src/{Microsoft.Azure.SignalR.Common/Utilities => Microsoft.Azure.SignalR.Management}/Rest/JsonPayloadMessageContent.cs (97%) rename src/{Microsoft.Azure.SignalR.Common/Utilities => Microsoft.Azure.SignalR.Management}/Rest/PayloadMessage.cs (86%) rename src/{Microsoft.Azure.SignalR.Common/Utilities => Microsoft.Azure.SignalR.Management/Rest}/RestApiAccessTokenGenerator.cs (96%) rename src/{Microsoft.Azure.SignalR.Common/Utilities => Microsoft.Azure.SignalR.Management/Rest}/RestApiEndpoint.cs (92%) rename src/{Microsoft.Azure.SignalR.Common/Utilities => Microsoft.Azure.SignalR.Management/Rest}/RestClient.cs (99%) rename test/{Microsoft.Azure.SignalR.Common.Tests/Utils => Microsoft.Azure.SignalR.Management.Tests}/Rest/BinaryPayloadMessageContentTest.cs (97%) rename test/{Microsoft.Azure.SignalR.Common.Tests/Utils => Microsoft.Azure.SignalR.Management.Tests}/Rest/JsonPayloadMessageContentTest.cs (95%) rename test/Microsoft.Azure.SignalR.Management.Tests/{ => Rest}/RestApiProviderFacts.cs (88%) rename test/{Microsoft.Azure.SignalR.Common.Tests/RestClients => Microsoft.Azure.SignalR.Management.Tests/Rest}/RestClientFacts.cs (93%) diff --git a/src/Microsoft.Azure.SignalR.Common/Auth/MicrosoftEntra/MicrosoftEntraAccessKey.Rest.cs b/src/Microsoft.Azure.SignalR.Common/Auth/MicrosoftEntra/MicrosoftEntraAccessKey.Rest.cs new file mode 100644 index 000000000..6cd65f114 --- /dev/null +++ b/src/Microsoft.Azure.SignalR.Common/Auth/MicrosoftEntra/MicrosoftEntraAccessKey.Rest.cs @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Azure.SignalR.Common; + +namespace Microsoft.Azure.SignalR; + +#nullable enable + +internal partial class MicrosoftEntraAccessKey +{ + internal sealed class RestApiEndpoint + { + public string Audience { get; } + + public string Token { get; } + + public RestApiEndpoint(string endpoint, string token) + { + Audience = endpoint; + Token = token; + } + } + + internal sealed class RestClient + { + private readonly IHttpClientFactory _httpClientFactory; + + public RestClient(IHttpClientFactory httpClientFactory) + { + _httpClientFactory = httpClientFactory; + } + + public Task SendAsync( + RestApiEndpoint api, + HttpMethod httpMethod, + Func>? handleExpectedResponseAsync = null, + CancellationToken cancellationToken = default) + { + return SendAsyncCore(Constants.HttpClientNames.UserDefault, api, httpMethod, handleExpectedResponseAsync, cancellationToken); + } + + private async Task ThrowExceptionOnResponseFailureAsync(HttpResponseMessage response) + { + if (response.IsSuccessStatusCode) + { + return; + } + + var detail = await response.Content.ReadAsStringAsync(); + +#if NET5_0_OR_GREATER + var innerException = new HttpRequestException( + $"Response status code does not indicate success: {(int)response.StatusCode} ({response.ReasonPhrase})", null, response.StatusCode); +#else + var innerException = new HttpRequestException( + $"Response status code does not indicate success: {(int)response.StatusCode} ({response.ReasonPhrase})"); +#endif + throw response.StatusCode switch + { + HttpStatusCode.BadRequest => new AzureSignalRInvalidArgumentException(response.RequestMessage?.RequestUri?.ToString(), innerException, detail), + HttpStatusCode.Unauthorized => new AzureSignalRUnauthorizedException(response.RequestMessage?.RequestUri?.ToString(), innerException), + HttpStatusCode.NotFound => new AzureSignalRInaccessibleEndpointException(response.RequestMessage?.RequestUri?.ToString(), innerException), + _ => new AzureSignalRRuntimeException(response.RequestMessage?.RequestUri?.ToString(), innerException), + }; + } + + private async Task SendAsyncCore( + string httpClientName, + RestApiEndpoint api, + HttpMethod httpMethod, + Func>? handleExpectedResponseAsync = null, + CancellationToken cancellationToken = default) + { + using var httpClient = _httpClientFactory.CreateClient(httpClientName); + using var request = BuildRequest(api, httpMethod); + + try + { + using var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken); + if (handleExpectedResponseAsync == null) + { + await ThrowExceptionOnResponseFailureAsync(response); + } + else + { + if (!await handleExpectedResponseAsync(response)) + { + await ThrowExceptionOnResponseFailureAsync(response); + } + } + } + catch (HttpRequestException ex) + { + throw new AzureSignalRException($"An error happened when making request to {request.RequestUri}", ex); + } + } + + private HttpRequestMessage BuildRequest(RestApiEndpoint api, HttpMethod httpMethod) + { + return GenerateHttpRequest(api.Audience, httpMethod, api.Token); + } + + private HttpRequestMessage GenerateHttpRequest(string url, HttpMethod httpMethod, string tokenString) + { + var request = new HttpRequestMessage(httpMethod, new Uri(url)); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokenString); + return request; + } + } +} diff --git a/src/Microsoft.Azure.SignalR.Common/Auth/MicrosoftEntra/MicrosoftEntraAccessKey.cs b/src/Microsoft.Azure.SignalR.Common/Auth/MicrosoftEntra/MicrosoftEntraAccessKey.cs index 76df07598..77cd76e6c 100644 --- a/src/Microsoft.Azure.SignalR.Common/Auth/MicrosoftEntra/MicrosoftEntraAccessKey.cs +++ b/src/Microsoft.Azure.SignalR.Common/Auth/MicrosoftEntra/MicrosoftEntraAccessKey.cs @@ -17,7 +17,7 @@ namespace Microsoft.Azure.SignalR; -internal class MicrosoftEntraAccessKey : AccessKey +internal partial class MicrosoftEntraAccessKey : AccessKey { internal static readonly TimeSpan GetAccessKeyTimeout = TimeSpan.FromSeconds(100); @@ -169,7 +169,7 @@ private async Task GetAccessKeyInternalAsync(string accessToken, CancellationTok { var api = new RestApiEndpoint(GetAccessKeyUrl, accessToken); - await new RestClient().SendAsync( + await new RestClient(HttpClientFactory.Instance).SendAsync( api, HttpMethod.Get, handleExpectedResponseAsync: HandleHttpResponseAsync, diff --git a/src/Microsoft.Azure.SignalR.Common/Utilities/Rest/BinaryPayloadContentBuilder.cs b/src/Microsoft.Azure.SignalR.Management/Rest/BinaryPayloadContentBuilder.cs similarity index 94% rename from src/Microsoft.Azure.SignalR.Common/Utilities/Rest/BinaryPayloadContentBuilder.cs rename to src/Microsoft.Azure.SignalR.Management/Rest/BinaryPayloadContentBuilder.cs index f1d29618d..65cffae7e 100644 --- a/src/Microsoft.Azure.SignalR.Common/Utilities/Rest/BinaryPayloadContentBuilder.cs +++ b/src/Microsoft.Azure.SignalR.Management/Rest/BinaryPayloadContentBuilder.cs @@ -7,7 +7,7 @@ #nullable enable -namespace Microsoft.Azure.SignalR.Common +namespace Microsoft.Azure.SignalR.Management { internal class BinaryPayloadContentBuilder : IPayloadContentBuilder { diff --git a/src/Microsoft.Azure.SignalR.Common/Utilities/Rest/BinaryPayloadMessageContent.cs b/src/Microsoft.Azure.SignalR.Management/Rest/BinaryPayloadMessageContent.cs similarity index 97% rename from src/Microsoft.Azure.SignalR.Common/Utilities/Rest/BinaryPayloadMessageContent.cs rename to src/Microsoft.Azure.SignalR.Management/Rest/BinaryPayloadMessageContent.cs index 5c91aad50..ecd49aa27 100644 --- a/src/Microsoft.Azure.SignalR.Common/Utilities/Rest/BinaryPayloadMessageContent.cs +++ b/src/Microsoft.Azure.SignalR.Management/Rest/BinaryPayloadMessageContent.cs @@ -10,10 +10,9 @@ using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; -using MessagePack; using Microsoft.AspNetCore.SignalR.Protocol; -namespace Microsoft.Azure.SignalR.Common +namespace Microsoft.Azure.SignalR.Management { internal class BinaryPayloadMessageContent : HttpContent { diff --git a/src/Microsoft.Azure.SignalR.Common/Utilities/Rest/IPayloadContentBuilder.cs b/src/Microsoft.Azure.SignalR.Management/Rest/IPayloadContentBuilder.cs similarity index 87% rename from src/Microsoft.Azure.SignalR.Common/Utilities/Rest/IPayloadContentBuilder.cs rename to src/Microsoft.Azure.SignalR.Management/Rest/IPayloadContentBuilder.cs index cbe9a12da..f84ff4998 100644 --- a/src/Microsoft.Azure.SignalR.Common/Utilities/Rest/IPayloadContentBuilder.cs +++ b/src/Microsoft.Azure.SignalR.Management/Rest/IPayloadContentBuilder.cs @@ -5,7 +5,7 @@ #nullable enable -namespace Microsoft.Azure.SignalR.Common +namespace Microsoft.Azure.SignalR.Management { internal interface IPayloadContentBuilder { diff --git a/src/Microsoft.Azure.SignalR.Common/Utilities/Rest/JsonPayloadContentBuilder.cs b/src/Microsoft.Azure.SignalR.Management/Rest/JsonPayloadContentBuilder.cs similarity index 94% rename from src/Microsoft.Azure.SignalR.Common/Utilities/Rest/JsonPayloadContentBuilder.cs rename to src/Microsoft.Azure.SignalR.Management/Rest/JsonPayloadContentBuilder.cs index 8cf1ffb0d..0b4a31582 100644 --- a/src/Microsoft.Azure.SignalR.Common/Utilities/Rest/JsonPayloadContentBuilder.cs +++ b/src/Microsoft.Azure.SignalR.Management/Rest/JsonPayloadContentBuilder.cs @@ -5,7 +5,7 @@ using Azure.Core.Serialization; #nullable enable -namespace Microsoft.Azure.SignalR.Common +namespace Microsoft.Azure.SignalR.Management { internal class JsonPayloadContentBuilder : IPayloadContentBuilder { diff --git a/src/Microsoft.Azure.SignalR.Common/Utilities/Rest/JsonPayloadMessageContent.cs b/src/Microsoft.Azure.SignalR.Management/Rest/JsonPayloadMessageContent.cs similarity index 97% rename from src/Microsoft.Azure.SignalR.Common/Utilities/Rest/JsonPayloadMessageContent.cs rename to src/Microsoft.Azure.SignalR.Management/Rest/JsonPayloadMessageContent.cs index e0e974e6f..f4e1d10c5 100644 --- a/src/Microsoft.Azure.SignalR.Common/Utilities/Rest/JsonPayloadMessageContent.cs +++ b/src/Microsoft.Azure.SignalR.Management/Rest/JsonPayloadMessageContent.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; using Azure.Core.Serialization; -namespace Microsoft.Azure.SignalR +namespace Microsoft.Azure.SignalR.Management { internal class JsonPayloadMessageContent : HttpContent { diff --git a/src/Microsoft.Azure.SignalR.Common/Utilities/Rest/PayloadMessage.cs b/src/Microsoft.Azure.SignalR.Management/Rest/PayloadMessage.cs similarity index 86% rename from src/Microsoft.Azure.SignalR.Common/Utilities/Rest/PayloadMessage.cs rename to src/Microsoft.Azure.SignalR.Management/Rest/PayloadMessage.cs index 8dff062b0..8837f67bf 100644 --- a/src/Microsoft.Azure.SignalR.Common/Utilities/Rest/PayloadMessage.cs +++ b/src/Microsoft.Azure.SignalR.Management/Rest/PayloadMessage.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -namespace Microsoft.Azure.SignalR +namespace Microsoft.Azure.SignalR.Management { internal class PayloadMessage { diff --git a/src/Microsoft.Azure.SignalR.Common/Utilities/RestApiAccessTokenGenerator.cs b/src/Microsoft.Azure.SignalR.Management/Rest/RestApiAccessTokenGenerator.cs similarity index 96% rename from src/Microsoft.Azure.SignalR.Common/Utilities/RestApiAccessTokenGenerator.cs rename to src/Microsoft.Azure.SignalR.Management/Rest/RestApiAccessTokenGenerator.cs index fcdb4ed8c..589826768 100644 --- a/src/Microsoft.Azure.SignalR.Common/Utilities/RestApiAccessTokenGenerator.cs +++ b/src/Microsoft.Azure.SignalR.Management/Rest/RestApiAccessTokenGenerator.cs @@ -5,7 +5,7 @@ using System.Security.Claims; using System.Threading.Tasks; -namespace Microsoft.Azure.SignalR +namespace Microsoft.Azure.SignalR.Management { internal class RestApiAccessTokenGenerator { diff --git a/src/Microsoft.Azure.SignalR.Common/Utilities/RestApiEndpoint.cs b/src/Microsoft.Azure.SignalR.Management/Rest/RestApiEndpoint.cs similarity index 92% rename from src/Microsoft.Azure.SignalR.Common/Utilities/RestApiEndpoint.cs rename to src/Microsoft.Azure.SignalR.Management/Rest/RestApiEndpoint.cs index 585046bf0..b01d2dbc4 100644 --- a/src/Microsoft.Azure.SignalR.Common/Utilities/RestApiEndpoint.cs +++ b/src/Microsoft.Azure.SignalR.Management/Rest/RestApiEndpoint.cs @@ -5,7 +5,7 @@ using Microsoft.Extensions.Primitives; -namespace Microsoft.Azure.SignalR +namespace Microsoft.Azure.SignalR.Management { internal class RestApiEndpoint { diff --git a/src/Microsoft.Azure.SignalR.Common/Utilities/RestClient.cs b/src/Microsoft.Azure.SignalR.Management/Rest/RestClient.cs similarity index 99% rename from src/Microsoft.Azure.SignalR.Common/Utilities/RestClient.cs rename to src/Microsoft.Azure.SignalR.Management/Rest/RestClient.cs index 1a22eec55..1b457ce33 100644 --- a/src/Microsoft.Azure.SignalR.Common/Utilities/RestClient.cs +++ b/src/Microsoft.Azure.SignalR.Management/Rest/RestClient.cs @@ -15,7 +15,7 @@ #nullable enable -namespace Microsoft.Azure.SignalR +namespace Microsoft.Azure.SignalR.Management { internal class RestClient { diff --git a/test/Microsoft.Azure.SignalR.Common.Tests/Utils/Rest/BinaryPayloadMessageContentTest.cs b/test/Microsoft.Azure.SignalR.Management.Tests/Rest/BinaryPayloadMessageContentTest.cs similarity index 97% rename from test/Microsoft.Azure.SignalR.Common.Tests/Utils/Rest/BinaryPayloadMessageContentTest.cs rename to test/Microsoft.Azure.SignalR.Management.Tests/Rest/BinaryPayloadMessageContentTest.cs index 67d0e6e33..dd963800d 100644 --- a/test/Microsoft.Azure.SignalR.Common.Tests/Utils/Rest/BinaryPayloadMessageContentTest.cs +++ b/test/Microsoft.Azure.SignalR.Management.Tests/Rest/BinaryPayloadMessageContentTest.cs @@ -6,12 +6,11 @@ using System.Collections.Generic; using System.IO; using System.Text; -using System.Threading.Tasks; using MessagePack; using Microsoft.AspNetCore.SignalR.Protocol; using Xunit; -namespace Microsoft.Azure.SignalR.Common.Tests +namespace Microsoft.Azure.SignalR.Management.Tests { public class BinaryPayloadMessageContentTest { diff --git a/test/Microsoft.Azure.SignalR.Common.Tests/Utils/Rest/JsonPayloadMessageContentTest.cs b/test/Microsoft.Azure.SignalR.Management.Tests/Rest/JsonPayloadMessageContentTest.cs similarity index 95% rename from test/Microsoft.Azure.SignalR.Common.Tests/Utils/Rest/JsonPayloadMessageContentTest.cs rename to test/Microsoft.Azure.SignalR.Management.Tests/Rest/JsonPayloadMessageContentTest.cs index 23dee8190..f139028a1 100644 --- a/test/Microsoft.Azure.SignalR.Common.Tests/Utils/Rest/JsonPayloadMessageContentTest.cs +++ b/test/Microsoft.Azure.SignalR.Management.Tests/Rest/JsonPayloadMessageContentTest.cs @@ -7,9 +7,10 @@ using System.Linq; using System.Threading.Tasks; using Azure.Core.Serialization; +using Microsoft.Azure.SignalR; using Xunit; -namespace Microsoft.Azure.SignalR.Common.Tests +namespace Microsoft.Azure.SignalR.Management.Tests { public class JsonPayloadMessageContentTest { diff --git a/test/Microsoft.Azure.SignalR.Management.Tests/RestApiProviderFacts.cs b/test/Microsoft.Azure.SignalR.Management.Tests/Rest/RestApiProviderFacts.cs similarity index 88% rename from test/Microsoft.Azure.SignalR.Management.Tests/RestApiProviderFacts.cs rename to test/Microsoft.Azure.SignalR.Management.Tests/Rest/RestApiProviderFacts.cs index 68991a25f..7ae148310 100644 --- a/test/Microsoft.Azure.SignalR.Management.Tests/RestApiProviderFacts.cs +++ b/test/Microsoft.Azure.SignalR.Management.Tests/Rest/RestApiProviderFacts.cs @@ -8,7 +8,7 @@ using Microsoft.Azure.SignalR.Tests; using Xunit; -namespace Microsoft.Azure.SignalR.Management.Tests +namespace Microsoft.Azure.SignalR.Management.Tests.Rest { public class RestApiProviderFacts { @@ -25,7 +25,7 @@ internal async Task RestApiTest(Task task, string expectedAudie { var api = await task; var token = JwtTokenHelper.JwtHandler.ReadJwtToken(api.Token); - string expectedTokenString = JwtTokenHelper.GenerateExpectedAccessToken(token, expectedAudience, _accessKey); + var expectedTokenString = JwtTokenHelper.GenerateExpectedAccessToken(token, expectedAudience, _accessKey); Assert.Equal(expectedAudience, api.Audience); Assert.Equal(expectedTokenString, api.Token); @@ -45,8 +45,8 @@ from pair in GetTestDataByContext(context) private static IEnumerable GetTestDataByContext((string appName, string hubName, string userId, string groupName, string connectionId) context) { - string commonEndpoint = $"{_endpoint}/api/hubs/{Uri.EscapeDataString(context.hubName.ToLowerInvariant())}"; - string commonQueryString = $"application={Uri.EscapeDataString(context.appName.ToLowerInvariant())}&api-version=2022-06-01"; + var commonEndpoint = $"{_endpoint}/api/hubs/{Uri.EscapeDataString(context.hubName.ToLowerInvariant())}"; + var commonQueryString = $"application={Uri.EscapeDataString(context.appName.ToLowerInvariant())}&api-version=2022-06-01"; yield return new object[] { _restApiProvider.GetBroadcastEndpointAsync(context.appName, context.hubName), $"{commonEndpoint}/:send?{commonQueryString}" }; yield return new object[] { _restApiProvider.GetSendToUserEndpointAsync(context.appName, context.hubName, context.userId), $"{commonEndpoint}/users/{Uri.EscapeDataString(context.userId)}/:send?{commonQueryString}" }; yield return new object[] { _restApiProvider.GetSendToGroupEndpointAsync(context.appName, context.hubName, context.groupName), $"{commonEndpoint}/groups/{Uri.EscapeDataString(context.groupName)}/:send?{commonQueryString}" }; diff --git a/test/Microsoft.Azure.SignalR.Common.Tests/RestClients/RestClientFacts.cs b/test/Microsoft.Azure.SignalR.Management.Tests/Rest/RestClientFacts.cs similarity index 93% rename from test/Microsoft.Azure.SignalR.Common.Tests/RestClients/RestClientFacts.cs rename to test/Microsoft.Azure.SignalR.Management.Tests/Rest/RestClientFacts.cs index 63d0922b4..0f0fd6443 100644 --- a/test/Microsoft.Azure.SignalR.Common.Tests/RestClients/RestClientFacts.cs +++ b/test/Microsoft.Azure.SignalR.Management.Tests/Rest/RestClientFacts.cs @@ -4,11 +4,12 @@ using System.Net; using System.Net.Http; using System.Threading.Tasks; +using Microsoft.Azure.SignalR.Common; using Microsoft.Azure.SignalR.Tests.Common; using Microsoft.Extensions.DependencyInjection; using Xunit; -namespace Microsoft.Azure.SignalR.Common.Tests.RestClients +namespace Microsoft.Azure.SignalR.Management.Tests.Rest { public class RestClientFacts {