diff --git a/src/Microsoft.Graph.Core/CoreConstants.cs b/src/Microsoft.Graph.Core/CoreConstants.cs index bb017acbd..a0f89b118 100644 --- a/src/Microsoft.Graph.Core/CoreConstants.cs +++ b/src/Microsoft.Graph.Core/CoreConstants.cs @@ -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"; diff --git a/src/Microsoft.Graph.Core/Microsoft.Graph.Core.csproj b/src/Microsoft.Graph.Core/Microsoft.Graph.Core.csproj index 7e9a18890..4bd9bc0e2 100644 --- a/src/Microsoft.Graph.Core/Microsoft.Graph.Core.csproj +++ b/src/Microsoft.Graph.Core/Microsoft.Graph.Core.csproj @@ -100,9 +100,9 @@ - + - + diff --git a/src/Microsoft.Graph.Core/Models/Date.cs b/src/Microsoft.Graph.Core/Models/Date.cs deleted file mode 100644 index d2dc3c58d..000000000 --- a/src/Microsoft.Graph.Core/Models/Date.cs +++ /dev/null @@ -1,82 +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 System; - using System.Text.Json.Serialization; - - /// - /// Custom Date model for serialization - /// - public class Date - { - /// - /// Internal Date constructor - /// - /// - internal Date(DateTime dateTime) - { - this.DateTime = dateTime; - } - - /// - /// Create a new Date object from a year, month, and day. - /// - /// The year. - /// The month. - /// The day of the month. - public Date(int year, int month, int day) - : this(new DateTime(year, month, day)) - { - } - - /// - /// The DateTime object. - /// - internal DateTime DateTime { get; set; } - - /// - /// The date's year. - /// - public int Year - { - get - { - return this.DateTime.Year; - } - } - - /// - /// The date's month. - /// - public int Month - { - get - { - return this.DateTime.Month; - } - } - - /// - /// The date's day. - /// - public int Day - { - get - { - return this.DateTime.Day; - } - } - - /// - /// Convert the date to a string. - /// - /// The string value of the date in the format "yyyy-MM-dd". - public override string ToString() - { - return this.DateTime.ToString("yyyy-MM-dd"); - } - } -} diff --git a/src/Microsoft.Graph.Core/Models/Duration.cs b/src/Microsoft.Graph.Core/Models/Duration.cs deleted file mode 100644 index 0b580bb11..000000000 --- a/src/Microsoft.Graph.Core/Models/Duration.cs +++ /dev/null @@ -1,46 +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 System; - using System.Xml; - using System.Text.Json.Serialization; - - /// - /// Represents an edm.duration value. - /// - public class Duration - { - internal TimeSpan TimeSpan { get; set; } - - /// - /// Create a Duration object from a TimeSpan. - /// - /// - public Duration(TimeSpan timeSpan) - { - this.TimeSpan = timeSpan; - } - - /// - /// Create a Duration object from an ISO8601 duration. - /// - /// An ISO8601 duration. http://en.wikipedia.org/wiki/ISO_8601#Durations - public Duration(string duration) - { - // Convert an ISO8601 duration to a TimeSpan. - this.TimeSpan = XmlConvert.ToTimeSpan(duration); - } - - /// - /// Convert the stored TimeSpan into an ISO8601 duration. - /// - /// An ISO8601 duration. For example, PT1M is "period time of 1 minute" - public override string ToString() - { - return XmlConvert.ToString(this.TimeSpan); - } - } -} diff --git a/src/Microsoft.Graph.Core/Models/IUploadSession.cs b/src/Microsoft.Graph.Core/Models/IUploadSession.cs index 43381d0a1..4a0d82d19 100644 --- a/src/Microsoft.Graph.Core/Models/IUploadSession.cs +++ b/src/Microsoft.Graph.Core/Models/IUploadSession.cs @@ -21,7 +21,7 @@ public interface IUploadSession: IParsable /// /// The ranges yet to be uploaded to the server /// - IEnumerable NextExpectedRanges { get; set; } + List NextExpectedRanges { get; set; } /// /// The URL for upload diff --git a/src/Microsoft.Graph.Core/Models/TimeOfDay.cs b/src/Microsoft.Graph.Core/Models/TimeOfDay.cs deleted file mode 100644 index 77ecd1414..000000000 --- a/src/Microsoft.Graph.Core/Models/TimeOfDay.cs +++ /dev/null @@ -1,75 +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 System; - using System.Text.Json.Serialization; - - /// - /// Time of day model. - /// - public class TimeOfDay - { - internal DateTime DateTime { get; set; } - - internal TimeOfDay(DateTime dateTime) - { - this.DateTime = dateTime; - } - - /// - /// Create a new TimeOfDay from hours, minutes, and seconds. - /// - /// The hour. - /// The minute. - /// The second. - public TimeOfDay(int hour, int minute, int second) - : this(new DateTime(1, 1, 1, hour, minute, second)) - { - } - - /// - /// The hour. - /// - public int Hour - { - get - { - return this.DateTime.Hour; - } - } - - /// - /// The minute. - /// - public int Minute - { - get - { - return this.DateTime.Minute; - } - } - - /// - /// The second. - /// - public int Second - { - get - { - return this.DateTime.Second; - } - } - - /// - /// The time of day, formatted as "HH:mm:ss". - /// - /// The string time of day. - public override string ToString() - { - return this.DateTime.ToString("HH:mm:ss"); - } - } -} diff --git a/src/Microsoft.Graph.Core/Models/UploadSession.cs b/src/Microsoft.Graph.Core/Models/UploadSession.cs index d7f1f034e..7c6623ae0 100644 --- a/src/Microsoft.Graph.Core/Models/UploadSession.cs +++ b/src/Microsoft.Graph.Core/Models/UploadSession.cs @@ -22,7 +22,7 @@ internal class UploadSession : IUploadSession /// /// The ranges yet to be uploaded to the server /// - public IEnumerable NextExpectedRanges { get; set; } + public List NextExpectedRanges { get; set; } /// /// The URL for upload diff --git a/src/Microsoft.Graph.Core/Requests/BaseClient.cs b/src/Microsoft.Graph.Core/Requests/BaseClient.cs index d2c5d052d..bfc6c75ad 100644 --- a/src/Microsoft.Graph.Core/Requests/BaseClient.cs +++ b/src/Microsoft.Graph.Core/Requests/BaseClient.cs @@ -13,7 +13,6 @@ namespace Microsoft.Graph using Microsoft.Kiota.Authentication.Azure; using System.Linq; using Microsoft.Kiota.Abstractions; - using Microsoft.Kiota.Http.HttpClientLibrary; /// /// A default client implementation. @@ -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 }) { } @@ -70,7 +69,7 @@ public BaseClient( /// The customized to be used for making requests 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 }) { } diff --git a/src/Microsoft.Graph.Core/Requests/BaseGraphRequestAdapter.cs b/src/Microsoft.Graph.Core/Requests/BaseGraphRequestAdapter.cs new file mode 100644 index 000000000..7a2e0564b --- /dev/null +++ b/src/Microsoft.Graph.Core/Requests/BaseGraphRequestAdapter.cs @@ -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; + + /// + /// The instance for use with microsoft graph + /// + public class BaseGraphRequestAdapter : HttpClientRequestAdapter + { + /// + /// The public constructor for + /// + /// The authentication provider. + /// The options for the graph client + /// The parse node factory. + /// The serialization writer factory. + /// The native HTTP client. + 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)) + { + } + } +} diff --git a/src/Microsoft.Graph.Core/Requests/GraphClientFactory.cs b/src/Microsoft.Graph.Core/Requests/GraphClientFactory.cs index 375e6fcf9..2f97932d3 100644 --- a/src/Microsoft.Graph.Core/Requests/GraphClientFactory.cs +++ b/src/Microsoft.Graph.Core/Requests/GraphClientFactory.cs @@ -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 @@ -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; + /// /// GraphClientFactory class to create the HTTP client /// 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); @@ -60,17 +45,19 @@ public static class GraphClientFactory /// /// The graph version to use. /// The national cloud endpoint to use. + /// The to use with the client /// The proxy to be used with created client. /// The last HttpMessageHandler to HTTP calls. /// The default implementation creates a new instance of for each HttpClient. /// public static HttpClient Create( + GraphClientOptions graphClientOptions = null, string version = "v1.0", string nationalCloud = Global_Cloud, IWebProxy proxy = null, HttpMessageHandler finalHandler = null) { - IList handlers = CreateDefaultHandlers(); + IList handlers = CreateDefaultHandlers(graphClientOptions); return Create(handlers, version, nationalCloud, proxy, finalHandler); } @@ -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); @@ -120,10 +106,12 @@ public static HttpClient Create( /// /// Create a default set of middleware for calling Microsoft Graph /// + /// The to use with the client /// - public static IList CreateDefaultHandlers() + public static IList CreateDefaultHandlers(GraphClientOptions graphClientOptions = null) { return new List { + new GraphTelemetryHandler(graphClientOptions), new OdataQueryHandler(), new CompressionHandler(), new RetryHandler(), diff --git a/src/Microsoft.Graph.Core/Requests/GraphClientOptions.cs b/src/Microsoft.Graph.Core/Requests/GraphClientOptions.cs new file mode 100644 index 000000000..9563cd7ca --- /dev/null +++ b/src/Microsoft.Graph.Core/Requests/GraphClientOptions.cs @@ -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 +{ + /// + /// The options for setting up a given graph client + /// + public class GraphClientOptions + { + /// + /// The target version of the api endpoint we are targeting (v1 or beta) + /// + public string GraphServiceTargetVersion { get; set; } + + /// + /// The version of the service library in use. Should be in the format `x.x.x` (Semantic version) + /// + public string GraphServiceLibraryClientVersion { get; set; } + + /// + /// The version of the core library in use. Should be in the format `x.x.x` (Semantic version). + /// + public string GraphCoreClientVersion { get; set; } + + /// + /// The product prefix to use in setting the telemetry headers. + /// Will default to `graph-dotnet` if not set. + /// + public string GraphProductPrefix { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.Graph.Core/Requests/Middleware/GraphTelemetryHandler.cs b/src/Microsoft.Graph.Core/Requests/Middleware/GraphTelemetryHandler.cs new file mode 100644 index 000000000..421662aeb --- /dev/null +++ b/src/Microsoft.Graph.Core/Requests/Middleware/GraphTelemetryHandler.cs @@ -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; + + /// + /// A implementation that telemetry for graph. + /// + 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; + + /// + /// The constructor. + /// + /// + public GraphTelemetryHandler(GraphClientOptions graphClientOptions = null) + { + this.graphClientOptions = graphClientOptions ?? new GraphClientOptions(); + } + + /// + /// 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)); + + // 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); + } + + } +} diff --git a/src/Microsoft.Graph.Core/Requests/Middleware/Options/ODataQueryHandlerOption.cs b/src/Microsoft.Graph.Core/Requests/Middleware/Options/ODataQueryHandlerOption.cs index 3d0858c2a..41fbb02b0 100644 --- a/src/Microsoft.Graph.Core/Requests/Middleware/Options/ODataQueryHandlerOption.cs +++ b/src/Microsoft.Graph.Core/Requests/Middleware/Options/ODataQueryHandlerOption.cs @@ -8,6 +8,9 @@ namespace Microsoft.Graph using System; using System.Net.Http; + /// + /// The to configure the + /// public class ODataQueryHandlerOption : IRequestOption { /// diff --git a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/BaseClientTests.cs b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/BaseClientTests.cs index ec0fbed6b..8305d0654 100644 --- a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/BaseClientTests.cs +++ b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/BaseClientTests.cs @@ -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 { @@ -36,7 +35,7 @@ public void BaseClient_InitializeWithTokenCredential() var baseClient = new BaseClient(expectedBaseUrl, this.tokenCredential.Object); Assert.Equal(expectedBaseUrl, baseClient.RequestAdapter.BaseUrl); - Assert.IsType(baseClient.RequestAdapter); + Assert.IsType(baseClient.RequestAdapter); } } diff --git a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/GraphClientFactoryTests.cs b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/GraphClientFactoryTests.cs index 9a6d77976..ddc532956 100644 --- a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/GraphClientFactoryTests.cs +++ b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/GraphClientFactoryTests.cs @@ -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(telemetryHandler); Assert.IsType(odataQueryHandler); Assert.IsType(compressionHandler); Assert.IsType(retryHandler); @@ -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(telemetryHandler); Assert.IsType(odataQueryHandler); Assert.IsType(compressionHandler); Assert.IsType(retryHandler); @@ -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 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() { diff --git a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/Middleware/TelemetryHandlerTests.cs b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/Middleware/TelemetryHandlerTests.cs new file mode 100644 index 000000000..4345a3a87 --- /dev/null +++ b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/Middleware/TelemetryHandlerTests.cs @@ -0,0 +1,99 @@ +// ------------------------------------------------------------------------------ +// 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.Linq; + using System.Net.Http; + using System.Runtime.InteropServices; + using System.Threading; + using System.Threading.Tasks; + using Xunit; + + public class TelemetryHandlerTests + { + private readonly HttpClientRequestAdapter requestAdapter; + public TelemetryHandlerTests() + { + requestAdapter = new HttpClientRequestAdapter(new AnonymousAuthenticationProvider()); + } + + [Fact] + public async Task TelemetryHandlerShouldSetTelemetryHeaderWithDefaults() + { + var configuredTelemetryHandler = new GraphTelemetryHandler(); + configuredTelemetryHandler.InnerHandler = new FakeSuccessHandler(); + var testInvoker = new HttpMessageInvoker(configuredTelemetryHandler); + + // Arrange + var requestInfo = new RequestInformation + { + HttpMethod = Method.GET, + URI = new Uri("http://localhost") + }; + + // Act and get a request message + var requestMessage = requestAdapter.GetRequestMessageFromRequestInformation(requestInfo); + Assert.Empty(requestMessage.Headers); + + // Act + var response = await testInvoker.SendAsync(requestMessage, new CancellationToken()); + + // Assert + Assert.True(response.RequestMessage.Headers.Contains(CoreConstants.Headers.SdkVersionHeaderName)); + Assert.True(response.RequestMessage.Headers.Contains(CoreConstants.Headers.ClientRequestId)); + var telemetryHeaderString = response.RequestMessage.Headers.GetValues(CoreConstants.Headers.SdkVersionHeaderName).First(); + Assert.Contains("graph-dotnet-core/", telemetryHeaderString); + Assert.Contains("(featureUsage=", telemetryHeaderString); + Assert.Contains($" hostOS={Environment.OSVersion};", telemetryHeaderString); + Assert.Contains($" hostArch={RuntimeInformation.OSArchitecture};", telemetryHeaderString); + Assert.Contains($" runtimeEnvironment={RuntimeInformation.FrameworkDescription};", telemetryHeaderString); + } + + [Fact] + public async Task TelemetryHandlerShouldSetTelemetryHeaderWithCustomConfiguration() + { + var clientOptions = new GraphClientOptions + { + GraphCoreClientVersion = "2.0.0", + GraphServiceLibraryClientVersion = "3.0.0", + GraphServiceTargetVersion = "beta", + GraphProductPrefix = "graph-cli" + }; + + var configuredTelemetryHandler = new GraphTelemetryHandler(clientOptions); + configuredTelemetryHandler.InnerHandler = new FakeSuccessHandler(); + var testInvoker = new HttpMessageInvoker(configuredTelemetryHandler); + + // Arrange + var requestInfo = new RequestInformation + { + HttpMethod = Method.GET, + URI = new Uri("http://localhost") + }; + + // Act and get a request message + var requestMessage = requestAdapter.GetRequestMessageFromRequestInformation(requestInfo); + Assert.Empty(requestMessage.Headers); + + // Act + var response = await testInvoker.SendAsync(requestMessage, new CancellationToken()); + + // Assert + Assert.True(response.RequestMessage.Headers.Contains(CoreConstants.Headers.SdkVersionHeaderName)); + Assert.True(response.RequestMessage.Headers.Contains(CoreConstants.Headers.ClientRequestId)); + var telemetryHeaderString = response.RequestMessage.Headers.GetValues(CoreConstants.Headers.SdkVersionHeaderName).First(); + Assert.Contains("graph-cli-core/2.0.0", telemetryHeaderString); + Assert.Contains("graph-cli-beta/3.0.0", telemetryHeaderString); + Assert.Contains("(featureUsage=", telemetryHeaderString); + Assert.Contains($" hostOS={Environment.OSVersion};", telemetryHeaderString); + Assert.Contains($" hostArch={RuntimeInformation.OSArchitecture};", telemetryHeaderString); + Assert.Contains($" runtimeEnvironment={RuntimeInformation.FrameworkDescription};", telemetryHeaderString); + } + } +} diff --git a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/Upload/UploadResponseHandlerTests.cs b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/Upload/UploadResponseHandlerTests.cs index 105f55175..7a21469f2 100644 --- a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/Upload/UploadResponseHandlerTests.cs +++ b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/Upload/UploadResponseHandlerTests.cs @@ -12,9 +12,17 @@ namespace Microsoft.Graph.DotnetCore.Core.Test.Requests using System.Threading.Tasks; using Xunit; using Microsoft.Graph.DotnetCore.Core.Test.TestModels.ServiceModels; + using Microsoft.Kiota.Abstractions.Serialization; + using Microsoft.Kiota.Serialization.Json; public class UploadResponseHandlerTests { + public UploadResponseHandlerTests() + { + // register the default serialization instance as the generator would. + ParseNodeFactoryRegistry.DefaultInstance.ContentTypeAssociatedFactories.TryAdd(CoreConstants.MimeTypeNames.Application.Json, new JsonParseNodeFactory()); + } + [Theory] [InlineData(HttpStatusCode.Created)] [InlineData(HttpStatusCode.OK)] diff --git a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/Upload/UploadSliceRequestTests.cs b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/Upload/UploadSliceRequestTests.cs index dd2a2e0c0..2fc337dbe 100644 --- a/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/Upload/UploadSliceRequestTests.cs +++ b/tests/Microsoft.Graph.DotnetCore.Core.Test/Requests/Upload/UploadSliceRequestTests.cs @@ -13,12 +13,19 @@ namespace Microsoft.Graph.DotnetCore.Core.Test.Requests using System.Threading.Tasks; using Microsoft.Graph.DotnetCore.Core.Test.Mocks; using Microsoft.Graph.DotnetCore.Core.Test.TestModels.ServiceModels; - using Microsoft.Kiota.Abstractions; + using Microsoft.Kiota.Abstractions.Serialization; using Microsoft.Kiota.Http.HttpClientLibrary; + using Microsoft.Kiota.Serialization.Json; using Xunit; public class UploadSliceRequests : RequestTestBase { + public UploadSliceRequests() + { + // register the default serialization instance as the generator would. + ParseNodeFactoryRegistry.DefaultInstance.ContentTypeAssociatedFactories.TryAdd(CoreConstants.MimeTypeNames.Application.Json, new JsonParseNodeFactory()); + } + [Fact] public async Task PutAsyncReturnsExpectedUploadSessionAsync() { diff --git a/tests/Microsoft.Graph.DotnetCore.Core.Test/Serialization/SerializerTests.cs b/tests/Microsoft.Graph.DotnetCore.Core.Test/Serialization/SerializerTests.cs index 1298a02e8..fbefa929e 100644 --- a/tests/Microsoft.Graph.DotnetCore.Core.Test/Serialization/SerializerTests.cs +++ b/tests/Microsoft.Graph.DotnetCore.Core.Test/Serialization/SerializerTests.cs @@ -7,6 +7,7 @@ namespace Microsoft.Graph.DotnetCore.Core.Test.Serialization using Microsoft.Graph.Core.Models; using Microsoft.Graph.DotnetCore.Core.Test.TestModels; using Microsoft.Graph.DotnetCore.Core.Test.TestModels.ServiceModels; + using Microsoft.Kiota.Abstractions; using Microsoft.Kiota.Serialization.Json; using System; using System.Collections.Generic; @@ -146,15 +147,15 @@ public void DeserializeDateEnumerableValue() Assert.Equal(2, deserializedObject.DateCollection.Count()); Assert.True(deserializedObject.DateCollection.Any( date => - date.Year == now.Year && - date.Month == now.Month && - date.Day == now.Day), + date.Value.Year == now.Year && + date.Value.Month == now.Month && + date.Value.Day == now.Day), "Now date not found."); Assert.Contains(deserializedObject.DateCollection, date => - date.Year == tomorrow.Year && - date.Month == tomorrow.Month && - date.Day == tomorrow.Day); + date.Value.Year == tomorrow.Year && + date.Value.Month == tomorrow.Month && + date.Value.Day == tomorrow.Day); } @@ -165,7 +166,7 @@ public void DeserializeMemorableDatesValue() var tomorrow = now.AddDays(1); var recurrence = new DateTestClass { - DateCollection = new List { new Date(now.Year, now.Month, now.Day), new Date(tomorrow.Year, tomorrow.Month, tomorrow.Day) }, + DateCollection = new List { new Date(now.Year, now.Month, now.Day), new Date(tomorrow.Year, tomorrow.Month, tomorrow.Day) }, }; var derivedTypeInstance = new DerivedTypeClass @@ -199,9 +200,9 @@ public void DeserializeDateValue() var parseNode = this.parseNodeFactory.GetRootParseNode(CoreConstants.MimeTypeNames.Application.Json, memoryStream); var dateClass = parseNode.GetObjectValue(); - Assert.Equal(now.Year, dateClass.NullableDate.Year); - Assert.Equal(now.Month, dateClass.NullableDate.Month); - Assert.Equal(now.Day, dateClass.NullableDate.Day); + Assert.Equal(now.Year, dateClass.NullableDate.Value.Year); + Assert.Equal(now.Month, dateClass.NullableDate.Value.Month); + Assert.Equal(now.Day, dateClass.NullableDate.Value.Day); } [Fact] @@ -316,7 +317,7 @@ public void SerializeDateEnumerableValue() var recurrence = new DateTestClass { - DateCollection = new List { new Date(now.Year, now.Month, now.Day), new Date(tomorrow.Year, tomorrow.Month, tomorrow.Day) }, + DateCollection = new List { new Date(now.Year, now.Month, now.Day), new Date(tomorrow.Year, tomorrow.Month, tomorrow.Day) }, }; using var jsonSerializerWriter = new JsonSerializationWriter(); diff --git a/tests/Microsoft.Graph.DotnetCore.Core.Test/TestModels/DateTestClass.cs b/tests/Microsoft.Graph.DotnetCore.Core.Test/TestModels/DateTestClass.cs index 523903218..0fc9459e4 100644 --- a/tests/Microsoft.Graph.DotnetCore.Core.Test/TestModels/DateTestClass.cs +++ b/tests/Microsoft.Graph.DotnetCore.Core.Test/TestModels/DateTestClass.cs @@ -4,6 +4,7 @@ namespace Microsoft.Graph.DotnetCore.Core.Test.TestModels { + using Microsoft.Kiota.Abstractions; using Microsoft.Kiota.Abstractions.Serialization; using System; using System.Collections.Generic; @@ -17,12 +18,12 @@ public class DateTestClass: IParsable /// /// Gets or sets nullableDate. /// - public Date NullableDate { get; set; } + public Date? NullableDate { get; set; } /// /// Gets or sets dateCollection. /// - public IEnumerable DateCollection { get; set; } + public IEnumerable DateCollection { get; set; } /// /// Gets or sets InvalidType. @@ -48,8 +49,8 @@ public IDictionary> GetFieldDeserializers() { return new Dictionary> { - {"nullableDate", (o,n) => { (o as DateTestClass).NullableDate = new Date(n.GetDateTimeOffsetValue().Value.DateTime); } }, - {"dateCollection", (o,n) => { (o as DateTestClass).DateCollection = n.GetCollectionOfPrimitiveValues().Select(dateTimeOffset => new Date(dateTimeOffset.Value.DateTime) ); } }, + {"nullableDate", (o,n) => { (o as DateTestClass).NullableDate = n.GetDateValue(); } }, + {"dateCollection", (o,n) => { (o as DateTestClass).DateCollection = n.GetCollectionOfPrimitiveValues(); } }, {"invalidType", (o,n) => { (o as DateTestClass).InvalidType = n.GetIntValue(); } }, }; } @@ -62,8 +63,8 @@ public IDictionary> GetFieldDeserializers() public void Serialize(ISerializationWriter writer) { _ = writer ?? throw new ArgumentNullException(nameof(writer)); - writer.WriteStringValue("nullableDate", NullableDate?.ToString()); - writer.WriteCollectionOfPrimitiveValues("dateCollection", DateCollection?.Select( date => date.ToString())); + writer.WriteDateValue("nullableDate", NullableDate); + writer.WriteCollectionOfPrimitiveValues("dateCollection", DateCollection); writer.WriteIntValue("invalidType", InvalidType); writer.WriteAdditionalData(AdditionalData); }