From f6398f5d2f395b60c2a4eb9dcc0f9b12cddc689b Mon Sep 17 00:00:00 2001 From: Will Norris Date: Fri, 11 Apr 2014 10:49:09 -0700 Subject: [PATCH] add support for new rate limit formats mark RateLimit method as deprecated in favor of RateLimits which returns both the core and search limits. (see deprecation notice in API docs: https://developer.github.com/v3/rate_limit/#deprecation-notice) --- github/github.go | 50 ++++++++++++++++++++++++++-------- github/github_test.go | 42 +++++++++++++++++++++++++++- tests/integration/misc_test.go | 25 ++++++++++++++++- 3 files changed, 104 insertions(+), 13 deletions(-) diff --git a/github/github.go b/github/github.go index 66e75e1..bfa0fdd 100644 --- a/github/github.go +++ b/github/github.go @@ -388,14 +388,7 @@ func parseBoolResponse(err error) (bool, error) { return false, err } -// API response wrapper to a rate limit request. -type rateResponse struct { - *Rate `json:"rate"` -} - -// Rate represents the rate limit for the current client. Unauthenticated -// requests are limited to 60 per hour. Authenticated requests are limited to -// 5,000 per hour. +// Rate represents the rate limit for the current client. type Rate struct { // The number of requests per hour the client is currently limited to. Limit int `json:"limit"` @@ -407,20 +400,55 @@ type Rate struct { Reset Timestamp `json:"reset"` } -// RateLimit returns the rate limit for the current client. +func (r Rate) String() string { + return Stringify(r) +} + +// RateLimits represents the rate limits for the current client. +type RateLimits struct { + // The rate limit for non-search API requests. Unauthenticated + // requests are limited to 60 per hour. Authenticated requests are + // limited to 5,000 per hour. + Core *Rate `json:"core"` + + // The rate limit for search API requests. Unauthenticated requests + // are limited to 5 requests per minutes. Authenticated requests are + // limited to 20 per minute. + // + // GitHub API docs: https://developer.github.com/v3/search/#rate-limit + Search *Rate `json:"search"` +} + +func (r RateLimits) String() string { + return Stringify(r) +} + +// RateLimit is deprecated. Use RateLimits instead. func (c *Client) RateLimit() (*Rate, *Response, error) { + limits, resp, err := c.RateLimits() + if limits == nil { + return nil, nil, err + } + + return limits.Core, resp, err +} + +// RateLimits returns the rate limits for the current client. +func (c *Client) RateLimits() (*RateLimits, *Response, error) { req, err := c.NewRequest("GET", "rate_limit", nil) if err != nil { return nil, nil, err } - response := new(rateResponse) + response := new(struct { + Resources *RateLimits `json:"resources"` + }) resp, err := c.Do(req, response) if err != nil { return nil, nil, err } - return response.Rate, resp, err + return response.Resources, resp, err } /* diff --git a/github/github_test.go b/github/github_test.go index 397565e..15ecce2 100644 --- a/github/github_test.go +++ b/github/github_test.go @@ -510,7 +510,11 @@ func TestRateLimit(t *testing.T) { if m := "GET"; m != r.Method { t.Errorf("Request method = %v, want %v", r.Method, m) } - fmt.Fprint(w, `{"rate":{"limit":2,"remaining":1,"reset":1372700873}}`) + //fmt.Fprint(w, `{"resources":{"core": {"limit":2,"remaining":1,"reset":1372700873}}}`) + fmt.Fprint(w, `{"resources":{ + "core": {"limit":2,"remaining":1,"reset":1372700873}, + "search": {"limit":3,"remaining":2,"reset":1372700874} + }}`) }) rate, _, err := client.RateLimit() @@ -528,6 +532,42 @@ func TestRateLimit(t *testing.T) { } } +func TestRateLimits(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/rate_limit", func(w http.ResponseWriter, r *http.Request) { + if m := "GET"; m != r.Method { + t.Errorf("Request method = %v, want %v", r.Method, m) + } + fmt.Fprint(w, `{"resources":{ + "core": {"limit":2,"remaining":1,"reset":1372700873}, + "search": {"limit":3,"remaining":2,"reset":1372700874} + }}`) + }) + + rate, _, err := client.RateLimits() + if err != nil { + t.Errorf("RateLimits returned error: %v", err) + } + + want := &RateLimits{ + Core: &Rate{ + Limit: 2, + Remaining: 1, + Reset: Timestamp{time.Date(2013, 7, 1, 17, 47, 53, 0, time.UTC).Local()}, + }, + Search: &Rate{ + Limit: 3, + Remaining: 2, + Reset: Timestamp{time.Date(2013, 7, 1, 17, 47, 54, 0, time.UTC).Local()}, + }, + } + if !reflect.DeepEqual(rate, want) { + t.Errorf("RateLimits returned %+v, want %+v", rate, want) + } +} + func TestUnauthenticatedRateLimitedTransport(t *testing.T) { setup() defer teardown() diff --git a/tests/integration/misc_test.go b/tests/integration/misc_test.go index 1bfb215..beb1928 100644 --- a/tests/integration/misc_test.go +++ b/tests/integration/misc_test.go @@ -5,7 +5,10 @@ package tests -import "testing" +import ( + "testing" + "time" +) func TestEmojis(t *testing.T) { emoji, _, err := client.ListEmojis() @@ -40,3 +43,23 @@ func TestAPIMeta(t *testing.T) { t.Errorf("APIMeta VerifiablePasswordAuthentication is false") } } + +func TestRateLimits(t *testing.T) { + limits, _, err := client.RateLimits() + if err != nil { + t.Fatalf("RateLimits returned error: %v", err) + } + + // do some sanity checks + if limits.Core.Limit == 0 { + t.Errorf("RateLimits returned 0 core limit") + } + + if limits.Core.Limit < limits.Core.Remaining { + t.Errorf("Core.Limits is less than Core.Remaining.") + } + + if limits.Core.Reset.Time.Before(time.Now().Add(-1 * time.Minute)) { + t.Errorf("Core.Reset is more than 1 minute in the past; that doesn't seem right.") + } +}