Created
January 1, 2018 13:17
-
-
Save lmas/f163e2d4b25b48f87869c8a0be00c388 to your computer and use it in GitHub Desktop.
Simple API client with json parsing, gzip compression, ratelimit and oauth tokens
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"compress/gzip" | |
"context" | |
"encoding/json" | |
"fmt" | |
"net/http" | |
"net/url" | |
"time" | |
"github.com/juju/ratelimit" | |
"golang.org/x/oauth2/clientcredentials" | |
) | |
type Query map[string]string | |
type Conf struct { | |
UserAgent string | |
ClientTimeout time.Duration | |
RateLimit int64 | |
OAuthConf clientcredentials.Config | |
} | |
type Client struct { | |
conf Conf | |
webClient *http.Client | |
rateLimiter *ratelimit.Bucket | |
} | |
func NewClient(conf Conf) *Client { | |
// NOTE: http.DefaultClient will be used to get the tokens, which means | |
// no timeouts are being used. It's fine being blocked forever I think, | |
// since we can't do anything without a valid token anyway. | |
// NOTE: also note that this returned client will automagically add the | |
// Authorization header when we have a token, but it won't show up in | |
// our requests until it's time to send it away! | |
wc := conf.OAuthConf.Client(context.Background()) | |
// Now on the other hand, we want all our regular requests to have a timeout! | |
wc.Timeout = conf.ClientTimeout | |
client := &Client{ | |
conf: conf, | |
webClient: wc, | |
rateLimiter: ratelimit.NewBucket(1*time.Second, conf.RateLimit), | |
} | |
return client | |
} | |
func (c *Client) Request(method, path string, query Query) (*http.Response, error) { | |
// TODO: add post body? | |
u, err := url.Parse(path) | |
if err != nil { | |
return nil, err | |
} | |
q := u.Query() | |
for k, v := range query { | |
if v != "" { | |
q.Add(k, v) | |
} | |
} | |
u.RawQuery = q.Encode() | |
req, err := http.NewRequest(method, u.String(), nil) | |
if err != nil { | |
return nil, err | |
} | |
req.Header.Add("User-Agent", c.conf.UserAgent) | |
req.Header.Add("Accept-Encoding", "gzip") | |
c.rateLimiter.Wait(1) | |
resp, err := c.webClient.Do(req) | |
if err != nil { | |
return nil, err | |
} | |
if resp.StatusCode != http.StatusOK { | |
return nil, fmt.Errorf("bad response: %s", resp.Status) | |
} | |
return resp, nil | |
} | |
func (c *Client) ParseJSON(resp *http.Response, model interface{}) error { | |
defer resp.Body.Close() | |
if model != nil { | |
b := resp.Body | |
if !resp.Uncompressed { | |
var err error | |
b, err = gzip.NewReader(resp.Body) | |
if err != nil { | |
return err | |
} | |
} | |
dec := json.NewDecoder(b) | |
return dec.Decode(model) | |
} | |
return nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment