From 44c8d8dcab70a8b3665c03d940f4287beb30641c Mon Sep 17 00:00:00 2001 From: Brandon Minnick <13558917+brminnick@users.noreply.github.com> Date: Thu, 26 Nov 2020 12:46:47 -0800 Subject: [PATCH] Add GitHubApiStatusService.IsProductHeaderValueValid and IsProductHeaderValueValid.IsAuthenticationHeaderValueSet --- ...sResponseFromAuthenticatedRequestTests.cs} | 16 ++--- ...lueTests.cs => ProductHeaderValueTests.cs} | 6 +- .../SetAuthenticationHeaderValueTests.cs | 1 + Src/GitHubApiStatus/GitHubApiStatus.csproj | 2 +- .../GitHubApiStatusException.cs | 11 +++- .../Interfaces/IGitHubApiStatusService.cs | 2 +- .../Models/GitHubApiStatusClient.cs | 8 +++ .../Services/GitHubApiStatusService.cs | 64 +++++++++++++++---- Src/GitStatus.ConsoleApp/Program.cs | 12 ++-- 9 files changed, 92 insertions(+), 30 deletions(-) rename Src/GitHubApiStatus.UnitTests/Tests/GitHubApiStatusServiceTests/{IsAuthenticatedTests.cs => IsResponseFromAuthenticatedRequestTests.cs} (74%) rename Src/GitHubApiStatus.UnitTests/Tests/GitHubApiStatusServiceTests/{AddProductHeaderValueTests.cs => ProductHeaderValueTests.cs} (88%) diff --git a/Src/GitHubApiStatus.UnitTests/Tests/GitHubApiStatusServiceTests/IsAuthenticatedTests.cs b/Src/GitHubApiStatus.UnitTests/Tests/GitHubApiStatusServiceTests/IsResponseFromAuthenticatedRequestTests.cs similarity index 74% rename from Src/GitHubApiStatus.UnitTests/Tests/GitHubApiStatusServiceTests/IsAuthenticatedTests.cs rename to Src/GitHubApiStatus.UnitTests/Tests/GitHubApiStatusServiceTests/IsResponseFromAuthenticatedRequestTests.cs index a08287a..c6a0a4b 100644 --- a/Src/GitHubApiStatus.UnitTests/Tests/GitHubApiStatusServiceTests/IsAuthenticatedTests.cs +++ b/Src/GitHubApiStatus.UnitTests/Tests/GitHubApiStatusServiceTests/IsResponseFromAuthenticatedRequestTests.cs @@ -8,7 +8,7 @@ namespace GitHubApiStatus.UnitTests class IsAuthenticatedTests : BaseTest { [Test] - public void IsUserAuthenticated_ValidHttpResponseHeaders_True() + public void IsResponseFromAuthenticatedRequest_ValidHttpResponseHeaders_True() { //Act bool isUserAuthenticated_Actual; @@ -21,14 +21,14 @@ public void IsUserAuthenticated_ValidHttpResponseHeaders_True() var validHttpResponseHeaders = CreateHttpResponseHeaders(rateLimit, DateTimeOffset.UtcNow, rateLimitRemaining, isAuthenticated: isUserAuthenticated_Expected); //Act - isUserAuthenticated_Actual = GitHubApiStatusService.IsAuthenticated(validHttpResponseHeaders); + isUserAuthenticated_Actual = GitHubApiStatusService.IsResponseFromAuthenticatedRequest(validHttpResponseHeaders); //Assert Assert.AreEqual(isUserAuthenticated_Expected, isUserAuthenticated_Actual); } [Test] - public void IsUserAuthenticated_ValidHttpResponseHeaders_False() + public void IsResponseFromAuthenticatedRequest_ValidHttpResponseHeaders_False() { //Act bool isUserAuthenticated_Actual; @@ -41,27 +41,27 @@ public void IsUserAuthenticated_ValidHttpResponseHeaders_False() var validHttpResponseHeaders = CreateHttpResponseHeaders(rateLimit, DateTimeOffset.UtcNow, rateLimitRemaining, isAuthenticated: isUserAuthenticated_Expected); //Act - isUserAuthenticated_Actual = GitHubApiStatusService.IsAuthenticated(validHttpResponseHeaders); + isUserAuthenticated_Actual = GitHubApiStatusService.IsResponseFromAuthenticatedRequest(validHttpResponseHeaders); //Assert Assert.AreEqual(isUserAuthenticated_Expected, isUserAuthenticated_Actual); } [Test] - public void IsUserAuthenticated_InvalidHttpResponseHeaders() + public void IsResponseFromAuthenticatedRequest_InvalidHttpResponseHeaders() { //Arrange var invalidHttpResponseMessage = new HttpResponseMessage(); //Act - var isUserAuthenticated = GitHubApiStatusService.IsAuthenticated(invalidHttpResponseMessage.Headers); + var isUserAuthenticated = GitHubApiStatusService.IsResponseFromAuthenticatedRequest(invalidHttpResponseMessage.Headers); //Assert Assert.IsFalse(isUserAuthenticated); } [Test] - public void IsUserAuthenticated_NullHttpResponseHeaders() + public void IsResponseFromAuthenticatedRequest_NullHttpResponseHeaders() { //Arrange HttpResponseHeaders? nullHttpResponseHeaders = null; @@ -70,7 +70,7 @@ public void IsUserAuthenticated_NullHttpResponseHeaders() //Assert #pragma warning disable CS8604 // Possible null reference argument. - Assert.Throws(() => GitHubApiStatusService.IsAuthenticated(nullHttpResponseHeaders)); + Assert.Throws(() => GitHubApiStatusService.IsResponseFromAuthenticatedRequest(nullHttpResponseHeaders)); #pragma warning restore CS8604 // Possible null reference argument. } } diff --git a/Src/GitHubApiStatus.UnitTests/Tests/GitHubApiStatusServiceTests/AddProductHeaderValueTests.cs b/Src/GitHubApiStatus.UnitTests/Tests/GitHubApiStatusServiceTests/ProductHeaderValueTests.cs similarity index 88% rename from Src/GitHubApiStatus.UnitTests/Tests/GitHubApiStatusServiceTests/AddProductHeaderValueTests.cs rename to Src/GitHubApiStatus.UnitTests/Tests/GitHubApiStatusServiceTests/ProductHeaderValueTests.cs index 4c83ec2..c8d0da4 100644 --- a/Src/GitHubApiStatus.UnitTests/Tests/GitHubApiStatusServiceTests/AddProductHeaderValueTests.cs +++ b/Src/GitHubApiStatus.UnitTests/Tests/GitHubApiStatusServiceTests/ProductHeaderValueTests.cs @@ -6,7 +6,7 @@ namespace GitHubApiStatus.UnitTests { - class AddProductHeaderValueTests + class AddProductHeaderValueTests : BaseTest { [Test] public void NullProductHeaderValueTest() @@ -20,6 +20,7 @@ public void NullProductHeaderValueTest() #pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. Assert.Throws(() => gitHubApiStatusService.AddProductHeaderValue(null)); #pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. + Assert.IsFalse(gitHubApiStatusService.IsProductHeaderValueValid); } [Test] @@ -32,6 +33,7 @@ public void NullNameTest() //Assert Assert.Throws(() => gitHubApiStatusService.AddProductHeaderValue(new ProductHeaderValue(null))); + Assert.IsFalse(gitHubApiStatusService.IsProductHeaderValueValid); } [Test] @@ -47,6 +49,8 @@ public async Task ValidProductHeaderValueTest() //Assert Assert.IsNotNull(gitHubApiStatusService); + Assert.IsTrue(gitHubApiStatusService.IsProductHeaderValueValid); + Assert.IsNotNull(apiRateLimits); Assert.IsNotNull(apiRateLimits.AppManifestConfiguration); Assert.IsNotNull(apiRateLimits.CodeScanningUpload); diff --git a/Src/GitHubApiStatus.UnitTests/Tests/GitHubApiStatusServiceTests/SetAuthenticationHeaderValueTests.cs b/Src/GitHubApiStatus.UnitTests/Tests/GitHubApiStatusServiceTests/SetAuthenticationHeaderValueTests.cs index 7579265..add0e5b 100644 --- a/Src/GitHubApiStatus.UnitTests/Tests/GitHubApiStatusServiceTests/SetAuthenticationHeaderValueTests.cs +++ b/Src/GitHubApiStatus.UnitTests/Tests/GitHubApiStatusServiceTests/SetAuthenticationHeaderValueTests.cs @@ -20,6 +20,7 @@ public void NullAuthenticationHeaderValue() #pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. Assert.Throws(() => gitHubApiStatusService.SetAuthenticationHeaderValue(null)); #pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. + Assert.IsFalse(gitHubApiStatusService.IsAuthenticationHeaderValueSet); } [TestCase("Basic")] diff --git a/Src/GitHubApiStatus/GitHubApiStatus.csproj b/Src/GitHubApiStatus/GitHubApiStatus.csproj index b50399d..2d821ed 100644 --- a/Src/GitHubApiStatus/GitHubApiStatus.csproj +++ b/Src/GitHubApiStatus/GitHubApiStatus.csproj @@ -46,7 +46,7 @@ New In This Release: - Added GitHubApiStatusService.SetAuthenticationHeaderValue() - Breaking Changes: Removed GitHubApiStatusService.Instance - 2.0.0-pre2 + 2.0.0-pre3 https://github.com/brminnick/GitHubApiStatus $(AssemblyName) ($(TargetFramework)) 1.0.0.0 diff --git a/Src/GitHubApiStatus/GitHubApiStatusException.cs b/Src/GitHubApiStatus/GitHubApiStatusException.cs index 345a8b9..724428e 100644 --- a/Src/GitHubApiStatus/GitHubApiStatusException.cs +++ b/Src/GitHubApiStatus/GitHubApiStatusException.cs @@ -1,9 +1,16 @@ using System; namespace GitHubApiStatus { - public class GitHubApiStatusException : Exception + /// + /// Exception thrown by GitHubApiStatus + /// + public sealed class GitHubApiStatusException : Exception { - public GitHubApiStatusException(string message) : base(message) + /// + /// Initialize GitHubApiStatusException + /// + /// + internal GitHubApiStatusException(string message) : base(message) { } } diff --git a/Src/GitHubApiStatus/Interfaces/IGitHubApiStatusService.cs b/Src/GitHubApiStatus/Interfaces/IGitHubApiStatusService.cs index 56c44b0..626501f 100644 --- a/Src/GitHubApiStatus/Interfaces/IGitHubApiStatusService.cs +++ b/Src/GitHubApiStatus/Interfaces/IGitHubApiStatusService.cs @@ -50,7 +50,7 @@ public interface IGitHubApiStatusService /// /// HttpResponseHeaders from GitHub API Response /// Whether the Http Response Was From an Authenticated Http Request - bool IsAuthenticated(in HttpResponseHeaders httpResponseHeaders); + bool IsResponseFromAuthenticatedRequest(in HttpResponseHeaders httpResponseHeaders); /// /// Get the DateTimeOffset When the GitHub API Rate Limit Will Reset diff --git a/Src/GitHubApiStatus/Models/GitHubApiStatusClient.cs b/Src/GitHubApiStatus/Models/GitHubApiStatusClient.cs index 641a0bc..542010a 100644 --- a/Src/GitHubApiStatus/Models/GitHubApiStatusClient.cs +++ b/Src/GitHubApiStatus/Models/GitHubApiStatusClient.cs @@ -3,8 +3,16 @@ namespace GitHubApiStatus { + /// + /// HttpClient for GitHub's API + /// public class GitHubApiClient : HttpClient { + /// + /// Initialize GitHubApiClient + /// + /// GitHub Personal Access Token, aka Bearer Token + /// User Agent public GitHubApiClient(AuthenticationHeaderValue authenticationHeaderValue, ProductHeaderValue productHeaderValue) { GitHubApiStatusService.ValidateAuthenticationHeaderValue(authenticationHeaderValue); diff --git a/Src/GitHubApiStatus/Services/GitHubApiStatusService.cs b/Src/GitHubApiStatus/Services/GitHubApiStatusService.cs index bfe8a96..3055ed5 100644 --- a/Src/GitHubApiStatus/Services/GitHubApiStatusService.cs +++ b/Src/GitHubApiStatus/Services/GitHubApiStatusService.cs @@ -18,6 +18,8 @@ namespace GitHubApiStatus /// public class GitHubApiStatusService : IGitHubApiStatusService { + + readonly GitHubApiClient _client; /// /// GitHub Http Response Rate Limit Header Key /// @@ -42,7 +44,7 @@ public class GitHubApiStatusService : IGitHubApiStatusService /// public GitHubApiStatusService() { - Client = new GitHubApiClient(); + _client = new GitHubApiClient(); } /// @@ -52,7 +54,7 @@ public GitHubApiStatusService() /// User-Agent Name public GitHubApiStatusService(AuthenticationHeaderValue authenticationHeaderValue, ProductHeaderValue productHeaderValue) { - Client = new GitHubApiClient(authenticationHeaderValue, productHeaderValue); + _client = new GitHubApiClient(authenticationHeaderValue, productHeaderValue); } /// @@ -67,31 +69,71 @@ public GitHubApiStatusService(GitHubApiClient client) ValidateAuthenticationHeaderValue(client.DefaultRequestHeaders.Authorization); ValidateProductHeaderValue(client.DefaultRequestHeaders.UserAgent); - Client = client; + _client = client; + } + + /// + /// Determines if GitHubApiClient.DefaultRequestHeaders.UserAgent is Valid + /// + public bool IsProductHeaderValueValid + { + get + { + try + { + ValidateProductHeaderValue(_client.DefaultRequestHeaders.UserAgent); + return true; + } + catch + { + return false; + } + } } - GitHubApiClient Client { get; } + /// + /// Determines if GitHubApiClient.DefaultRequestHeaders.Authorization is Valid + /// + public bool IsAuthenticationHeaderValueSet + { + get + { + try + { + ValidateAuthenticationHeaderValue(_client.DefaultRequestHeaders.Authorization); + return true; + } + catch + { + return false; + } + } + } #if NETSTANDARD static JsonSerializer Serializer => _serializerHolder.Value; #endif /// - /// Adds ProductHeaderValue to + /// Add ProductHeaderValue to HttpClient.DefaultRequestHeaders.UserAgent /// /// public void AddProductHeaderValue(ProductHeaderValue productHeaderValue) { ValidateProductHeaderValue(productHeaderValue); - Client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(productHeaderValue)); + _client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(productHeaderValue)); } + /// + /// Set HttpClient.DefaultRequestHeaders.Authorization + /// + /// public void SetAuthenticationHeaderValue(AuthenticationHeaderValue authenticationHeaderValue) { ValidateAuthenticationHeaderValue(authenticationHeaderValue); - Client.DefaultRequestHeaders.Authorization = authenticationHeaderValue; + _client.DefaultRequestHeaders.Authorization = authenticationHeaderValue; } /// @@ -107,7 +149,7 @@ public void SetAuthenticationHeaderValue(AuthenticationHeaderValue authenticatio /// public async Task GetApiRateLimits(CancellationToken cancellationToken) { - var response = await GetGitHubApiRateLimitResponse(Client, cancellationToken).ConfigureAwait(false); + var response = await GetGitHubApiRateLimitResponse(_client, cancellationToken).ConfigureAwait(false); return response.Results; } @@ -157,10 +199,10 @@ public int GetRemainingRequestCount(in HttpResponseHeaders httpResponseHeaders) /// /// HttpResponseHeaders from GitHub API Response /// Whether the Http Response Was From an Authenticated Http Request - public bool IsAuthenticated(in HttpResponseHeaders httpResponseHeaders) + public bool IsResponseFromAuthenticatedRequest(in HttpResponseHeaders httpResponseHeaders) { ValidateHttpResponseHeaders(httpResponseHeaders); - return httpResponseHeaders?.Vary.Any(x => x is "Authorization") ?? throw new ArgumentNullException(nameof(httpResponseHeaders)); + return httpResponseHeaders.Vary.Any(x => x is "Authorization"); } /// @@ -187,7 +229,7 @@ public long GetRateLimitResetDateTime_UnixEpochSeconds(in HttpResponseHeaders ht { ValidateHttpResponseHeaders(httpResponseHeaders); - var rateLimitResetHeader = httpResponseHeaders?.Single(x => x.Key.Equals(RateLimitResetHeader, StringComparison.OrdinalIgnoreCase)) ?? throw new ArgumentNullException(nameof(httpResponseHeaders)); + var rateLimitResetHeader = httpResponseHeaders.Single(x => x.Key.Equals(RateLimitResetHeader, StringComparison.OrdinalIgnoreCase)); return long.Parse(rateLimitResetHeader.Value.First()); } diff --git a/Src/GitStatus.ConsoleApp/Program.cs b/Src/GitStatus.ConsoleApp/Program.cs index fd72672..301dbba 100644 --- a/Src/GitStatus.ConsoleApp/Program.cs +++ b/Src/GitStatus.ConsoleApp/Program.cs @@ -16,11 +16,11 @@ static async Task Main(string[] args) { var restApiRateLimitDataFromHeaders = await GetRestApiRateLimitDataFromHeaders(); - Console.WriteLine($"What is the GitHub REST API Rate Limit? {restApiRateLimitDataFromHeaders.RateLimit}"); // What is the GitHub REST API Rate Limit? 60 + Console.WriteLine($"What is the GitHub REST API Rate Limit? {restApiRateLimitDataFromHeaders.RateLimit}"); // What is the GitHub REST API Rate Limit? 5000 Console.WriteLine($"Have I reached the Maximum REST API Limit? {restApiRateLimitDataFromHeaders.HasReachedMaximumApiLimit}"); // Have I reached the Maximum REST API Limit? False - Console.WriteLine($"How many REST API requests do I have remaining? {restApiRateLimitDataFromHeaders.RemainingRequestCount}"); // How many REST API requests do I have remaining? 56 + Console.WriteLine($"How many REST API requests do I have remaining? {restApiRateLimitDataFromHeaders.RemainingRequestCount}"); // How many REST API requests do I have remaining? 4956 Console.WriteLine($"How long until the GitHub REST API Rate Limit resets? {restApiRateLimitDataFromHeaders.RateLimitTimeRemaining}"); // How long until the GitHub REST API Rate Limit resets? 00:29:12.4134330 - Console.WriteLine($"Did the GitHub REST API Request include a Bearer Token? {restApiRateLimitDataFromHeaders.IsAuthenticated}"); // Did GitHub REST API Request include a Bearer Token? False + Console.WriteLine($"Did the GitHub REST API Request include a Bearer Token? {restApiRateLimitDataFromHeaders.IsResponseFromAuthenticatedRequest}"); // Did GitHub REST API Request include a Bearer Token? True Console.WriteLine(); @@ -75,7 +75,7 @@ static async Task Main(string[] args) Console.WriteLine(); } - static async Task<(TimeSpan RateLimitTimeRemaining, int RateLimit, int RemainingRequestCount, bool IsAuthenticated, bool HasReachedMaximumApiLimit)> GetRestApiRateLimitDataFromHeaders() + static async Task<(TimeSpan RateLimitTimeRemaining, int RateLimit, int RemainingRequestCount, bool IsResponseFromAuthenticatedRequest, bool HasReachedMaximumApiLimit)> GetRestApiRateLimitDataFromHeaders() { HttpResponseMessage restApiResponse = await _client.GetAsync($"{GitHubConstants.GitHubRestApiUrl}/repos/brminnick/GitHubApiStatus"); restApiResponse.EnsureSuccessStatusCode(); @@ -85,11 +85,11 @@ static async Task Main(string[] args) int rateLimit = _gitHubApiStatusService.GetRateLimit(restApiResponse.Headers); int remainingRequestCount = _gitHubApiStatusService.GetRemainingRequestCount(restApiResponse.Headers); - bool isAuthenticated = _gitHubApiStatusService.IsAuthenticated(restApiResponse.Headers); + bool isResponseFromAuthenticatedRequest = _gitHubApiStatusService.IsResponseFromAuthenticatedRequest(restApiResponse.Headers); bool hasReachedMaximumApiLimit = _gitHubApiStatusService.HasReachedMaximimApiCallLimit(restApiResponse.Headers); - return (rateLimitTimeRemaining, rateLimit, remainingRequestCount, isAuthenticated, hasReachedMaximumApiLimit); + return (rateLimitTimeRemaining, rateLimit, remainingRequestCount, isResponseFromAuthenticatedRequest, hasReachedMaximumApiLimit); } static Task GetApiRateLimits()