Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Microsoft.Graph.Core/CoreConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public static class Headers
public const string SdkVersionHeaderName = "SdkVersion";

/// SDK Version header
public const string SdkVersionHeaderValueFormatString = "{0}-dotnet-{1}.{2}.{3}";
public const string SdkVersionHeaderValueFormatString = "graph-dotnet-core/{0}.{1}.{2}";

/// Content-Type header
public const string FormUrlEncodedContentType = "application/x-www-form-urlencoded";
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.Graph.Core/Models/IUploadSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public interface IUploadSession: IParsable
/// <summary>
/// The ranges yet to be uploaded to the server
/// </summary>
IEnumerable<string> NextExpectedRanges { get; set; }
List<string> NextExpectedRanges { get; set; }

/// <summary>
/// The URL for upload
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.Graph.Core/Models/UploadSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ internal class UploadSession : IUploadSession
/// <summary>
/// The ranges yet to be uploaded to the server
/// </summary>
public IEnumerable<string> NextExpectedRanges { get; set; }
public List<string> NextExpectedRanges { get; set; }

/// <summary>
/// The URL for upload
Expand Down
5 changes: 2 additions & 3 deletions src/Microsoft.Graph.Core/Requests/BaseClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ namespace Microsoft.Graph
using Microsoft.Kiota.Authentication.Azure;
using System.Linq;
using Microsoft.Kiota.Abstractions;
using Microsoft.Kiota.Http.HttpClientLibrary;

/// <summary>
/// A default client implementation.
Expand Down Expand Up @@ -45,7 +44,7 @@ public BaseClient(
public BaseClient(
string baseUrl,
IAuthenticationProvider authenticationProvider
): this(new HttpClientRequestAdapter(authenticationProvider , httpClient: GraphClientFactory.Create()){ BaseUrl = baseUrl })
): this(new BaseGraphRequestAdapter(authenticationProvider){ BaseUrl = baseUrl })
{
}

Expand All @@ -70,7 +69,7 @@ public BaseClient(
/// <param name="httpClient">The customized <see cref="HttpClient"/> to be used for making requests</param>
public BaseClient(
string baseUrl,
HttpClient httpClient):this(new HttpClientRequestAdapter(new AnonymousAuthenticationProvider(), httpClient: httpClient) { BaseUrl = baseUrl })
HttpClient httpClient):this(new BaseGraphRequestAdapter(new AnonymousAuthenticationProvider(), httpClient: httpClient) { BaseUrl = baseUrl })
{
}

Expand Down
31 changes: 31 additions & 0 deletions src/Microsoft.Graph.Core/Requests/BaseGraphRequestAdapter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// ------------------------------------------------------------------------------
// 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 Microsoft.Kiota.Abstractions.Authentication;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Http.HttpClientLibrary;
using System.Net.Http;

/// <summary>
/// The <see cref="IRequestAdapter"/> instance for use with microsoft graph
/// </summary>
public class BaseGraphRequestAdapter : HttpClientRequestAdapter
{
/// <summary>
/// The public constructor for <see cref="BaseGraphRequestAdapter"/>
/// </summary>
/// <param name="authenticationProvider">The authentication provider.</param>
/// <param name="graphClientOptions">The options for the graph client</param>
/// <param name="parseNodeFactory">The parse node factory.</param>
/// <param name="serializationWriterFactory">The serialization writer factory.</param>
/// <param name="httpClient">The native HTTP client.</param>
public BaseGraphRequestAdapter(IAuthenticationProvider authenticationProvider, GraphClientOptions graphClientOptions = null, IParseNodeFactory parseNodeFactory = null, ISerializationWriterFactory serializationWriterFactory = null, HttpClient httpClient = null)
: base(authenticationProvider, parseNodeFactory ?? ParseNodeFactoryRegistry.DefaultInstance, serializationWriterFactory ?? SerializationWriterFactoryRegistry.DefaultInstance, httpClient ?? GraphClientFactory.Create(graphClientOptions))
{
}
}
}
28 changes: 8 additions & 20 deletions src/Microsoft.Graph.Core/Requests/GraphClientFactory.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
// ------------------------------------------------------------------------------
namespace Microsoft.Graph
Expand All @@ -8,29 +8,14 @@ namespace Microsoft.Graph
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Net.Http.Headers;
using Microsoft.Kiota.Http.HttpClientLibrary.Middleware;
using Microsoft.Kiota.Abstractions.Authentication;

/// <summary>
/// GraphClientFactory class to create the HTTP client
/// </summary>
public static class GraphClientFactory
{
/// The key for the SDK version header.
private static readonly string SdkVersionHeaderName = CoreConstants.Headers.SdkVersionHeaderName;

/// The version for current assembly.
private static Version assemblyVersion = typeof(GraphClientFactory).GetTypeInfo().Assembly.GetName().Version;

/// The value for the SDK version header.
private static string SdkVersionHeaderValue = string.Format(
CoreConstants.Headers.SdkVersionHeaderValueFormatString,
"Graph",
assemblyVersion.Major,
assemblyVersion.Minor,
assemblyVersion.Build);

/// The default value for the overall request timeout.
private static readonly TimeSpan defaultTimeout = TimeSpan.FromSeconds(100);

Expand Down Expand Up @@ -60,17 +45,19 @@ public static class GraphClientFactory
/// </summary>
/// <param name="version">The graph version to use.</param>
/// <param name="nationalCloud">The national cloud endpoint to use.</param>
/// <param name="graphClientOptions">The <see cref="GraphClientOptions"/> to use with the client</param>
/// <param name="proxy">The proxy to be used with created client.</param>
/// <param name="finalHandler">The last HttpMessageHandler to HTTP calls.
/// The default implementation creates a new instance of <see cref="HttpClientHandler"/> for each HttpClient.</param>
/// <returns></returns>
public static HttpClient Create(
GraphClientOptions graphClientOptions = null,
string version = "v1.0",
string nationalCloud = Global_Cloud,
IWebProxy proxy = null,
HttpMessageHandler finalHandler = null)
{
IList<DelegatingHandler> handlers = CreateDefaultHandlers();
IList<DelegatingHandler> handlers = CreateDefaultHandlers(graphClientOptions);
return Create(handlers, version, nationalCloud, proxy, finalHandler);
}

Expand Down Expand Up @@ -109,7 +96,6 @@ public static HttpClient Create(

var pipelineWithFlags = CreatePipelineWithFeatureFlags(handlers, finalHandler);
HttpClient client = new HttpClient(pipelineWithFlags.Pipeline);
client.DefaultRequestHeaders.Add(SdkVersionHeaderName, SdkVersionHeaderValue);
client.SetFeatureFlag(pipelineWithFlags.FeatureFlags);
client.Timeout = defaultTimeout;
client.BaseAddress = DetermineBaseAddress(nationalCloud, version);
Expand All @@ -120,10 +106,12 @@ public static HttpClient Create(
/// <summary>
/// Create a default set of middleware for calling Microsoft Graph
/// </summary>
/// <param name="graphClientOptions">The <see cref="GraphClientOptions"/> to use with the client</param>
/// <returns></returns>
public static IList<DelegatingHandler> CreateDefaultHandlers()
public static IList<DelegatingHandler> CreateDefaultHandlers(GraphClientOptions graphClientOptions = null)
{
return new List<DelegatingHandler> {
new GraphTelemetryHandler(graphClientOptions),
new OdataQueryHandler(),
new CompressionHandler(),
new RetryHandler(),
Expand Down
33 changes: 33 additions & 0 deletions src/Microsoft.Graph.Core/Requests/GraphClientOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// ------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
// ------------------------------------------------------------------------------

namespace Microsoft.Graph
{
/// <summary>
/// The options for setting up a given graph client
/// </summary>
public class GraphClientOptions
{
/// <summary>
/// The target version of the api endpoint we are targeting (v1 or beta)
/// </summary>
public string GraphServiceTargetVersion { get; set; }

/// <summary>
/// The version of the service library in use. Should be in the format `x.x.x` (Semantic version)
/// </summary>
public string GraphServiceLibraryClientVersion { get; set; }

/// <summary>
/// The version of the core library in use. Should be in the format `x.x.x` (Semantic version).
/// </summary>
public string GraphCoreClientVersion { get; set; }

/// <summary>
/// The product prefix to use in setting the telemetry headers.
/// Will default to `graph-dotnet` if not set.
/// </summary>
public string GraphProductPrefix { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// ------------------------------------------------------------------------------
// 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 System;
using System.Net.Http;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

/// <summary>
/// A <see cref="DelegatingHandler"/> implementation that telemetry for graph.
/// </summary>
public class GraphTelemetryHandler : DelegatingHandler
{
/// The version for current assembly.
private static Version assemblyVersion = typeof(GraphTelemetryHandler).GetTypeInfo().Assembly.GetName().Version;

/// The value for the SDK version header.
private static string SdkVersionHeaderValue = string.Format(
CoreConstants.Headers.SdkVersionHeaderValueFormatString,
assemblyVersion.Major,
assemblyVersion.Minor,
assemblyVersion.Build);

private readonly GraphClientOptions graphClientOptions;

/// <summary>
/// The <see cref="GraphClientOptions"/> constructor.
/// </summary>
/// <param name="graphClientOptions"></param>
public GraphTelemetryHandler(GraphClientOptions graphClientOptions = null)
{
this.graphClientOptions = graphClientOptions ?? new GraphClientOptions();
}

/// <summary>
/// Sends a HTTP request.
/// </summary>
/// <param name="httpRequest">The <see cref="HttpRequestMessage"/> to be sent.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the request.</param>
/// <returns></returns>
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage httpRequest, CancellationToken cancellationToken)
{
if (httpRequest == null)
throw new ArgumentNullException(nameof(httpRequest));

// Build the service library string from the options
var serviceLibraryString = string.Empty;
if (!string.IsNullOrEmpty(graphClientOptions?.GraphServiceLibraryClientVersion))
{
serviceLibraryString = graphClientOptions?.GraphProductPrefix ?? "graph-dotnet";
if (!string.IsNullOrEmpty(graphClientOptions?.GraphServiceTargetVersion))
serviceLibraryString += $"-{graphClientOptions?.GraphServiceTargetVersion}";
serviceLibraryString += $"/{graphClientOptions?.GraphServiceLibraryClientVersion},";
}

// Default to the version string we have, otherwise use the ope provided
var coreLibraryString = SdkVersionHeaderValue;
if (!string.IsNullOrEmpty(graphClientOptions?.GraphCoreClientVersion) && !string.IsNullOrEmpty(graphClientOptions?.GraphProductPrefix))
{
coreLibraryString = $"{graphClientOptions?.GraphProductPrefix}-core/{graphClientOptions?.GraphCoreClientVersion}";
}

// Get the features section of the telemetry header
var features = string.Empty;
if (Environment.OSVersion != null)
features += " hostOS=" + Environment.OSVersion + ";" + " hostArch=" + RuntimeInformation.OSArchitecture + ";"; ;
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());

return base.SendAsync(httpRequest, cancellationToken);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ namespace Microsoft.Graph
using System;
using System.Net.Http;

/// <summary>
/// The <see cref="IRequestOption"/> to configure the <see cref="OdataQueryHandler"/>
/// </summary>
public class ODataQueryHandlerOption : IRequestOption
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
namespace Microsoft.Graph.DotnetCore.Core.Test.Requests
{
using Microsoft.Graph.DotnetCore.Core.Test.Mocks;
using Microsoft.Kiota.Http.HttpClientLibrary;
using Xunit;
public class BaseClientTests
{
Expand Down Expand Up @@ -36,7 +35,7 @@ public void BaseClient_InitializeWithTokenCredential()
var baseClient = new BaseClient(expectedBaseUrl, this.tokenCredential.Object);

Assert.Equal(expectedBaseUrl, baseClient.RequestAdapter.BaseUrl);
Assert.IsType<HttpClientRequestAdapter>(baseClient.RequestAdapter);
Assert.IsType<BaseGraphRequestAdapter>(baseClient.RequestAdapter);

}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,20 @@ public void Should_CreatePipeline_Without_CompressionHandler()
[Fact]
public void Should_CreatePipeline_Without_HttpMessageHandlerInput()
{
using OdataQueryHandler odataQueryHandler = (OdataQueryHandler)GraphClientFactory.CreatePipeline(handlers);
using GraphTelemetryHandler telemetryHandler = (GraphTelemetryHandler)GraphClientFactory.CreatePipeline(handlers);
using OdataQueryHandler odataQueryHandler = (OdataQueryHandler)telemetryHandler.InnerHandler;
using CompressionHandler compressionHandler = (CompressionHandler)odataQueryHandler.InnerHandler;
using RetryHandler retryHandler = (RetryHandler)compressionHandler.InnerHandler;
using RedirectHandler redirectHandler = (RedirectHandler)retryHandler.InnerHandler;
using HttpMessageHandler innerMost = redirectHandler.InnerHandler;

Assert.NotNull(telemetryHandler);
Assert.NotNull(odataQueryHandler);
Assert.NotNull(compressionHandler);
Assert.NotNull(retryHandler);
Assert.NotNull(redirectHandler);
Assert.NotNull(innerMost);
Assert.IsType<GraphTelemetryHandler>(telemetryHandler);
Assert.IsType<OdataQueryHandler>(odataQueryHandler);
Assert.IsType<CompressionHandler>(compressionHandler);
Assert.IsType<RetryHandler>(retryHandler);
Expand All @@ -90,17 +93,20 @@ public void Should_CreatePipeline_Without_HttpMessageHandlerInput()
[Fact]
public void CreatePipelineWithHttpMessageHandlerInput()
{
using OdataQueryHandler odataQueryHandler = (OdataQueryHandler)GraphClientFactory.CreatePipeline(handlers, new MockRedirectHandler());
using GraphTelemetryHandler telemetryHandler = (GraphTelemetryHandler)GraphClientFactory.CreatePipeline(handlers, new MockRedirectHandler());
using OdataQueryHandler odataQueryHandler = (OdataQueryHandler)telemetryHandler.InnerHandler;
using CompressionHandler compressionHandler = (CompressionHandler)odataQueryHandler.InnerHandler;
using RetryHandler retryHandler = (RetryHandler)compressionHandler.InnerHandler;
using RedirectHandler redirectHandler = (RedirectHandler)retryHandler.InnerHandler;
using MockRedirectHandler innerMost = (MockRedirectHandler)redirectHandler.InnerHandler;


Assert.NotNull(telemetryHandler);
Assert.NotNull(odataQueryHandler);
Assert.NotNull(compressionHandler);
Assert.NotNull(retryHandler);
Assert.NotNull(redirectHandler);
Assert.NotNull(innerMost);
Assert.IsType<GraphTelemetryHandler>(telemetryHandler);
Assert.IsType<OdataQueryHandler>(odataQueryHandler);
Assert.IsType<CompressionHandler>(compressionHandler);
Assert.IsType<RetryHandler>(retryHandler);
Expand Down Expand Up @@ -173,27 +179,6 @@ public void CreateClient_SelectedCloudWithExceptions()
}
}

[Fact]
public void CreateClient_WithInnerHandler()
{
using (HttpClient httpClient = GraphClientFactory.Create(finalHandler: this.testHttpMessageHandler))
{
Assert.NotNull(httpClient);
Assert.True(httpClient.DefaultRequestHeaders.Contains(CoreConstants.Headers.SdkVersionHeaderName), "SDK version not set.");
Version assemblyVersion = typeof(GraphClientFactory).GetTypeInfo().Assembly.GetName().Version;
string value = string.Format(
CoreConstants.Headers.SdkVersionHeaderValueFormatString,
"Graph",
assemblyVersion.Major,
assemblyVersion.Minor,
assemblyVersion.Build);
IEnumerable<string> values;
Assert.True(httpClient.DefaultRequestHeaders.TryGetValues(CoreConstants.Headers.SdkVersionHeaderName, out values), "SDK version value not set");
Assert.Single(values);
Assert.Equal(values.First(), value);
}
}

[Fact]
public void CreateClient_WithHandlers()
{
Expand Down
Loading