Skip to content

Commit

Permalink
Feature/issue 28 user lookup (#35)
Browse files Browse the repository at this point in the history
* added the user by id lookup (standard)

* add the user by username lookup

* added user dictionary

* added tweet raw dictionary

* added the user raw dictionary

* added examples

* updated the readme

* fixed the golint suggestions

* corrected example comments
  • Loading branch information
g8rswimmer authored Dec 31, 2020
1 parent e5be3d8 commit df4a984
Show file tree
Hide file tree
Showing 14 changed files with 1,242 additions and 4 deletions.
4 changes: 4 additions & 0 deletions v2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ Here are the current twitter `v2` API features supported:
* [Tweet Lookup](https://developer.twitter.com/en/docs/twitter-api/tweets/lookup/introduction)
* [example](./_examples/tweet-lookup)
* [Tweet Dictionary](https://developer.twitter.com/en/docs/twitter-api/data-dictionary/object-model/tweet) - the tweet and all of its references related to it
* [User Lookup](https://developer.twitter.com/en/docs/twitter-api/users/lookup/introduction)
* [example](./_examples/user-lookup)
* [example: by usernames](./_examples/username-lookup)
* [User Dictionary](https://developer.twitter.com/en/docs/twitter-api/data-dictionary/object-model/user) - the user and all of its references related to it

## Examples
Much like `v1`, there is an `_example` directory to demostrate library usage. Refer to the [readme](./_examples) for more information.
Expand Down
16 changes: 15 additions & 1 deletion v2/_examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,22 @@ Within this directory there are basic examples for the twitter v2 APIs.
In order to run these examples, an authorization token will need to be provided.

## Tweet Lookup
This [example](./tweet-lookup) demostrats the tweets lookup API call.
This [example](./tweet-lookup) demonstrates the tweets lookup API call.

```
go run *.go -token=YOUR_API_TOKEN -ids=1261326399320715264,1278347468690915330
```

## User Lookup
This [example](./user-lookup) demonstrates the users lookup API call.

```
go run *.go -token=YOUR_API_TOKEN -ids=2244994945,18080585
```

## Username Lookup
This [example](./username-lookup) demonstrates the usernames lookup API call.

```
go run *.go -token=YOUR_API_TOKEN -ids=TwitterDev,MongoDB
```
56 changes: 56 additions & 0 deletions v2/_examples/user-lookup/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package main

import (
"context"
"encoding/json"
"flag"
"fmt"
"log"
"net/http"
"strings"

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 user ids.
**/
func main() {
token := flag.String("token", "", "twitter API token")
ids := flag.String("ids", "", "user ids")
flag.Parse()

client := &twitter.Client{
Authorizer: authorize{
Token: *token,
},
Client: http.DefaultClient,
Host: "https://api.twitter.com",
}
opts := twitter.UserLookupOpts{
Expansions: []twitter.Expansion{twitter.ExpansionPinnedTweetID},
}

fmt.Println("Callout to user lookup callout")

userResponse, err := client.UserLookup(context.Background(), strings.Split(*ids, ","), opts)
if err != nil {
log.Panicf("user lookup error: %v", err)
}

dictionaries := userResponse.Raw.UserDictionaries()

enc, err := json.MarshalIndent(dictionaries, "", " ")
if err != nil {
log.Panic(err)
}
fmt.Println(string(enc))
}
56 changes: 56 additions & 0 deletions v2/_examples/username-lookup/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package main

import (
"context"
"encoding/json"
"flag"
"fmt"
"log"
"net/http"
"strings"

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 usernames.
**/
func main() {
token := flag.String("token", "", "twitter API token")
names := flag.String("names", "", "user names")
flag.Parse()

client := &twitter.Client{
Authorizer: authorize{
Token: *token,
},
Client: http.DefaultClient,
Host: "https://api.twitter.com",
}
opts := twitter.UserLookupOpts{
Expansions: []twitter.Expansion{twitter.ExpansionPinnedTweetID},
}

fmt.Println("Callout to user lookup callout")

userResponse, err := client.UserNameLookup(context.Background(), strings.Split(*names, ","), opts)
if err != nil {
log.Panicf("user lookup error: %v", err)
}

dictionaries := userResponse.Raw.UserDictionaries()

enc, err := json.MarshalIndent(dictionaries, "", " ")
if err != nil {
log.Panic(err)
}
fmt.Println(string(enc))
}
132 changes: 131 additions & 1 deletion v2/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import (
)

const (
tweetMaxIDs = 100
tweetMaxIDs = 100
userMaxIDs = 100
userMaxNames = 100
)

// Client is used to make twitter v2 API callouts.
Expand Down Expand Up @@ -82,3 +84,131 @@ func (c *Client) TweetLookup(ctx context.Context, ids []string, opts TweetLookup
Raw: raw,
}, nil
}

// UserLookup returns information about an user or group of users specified by a group of user ids.
func (c *Client) UserLookup(ctx context.Context, ids []string, opts UserLookupOpts) (*UserLookupResponse, error) {
ep := userLookupEndpoint.url(c.Host)
switch {
case len(ids) == 0:
return nil, fmt.Errorf("user lookup: an id is required: %w", ErrParameter)
case len(ids) > userMaxIDs:
return nil, fmt.Errorf("user lookup: ids %d is greater than max %d: %w", len(ids), userMaxIDs, ErrParameter)
case len(ids) == 1:
ep += fmt.Sprintf("/%s", ids[0])
default:
}

req, err := http.NewRequestWithContext(ctx, http.MethodGet, ep, nil)
if err != nil {
return nil, fmt.Errorf("user lookup request: %w", err)
}
req.Header.Add("Accept", "application/json")
c.Authorizer.Add(req)
opts.addQuery(req)
if len(ids) > 1 {
q := req.URL.Query()
q.Add("ids", strings.Join(ids, ","))
req.URL.RawQuery = q.Encode()
}

resp, err := c.Client.Do(req)
if err != nil {
return nil, fmt.Errorf("user lookup response: %w", err)
}
defer resp.Body.Close()

decoder := json.NewDecoder(resp.Body)

if resp.StatusCode != http.StatusOK {
e := &ErrorResponse{}
if err := decoder.Decode(e); err != nil {
return nil, fmt.Errorf("user lookup response error decode: %w", err)
}
e.StatusCode = resp.StatusCode
return nil, e
}

raw := &UserRaw{}
switch {
case len(ids) == 1:
single := &userraw{}
if err := decoder.Decode(single); err != nil {
return nil, fmt.Errorf("user lookup single dictionary: %w", err)
}
raw.Users = make([]*UserObj, 1)
raw.Users[0] = single.User
raw.Includes = single.Includes
raw.Errors = single.Errors
default:
if err := decoder.Decode(raw); err != nil {
return nil, fmt.Errorf("user lookup dictionary: %w", err)
}
}
return &UserLookupResponse{
Raw: raw,
}, nil
}

// UserNameLookup returns information about an user or group of users specified by a group of usernames.
func (c *Client) UserNameLookup(ctx context.Context, usernames []string, opts UserLookupOpts) (*UserLookupResponse, error) {
ep := userNameLookupEndpoint.url(c.Host)
switch {
case len(usernames) == 0:
return nil, fmt.Errorf("username lookup: an username is required: %w", ErrParameter)
case len(usernames) > userMaxIDs:
return nil, fmt.Errorf("username lookup: usernames %d is greater than max %d: %w", len(usernames), userMaxNames, ErrParameter)
case len(usernames) == 1:
ep += fmt.Sprintf("/username/%s", usernames[0])
default:
}

req, err := http.NewRequestWithContext(ctx, http.MethodGet, ep, nil)
if err != nil {
return nil, fmt.Errorf("username lookup request: %w", err)
}
req.Header.Add("Accept", "application/json")
c.Authorizer.Add(req)
opts.addQuery(req)
if len(usernames) > 1 {
q := req.URL.Query()
q.Add("usernames", strings.Join(usernames, ","))
req.URL.RawQuery = q.Encode()
}

resp, err := c.Client.Do(req)
if err != nil {
return nil, fmt.Errorf("username lookup response: %w", err)
}
defer resp.Body.Close()

decoder := json.NewDecoder(resp.Body)

if resp.StatusCode != http.StatusOK {
e := &ErrorResponse{}
if err := decoder.Decode(e); err != nil {
return nil, fmt.Errorf("username lookup response error decode: %w", err)
}
e.StatusCode = resp.StatusCode
return nil, e
}

raw := &UserRaw{}
switch {
case len(usernames) == 1:
single := &userraw{}
if err := decoder.Decode(single); err != nil {
return nil, fmt.Errorf("username lookup single dictionary: %w", err)
}
raw.Users = make([]*UserObj, 1)
raw.Users[0] = single.User
raw.Includes = single.Includes
raw.Errors = single.Errors
default:
if err := decoder.Decode(raw); err != nil {
return nil, fmt.Errorf("username lookup dictionary: %w", err)
}
}
return &UserLookupResponse{
Raw: raw,
}, nil
}
Loading

0 comments on commit df4a984

Please sign in to comment.