Skip to content
This repository has been archived by the owner on Nov 5, 2022. It is now read-only.

Commit

Permalink
Merge pull request #36 from Fallenstedt/rc/0.4.1
Browse files Browse the repository at this point in the history
0.4.1
  • Loading branch information
Fallenstedt authored Dec 23, 2021
2 parents 04b1953 + de2d3ae commit 15e71f0
Show file tree
Hide file tree
Showing 16 changed files with 278 additions and 48 deletions.
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ api.SetUnmarshalHook(func(bytes []byte) (interface{}, error) {

##### Start Stream
Start your stream. This is a long-running HTTP GET request.
You can get specific data you want by adding [query params](https://developer.twitter.com/en/docs/twitter-api/tweets/filtered-stream/api-reference/get-tweets-search-stream).
Additionally, [view an example of query params here](https://developer.twitter.com/en/docs/twitter-api/expansions), or in the [examples](https://github.com/fallenstedt/twitter-stream/tree/master/example)
You can request additional tweet data by adding [query params](https://developer.twitter.com/en/docs/twitter-api/tweets/filtered-stream/api-reference/get-tweets-search-stream).
Use the `twitterstream.NewStreamQueryParamsBuilder()` to start a stream with the data you want.

```go

Expand All @@ -142,7 +142,15 @@ func fetchTweets() stream.IStream {
}
return data, err
})
err = api.StartStream("?expansions=author_id&tweet.fields=created_at")

// https://developer.twitter.com/en/docs/twitter-api/tweets/filtered-stream/api-reference/get-tweets-search-stream
streamExpansions := twitterstream.NewStreamQueryParamsBuilder().
AddExpansion("author_id").
AddTweetField("created_at").
Build()

// StartStream will start the stream
err = api.StartStream(streamExpansions)

if err != nil {
panic(err)
Expand All @@ -162,7 +170,7 @@ func initiateStream() {
// When the loop below ends, restart the stream
defer initiateStream()

// Start processing data from twitter
// Start processing data from twitter after starting the stream
for tweet := range api.GetMessages() {

// Handle disconnections from twitter
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.4.0
0.4.1
4 changes: 2 additions & 2 deletions example/create_rules_example.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func deleteRules() {
api := twitterstream.NewTwitterStream(tok.AccessToken)

// use api.Rules.Get to find the ID number for an existing rule
res, err := api.Rules.Delete(rules.NewDeleteRulesRequest(1469776000158363653, 1469776000158363654), false)
res, err := api.Rules.Delete(rules.NewDeleteRulesRequest(1469777072675450881, 74893274932), false)

if err != nil {
panic(err)
Expand All @@ -89,4 +89,4 @@ func printRules(data []rules.DataRule) {
fmt.Printf("Tag: %v\n",datum.Tag)
fmt.Printf("Value: %v\n\n", datum.Value)
}
}
}
1 change: 0 additions & 1 deletion example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ const SECRET = "SECRET"

func main() {
// Run an example function

addRules()
getRules()
initiateStream()
Expand Down
15 changes: 14 additions & 1 deletion example/stream_forever.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,24 +75,37 @@ func initiateStream() {
}

func fetchTweets() stream.IStream {
// Get Bearer Token using API keys
tok, err := getTwitterToken()
if err != nil {
panic(err)
}

// Instantiate an instance of twitter stream using the bearer token
api := getTwitterStreamApi(tok)

// On Each tweet, decode the bytes into a StreamDataExample struct
api.SetUnmarshalHook(func(bytes []byte) (interface{}, error) {
data := StreamDataExample{}
if err := json.Unmarshal(bytes, &data); err != nil {
fmt.Printf("failed to unmarshal bytes: %v", err)
}
return data, err
})
err = api.StartStream("?expansions=author_id&tweet.fields=created_at")

// Request additional data from teach tweet
streamExpansions := twitterstream.NewStreamQueryParamsBuilder().
AddExpansion("author_id").
AddTweetField("created_at").
Build()

// Start the Stream
err = api.StartStream(streamExpansions)
if err != nil {
panic(err)
}

// Return the twitter stream api instance
return api
}

Expand Down
17 changes: 10 additions & 7 deletions httpclient/http_client_mock.go
Original file line number Diff line number Diff line change
@@ -1,33 +1,36 @@
package httpclient

import "net/http"
import (
"net/http"
"net/url"
)

type mockHttpClient struct {
token string
MockNewHttpRequest func(opts *RequestOpts) (*http.Response, error)
MockGetSearchStream func(queryParams string) (*http.Response, error)
MockGetSearchStream func(queryParams *url.Values) (*http.Response, error)
MockGetRules func() (*http.Response, error)
MockAddRules func(queryParams string, body string) (*http.Response, error)
MockGenerateUrl func(name string, queryParams string) (string, error)
MockAddRules func(queryParams *url.Values, body string) (*http.Response, error)
MockGenerateUrl func(name string, queryParams *url.Values) (string, error)
}

func NewHttpClientMock(token string) *mockHttpClient {
return &mockHttpClient{token: token}
}

func (t *mockHttpClient) GenerateUrl(name string, queryParams string) (string, error) {
func (t *mockHttpClient) GenerateUrl(name string, queryParams *url.Values) (string, error) {
return t.MockGenerateUrl(name, queryParams)
}

func (t *mockHttpClient) GetRules() (*http.Response, error) {
return t.MockGetRules()
}

func (t *mockHttpClient) AddRules(queryParams string, body string) (*http.Response, error) {
func (t *mockHttpClient) AddRules(queryParams *url.Values, body string) (*http.Response, error) {
return t.MockAddRules(queryParams, body)
}

func (t *mockHttpClient) GetSearchStream(queryParams string) (*http.Response, error) {
func (t *mockHttpClient) GetSearchStream(queryParams *url.Values) (*http.Response, error) {
return t.MockGetSearchStream(queryParams)
}

Expand Down
18 changes: 10 additions & 8 deletions httpclient/httpclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package httpclient
import (
"bytes"
"errors"
"fmt"
"log"
"net/http"
"net/url"
"strings"
)

Expand All @@ -18,9 +20,9 @@ type (
IHttpClient interface {
NewHttpRequest(opts *RequestOpts) (*http.Response, error)
GetRules() (*http.Response, error)
GetSearchStream(queryParams string) (*http.Response, error)
AddRules(queryParams string, body string) (*http.Response, error)
GenerateUrl(name string, queryParams string) (string, error)
GetSearchStream(queryParams *url.Values) (*http.Response, error)
AddRules(queryParams *url.Values, body string) (*http.Response, error)
GenerateUrl(name string, queryParams *url.Values) (string, error)
}

httpClient struct {
Expand Down Expand Up @@ -48,7 +50,7 @@ func (t *httpClient) GetRules() (*http.Response, error) {
}

// AddRules will add rules for you to stream with.
func (t *httpClient) AddRules(queryParams string, body string) (*http.Response, error) {
func (t *httpClient) AddRules(queryParams *url.Values, body string) (*http.Response, error) {
url, err := t.GenerateUrl("rules", queryParams)

if err != nil {
Expand All @@ -69,7 +71,7 @@ func (t *httpClient) AddRules(queryParams string, body string) (*http.Response,
}

// GetSearchStream will start the stream with twitter.
func (t *httpClient) GetSearchStream(queryParams string) (*http.Response, error) {
func (t *httpClient) GetSearchStream(queryParams *url.Values) (*http.Response, error) {
// Make an HTTP GET request to GET /2/tweets/search/stream
url, err := t.GenerateUrl("stream", queryParams)

Expand All @@ -90,10 +92,10 @@ func (t *httpClient) GetSearchStream(queryParams string) (*http.Response, error)
}

// GenerateUrl is a utility function for httpclient package to generate a valid url for api.twitter.
func (t *httpClient) GenerateUrl(name string, queryParams string) (string, error) {
func (t *httpClient) GenerateUrl(name string, queryParams *url.Values) (string, error) {
var url string
if len(queryParams) > 0 {
url = Endpoints[name] + queryParams
if queryParams != nil {
url = Endpoints[name] + fmt.Sprintf("?%v", queryParams.Encode())
} else {
url = Endpoints[name]
}
Expand Down
9 changes: 8 additions & 1 deletion rules/rule_builder.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
package rules

type (
// IRuleBuilder is an interface that describers how to implement a RuleBuilder.
IRuleBuilder interface {
AddRule(value string, tag string) *RuleBuilder
Build() CreateRulesRequest
}

// RuleValue is a struct used to help create twitter stream rules.
// It takes in a value and a tag.
RuleValue struct {
Value *string `json:"value,omitempty"`
Tag *string `json:"tag,omitempty"`
}

// RuleBuilder is struct used to help create twitter stream rules.
RuleBuilder struct {
rules []*RuleValue
}

// CreateRulesRequest is a struct used to create the payload for creating rules.
CreateRulesRequest struct {
Add []*RuleValue `json:"add"`
}

// DeleteRulesRequest is a struct used to create the payload for deleting rules.
DeleteRulesRequest struct {
Delete struct {
Ids []int `json:"ids"`
Expand All @@ -27,12 +33,14 @@ type (

)

// NewDeleteRulesRequest will create an instance of DeleteRulesRequest.
func NewDeleteRulesRequest(ids ...int) DeleteRulesRequest {
return DeleteRulesRequest{Delete: struct {
Ids []int `json:"ids"`
}(struct{ Ids []int }{Ids: ids})}
}

// NewRuleBuilder will create an instance of `RuleBuilder`.
func NewRuleBuilder() *RuleBuilder {
return &RuleBuilder{
rules: []*RuleValue{},
Expand Down Expand Up @@ -64,4 +72,3 @@ func (r *RuleValue) setValueTag(value string, tag string) *RuleValue {
r.Tag = &tag
return r
}

2 changes: 1 addition & 1 deletion rules/rule_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,4 @@ func TestNewRuleBuilderBuildsManyRules(t *testing.T) {
if string(body) != "{\"add\":[{\"value\":\"cats\",\"tag\":\"cat tweets\"},{\"value\":\"dogs\",\"tag\":\"dog tweets\"}]}" {
t.Errorf("Expected %v to equal %v", string(body), "{\"add\":[{\"value\":\"cats\",\"tag\":\"cat tweets\"},{\"value\":\"dogs\",\"tag\":\"dog tweets\"}]}")
}
}
}
28 changes: 14 additions & 14 deletions rules/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package rules
import (
"encoding/json"
"github.com/fallenstedt/twitter-stream/httpclient"
"net/url"
)

type (
Expand Down Expand Up @@ -68,13 +69,7 @@ func (t *rules) Create(rules CreateRulesRequest, dryRun bool) (*TwitterRuleRespo
return nil, err
}

res, err := t.httpClient.AddRules(func() string {
if dryRun {
return "?dry_run=true"
} else {
return ""
}
}(), string(body))
res, err := t.httpClient.AddRules(t.addDryRun(dryRun), string(body))

if err != nil {
return nil, err
Expand All @@ -95,13 +90,7 @@ func (t *rules) Delete(req DeleteRulesRequest, dryRun bool) (*TwitterRuleRespons
return nil, err
}

res, err := t.httpClient.AddRules(func() string {
if dryRun {
return "?dry_run=true"
} else {
return ""
}
}(), string(body))
res, err := t.httpClient.AddRules(t.addDryRun(dryRun), string(body))


defer res.Body.Close()
Expand All @@ -127,3 +116,14 @@ func (t *rules) Get() (*TwitterRuleResponse, error) {
return data, nil
}



func (t *rules) addDryRun(dryRun bool) *url.Values {
if dryRun {
query := new(url.URL).Query()
query.Add("dry_run", "true")
return &query
} else {
return nil
}
}
9 changes: 5 additions & 4 deletions rules/rules_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,20 @@ import (
"github.com/fallenstedt/twitter-stream/httpclient"
"io/ioutil"
"net/http"
"net/url"
"testing"
)

func TestCreate(t *testing.T) {

var tests = []struct {
body CreateRulesRequest
mockRequest func(queryParams string, body string) (*http.Response, error)
mockRequest func(queryParams *url.Values, body string) (*http.Response, error)
result *TwitterRuleResponse
}{
{
NewRuleBuilder().AddRule("cat has:images", "cat tweets with images").Build(),
func(queryParams string, bodyRequest string) (*http.Response, error) {
func(queryParams *url.Values, bodyRequest string) (*http.Response, error) {
json := `{
"data": [{
"Value": "cat has:images",
Expand Down Expand Up @@ -108,12 +109,12 @@ func TestDelete(t *testing.T) {

var tests = []struct {
body DeleteRulesRequest
mockRequest func(queryParams string, body string) (*http.Response, error)
mockRequest func(queryParams *url.Values, body string) (*http.Response, error)
result *TwitterRuleResponse
}{
{
NewDeleteRulesRequest(123),
func(queryParams string, bodyRequest string) (*http.Response, error) {
func(queryParams *url.Values, bodyRequest string) (*http.Response, error) {
json := `{
"data": [{
"Value": "cat has:images",
Expand Down
6 changes: 4 additions & 2 deletions stream/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package stream
import (
"github.com/fallenstedt/twitter-stream/httpclient"
"net/http"
"net/url"
)

type (
Expand All @@ -11,7 +12,7 @@ type (

// IStream is the interface that the stream struct implements.
IStream interface {
StartStream(queryParams string) error
StartStream(queryParams *url.Values) error
StopStream()
GetMessages() <-chan StreamMessage
SetUnmarshalHook(hook UnmarshalHook)
Expand All @@ -36,6 +37,7 @@ type (
}
)

// NewStream creates an instance of `Stream`. This is used to manage the stream with Twitter.
func NewStream(httpClient httpclient.IHttpClient, reader IStreamResponseBodyReader) IStream {
return &Stream{
unmarshalHook: func(bytes []byte) (interface{}, error) {
Expand Down Expand Up @@ -69,7 +71,7 @@ func (s *Stream) StopStream() {
// Accepts query params described in GET /2/tweets/search/stream to expand the payload that is returned. Query params string must begin with a ?.
// See available query params here https://developer.twitter.com/en/docs/twitter-api/tweets/filtered-stream/api-reference/get-tweets-search-stream.
// See an example here: https://developer.twitter.com/en/docs/twitter-api/expansions.
func (s *Stream) StartStream(optionalQueryParams string) error {
func (s *Stream) StartStream(optionalQueryParams *url.Values) error {
res, err := s.httpClient.GetSearchStream(optionalQueryParams)

if err != nil {
Expand Down
Loading

0 comments on commit 15e71f0

Please sign in to comment.