Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding in handling for secondary rate limit exceptions #2473

Merged
merged 2 commits into from Jul 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 23 additions & 1 deletion Octokit.Tests/Http/ConnectionTests.cs
Expand Up @@ -180,7 +180,7 @@ public async Task ThrowsApiValidationExceptionFor422Response()
}

[Fact]
public async Task ThrowsRateLimitExceededExceptionForForbidderResponse()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public async Task ThrowsRateLimitExceededExceptionForForbiddenResponse()
{
var httpClient = Substitute.For<IHttpClient>();
var response = CreateResponse(
Expand All @@ -202,6 +202,28 @@ public async Task ThrowsRateLimitExceededExceptionForForbidderResponse()
exception.Message);
}

[Fact]
public async Task ThrowsSecondaryRateLimitExceededExceptionForForbiddenResponse()
{
var httpClient = Substitute.For<IHttpClient>();
var response = CreateResponse(
HttpStatusCode.Forbidden,
"{\"message\":\"You have exceeded a secondary rate limit. Please wait a few minutes before you try again.\"}");

httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
var connection = new Connection(new ProductHeaderValue("OctokitTests"),
_exampleUri,
Substitute.For<ICredentialStore>(),
httpClient,
Substitute.For<IJsonSerializer>());

var exception = await Assert.ThrowsAsync<SecondaryRateLimitExceededException>(
() => connection.GetResponse<string>(new Uri("endpoint", UriKind.Relative)));

Assert.Equal("You have exceeded a secondary rate limit. Please wait a few minutes before you try again.",
exception.Message);
}

[Fact]
public async Task ThrowsLoginAttemptsExceededExceptionForForbiddenResponse()
{
Expand Down
67 changes: 67 additions & 0 deletions Octokit/Exceptions/SecondaryRateLimitExceededException.cs
@@ -0,0 +1,67 @@
using System;
using System.Diagnostics.CodeAnalysis;
#if !NO_SERIALIZABLE
using System.Runtime.Serialization;
#endif

namespace Octokit
{
/// <summary>
/// Exception thrown when Secondary GitHub API Rate limits are exceeded.
/// </summary>
/// <summary>
/// <para>
/// This occurs when GitHub perceives misuse of the API. You may get this if
/// you are polling heavily, creating content rapidly or making concurrent requests.
/// </para>
/// <para>See https://docs.github.com/en/rest/overview/resources-in-the-rest-api#secondary-rate-limits for more details.</para>
/// </summary>
#if !NO_SERIALIZABLE
[Serializable]
#endif
[SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors",
Justification = "These exceptions are specific to the GitHub API and not general purpose exceptions")]
public class SecondaryRateLimitExceededException : ForbiddenException
{
/// <summary>
/// Constructs an instance of the <see cref="Octokit.SecondaryRateLimitExceededException"/> class.
/// </summary>
/// <param name="response">The HTTP payload from the server</param>
public SecondaryRateLimitExceededException(IResponse response) : this(response, null)
{
}

/// <summary>
/// Constructs an instance of the <see cref="Octokit.SecondaryRateLimitExceededException"/> class.
/// </summary>
/// <param name="response">The HTTP payload from the server</param>
/// <param name="innerException">The inner exception</param>
public SecondaryRateLimitExceededException(IResponse response, Exception innerException) : base(response, innerException)
{
Ensure.ArgumentNotNull(response, nameof(response));
}

public override string Message
{
get { return ApiErrorMessageSafe ?? "Secondary API Rate Limit exceeded"; }
}

#if !NO_SERIALIZABLE
/// <summary>
/// Constructs an instance of <see cref="Octokit.SecondaryRateLimitExceededException"/>.
/// </summary>
/// <param name="info">
/// The <see cref="SerializationInfo"/> that holds the
/// serialized object data about the exception being thrown.
/// </param>
/// <param name="context">
/// The <see cref="StreamingContext"/> that contains
/// contextual information about the source or destination.
/// </param>
protected SecondaryRateLimitExceededException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
#endif
}
}
5 changes: 5 additions & 0 deletions Octokit/Http/Connection.cs
Expand Up @@ -724,6 +724,11 @@ static Exception GetExceptionForForbidden(IResponse response)
return new RateLimitExceededException(response);
}

if (body.Contains("secondary rate limit"))
{
return new SecondaryRateLimitExceededException(response);
}

if (body.Contains("number of login attempts exceeded"))
{
return new LoginAttemptsExceededException(response);
Expand Down