Skip to content

Commit

Permalink
Improved handling of ratelimits & errors
Browse files Browse the repository at this point in the history
  • Loading branch information
Altarrel committed Jun 18, 2018
1 parent 38c6651 commit bf89c67
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 8 deletions.
23 changes: 15 additions & 8 deletions client.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package goroyale

import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
Expand All @@ -14,7 +14,7 @@ const baseURL = "https://api.royaleapi.com"

type ratelimit struct {
remaining int
reset int64
reset time.Time
}

// Client allows you to easily interact with RoyaleAPI.
Expand All @@ -38,16 +38,15 @@ func New(token string, timeout time.Duration) (c *Client, err error) {
} else {
c.client = http.Client{Timeout: (timeout)}
}

return
}

func (c *Client) checkRatelimit() error {
if c.ratelimit.remaining == 0 || c.ratelimit.reset == 0 {
if c.ratelimit.remaining == 0 && c.ratelimit.reset.IsZero() {
return nil
}
if now := time.Now().UnixNano() / 1000000; c.ratelimit.remaining == 0 && now < c.ratelimit.reset {
return fmt.Errorf("ratelimit, retry in: %dms", c.ratelimit.reset-now)
if wait := time.Until(c.ratelimit.reset); c.ratelimit.remaining == 0 && wait > 0 {
return newRatelimitError(wait)
}
return nil
}
Expand All @@ -63,11 +62,11 @@ func (c *Client) updateRatelimit(resp *http.Response) error {
}
reset := resp.Header.Get("x-ratelimit-reset")
if reset != "" {
resetI, err := strconv.ParseInt(reset, 10, 64)
ms, err := strconv.ParseInt(reset, 10, 64)
if err != nil {
return err
}
c.ratelimit.reset = resetI
c.ratelimit.reset = time.Unix(0, ms*1000000) // * 1000 as have ms, need nsec
}
return nil
}
Expand All @@ -91,7 +90,15 @@ func (c *Client) get(path string, params url.Values) (bytes []byte, err error) {
return
}
defer resp.Body.Close()

bytes, err = ioutil.ReadAll(resp.Body)

if resp.StatusCode != 200 {
var apiErr APIError
json.Unmarshal(bytes, &apiErr)
return []byte{}, apiErr
}

err = c.updateRatelimit(resp)
return
}
33 changes: 33 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package goroyale

import (
"fmt"
"time"
)

// APIError represents an error returned from the API.
// https://docs.royaleapi.com/#/errors
type APIError struct {
StatusCode int `json:"status"` // http reponse code
Message string // human readable message explaining the error
}

func (err APIError) Error() string {
return err.Message
}

// RatelimitError is returned when it is detected that you will hit the ratelimit.
type RatelimitError struct {
RetryAfter time.Duration
}

func (err RatelimitError) Error() string {
return fmt.Sprintf("ratelimit, retry in: %v", err.RetryAfter)
}

func newRatelimitError(retryAfter time.Duration) *RatelimitError {
err := &RatelimitError{
RetryAfter: retryAfter,
}
return err
}

0 comments on commit bf89c67

Please sign in to comment.