diff --git a/github_ratelimit/detect.go b/github_ratelimit/detect.go index 19a253a..8809d3a 100644 --- a/github_ratelimit/detect.go +++ b/github_ratelimit/detect.go @@ -14,16 +14,16 @@ type SecondaryRateLimitBody struct { } const ( - SecondaryRateLimitMessage = `You have exceeded a secondary rate limit` - SecondaryRateLimitDocumentationPath = `/rest/overview/resources-in-the-rest-api#secondary-rate-limits` + SecondaryRateLimitMessage = `You have exceeded a secondary rate limit` + SecondaryRateLimitDocumentationPathSuffix = `secondary-rate-limits` ) // IsSecondaryRateLimit checks whether the response is a legitimate secondary rate limit. // It checks the prefix of the message and the suffix of the documentation URL in the response body in case // the message or documentation URL is modified in the future. -// https://docs.github.com/en/rest/overview/resources-in-the-rest-api#secondary-rate-limits +// https://docs.github.com/en/rest/overview/rate-limits-for-the-rest-api#about-secondary-rate-limits func (s SecondaryRateLimitBody) IsSecondaryRateLimit() bool { - return strings.HasPrefix(s.Message, SecondaryRateLimitMessage) && strings.HasSuffix(s.DocumentURL, SecondaryRateLimitDocumentationPath) + return strings.HasPrefix(s.Message, SecondaryRateLimitMessage) || strings.HasSuffix(s.DocumentURL, SecondaryRateLimitDocumentationPathSuffix) } // isSecondaryRateLimit checks whether the response is a legitimate secondary rate limit. diff --git a/github_ratelimit/github_ratelimit_test/ratelimit_injecter.go b/github_ratelimit/github_ratelimit_test/ratelimit_injecter.go index ec1c0df..8abf444 100644 --- a/github_ratelimit/github_ratelimit_test/ratelimit_injecter.go +++ b/github_ratelimit/github_ratelimit_test/ratelimit_injecter.go @@ -18,18 +18,22 @@ const ( ) const ( - SecondaryRateLimitMessage = `You have exceeded a secondary rate limit. Please wait a few minutes before you try again.` - SecondaryRateLimitDocumentationURL = `https://docs.github.com/rest/overview/resources-in-the-rest-api#secondary-rate-limits` - SecondaryRateLimitAlternateDocumentationURL = `https://docs.github.com/free-pro-team@latest/rest/overview/resources-in-the-rest-api#secondary-rate-limits` + SecondaryRateLimitMessage = `You have exceeded a secondary rate limit. Please wait a few minutes before you try again.` ) +var SecondaryRateLimitDocumentationURLs = []string{ + `https://docs.github.com/rest/overview/resources-in-the-rest-api#secondary-rate-limits`, + `https://docs.github.com/free-pro-team@latest/rest/overview/resources-in-the-rest-api#secondary-rate-limits`, + `https://docs.github.com/en/free-pro-team@latest/rest/overview/rate-limits-for-the-rest-api#about-secondary-rate-limits`, +} + type SecondaryRateLimitInjecterOptions struct { - Every time.Duration - Sleep time.Duration - InvalidBody bool - UseXRateLimit bool - UsePrimaryRateLimit bool - UseAlternateDocumentationURL bool + Every time.Duration + Sleep time.Duration + InvalidBody bool + UseXRateLimit bool + UsePrimaryRateLimit bool + DocumentationURL string } func NewRateLimitInjecter(base http.RoundTripper, options *SecondaryRateLimitInjecterOptions) (http.RoundTripper, error) { @@ -109,16 +113,14 @@ func (r *SecondaryRateLimitInjecter) NextSleepStart() time.Time { return r.blockUntil.Add(r.options.Every) } -func getSecondaryRateLimitBody(useAlternatateDocumentationURL bool) (io.ReadCloser, error) { - documentURL := SecondaryRateLimitDocumentationURL - - if useAlternatateDocumentationURL { - documentURL = SecondaryRateLimitAlternateDocumentationURL +func getSecondaryRateLimitBody(documentationURL string) (io.ReadCloser, error) { + if len(documentationURL) == 0 { + documentationURL = SecondaryRateLimitDocumentationURLs[0] } body := github_ratelimit.SecondaryRateLimitBody{ Message: SecondaryRateLimitMessage, - DocumentURL: documentURL, + DocumentURL: documentationURL, } bodyBytes, err := json.Marshal(body) if err != nil { @@ -132,7 +134,7 @@ func (t *SecondaryRateLimitInjecter) inject(resp *http.Response) (*http.Response if t.options.UsePrimaryRateLimit { return t.toPrimaryRateLimitResponse(resp), nil } else { - body, err := getSecondaryRateLimitBody(t.options.UseAlternateDocumentationURL) + body, err := getSecondaryRateLimitBody(t.options.DocumentationURL) if err != nil { return nil, err } diff --git a/github_ratelimit/github_ratelimit_test/ratelimit_test.go b/github_ratelimit/github_ratelimit_test/ratelimit_test.go index 291fbd4..0ab0b26 100644 --- a/github_ratelimit/github_ratelimit_test/ratelimit_test.go +++ b/github_ratelimit/github_ratelimit_test/ratelimit_test.go @@ -108,58 +108,40 @@ func TestSecondaryRateLimitBody(t *testing.T) { const every = 1 * time.Second const sleep = 1 * time.Second - slept := false - callback := func(*github_ratelimit.CallbackContext) { - slept = true - } - - // test documentation URL - i := setupInjecterWithOptions(t, SecondaryRateLimitInjecterOptions{ - Every: every, - Sleep: sleep, - UseAlternateDocumentationURL: false, - }) - c, err := github_ratelimit.NewRateLimitWaiterClient(i, github_ratelimit.WithLimitDetectedCallback(callback)) - if err != nil { - t.Fatal(err) - } - - // initialize injecter timing - _, _ = c.Get("/") - waitForNextSleep(i) - - // attempt during rate limit - _, err = c.Get("/") - if err != nil { - t.Fatal(err) - } - if !slept { - t.Fatal(slept) - } - - // test alternate documentation URL - slept = false - i = setupInjecterWithOptions(t, SecondaryRateLimitInjecterOptions{ - Every: every, - Sleep: sleep, - UseAlternateDocumentationURL: true, - }) - c, err = github_ratelimit.NewRateLimitWaiterClient(i, github_ratelimit.WithLimitDetectedCallback(callback)) - if err != nil { - t.Fatal(err) - } - - // initialize injecter timing - _, _ = c.Get("/") - waitForNextSleep(i) - - // attempt during rate limit - _, err = c.Get("/") - if err != nil { - t.Fatal(err) - } - if !slept { - t.Fatal(slept) + for i, docURL := range SecondaryRateLimitDocumentationURLs { + docURL := docURL + t.Run(fmt.Sprintf("docURL_%d", i), func(t *testing.T) { + t.Parallel() + + slept := false + callback := func(*github_ratelimit.CallbackContext) { + slept = true + } + + // test documentation URL + i := setupInjecterWithOptions(t, SecondaryRateLimitInjecterOptions{ + Every: every, + Sleep: sleep, + DocumentationURL: docURL, + }) + c, err := github_ratelimit.NewRateLimitWaiterClient(i, github_ratelimit.WithLimitDetectedCallback(callback)) + if err != nil { + t.Fatal(err) + } + + // initialize injecter timing + _, _ = c.Get("/") + waitForNextSleep(i) + + // attempt during rate limit + _, err = c.Get("/") + if err != nil { + t.Fatal(err) + } + if !slept { + t.Fatal(slept) + } + }) } }