diff --git a/src/Microsoft.Graph.Core/Microsoft.Graph.Core.csproj b/src/Microsoft.Graph.Core/Microsoft.Graph.Core.csproj index 8878232fe..0c0e0d0a0 100644 --- a/src/Microsoft.Graph.Core/Microsoft.Graph.Core.csproj +++ b/src/Microsoft.Graph.Core/Microsoft.Graph.Core.csproj @@ -101,10 +101,10 @@ - - - - - + + + + + diff --git a/src/Microsoft.Graph.Core/Requests/GraphClientFactory.cs b/src/Microsoft.Graph.Core/Requests/GraphClientFactory.cs index 2f97932d3..53479ed67 100644 --- a/src/Microsoft.Graph.Core/Requests/GraphClientFactory.cs +++ b/src/Microsoft.Graph.Core/Requests/GraphClientFactory.cs @@ -9,6 +9,7 @@ namespace Microsoft.Graph using System.Net; using System.Net.Http; using System.Net.Http.Headers; + using Microsoft.Kiota.Http.HttpClientLibrary; using Microsoft.Kiota.Http.HttpClientLibrary.Middleware; /// @@ -110,13 +111,13 @@ public static HttpClient Create( /// public static IList CreateDefaultHandlers(GraphClientOptions graphClientOptions = null) { - return new List { - new GraphTelemetryHandler(graphClientOptions), - new OdataQueryHandler(), - new CompressionHandler(), - new RetryHandler(), - new RedirectHandler() - }; + var handlers = KiotaClientFactory.CreateDefaultHandlers(); + handlers.Add(new GraphTelemetryHandler(graphClientOptions));// add the telemetry handler last. + + // TODO remove this once https://github.com/microsoft/kiota/issues/598 is closed. + handlers.Insert(0, new CompressionHandler()); + + return handlers; } /// diff --git a/src/Microsoft.Graph.Core/Requests/Middleware/GraphTelemetryHandler.cs b/src/Microsoft.Graph.Core/Requests/Middleware/GraphTelemetryHandler.cs index 421662aeb..71114046c 100644 --- a/src/Microsoft.Graph.Core/Requests/Middleware/GraphTelemetryHandler.cs +++ b/src/Microsoft.Graph.Core/Requests/Middleware/GraphTelemetryHandler.cs @@ -72,8 +72,10 @@ protected override Task SendAsync(HttpRequestMessage httpRe features += " runtimeEnvironment=" + RuntimeInformation.FrameworkDescription + ";"; var telemetryString = $"{serviceLibraryString} {coreLibraryString} (featureUsage={Enum.Format(typeof(FeatureFlag), httpRequest.GetFeatureFlags(), "x")};{features})"; - httpRequest.Headers.Add(CoreConstants.Headers.SdkVersionHeaderName, telemetryString); - httpRequest.Headers.Add(CoreConstants.Headers.ClientRequestId, Guid.NewGuid().ToString()); + if(!httpRequest.Headers.Contains(CoreConstants.Headers.SdkVersionHeaderName)) + httpRequest.Headers.Add(CoreConstants.Headers.SdkVersionHeaderName, telemetryString); + if (!httpRequest.Headers.Contains(CoreConstants.Headers.ClientRequestId)) + httpRequest.Headers.Add(CoreConstants.Headers.ClientRequestId, Guid.NewGuid().ToString()); return base.SendAsync(httpRequest, cancellationToken); } diff --git a/src/Microsoft.Graph.Core/Requests/Middleware/OdataQueryHandler.cs b/src/Microsoft.Graph.Core/Requests/Middleware/OdataQueryHandler.cs deleted file mode 100644 index 3409a8394..000000000 --- a/src/Microsoft.Graph.Core/Requests/Middleware/OdataQueryHandler.cs +++ /dev/null @@ -1,59 +0,0 @@ -// ------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information. -// ------------------------------------------------------------------------------ - -namespace Microsoft.Graph -{ - using Microsoft.Kiota.Http.HttpClientLibrary.Extensions; - using System; - using System.Net.Http; - using System.Text.RegularExpressions; - using System.Threading; - using System.Threading.Tasks; - - /// - /// A implementation that handles compression. - /// - public class OdataQueryHandler : DelegatingHandler - { - private readonly ODataQueryHandlerOption odataQueryHandlerOption; - private readonly Regex odataQueryRegex = new Regex("([^$])(count|deltatoken|expand|filter|format|orderby|search|select|skip|skiptoken|top)=",RegexOptions.Compiled | RegexOptions.IgnoreCase); - - /// - /// The constructor - /// - /// The to use - public OdataQueryHandler(ODataQueryHandlerOption handlerOption = null) - { - this.odataQueryHandlerOption = handlerOption ?? new ODataQueryHandlerOption(); - } - - /// - /// Sends a HTTP request. - /// - /// The to be sent. - /// The for the request. - /// - protected override Task SendAsync(HttpRequestMessage httpRequest, CancellationToken cancellationToken) - { - if (httpRequest == null) - throw new ArgumentNullException(nameof(httpRequest)); - - // Check if the request has a preconfigured option otherwise use the one provided in this instance - var odataQueryOption = httpRequest.GetRequestOption() ?? odataQueryHandlerOption; - // If the request is replacable, just do it - if (odataQueryOption.ShouldReplace(httpRequest)) - { - var queryString = string.Empty; - if (httpRequest.RequestUri.Query != null && httpRequest.RequestUri.Query.Length > 1) - // We insert and remove the ? sign so we can make no dollar mandatory and avoid adding a second dollar when already here - queryString = odataQueryRegex.Replace("?" + httpRequest.RequestUri.Query, "$1$$$2=")[1..]; - - // replace the uri with the new query options - httpRequest.RequestUri = new UriBuilder(httpRequest.RequestUri) { Query = queryString }.Uri; - } - - return base.SendAsync(httpRequest, cancellationToken); - } - } -} \ No newline at end of file diff --git a/src/Microsoft.Graph.Core/Requests/Middleware/Options/ODataQueryHandlerOption.cs b/src/Microsoft.Graph.Core/Requests/Middleware/Options/ODataQueryHandlerOption.cs deleted file mode 100644 index 41fbb02b0..000000000 --- a/src/Microsoft.Graph.Core/Requests/Middleware/Options/ODataQueryHandlerOption.cs +++ /dev/null @@ -1,23 +0,0 @@ -// ------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information. -// ------------------------------------------------------------------------------ - -namespace Microsoft.Graph -{ - using Microsoft.Kiota.Abstractions; - using System; - using System.Net.Http; - - /// - /// The to configure the - /// - public class ODataQueryHandlerOption : IRequestOption - { - /// - /// Function to determine whether a request should be modified. Defaults to returning true. - /// - public Func ShouldReplace { get; set; } = (requestMessage) => true; - } -} - - diff --git a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/GraphClientFactoryTests.cs b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/GraphClientFactoryTests.cs index ddc532956..2b6748006 100644 --- a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/GraphClientFactoryTests.cs +++ b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/GraphClientFactoryTests.cs @@ -68,12 +68,12 @@ public void Should_CreatePipeline_Without_CompressionHandler() [Fact] public void Should_CreatePipeline_Without_HttpMessageHandlerInput() { - using GraphTelemetryHandler telemetryHandler = (GraphTelemetryHandler)GraphClientFactory.CreatePipeline(handlers); - using OdataQueryHandler odataQueryHandler = (OdataQueryHandler)telemetryHandler.InnerHandler; - using CompressionHandler compressionHandler = (CompressionHandler)odataQueryHandler.InnerHandler; + using CompressionHandler compressionHandler = (CompressionHandler)GraphClientFactory.CreatePipeline(handlers, new MockRedirectHandler()); using RetryHandler retryHandler = (RetryHandler)compressionHandler.InnerHandler; using RedirectHandler redirectHandler = (RedirectHandler)retryHandler.InnerHandler; - using HttpMessageHandler innerMost = redirectHandler.InnerHandler; + using ParametersNameDecodingHandler odataQueryHandler = (ParametersNameDecodingHandler)redirectHandler.InnerHandler; + using GraphTelemetryHandler telemetryHandler = (GraphTelemetryHandler)odataQueryHandler.InnerHandler; + using MockRedirectHandler innerMost = (MockRedirectHandler)telemetryHandler.InnerHandler; Assert.NotNull(telemetryHandler); Assert.NotNull(odataQueryHandler); @@ -82,7 +82,7 @@ public void Should_CreatePipeline_Without_HttpMessageHandlerInput() Assert.NotNull(redirectHandler); Assert.NotNull(innerMost); Assert.IsType(telemetryHandler); - Assert.IsType(odataQueryHandler); + Assert.IsType(odataQueryHandler); Assert.IsType(compressionHandler); Assert.IsType(retryHandler); Assert.IsType(redirectHandler); @@ -93,12 +93,12 @@ public void Should_CreatePipeline_Without_HttpMessageHandlerInput() [Fact] public void CreatePipelineWithHttpMessageHandlerInput() { - using GraphTelemetryHandler telemetryHandler = (GraphTelemetryHandler)GraphClientFactory.CreatePipeline(handlers, new MockRedirectHandler()); - using OdataQueryHandler odataQueryHandler = (OdataQueryHandler)telemetryHandler.InnerHandler; - using CompressionHandler compressionHandler = (CompressionHandler)odataQueryHandler.InnerHandler; + using CompressionHandler compressionHandler = (CompressionHandler)GraphClientFactory.CreatePipeline(handlers, new MockRedirectHandler()); using RetryHandler retryHandler = (RetryHandler)compressionHandler.InnerHandler; using RedirectHandler redirectHandler = (RedirectHandler)retryHandler.InnerHandler; - using MockRedirectHandler innerMost = (MockRedirectHandler)redirectHandler.InnerHandler; + using ParametersNameDecodingHandler odataQueryHandler = (ParametersNameDecodingHandler)redirectHandler.InnerHandler; + using GraphTelemetryHandler telemetryHandler = (GraphTelemetryHandler)odataQueryHandler.InnerHandler; + using MockRedirectHandler innerMost = (MockRedirectHandler)telemetryHandler.InnerHandler; Assert.NotNull(telemetryHandler); Assert.NotNull(odataQueryHandler); @@ -107,7 +107,7 @@ public void CreatePipelineWithHttpMessageHandlerInput() Assert.NotNull(redirectHandler); Assert.NotNull(innerMost); Assert.IsType(telemetryHandler); - Assert.IsType(odataQueryHandler); + Assert.IsType(odataQueryHandler); Assert.IsType(compressionHandler); Assert.IsType(retryHandler); Assert.IsType(redirectHandler); diff --git a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/Middleware/OdataQueryHandlerTests.cs b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/Middleware/OdataQueryHandlerTests.cs deleted file mode 100644 index 673800af2..000000000 --- a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/Middleware/OdataQueryHandlerTests.cs +++ /dev/null @@ -1,106 +0,0 @@ -// ------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information. -// ------------------------------------------------------------------------------ - -namespace Microsoft.Graph.DotnetCore.Core.Test.Requests.Middleware -{ - using Microsoft.Kiota.Abstractions; - using Microsoft.Kiota.Abstractions.Authentication; - using Microsoft.Kiota.Http.HttpClientLibrary; - using System; - using System.Net; - using System.Net.Http; - using System.Threading; - using System.Threading.Tasks; - using Xunit; - - public class OdataQueryHandlerTests - { - private readonly HttpMessageInvoker _invoker; - private readonly HttpClientRequestAdapter requestAdapter; - - public OdataQueryHandlerTests() - { - var odataQueryHandler = new OdataQueryHandler - { - InnerHandler = new FakeSuccessHandler() - }; - this._invoker = new HttpMessageInvoker(odataQueryHandler); - requestAdapter = new HttpClientRequestAdapter(new AnonymousAuthenticationProvider()); - } - - [Fact] - public async Task ItReplacesOdataQueryParametersByDefaultAsync() - { - // Arrange - var requestInfo = new RequestInformation - { - HttpMethod = Method.GET, - URI = new Uri("http://localhost?Select=something&exPand=somethingElse(select=nested)&$top=10") - }; - - // Act and get a request message - var requestMessage = requestAdapter.GetRequestMessageFromRequestInformation(requestInfo); - Assert.Empty(requestMessage.Headers); - - // Act - var response = await _invoker.SendAsync(requestMessage, new CancellationToken()); - - var queryString = response.RequestMessage.RequestUri.Query; - - // Assert the request was enriched as expected - Assert.Contains("$Select=something", queryString); - Assert.Contains("$exPand=somethingElse", queryString); - Assert.Contains("$select=nested", queryString); - Assert.Contains("&$top=10", queryString); // No doulble $ for already existing ones - } - - [Fact] - public async Task ItDoesNotReplaceOdataQueryParametersUsingConsfigurator() - { - // Arrange - var requestInfo = new RequestInformation - { - HttpMethod = Method.GET, - URI = new Uri("http://localhost?Select=something&exPand=somethingElse(select=nested)&$top=10") - }; - var requestOption = new ODataQueryHandlerOption - { - ShouldReplace = (request) => false // do not change the query options - }; - - - // Act and get a request message - requestInfo.AddRequestOptions(requestOption); - var requestMessage = requestAdapter.GetRequestMessageFromRequestInformation(requestInfo); - Assert.Empty(requestMessage.Headers); - - // Act - var response = await _invoker.SendAsync(requestMessage, new CancellationToken()); - - var queryString = response.RequestMessage.RequestUri.Query; - - // Assert the request was enriched as expected - Assert.Contains("Select=something", queryString); - Assert.DoesNotContain("$Select=something", queryString); - Assert.Contains("exPand=somethingElse", queryString); - Assert.DoesNotContain("$exPand=somethingElse", queryString); - Assert.Contains("select=nested", queryString); - Assert.DoesNotContain("$select=nested", queryString); - Assert.Contains("&$top=10", queryString); // No doulble $ for already existing ones - } - } - - internal class FakeSuccessHandler : DelegatingHandler - { - protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - var response = new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - RequestMessage = request - }; - return Task.FromResult(response); - } - } -} diff --git a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/Middleware/TelemetryHandlerTests.cs b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/Middleware/TelemetryHandlerTests.cs index 4345a3a87..1db73c4c9 100644 --- a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/Middleware/TelemetryHandlerTests.cs +++ b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/Middleware/TelemetryHandlerTests.cs @@ -96,4 +96,17 @@ public async Task TelemetryHandlerShouldSetTelemetryHeaderWithCustomConfiguratio Assert.Contains($" runtimeEnvironment={RuntimeInformation.FrameworkDescription};", telemetryHeaderString); } } + + internal class FakeSuccessHandler : DelegatingHandler + { + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var response = new HttpResponseMessage + { + StatusCode = System.Net.HttpStatusCode.OK, + RequestMessage = request + }; + return Task.FromResult(response); + } + } }