From b023602347a57238b634853568111f3584faf0ce Mon Sep 17 00:00:00 2001 From: Chris Simpson Date: Mon, 11 Jul 2022 15:03:31 +0100 Subject: [PATCH] [FEAT] Adding in handling for secondary rate limit exceptions (#2473) --- Octokit.Tests/Http/ConnectionTests.cs | 24 ++++++- .../SecondaryRateLimitExceededException.cs | 67 +++++++++++++++++++ Octokit/Http/Connection.cs | 5 ++ 3 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 Octokit/Exceptions/SecondaryRateLimitExceededException.cs diff --git a/Octokit.Tests/Http/ConnectionTests.cs b/Octokit.Tests/Http/ConnectionTests.cs index c38aee0759..0dc817c246 100644 --- a/Octokit.Tests/Http/ConnectionTests.cs +++ b/Octokit.Tests/Http/ConnectionTests.cs @@ -180,7 +180,7 @@ public async Task ThrowsApiValidationExceptionFor422Response() } [Fact] - public async Task ThrowsRateLimitExceededExceptionForForbidderResponse() + public async Task ThrowsRateLimitExceededExceptionForForbiddenResponse() { var httpClient = Substitute.For(); var response = CreateResponse( @@ -202,6 +202,28 @@ public async Task ThrowsRateLimitExceededExceptionForForbidderResponse() exception.Message); } + [Fact] + public async Task ThrowsSecondaryRateLimitExceededExceptionForForbiddenResponse() + { + var httpClient = Substitute.For(); + 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(), + httpClient, + Substitute.For()); + + var exception = await Assert.ThrowsAsync( + () => connection.GetResponse(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() { diff --git a/Octokit/Exceptions/SecondaryRateLimitExceededException.cs b/Octokit/Exceptions/SecondaryRateLimitExceededException.cs new file mode 100644 index 0000000000..48d264a751 --- /dev/null +++ b/Octokit/Exceptions/SecondaryRateLimitExceededException.cs @@ -0,0 +1,67 @@ +using System; +using System.Diagnostics.CodeAnalysis; +#if !NO_SERIALIZABLE +using System.Runtime.Serialization; +#endif + +namespace Octokit +{ + /// + /// Exception thrown when Secondary GitHub API Rate limits are exceeded. + /// + /// + /// + /// 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. + /// + /// See https://docs.github.com/en/rest/overview/resources-in-the-rest-api#secondary-rate-limits for more details. + /// +#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 + { + /// + /// Constructs an instance of the class. + /// + /// The HTTP payload from the server + public SecondaryRateLimitExceededException(IResponse response) : this(response, null) + { + } + + /// + /// Constructs an instance of the class. + /// + /// The HTTP payload from the server + /// The inner exception + 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 + /// + /// Constructs an instance of . + /// + /// + /// The that holds the + /// serialized object data about the exception being thrown. + /// + /// + /// The that contains + /// contextual information about the source or destination. + /// + protected SecondaryRateLimitExceededException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + } +} diff --git a/Octokit/Http/Connection.cs b/Octokit/Http/Connection.cs index 81759fdc2a..46bb710f12 100644 --- a/Octokit/Http/Connection.cs +++ b/Octokit/Http/Connection.cs @@ -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);