Browse Source

add support for GitHub's new rate reset

Because the reset date is expressed as a Unix timestamp (rather than an
ISO 8601 timstamp), we can't directly unmarshal the JSON response.
Instead we have to do a little weird indirection to unmarshal it as an
int first, and then convert it into a proper Time object.
Will Norris 13 years ago
parent
commit
5f92f5d07f
2 changed files with 48 additions and 7 deletions
  1. +27
    -4
      github/github.go
  2. +21
    -3
      github/github_test.go

+ 27
- 4
github/github.go View File

@ -53,6 +53,7 @@ import (
"net/http"
"net/url"
"strconv"
"time"
)
const (
@ -62,6 +63,7 @@ const (
headerRateLimit = "X-RateLimit-Limit"
headerRateRemaining = "X-RateLimit-Remaining"
headerRateReset = "X-RateLimit-Reset"
)
// A Client manages communication with the GitHub API.
@ -168,6 +170,11 @@ func (c *Client) Do(req *http.Request, v interface{}) (*http.Response, error) {
if remaining := resp.Header.Get(headerRateRemaining); remaining != "" {
c.Rate.Remaining, _ = strconv.Atoi(remaining)
}
if reset := resp.Header.Get(headerRateReset); reset != "" {
if v, _ := strconv.ParseInt(reset, 10, 64); v != 0 {
c.Rate.Reset = time.Unix(v, 0)
}
}
err = CheckResponse(resp)
if err != nil {
@ -261,7 +268,11 @@ func parseBoolResponse(err error) (bool, error) {
// API response wrapper to a rate limit request.
type rateResponse struct {
Rate *Rate `json:rate`
Rate struct {
Limit int `json:limit`
Remaining int `json:remaining`
Reset int64 `json:reset`
} `json:rate`
}
// Rate represents the rate limit for the current client. Unauthenticated
@ -269,10 +280,13 @@ type rateResponse struct {
// 5,000 per hour.
type Rate struct {
// The number of requests per hour the client is currently limited to.
Limit int `json:limit`
Limit int
// The number of remaining requests the client can make this hour.
Remaining int `json:remaining`
Remaining int
// The time at which the current rate limit will reset.
Reset time.Time
}
// RateLimit returns the rate limit for the current client.
@ -284,7 +298,16 @@ func (c *Client) RateLimit() (*Rate, error) {
response := new(rateResponse)
_, err = c.Do(req, response)
return response.Rate, err
if err != nil {
return nil, err
}
rate := &Rate{
Limit: response.Rate.Limit,
Remaining: response.Rate.Remaining,
Reset: time.Unix(response.Rate.Reset, 0),
}
return rate, err
}
/*


+ 21
- 3
github/github_test.go View File

@ -15,6 +15,7 @@ import (
"reflect"
"strings"
"testing"
"time"
)
var (
@ -199,6 +200,7 @@ func TestDo_rateLimit(t *testing.T) {
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add(headerRateLimit, "60")
w.Header().Add(headerRateRemaining, "59")
w.Header().Add(headerRateReset, "1372700873")
})
var want int
@ -207,7 +209,10 @@ func TestDo_rateLimit(t *testing.T) {
t.Errorf("Client rate limit = %v, want %v", client.Rate.Limit, want)
}
if want = 0; client.Rate.Limit != want {
t.Errorf("Client rate remaining, got %v", client.Rate.Remaining, want)
t.Errorf("Client rate remaining = %v, got %v", client.Rate.Remaining, want)
}
if !client.Rate.Reset.IsZero() {
t.Errorf("Client rate reset not initialized to zero value")
}
req, _ := client.NewRequest("GET", "/", nil)
@ -219,6 +224,10 @@ func TestDo_rateLimit(t *testing.T) {
if want = 59; client.Rate.Remaining != want {
t.Errorf("Client rate remaining = %v, want %v", client.Rate.Remaining, want)
}
reset := time.Date(2013, 7, 1, 17, 47, 53, 0, time.UTC)
if client.Rate.Reset.UTC() != reset {
t.Errorf("Client rate reset = %v, want %v", client.Rate.Reset, reset)
}
}
func TestDo_rateLimit_errorResponse(t *testing.T) {
@ -228,6 +237,7 @@ func TestDo_rateLimit_errorResponse(t *testing.T) {
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add(headerRateLimit, "60")
w.Header().Add(headerRateRemaining, "59")
w.Header().Add(headerRateReset, "1372700873")
http.Error(w, "Bad Request", 400)
})
@ -242,6 +252,10 @@ func TestDo_rateLimit_errorResponse(t *testing.T) {
if want = 59; client.Rate.Remaining != want {
t.Errorf("Client rate remaining = %v, want %v", client.Rate.Remaining, want)
}
reset := time.Date(2013, 7, 1, 17, 47, 53, 0, time.UTC)
if client.Rate.Reset.UTC() != reset {
t.Errorf("Client rate reset = %v, want %v", client.Rate.Reset, reset)
}
}
func TestCheckResponse(t *testing.T) {
@ -350,7 +364,7 @@ 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}}`)
fmt.Fprint(w, `{"rate":{"limit":2,"remaining":1,"reset":1372700873}}`)
})
rate, err := client.RateLimit()
@ -358,7 +372,11 @@ func TestRateLimit(t *testing.T) {
t.Errorf("Rate limit returned error: %v", err)
}
want := &Rate{Limit: 2, Remaining: 1}
want := &Rate{
Limit: 2,
Remaining: 1,
Reset: time.Date(2013, 7, 1, 17, 47, 53, 0, time.UTC).Local(),
}
if !reflect.DeepEqual(rate, want) {
t.Errorf("RateLimit returned %+v, want %+v", rate, want)
}


Loading…
Cancel
Save