From 6c49f091c84dd9582ca1836b93fbf2e489c4d630 Mon Sep 17 00:00:00 2001 From: Philip Gichuhi Date: Wed, 30 Oct 2024 10:46:36 +0200 Subject: [PATCH 1/7] chore: remove unused usings --- .../Extensions/HttpRequestMessageExtensions.cs | 3 --- .../Extensions/IDecryptableContentExtensions.cs | 4 +--- src/Microsoft.Graph.Core/Properties/AssemblyInfo.cs | 4 +--- src/Microsoft.Graph.Core/Requests/AsyncMonitor.cs | 1 - .../Requests/Content/BatchRequestContent.cs | 1 - .../Requests/Content/BatchResponseContentCollection.cs | 1 - .../Requests/DeltaResponseHandler.cs | 9 ++++----- src/Microsoft.Graph.Core/Requests/GraphClientFactory.cs | 3 +-- src/Microsoft.Graph.Core/Requests/GraphRequestContext.cs | 2 -- src/Microsoft.Graph.Core/Requests/GraphResponse{T}.cs | 3 +-- src/Microsoft.Graph.Core/Requests/ResponseHandler.cs | 2 -- .../Helpers/ReadOnlySubStreamTests.cs | 3 --- .../Mocks/BaseClient.cs | 1 - .../Properties/AssemblyInfo.cs | 1 - .../Requests/AsyncMonitorTests.cs | 1 - .../Requests/GraphClientFactoryTests.cs | 1 - .../Requests/ResponseHandlerTests.cs | 9 ++++----- .../TestModels/DateTestClass.cs | 1 - 18 files changed, 12 insertions(+), 38 deletions(-) diff --git a/src/Microsoft.Graph.Core/Extensions/HttpRequestMessageExtensions.cs b/src/Microsoft.Graph.Core/Extensions/HttpRequestMessageExtensions.cs index 672a6df46..7c40e6235 100644 --- a/src/Microsoft.Graph.Core/Extensions/HttpRequestMessageExtensions.cs +++ b/src/Microsoft.Graph.Core/Extensions/HttpRequestMessageExtensions.cs @@ -6,11 +6,8 @@ namespace Microsoft.Graph { using System; using System.Collections.Generic; - using System.IO; using System.Linq; using System.Net.Http; - using System.Threading.Tasks; - using Microsoft.Kiota.Http.HttpClientLibrary.Extensions; /// /// Contains extension methods for diff --git a/src/Microsoft.Graph.Core/Extensions/IDecryptableContentExtensions.cs b/src/Microsoft.Graph.Core/Extensions/IDecryptableContentExtensions.cs index d49222ba2..3cf58e039 100644 --- a/src/Microsoft.Graph.Core/Extensions/IDecryptableContentExtensions.cs +++ b/src/Microsoft.Graph.Core/Extensions/IDecryptableContentExtensions.cs @@ -9,10 +9,8 @@ namespace Microsoft.Graph using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; - using System.Text.Json; using System.Threading.Tasks; using Microsoft.Kiota.Abstractions.Serialization; - using Microsoft.Kiota.Serialization.Json; /// /// Contains extension methods for @@ -43,7 +41,7 @@ public static class IDecryptableContentExtensions /// /// Validates the signature and decrypted content attached with the notification. - /// https://docs.microsoft.com/en-us/graph/webhooks-with-resource-data#decrypting-resource-data-from-change-notifications + /// https://docs.microsoft.com/en-us/graph/webhooks-with-resource-data#decrypting-resource-data-from-change-notifications /// /// The encrypted content of type /// Certificate provider to decrypt the content. diff --git a/src/Microsoft.Graph.Core/Properties/AssemblyInfo.cs b/src/Microsoft.Graph.Core/Properties/AssemblyInfo.cs index fee30d88a..12434a437 100644 --- a/src/Microsoft.Graph.Core/Properties/AssemblyInfo.cs +++ b/src/Microsoft.Graph.Core/Properties/AssemblyInfo.cs @@ -1,6 +1,4 @@ -using System.Reflection; -using System.Resources; -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; #if DEBUG [assembly: InternalsVisibleTo("Microsoft.Graph.DotnetCore.Core.Test")] #endif diff --git a/src/Microsoft.Graph.Core/Requests/AsyncMonitor.cs b/src/Microsoft.Graph.Core/Requests/AsyncMonitor.cs index f6a29b176..83290c1b2 100644 --- a/src/Microsoft.Graph.Core/Requests/AsyncMonitor.cs +++ b/src/Microsoft.Graph.Core/Requests/AsyncMonitor.cs @@ -7,7 +7,6 @@ namespace Microsoft.Graph using System; using System.Net; using System.Net.Http; - using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.Kiota.Abstractions; diff --git a/src/Microsoft.Graph.Core/Requests/Content/BatchRequestContent.cs b/src/Microsoft.Graph.Core/Requests/Content/BatchRequestContent.cs index 6e46c974e..1679a9649 100644 --- a/src/Microsoft.Graph.Core/Requests/Content/BatchRequestContent.cs +++ b/src/Microsoft.Graph.Core/Requests/Content/BatchRequestContent.cs @@ -5,7 +5,6 @@ namespace Microsoft.Graph { using System; - using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.IO; diff --git a/src/Microsoft.Graph.Core/Requests/Content/BatchResponseContentCollection.cs b/src/Microsoft.Graph.Core/Requests/Content/BatchResponseContentCollection.cs index dca231367..ca10c972b 100644 --- a/src/Microsoft.Graph.Core/Requests/Content/BatchResponseContentCollection.cs +++ b/src/Microsoft.Graph.Core/Requests/Content/BatchResponseContentCollection.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Net; using System.Net.Http; - using System.Text.Json; using System.Threading.Tasks; using Microsoft.Kiota.Abstractions; using Microsoft.Kiota.Abstractions.Serialization; diff --git a/src/Microsoft.Graph.Core/Requests/DeltaResponseHandler.cs b/src/Microsoft.Graph.Core/Requests/DeltaResponseHandler.cs index 5809b677e..40a5f0059 100644 --- a/src/Microsoft.Graph.Core/Requests/DeltaResponseHandler.cs +++ b/src/Microsoft.Graph.Core/Requests/DeltaResponseHandler.cs @@ -4,7 +4,6 @@ namespace Microsoft.Graph { - using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -21,7 +20,7 @@ namespace Microsoft.Graph #endif /// - /// PREVIEW + /// PREVIEW /// A response handler that exposes the list of changes returned in a response. /// This supports scenarios where the service expresses changes to 'null'. The /// deserializer can't express changes to null so you can now discover if a property @@ -56,7 +55,7 @@ public async Task HandleResponseAsync( // set on the response body object. var responseString = await GetResponseStringAsync(responseMessage).ConfigureAwait(false); - // Get the response body object with the change list + // Get the response body object with the change list // set on each response item. var responseWithChangeList = await GetResponseBodyWithChangelistAsync(responseString).ConfigureAwait(false); using var responseWithChangeListStream = new MemoryStream(Encoding.UTF8.GetBytes(responseWithChangeList)); @@ -112,7 +111,7 @@ private async Task GetResponseBodyWithChangelistAsync(string deltaRespon // return a string instead. using (var responseJsonDocument = JsonDocument.Parse(deltaResponseBody)) { - // An array of delta objects. We will need to process + // An array of delta objects. We will need to process // each one independently of each other. if (!responseJsonDocument.RootElement.TryGetProperty("value", out var pageOfDeltaObjects)) { @@ -193,7 +192,7 @@ private async Task GetObjectPropertiesAsync(JsonElement changedObject, List /// The graph request context class diff --git a/src/Microsoft.Graph.Core/Requests/GraphResponse{T}.cs b/src/Microsoft.Graph.Core/Requests/GraphResponse{T}.cs index 4ee102c9a..98916de86 100644 --- a/src/Microsoft.Graph.Core/Requests/GraphResponse{T}.cs +++ b/src/Microsoft.Graph.Core/Requests/GraphResponse{T}.cs @@ -4,7 +4,6 @@ namespace Microsoft.Graph { - using System; using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; @@ -27,7 +26,7 @@ public GraphResponse(RequestInformation requestInformation, HttpResponseMessage } /// - /// Gets the deserialized object + /// Gets the deserialized object /// /// The response handler to use for the reponse /// The errorMappings to use in the event of a non sucess request diff --git a/src/Microsoft.Graph.Core/Requests/ResponseHandler.cs b/src/Microsoft.Graph.Core/Requests/ResponseHandler.cs index 4599f75e5..3979c35af 100644 --- a/src/Microsoft.Graph.Core/Requests/ResponseHandler.cs +++ b/src/Microsoft.Graph.Core/Requests/ResponseHandler.cs @@ -6,9 +6,7 @@ namespace Microsoft.Graph { using System; using System.Collections.Generic; - using System.Net; using System.Net.Http; - using System.Text.Json; using System.Threading.Tasks; using Microsoft.Kiota.Abstractions; using Microsoft.Kiota.Abstractions.Serialization; diff --git a/tests/Microsoft.Graph.DotnetCore.Core.Test/Helpers/ReadOnlySubStreamTests.cs b/tests/Microsoft.Graph.DotnetCore.Core.Test/Helpers/ReadOnlySubStreamTests.cs index 03d4d157c..7c8737869 100644 --- a/tests/Microsoft.Graph.DotnetCore.Core.Test/Helpers/ReadOnlySubStreamTests.cs +++ b/tests/Microsoft.Graph.DotnetCore.Core.Test/Helpers/ReadOnlySubStreamTests.cs @@ -4,9 +4,6 @@ using System.Collections.Generic; using System.IO; -using System.Net.Http; -using System.Threading.Tasks; -using Microsoft.Kiota.Http.HttpClientLibrary.Extensions; using Xunit; namespace Microsoft.Graph.DotnetCore.Core.Test.Helpers diff --git a/tests/Microsoft.Graph.DotnetCore.Core.Test/Mocks/BaseClient.cs b/tests/Microsoft.Graph.DotnetCore.Core.Test/Mocks/BaseClient.cs index 1d8e9ab47..50cfcd521 100644 --- a/tests/Microsoft.Graph.DotnetCore.Core.Test/Mocks/BaseClient.cs +++ b/tests/Microsoft.Graph.DotnetCore.Core.Test/Mocks/BaseClient.cs @@ -4,7 +4,6 @@ namespace Microsoft.Graph.DotnetCore.Core.Test.Mocks { - using System.Net.Http; using Microsoft.Graph.Core.Requests; using Microsoft.Kiota.Abstractions; using Microsoft.Kiota.Abstractions.Authentication; diff --git a/tests/Microsoft.Graph.DotnetCore.Core.Test/Properties/AssemblyInfo.cs b/tests/Microsoft.Graph.DotnetCore.Core.Test/Properties/AssemblyInfo.cs index 9cc69293f..bf8611af4 100644 --- a/tests/Microsoft.Graph.DotnetCore.Core.Test/Properties/AssemblyInfo.cs +++ b/tests/Microsoft.Graph.DotnetCore.Core.Test/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/AsyncMonitorTests.cs b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/AsyncMonitorTests.cs index 389c2252d..4dacd2bf1 100644 --- a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/AsyncMonitorTests.cs +++ b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/AsyncMonitorTests.cs @@ -6,7 +6,6 @@ namespace Microsoft.Graph.DotnetCore.Core.Test.Requests { using System; using System.Collections.Generic; - using System.IO; using System.Net; using System.Net.Http; using System.Text.Json; diff --git a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/GraphClientFactoryTests.cs b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/GraphClientFactoryTests.cs index 2f547dff2..1356321d5 100644 --- a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/GraphClientFactoryTests.cs +++ b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/GraphClientFactoryTests.cs @@ -10,7 +10,6 @@ namespace Microsoft.Graph.DotnetCore.Core.Test.Requests using System.Net; using System.Net.Http; using System.Net.Http.Headers; - using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.Kiota.Http.HttpClientLibrary.Middleware; diff --git a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/ResponseHandlerTests.cs b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/ResponseHandlerTests.cs index 4e62e02d5..5a9bd4005 100644 --- a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/ResponseHandlerTests.cs +++ b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/ResponseHandlerTests.cs @@ -4,7 +4,6 @@ namespace Microsoft.Graph.DotnetCore.Core.Test.Requests { - using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; @@ -231,7 +230,7 @@ public async Task HandleEventDeltaResponseWithNullValuesAsync() // Update the value on a complex type property's value. Developer can't just replace the body // as that could result in overwriting other unchanged property. Essentially, they need to inspect // every leaf node in the selected property set. - if (changeList.Exists(x => x.Equals("body.content"))) // + if (changeList.Exists(x => x.Equals("body.content"))) // { if (myModel.Body == null) { @@ -247,7 +246,7 @@ public async Task HandleEventDeltaResponseWithNullValuesAsync() var attendeesChangelist = changeList.FindAll(x => x.Contains("attendees")); if (attendeesChangelist.Count > 0) { - // This is where if we provided the delta response as a JSON object, + // This is where if we provided the delta response as a JSON object, // we could let the developer use JMESPath to query the changes. if (changeList.Exists(x => x.Equals("attendees[0].emailAddress.name"))) { @@ -259,7 +258,7 @@ public async Task HandleEventDeltaResponseWithNullValuesAsync() } else // Attendees list is being updated. { - // We need to inspect each object, and determine which objects and properties + // We need to inspect each object, and determine which objects and properties // need to be initialized and/or updated. } } @@ -324,7 +323,7 @@ public async Task HandleEventDeltaResponseWithEmptyCollectionPropertyAsync() // To view and format this test string, replace all \" with ", and use a JSON formatter // to make it pretty. - // In this scenario the attendees for the first event have been removed and the api has returned + // In this scenario the attendees for the first event have been removed and the api has returned // an empty collection to the property var testString = "{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#Collection(event)\",\"@odata.nextLink\":\"https://graph.microsoft.com/v1.0/me/calendarView/delta?$skiptoken=R0usmci39OQxqJrxK4\",\"value\":[{\"@odata.type\":\"#microsoft.graph.event\",\"@odata.etag\":\"EZ9r3czxY0m2jz8c45czkwAAFXcvIw==\",\"subject\":\"Get food\",\"body\":{\"contentType\":\"html\",\"content\":\"\"},\"start\":{\"dateTime\":\"2016-12-10T19:30:00.0000000\",\"timeZone\":\"UTC\"},\"end\":{\"dateTime\":\"2016-12-10T21:30:00.0000000\",\"timeZone\":\"UTC\"},\"attendees\":[],\"organizer\":{\"emailAddress\":{\"name\":\"Samantha Booth\",\"address\":\"samanthab@contoso.onmicrosoft.com\"}},\"id\":\"AAMkADVxTAAA=\"},{\"@odata.type\":\"#microsoft.graph.event\",\"@odata.etag\":\"WEZ9r3czxY0m2jz8c45czkwAAFXcvJA==\",\"subject\":\"Prepare food\",\"body\":{\"contentType\":\"html\",\"content\":\"\"},\"start\":{\"dateTime\":\"2016-12-10T22:00:00.0000000\",\"timeZone\":\"UTC\"},\"end\":{\"dateTime\":\"2016-12-11T00:00:00.0000000\",\"timeZone\":\"UTC\"},\"attendees\":[],\"organizer\":{\"emailAddress\":{\"name\":\"Samantha Booth\",\"address\":\"samanthab@contoso.onmicrosoft.com\"}},\"id\":\"AAMkADVxUAAA=\"}]}"; diff --git a/tests/Microsoft.Graph.DotnetCore.Core.Test/TestModels/DateTestClass.cs b/tests/Microsoft.Graph.DotnetCore.Core.Test/TestModels/DateTestClass.cs index 1cbea2644..f927bea43 100644 --- a/tests/Microsoft.Graph.DotnetCore.Core.Test/TestModels/DateTestClass.cs +++ b/tests/Microsoft.Graph.DotnetCore.Core.Test/TestModels/DateTestClass.cs @@ -6,7 +6,6 @@ namespace Microsoft.Graph.DotnetCore.Core.Test.TestModels { using System; using System.Collections.Generic; - using System.Linq; using Microsoft.Kiota.Abstractions; using Microsoft.Kiota.Abstractions.Serialization; From ce6a88b658e377692f93b48c0685cef7ba30f225 Mon Sep 17 00:00:00 2001 From: Philip Gichuhi Date: Wed, 30 Oct 2024 12:11:15 +0200 Subject: [PATCH 2/7] feat: Add create() overloads to GraphClientFactory that enable requests to be authenticated --- .../Requests/GraphClientFactory.cs | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/Microsoft.Graph.Core/Requests/GraphClientFactory.cs b/src/Microsoft.Graph.Core/Requests/GraphClientFactory.cs index ed21c14d5..e7cbb5ae5 100644 --- a/src/Microsoft.Graph.Core/Requests/GraphClientFactory.cs +++ b/src/Microsoft.Graph.Core/Requests/GraphClientFactory.cs @@ -9,6 +9,9 @@ namespace Microsoft.Graph using System.Net; using System.Net.Http; using System.Net.Http.Headers; + using Azure.Core; + using Microsoft.Graph.Authentication; + using Microsoft.Kiota.Abstractions.Authentication; using Microsoft.Kiota.Http.HttpClientLibrary; using Microsoft.Kiota.Http.HttpClientLibrary.Middleware; @@ -106,6 +109,64 @@ public static HttpClient Create( return client; } + /// + /// Creates a new instance configured to authenticate requests using the provided . + /// + /// The authentication provider to initialise the Authorization handler + /// Custom middleware pipeline to which the Authorization handler is appended. If null, default handlers are initialised + /// The Graph version to use in the base URL + /// The national cloud endpoint to use + /// The proxy to be used with the created client + /// The last HttpMessageHandler to HTTP calls. + /// true if the inner handler should be disposed of by Dispose(), false if you intend to reuse the inner handler.. + /// + public static HttpClient Create( + BaseBearerTokenAuthenticationProvider authenticationProvider, + IEnumerable handlers = null, + string version = "v1.0", + string nationalCloud = Global_Cloud, + IWebProxy proxy = null, + HttpMessageHandler finalHandler = null, + bool disposeHandler = true) + { + if (handlers == null) + { + handlers = CreateDefaultHandlers(); + } + handlers = handlers.Append(new AuthorizationHandler(authenticationProvider)); + return Create(handlers, version, nationalCloud, proxy, finalHandler, disposeHandler); + } + + /// + /// Creates a new instance configured to authenticate requests using the provided . + /// + /// Token credential object use to initialise an + /// Custom middleware pipeline to which the Authorization handler is appended. If null, default handlers are initialised + /// The Graph version to use in the base URL + /// The national cloud endpoint to use + /// The proxy to be used with the created client + /// The last HttpMessageHandler to HTTP calls + /// true if the inner handler should be disposed of by Dispose(), false if you intend to reuse the inner handler.. + /// + public static HttpClient Create( + TokenCredential tokenCredential, + IEnumerable handlers = null, + string version = "v1.0", + string nationalCloud = Global_Cloud, + IWebProxy proxy = null, + HttpMessageHandler finalHandler = null, + bool disposeHandler = true) + { + if (handlers == null) + { + handlers = CreateDefaultHandlers(); + } + handlers = handlers.Append(new AuthorizationHandler( + new AzureIdentityAuthenticationProvider(tokenCredential, null, null, true) + )); + return Create(handlers, version, nationalCloud, proxy, finalHandler, disposeHandler); + } + /// /// Create a default set of middleware for calling Microsoft Graph /// From bf519044517e626341bda716162ced312c571c6d Mon Sep 17 00:00:00 2001 From: Philip Gichuhi Date: Wed, 30 Oct 2024 12:15:27 +0200 Subject: [PATCH 3/7] Update README sample --- README.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b893be4a4..c796d77c0 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,29 @@ For an example of authenticating a UWP app using the V2 Authentication Endpoint, You can create an instance of **HttpClient** that is pre-configured for making requests to Microsoft Graph APIs using `GraphClientFactory`. ```cs -HttpClient httpClient = GraphClientFactory.Create( version: "beta"); +// The client credentials flow requires that you request the +// /.default scope, and pre-configure your permissions on the +// app registration in Azure. An administrator must grant consent +// to those permissions beforehand. +var scopes = new[] { "https://graph.microsoft.com/.default" }; + +// Values from app registration +var clientId = "YOUR_CLIENT_ID"; +var tenantId = "YOUR_TENANT_ID"; +var clientSecret = "YOUR_CLIENT_SECRET"; + +// using Azure.Identity; +var options = new ClientSecretCredentialOptions +{ + AuthorityHost = AzureAuthorityHosts.AzurePublicCloud, +}; + +// https://learn.microsoft.com/dotnet/api/azure.identity.clientsecretcredential +var clientSecretCredential = new ClientSecretCredential( + tenantId, clientId, clientSecret, options); + +HttpClient httpClient = GraphClientFactory.create(tokenCredential: clientSecretCredential, version: "beta"); + ``` For more information on initializing a client instance, see the [library overview](https://docs.microsoft.com/en-us/graph/sdks/sdks-overview) From 4d87860dd1b609bd5bf9ee2ae7c6621ef3bb8d47 Mon Sep 17 00:00:00 2001 From: Philip Gichuhi Date: Wed, 6 Nov 2024 11:12:46 +0200 Subject: [PATCH 4/7] Bump Kiota dependencies --- src/Microsoft.Graph.Core/Microsoft.Graph.Core.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.Graph.Core/Microsoft.Graph.Core.csproj b/src/Microsoft.Graph.Core/Microsoft.Graph.Core.csproj index c76892bbf..b217a4f0b 100644 --- a/src/Microsoft.Graph.Core/Microsoft.Graph.Core.csproj +++ b/src/Microsoft.Graph.Core/Microsoft.Graph.Core.csproj @@ -68,10 +68,10 @@ - - + + - + runtime; build; native; contentfiles; analyzers; buildtransitive all From 6e244b8f94281e71f40d68cf003a8d1cc064251e Mon Sep 17 00:00:00 2001 From: Philip Gichuhi Date: Thu, 7 Nov 2024 14:23:51 +0200 Subject: [PATCH 5/7] fix build issues --- .../Requests/GraphClientFactory.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.Graph.Core/Requests/GraphClientFactory.cs b/src/Microsoft.Graph.Core/Requests/GraphClientFactory.cs index e7cbb5ae5..1ae830898 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 System.Threading; using Azure.Core; using Microsoft.Graph.Authentication; using Microsoft.Kiota.Abstractions.Authentication; @@ -119,7 +120,7 @@ public static HttpClient Create( /// The proxy to be used with the created client /// The last HttpMessageHandler to HTTP calls. /// true if the inner handler should be disposed of by Dispose(), false if you intend to reuse the inner handler.. - /// + /// An instance with the configured handlers public static HttpClient Create( BaseBearerTokenAuthenticationProvider authenticationProvider, IEnumerable handlers = null, @@ -133,21 +134,22 @@ public static HttpClient Create( { handlers = CreateDefaultHandlers(); } - handlers = handlers.Append(new AuthorizationHandler(authenticationProvider)); - return Create(handlers, version, nationalCloud, proxy, finalHandler, disposeHandler); + var handlerList = handlers.ToList(); + handlerList.Add(new AuthorizationHandler(authenticationProvider)); + return Create(handlerList, version, nationalCloud, proxy, finalHandler, disposeHandler); } /// /// Creates a new instance configured to authenticate requests using the provided . /// - /// Token credential object use to initialise an + /// Token credential object use to initialise an /// Custom middleware pipeline to which the Authorization handler is appended. If null, default handlers are initialised /// The Graph version to use in the base URL /// The national cloud endpoint to use /// The proxy to be used with the created client /// The last HttpMessageHandler to HTTP calls - /// true if the inner handler should be disposed of by Dispose(), false if you intend to reuse the inner handler.. - /// + /// true if the inner handler should be disposed of by Dispose(), false if you intend to reuse the inner handler. + /// An instance with the configured handlers public static HttpClient Create( TokenCredential tokenCredential, IEnumerable handlers = null, @@ -161,10 +163,11 @@ public static HttpClient Create( { handlers = CreateDefaultHandlers(); } - handlers = handlers.Append(new AuthorizationHandler( + var handlerList = handlers.ToList(); + handlerList.Add(new AuthorizationHandler( new AzureIdentityAuthenticationProvider(tokenCredential, null, null, true) )); - return Create(handlers, version, nationalCloud, proxy, finalHandler, disposeHandler); + return Create(handlerList, version, nationalCloud, proxy, finalHandler, disposeHandler); } /// From 931037c3bcde9b6803b4cebd3c83af1e48422e68 Mon Sep 17 00:00:00 2001 From: Philip Gichuhi Date: Thu, 7 Nov 2024 17:06:38 +0200 Subject: [PATCH 6/7] Add tests --- .../Requests/GraphClientFactory.cs | 10 +---- .../Mocks/MockAccessTokenProvider.cs | 30 +++++++++++++++ .../Requests/GraphClientFactoryTests.cs | 37 +++++++++++++++++++ 3 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 tests/Microsoft.Graph.DotnetCore.Core.Test/Mocks/MockAccessTokenProvider.cs diff --git a/src/Microsoft.Graph.Core/Requests/GraphClientFactory.cs b/src/Microsoft.Graph.Core/Requests/GraphClientFactory.cs index 1ae830898..0bd3a4244 100644 --- a/src/Microsoft.Graph.Core/Requests/GraphClientFactory.cs +++ b/src/Microsoft.Graph.Core/Requests/GraphClientFactory.cs @@ -159,15 +159,7 @@ public static HttpClient Create( HttpMessageHandler finalHandler = null, bool disposeHandler = true) { - if (handlers == null) - { - handlers = CreateDefaultHandlers(); - } - var handlerList = handlers.ToList(); - handlerList.Add(new AuthorizationHandler( - new AzureIdentityAuthenticationProvider(tokenCredential, null, null, true) - )); - return Create(handlerList, version, nationalCloud, proxy, finalHandler, disposeHandler); + return Create(new AzureIdentityAuthenticationProvider(tokenCredential, null, null, true), handlers, version, nationalCloud, proxy, finalHandler, disposeHandler); } /// diff --git a/tests/Microsoft.Graph.DotnetCore.Core.Test/Mocks/MockAccessTokenProvider.cs b/tests/Microsoft.Graph.DotnetCore.Core.Test/Mocks/MockAccessTokenProvider.cs new file mode 100644 index 000000000..7c3b56c10 --- /dev/null +++ b/tests/Microsoft.Graph.DotnetCore.Core.Test/Mocks/MockAccessTokenProvider.cs @@ -0,0 +1,30 @@ +// ------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information. +// ------------------------------------------------------------------------------ + + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Kiota.Abstractions.Authentication; +using Moq; + +namespace Microsoft.Graph.DotnetCore.Core.Test.Mocks +{ + public class MockAccessTokenProvider : Mock + { + public MockAccessTokenProvider(string accessToken = null) : base(MockBehavior.Strict) + { + this.Setup(x => x.GetAuthorizationTokenAsync( + It.IsAny(), + It.IsAny>(), + It.IsAny() + )).Returns(Task.FromResult(accessToken)); + + this.Setup(x => x.AllowedHostsValidator).Returns( + new AllowedHostsValidator(new List { "graph.microsoft.com" }) + ); + } + } +} diff --git a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/GraphClientFactoryTests.cs b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/GraphClientFactoryTests.cs index 1356321d5..f2d8c1d31 100644 --- a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/GraphClientFactoryTests.cs +++ b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/GraphClientFactoryTests.cs @@ -12,9 +12,12 @@ namespace Microsoft.Graph.DotnetCore.Core.Test.Requests using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; + using Azure.Core; + using Microsoft.Kiota.Abstractions.Authentication; using Microsoft.Kiota.Http.HttpClientLibrary.Middleware; using Microsoft.Kiota.Http.HttpClientLibrary.Middleware.Options; using Mocks; + using Moq; using Xunit; public class GraphClientFactoryTests : IDisposable @@ -314,6 +317,40 @@ public void CreateClientWithFinalHandlerDisposesTheFinalHandler(bool shouldDispo Assert.Equal(shouldDisposeHandler, finalHandler.Disposed); } + [Fact] + public async Task CreateClientWithAuthenticationProviderAuthenticatesRequest() + { + var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "https://graph.microsoft.com/me"); + var responseMessage = new HttpResponseMessage(HttpStatusCode.OK); + this.testHttpMessageHandler.SetHttpResponse(responseMessage); + + var authProvider = new Mock(new MockAccessTokenProvider("token").Object); + + using (HttpClient client = GraphClientFactory.Create(authenticationProvider: authProvider.Object, finalHandler: this.testHttpMessageHandler)) + { + var response = await client.SendAsync(httpRequestMessage, new CancellationToken()); + Assert.Equal("Bearer token", response.RequestMessage.Headers.Authorization.ToString()); + } + } + + [Fact] + public async Task CreateClientWithTokenCredentialAuthenticatesRequest() + { + var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "https://graph.microsoft.com/me"); + var responseMessage = new HttpResponseMessage(HttpStatusCode.OK); + this.testHttpMessageHandler.SetHttpResponse(responseMessage); + + var tokenCredential = new Mock(); + tokenCredential.Setup(x => x.GetTokenAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new AccessToken("mockToken", DateTimeOffset.UtcNow.AddMinutes(10))); + + using (HttpClient client = GraphClientFactory.Create(tokenCredential: tokenCredential.Object, finalHandler: this.testHttpMessageHandler)) + { + var response = await client.SendAsync(httpRequestMessage, new CancellationToken()); + Assert.Equal("Bearer mockToken", response.RequestMessage.Headers.Authorization.ToString()); + } + } + private class MockHttpHandler : HttpMessageHandler { public bool Disposed; From b77114d9afb34faf33953502ec5a5c5a60c3c7dc Mon Sep 17 00:00:00 2001 From: Philip Gichuhi Date: Thu, 7 Nov 2024 17:18:45 +0200 Subject: [PATCH 7/7] Fix format issues --- .../Mocks/MockAccessTokenProvider.cs | 3 +-- .../Requests/GraphClientFactoryTests.cs | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/Microsoft.Graph.DotnetCore.Core.Test/Mocks/MockAccessTokenProvider.cs b/tests/Microsoft.Graph.DotnetCore.Core.Test/Mocks/MockAccessTokenProvider.cs index 7c3b56c10..b5af4cbf3 100644 --- a/tests/Microsoft.Graph.DotnetCore.Core.Test/Mocks/MockAccessTokenProvider.cs +++ b/tests/Microsoft.Graph.DotnetCore.Core.Test/Mocks/MockAccessTokenProvider.cs @@ -1,8 +1,7 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information. // ------------------------------------------------------------------------------ - using System; using System.Collections.Generic; using System.Threading; diff --git a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/GraphClientFactoryTests.cs b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/GraphClientFactoryTests.cs index f2d8c1d31..84ef41db8 100644 --- a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/GraphClientFactoryTests.cs +++ b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/GraphClientFactoryTests.cs @@ -318,7 +318,7 @@ public void CreateClientWithFinalHandlerDisposesTheFinalHandler(bool shouldDispo } [Fact] - public async Task CreateClientWithAuthenticationProviderAuthenticatesRequest() + public async Task CreateClientWithAuthenticationProviderAuthenticatesRequestAsync() { var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "https://graph.microsoft.com/me"); var responseMessage = new HttpResponseMessage(HttpStatusCode.OK); @@ -334,7 +334,7 @@ public async Task CreateClientWithAuthenticationProviderAuthenticatesRequest() } [Fact] - public async Task CreateClientWithTokenCredentialAuthenticatesRequest() + public async Task CreateClientWithTokenCredentialAuthenticatesRequestAsync() { var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "https://graph.microsoft.com/me"); var responseMessage = new HttpResponseMessage(HttpStatusCode.OK);