Skip to content

Commit bb2cfae

Browse files
Add GitHubApiStatusService.IsAbuseRateLimit
1 parent fe6d871 commit bb2cfae

6 files changed

Lines changed: 74 additions & 8 deletions

File tree

Src/GitHubApiStatus.UnitTests/GitHubApiStatus.UnitTests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<ItemGroup>
1212
<PackageReference Include="nunit" Version="3.12.0" />
1313
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
14-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
14+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
1515
<PackageReference Include="RichardSzalay.MockHttp" Version="6.0.0" />
1616
</ItemGroup>
1717

Src/GitHubApiStatus.UnitTests/Tests/Base/BaseTest.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ protected virtual Task AfterEachTest()
2727
return Task.CompletedTask;
2828
}
2929

30-
protected static HttpResponseHeaders CreateHttpResponseHeaders(in int rateLimit, in DateTimeOffset rateLimitResetTime, in int remainingRequestCount, in HttpStatusCode httpStatusCode = HttpStatusCode.OK, in bool isAuthenticated = true)
30+
protected static HttpResponseHeaders CreateHttpResponseHeaders(in int rateLimit, in DateTimeOffset rateLimitResetTime, in int remainingRequestCount, in HttpStatusCode httpStatusCode = HttpStatusCode.OK, in bool isAuthenticated = true, in bool isAbuseRateLimit = false)
3131
{
3232
if (remainingRequestCount > rateLimit)
3333
throw new ArgumentOutOfRangeException(nameof(remainingRequestCount), $"{nameof(remainingRequestCount)} must be less than or equal to {nameof(rateLimit)}");
@@ -42,6 +42,9 @@ protected static HttpResponseHeaders CreateHttpResponseHeaders(in int rateLimit,
4242
}
4343
};
4444

45+
if (isAbuseRateLimit)
46+
httpResponse.Headers.RetryAfter = new RetryConditionHeaderValue(TimeSpan.FromMinutes(1));
47+
4548
if (isAuthenticated)
4649
httpResponse.Headers.Vary.Add(_authorizationHeaderKey);
4750

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using System;
2+
using NUnit.Framework;
3+
4+
namespace GitHubApiStatus.UnitTests
5+
{
6+
class IsAbuseRateLimitTest : BaseTest
7+
{
8+
[Test]
9+
public void IsAbuseRateLimit()
10+
{
11+
//Arrange
12+
var httpResponseHeaders = CreateHttpResponseHeaders(500, DateTimeOffset.UtcNow, 0, isAbuseRateLimit: true);
13+
14+
//Act
15+
var isAbuseRateLimit = GitHubApiStatusService.IsAbuseRateLimit(httpResponseHeaders, out TimeSpan? delta);
16+
17+
//Assert
18+
Assert.IsTrue(isAbuseRateLimit);
19+
Assert.IsNotNull(delta);
20+
}
21+
22+
[Test]
23+
public void IsNotAbuseRateLimit()
24+
{
25+
//Arrange
26+
var httpResponseHeaders = CreateHttpResponseHeaders(500, DateTimeOffset.UtcNow, 0);
27+
28+
//Act
29+
var isAbuseRateLimit = GitHubApiStatusService.IsAbuseRateLimit(httpResponseHeaders, out TimeSpan? delta);
30+
31+
//Assert
32+
Assert.IsFalse(isAbuseRateLimit);
33+
Assert.IsNull(delta);
34+
}
35+
36+
[Test]
37+
public void NullHttpResponseHeaders()
38+
{
39+
//Arrange
40+
41+
//Act
42+
43+
//Assert
44+
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
45+
Assert.Throws<GitHubApiStatusException>(() => GitHubApiStatusService.IsAbuseRateLimit(null, out _));
46+
#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
47+
}
48+
}
49+
}

Src/GitHubApiStatus.UnitTests/Tests/ProductHeaderValueTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ public void NullNameTest()
3232
//Act
3333

3434
//Assert
35+
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
3536
Assert.Throws<ArgumentException>(() => gitHubApiStatusService.AddProductHeaderValue(new ProductHeaderValue(null)));
37+
#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
3638
Assert.IsFalse(gitHubApiStatusService.IsProductHeaderValueValid);
3739
}
3840

Src/GitHubApiStatus/Services/GitHubApiStatusService.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,12 +170,26 @@ public int GetRateLimit(in HttpResponseHeaders httpResponseHeaders)
170170
{
171171
ValidateHttpResponseHeaders(httpResponseHeaders);
172172

173-
var rateLimitRemainingHeader = httpResponseHeaders?.Single(x => x.Key.Equals(RateLimitHeader, StringComparison.OrdinalIgnoreCase)) ?? throw new ArgumentNullException(nameof(httpResponseHeaders));
173+
var rateLimitRemainingHeader = httpResponseHeaders.Single(x => x.Key.Equals(RateLimitHeader, StringComparison.OrdinalIgnoreCase));
174174
var rateLimit = int.Parse(rateLimitRemainingHeader.Value.First());
175175

176176
return rateLimit;
177177
}
178178

179+
/// <summary>
180+
/// Determines Whether GitHub's Abuse Rate Limit Has Been Reached
181+
/// </summary>
182+
/// <param name="httpResponseHeaders">HttpResponseHeaders from GitHub API Response</param>
183+
/// <param name="delta">Time Remaining in Retry-After Header</param>
184+
/// <returns></returns>
185+
public bool IsAbuseRateLimit(in HttpResponseHeaders httpResponseHeaders, out TimeSpan? delta)
186+
{
187+
ValidateHttpResponseHeaders(httpResponseHeaders);
188+
189+
delta = httpResponseHeaders.RetryAfter?.Delta;
190+
return delta is not null;
191+
}
192+
179193
/// <summary>
180194
/// Determines Whether GitHub's Maximum API Limit Has Been Reached
181195
/// </summary>
@@ -196,7 +210,7 @@ public int GetRemainingRequestCount(in HttpResponseHeaders httpResponseHeaders)
196210
{
197211
ValidateHttpResponseHeaders(httpResponseHeaders);
198212

199-
var rateLimitRemainingHeader = httpResponseHeaders?.Single(x => x.Key.Equals(RateLimitRemainingHeader, StringComparison.OrdinalIgnoreCase)) ?? throw new ArgumentNullException(nameof(httpResponseHeaders));
213+
var rateLimitRemainingHeader = httpResponseHeaders.Single(x => x.Key.Equals(RateLimitRemainingHeader, StringComparison.OrdinalIgnoreCase));
200214
var remainingApiRequests = int.Parse(rateLimitRemainingHeader.Value.First());
201215

202216
return remainingApiRequests;

Src/GitStatus.API/GetRateLimits.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,9 @@ class GitHubApiStatus
1616
public GitHubApiStatus(IGitHubApiStatusService gitHubApiStatusService) => _gitHubApiStatusService = gitHubApiStatusService;
1717

1818
[FunctionName(nameof(GitHubApiStatus))]
19-
public async Task<IActionResult> Run(
20-
[HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req,
21-
ILogger log)
19+
public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req, ILogger log)
2220
{
23-
log.LogInformation("C# HTTP trigger function processed a request.");
21+
log.LogInformation("Retrieving Api Rate Limits");
2422

2523
var apiStatus = await _gitHubApiStatusService.GetApiRateLimits(CancellationToken.None).ConfigureAwait(false);
2624

0 commit comments

Comments
 (0)