Skip to content

Commit

Permalink
Feature/issue 102 rate limit handling (#104)
Browse files Browse the repository at this point in the history
* added rate limits

* added rate limit to callouts

* add rate limiting to hide replies

* rate limit error helper

* added response decode error

* updated the error message and name

* updated unit tests

* add unit test for rate limit helper

* added the rate limit example

* updated the readme

* updated readme

* update for release migration

* fix per golint
  • Loading branch information
g8rswimmer authored Feb 15, 2022
1 parent 3f09833 commit 7783042
Show file tree
Hide file tree
Showing 41 changed files with 1,700 additions and 206 deletions.
44 changes: 44 additions & 0 deletions v2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,27 @@ The following are changes between `v1` and `v2` of the library.
* One structure for all endpoint callouts. In `v1` there were two structures, `Tweet` and `User`, to preform callouts. This required management of two structures and knowledge which structure contained the desired method callouts. At the time, the grouping looked logical. However with the addtion of the timeline endpoints, it makes more sense to have a single struture `Client` to handle all of the callouts. If the user would like to separate the functionality, interfaces can be used to achieve this.
* Endpoint methods will return the entire response. One of the major drawbacks of `v1` was the object returned was not the entire response sent by the callout. For example, the `errors` object in the response is included in `v1` response which does not allow the caller to properly handle partial errors. In `v2`, the first focus is to return the response from twitter to allow the caller to use it as they see fit. However, it does not mean that methods can not be added to the response object to provide groupings, etc.

### Breaking Changes
These are some breaking changes and what release they are from. These type of changes will try to be avoided but if necessary for a better library, it will be done.

#### v2.0.0-beta11
* The client callout for tweet hide reply, `TweetHideReplies`, has been changed to return the response instead of just an error. This allow for the data and the rate limits of the callout to be returned.
##### Migration
```go
// old way
err := client.TweetHideReplies(context.Background(), id, true)
if err != nil {
// handle error
}
```
```go
// new way
hideResponse, err := client.TweetHideReplies(context.Background(), id, true)
if err != nil {
// handle error
}
```

## Features
Here are the current twitter `v2` API features supported:
* [Tweet Lookup](https://developer.twitter.com/en/docs/twitter-api/tweets/lookup/introduction)
Expand Down Expand Up @@ -88,6 +109,29 @@ Here are the current twitter `v2` API features supported:
* [user followed lists](./examples/user-followed-lists)
* [list followers](./examples/list-followers)

## Rate Limiting
With each response, the rate limits from the response header is returned. This allows the caller to manage any limits that are imposed. Along with the response, errors that are returned may have rate limits as well. If the error occurs after the request is sent, then rate limits may apply and are returned.

There is an example of rate limiting from a response [here](./examples/rate-limit).

This is an example of a twitter callout and if the limits have been reached, then it will backoff and try again.
```go
func TweetLikes(ctx context.Context, id string, client *twitter.Client) (*twitter.TweetLikesLookupResponse, error) {
var er *ErrorResponse

opts := twitter.ListUserMembersOpts{
MaxResults: 1,
}
tweetResponse, err := client.TweetLikesLookup(ctx, id, opts)

if rateLimit, has := twitter.RateLimitFromError(err); has && rateLimit.Remaining == 0 {
time.Sleep(time.Until(rateLimit.Reset.Time()))
return client.TweetLikesLookup(ctx, id, opts)
}
return tweetResponse, err
}
```

## Examples
Much like `v1`, there is an `_example` directory to demostrate library usage. Refer to the [readme](./_examples) for more information.

Expand Down
7 changes: 7 additions & 0 deletions v2/_examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -307,3 +307,10 @@ This [example](./list-followers) demonstrates list followers API.
```
go run *.go -token=YOUR_API_TOKEN -list_id='LIST_ID'
```

## Rate Limit
This [example](./rate-limit) demonstrates rate limiting API.

```
go run *.go -token=YOUR_API_TOKEN
```
63 changes: 63 additions & 0 deletions v2/_examples/rate-limit/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package main

import (
"context"
"flag"
"fmt"
"log"
"net/http"
"time"

twitter "github.com/g8rswimmer/go-twitter/v2"
)

type authorize struct {
Token string
}

func (a authorize) Add(req *http.Request) {
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", a.Token))
}

/**
In order to run, the user will need to provide the bearer token and the list of tweet ids.
**/
func main() {
token := flag.String("token", "", "twitter API token")
flag.Parse()

client := &twitter.Client{
Authorizer: authorize{
Token: *token,
},
Client: http.DefaultClient,
Host: "https://api.twitter.com",
}

fmt.Println("Rate Limiting Example\n\n")

var rateLimit *twitter.RateLimit

rateLimit = callout(client)
fmt.Printf("Rate Limits: %v\n", *rateLimit)
rateLimit = callout(client)
fmt.Printf("Rate Limits: %v\n\n", *rateLimit)

fmt.Printf("Wait for reset %v\n", rateLimit.Reset.Time())
time.Sleep(time.Until(rateLimit.Reset.Time()))
fmt.Println("Rate Limits are reset")

rateLimit = callout(client)
fmt.Printf("Rate Limits: %v\n", *rateLimit)
}

func callout(client *twitter.Client) *twitter.RateLimit {
opts := twitter.ListUserMembersOpts{
MaxResults: 1,
}
listResponse, err := client.ListUserMembers(context.Background(), "84839422", opts)
if err != nil {
log.Panicf("list members error: %v", err)
}
return listResponse.RateLimit
}
11 changes: 9 additions & 2 deletions v2/_examples/tweet-hide-replies/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"context"
"encoding/json"
"flag"
"fmt"
"log"
Expand Down Expand Up @@ -43,9 +44,15 @@ func main() {

fmt.Println("Callout to tweet hide replies")

if err := client.TweetHideReplies(context.Background(), *id, hideBool); err != nil {
hideResponse, err := client.TweetHideReplies(context.Background(), *id, hideBool)
if err != nil {
log.Panicf("tweet hide replies error: %v", err)
}

fmt.Printf("tweet %s hide replies %v", *ids, hideBool)
enc, err := json.MarshalIndent(hideResponse, "", " ")
if err != nil {
log.Panic(err)
}
fmt.Println(string(enc))

}
Loading

0 comments on commit 7783042

Please sign in to comment.