From fdacbd9b490c272dd409a3378b5d2235571b933a Mon Sep 17 00:00:00 2001 From: JustAdam Date: Sun, 22 Jun 2014 13:37:30 +0200 Subject: [PATCH] #1 code review comment fixes. --- client.go | 127 ++++++++++++++++++++++++++++--------------------- client_test.go | 18 +++---- rest.go | 36 +++++++------- rest_test.go | 20 ++++---- stream.go | 51 ++++++++++---------- stream_test.go | 50 +++++++++---------- 6 files changed, 164 insertions(+), 138 deletions(-) diff --git a/client.go b/client.go index 19b5f0d..014f65b 100644 --- a/client.go +++ b/client.go @@ -24,40 +24,50 @@ var ( const ( // Layout of Twitter's timestamp - twitter_time_layout = "Mon Jan 02 15:04:05 Z0700 2006" + twitterTimeLayout = "Mon Jan 02 15:04:05 Z0700 2006" ) -// @todo Calling code should know which stream/request finishes or errors +// StreamClient provides a client to access to the Twitter API. The client is unusable until +// it is authenticated with Twitter (call Authenticate()). type StreamClient struct { oauthClient *oauth.Client token *oauth.Credentials - // Tweets received from every open stream will be sent here + /* @todo Calling code should know which stream/request finishes or errors? */ + + // Tweets received from any open stream will be sent here. Tweets chan *TwitterStatus - // Any received errors are sent here (Embedded API errors are current not fully supported) + // Any received errors are sent here (Embedded API errors are currently not fully supported) Errors chan error - // When a call has finished, this channel will receive data + // When a request has finished, this channel will receive data. Finished chan struct{} } -type TwitterApiUrl struct { - // HTTP method which should be used to access the method (currently only get & post is supported) - AccessMethod string +// A TwitterAPIURL provides details on how to access Twitter API URLs. +type TwitterAPIURL struct { + // HTTP method which should be used to access the method (currently only get, post & custom is supported) + AccessMethod string + // If setting AccessMethod to custom then you must provide your own client handler. Otherwise all + // requests go via the oauthClient. CustomHandler func(*http.Client, *oauth.Credentials, string, url.Values) (*http.Response, error) - Url string + // An actual Twitter API resource URL. + URL string // API type being accessed (stream or rest) Type string } +// A TwitterError will be generated when there is a problem with the request or stream. +// JSON decoding errors are not changed. type TwitterError struct { - Id int + ID int Msg string } +// TwitterStatus represents a tweet with all supporting & available information. type TwitterStatus struct { - Id string `json:"id_str"` - ReplyToStatusIdStr string `json:"in_reply_to_status_id_str"` - ReplyToUserIdStr string `json:"in_reply_to_user_id_str"` + ID string `json:"id_str"` + ReplyToStatusIDStr string `json:"in_reply_to_status_id_str"` + ReplyToUserIDStr string `json:"in_reply_to_user_id_str"` ReplyToUserScreenName string `json:"in_reply_to_screen_name"` CreatedAt TwitterTime `json:"created_at"` Text string `json:"text"` @@ -76,18 +86,19 @@ type TwitterStatus struct { Entities TwitterEntity `json:"entities"` } -// Easier JSON unmarshaling help +// TwitterTime provides a timestamp. It is seperate for easier JSON unmarshaling help. type TwitterTime struct { T time.Time } +// TwitterUser represents a Twitter user with all supporting & available information. type TwitterUser struct { - Id string `json:"id_str"` + ID string `json:"id_str"` Name string `json:"name"` ScreenName string `json:"screen_name"` CreatedAt TwitterTime `json:"created_at"` Location string `json:"location"` - Url string `json:"url"` + URL string `json:"url"` Description string `json:"description"` Protected bool `json:"protected"` FollowersCount uint32 `json:"followers_count"` @@ -120,14 +131,16 @@ type TwitterUser struct { Status map[string]interface{} `json:"status"` } +// TwitterCoordinate is a Twitter platform object and stores coordinates. type TwitterCoordinate struct { Type string `json:"type"` Coordinates []interface{} `json:"coordinates"` } +// TwitterPlace is a Twitter platform object for places. type TwitterPlace struct { - Id string `json:"id"` - Url string `json:"url"` + ID string `json:"id"` + URL string `json:"url"` PlaceType string `json:"place_type"` Name string `json:"name"` FullName string `json:"full_name"` @@ -137,45 +150,51 @@ type TwitterPlace struct { ContainedWithin map[string]interface{} `json:"contained_within"` } +// TwitterEntity contains entity information associated to a tweet. type TwitterEntity struct { Hashtags []TweetHashTag `json:"hashtags"` Media []TweetMedia `json:"media"` - Urls []TweetUrl `json:"urls"` + URLs []TweetURL `json:"urls"` UserMentions []TweetUserMention `json:"user_mentions"` } +// TweetHashTag contains any hashtags that are found within the tweet. type TweetHashTag struct { Text string `json:"text"` Indices []uint `json:"indices"` } +// TweetMedia contains any media types that are associated to the tweet. type TweetMedia struct { - Id string `json:"id_str"` + ID string `json:"id_str"` Type string `json:"type"` - Url string `json:"url"` - DisplayUrl string `json:"display_url"` - ExpandedUrl string `json:"expanded_url"` - MediaUrl string `json:"media_url"` - MediaUrlHttps string `json:"media_url_https"` + URL string `json:"url"` + DisplayURL string `json:"display_url"` + ExpandedURL string `json:"expanded_url"` + MediaURL string `json:"media_url"` + MediaURLHttps string `json:"media_url_https"` Sizes map[string]interface{} `json:"sizes"` // https://dev.twitter.com/docs/platform-objects/entities#obj-sizes Indices []uint `json:"indices"` - SourceStatusId string `json:"source_id_status_str"` + SourceStatusID string `json:"source_id_status_str"` } -type TweetUrl struct { - Url string `json:"url"` - DisplayUrl string `json:"display_url"` - ExpandedUrl string `json:"expanded_url"` +// TweetURL contains any URLs that are found within the tweet. +type TweetURL struct { + URL string `json:"url"` + DisplayURL string `json:"display_url"` + ExpandedURL string `json:"expanded_url"` Indices []uint `json:"indices"` } +// TweetUserMention containis any users who were mentioned within the tweet. type TweetUserMention struct { - Id string `json:"id_str"` + ID string `json:"id_str"` Name string `json:"name"` ScreenName string `json:"screen_name"` Indices []uint `json:"indices"` } +// Create a new StreamClient. func NewClient() (client *StreamClient) { client = new(StreamClient) client.oauthClient = &oauth.Client{ @@ -190,16 +209,16 @@ func NewClient() (client *StreamClient) { return } -// Authenicate the app and user with Twitter. +// Authenicate the app, and user with Twitter using the oauth client. +// Token information for the app stored in the following JSON format: +// { +// "App":{ +// "Token":"YOUR APP TOKEN HERE", +// "Secret":"APP SECRET HERE" +// } +// } func (s *StreamClient) Authenticate(tokenFile *string) error { - // Read in applications's token information. In json format: - // { - // "App":{ - // "Token":"YOUR APP TOKEN HERE", - // "Secret":"APP SECRET HERE" - // } - //} cf, err := ioutil.ReadFile(*tokenFile) if err != nil { return err @@ -213,11 +232,11 @@ func (s *StreamClient) Authenticate(tokenFile *string) error { app, ok := credentials["App"] if ok != true { - return errors.New("Missing App token") + return errors.New("missing App token") } s.oauthClient.Credentials = *app if s.oauthClient.Credentials.Token == "" || s.oauthClient.Credentials.Secret == "" { - return errors.New("Missing app's Token or Secret") + return errors.New("missing app's Token or Secret") } // Check for token information from the user (they need to grant your app access for feed access) @@ -240,6 +259,7 @@ func (s *StreamClient) Authenticate(tokenFile *string) error { return err } + // Save the user's token information credentials["User"] = token save, err := json.Marshal(credentials) if err != nil { @@ -258,8 +278,9 @@ func (s *StreamClient) Authenticate(tokenFile *string) error { return nil } +// Send a request to Twitter. // Calling method is responsible for closing the connection. -func (s *StreamClient) sendRequest(stream *TwitterApiUrl, formValues *url.Values) (*http.Response, error) { +func (s *StreamClient) sendRequest(stream *TwitterAPIURL, formValues *url.Values) (*http.Response, error) { var method func(*http.Client, *oauth.Credentials, string, url.Values) (*http.Response, error) if stream.AccessMethod == "custom" { method = stream.CustomHandler @@ -271,7 +292,7 @@ func (s *StreamClient) sendRequest(stream *TwitterApiUrl, formValues *url.Values } } - resp, err := method(http.DefaultClient, s.token, stream.Url, *formValues) + resp, err := method(http.DefaultClient, s.token, stream.URL, *formValues) if err != nil { return nil, err } @@ -281,37 +302,37 @@ func (s *StreamClient) sendRequest(stream *TwitterApiUrl, formValues *url.Values case 401: // Delete User entry in tokens json file? return nil, &TwitterError{ - Id: resp.StatusCode, + ID: resp.StatusCode, Msg: "Incorrect usename or password.", } case 403: return nil, &TwitterError{ - Id: resp.StatusCode, + ID: resp.StatusCode, Msg: "Access to resource is forbidden", } case 404: return nil, &TwitterError{ - Id: resp.StatusCode, + ID: resp.StatusCode, Msg: "Resource does not exist.", } case 406: return nil, &TwitterError{ - Id: resp.StatusCode, + ID: resp.StatusCode, Msg: "One or more required parameters are missing or are not suitable (see relevant stream API for more information).", } case 413: return nil, &TwitterError{ - Id: resp.StatusCode, + ID: resp.StatusCode, Msg: "A parameter list is too long (contact Twitter for increased access).", } case 416: return nil, &TwitterError{ - Id: resp.StatusCode, + ID: resp.StatusCode, Msg: "Range unacceptable.", } case 420: return nil, &TwitterError{ - Id: resp.StatusCode, + ID: resp.StatusCode, Msg: "Rate limited.", } default: @@ -319,12 +340,12 @@ func (s *StreamClient) sendRequest(stream *TwitterApiUrl, formValues *url.Values } } +// Unmarshal a timestamp from Twitter func (tt *TwitterTime) UnmarshalJSON(b []byte) (err error) { - // Remove start and end quotes - tt.T, err = time.Parse(twitter_time_layout, string(b[1:len(b)-1])) + tt.T, err = time.Parse(twitterTimeLayout, string(b[1:len(b)-1])) return } func (e TwitterError) Error() string { - return fmt.Sprintf("%s (%d)", e.Msg, e.Id) + return fmt.Sprintf("%s (%d)", e.Msg, e.ID) } diff --git a/client_test.go b/client_test.go index 4b4c1a2..15a2bcd 100644 --- a/client_test.go +++ b/client_test.go @@ -15,7 +15,7 @@ func TestAuthenticateMissingAppDataError(t *testing.T) { file := "test_data/tokens.json" err := client.Authenticate(&file) - if err.Error() != "Missing App token" { + if err.Error() != "missing App token" { t.Errorf("Expecting error \"Missing App token\", got %v", err) } } @@ -25,7 +25,7 @@ func TestAuthenticateMissingAppTokenSecretError(t *testing.T) { file := "test_data/tokens_empty.json" err := client.Authenticate(&file) - if err.Error() != "Missing app's Token or Secret" { + if err.Error() != "missing app's Token or Secret" { t.Errorf("Expecting error \"Missing app's Token or Secret\", got %v", err) } } @@ -42,12 +42,12 @@ func TestAuthenticateAccessTokenIsSetInFile(t *testing.T) { func TestTwitterErrorOutput(t *testing.T) { err := &TwitterError{ - Id: 101, - Msg: "Error message", + ID: 101, + Msg: "error message", } - if err.Error() != "Error message (101)" { - t.Errorf("Expecting \"Error message (101)\", got %v", err) + if err.Error() != "error message (101)" { + t.Errorf("Expecting \"error message (101)\", got %v", err) } } @@ -63,7 +63,7 @@ func TestSendResponseErrorOutput(t *testing.T) { return resp, nil } - testurl := &TwitterApiUrl{ + testurl := &TwitterAPIURL{ AccessMethod: "custom", CustomHandler: handler, } @@ -72,8 +72,8 @@ func TestSendResponseErrorOutput(t *testing.T) { if rerr, ok := err.(*TwitterError); !ok { t.Errorf("Expecting TwitterError, got %v", reflect.TypeOf(err)) - } else if rerr.Id != v { - t.Errorf("Expecting error ID %v, got %v", v, rerr.Id) + } else if rerr.ID != v { + t.Errorf("Expecting error ID %v, got %v", v, rerr.ID) } } } diff --git a/rest.go b/rest.go index 3199f31..6585199 100644 --- a/rest.go +++ b/rest.go @@ -3,6 +3,7 @@ // Package streamingtwitter provides access to Twitter's streaming API. // See https://dev.twitter.com/docs/api/streaming for more information. + package streamingtwitter import ( @@ -10,23 +11,24 @@ import ( "net/url" ) -// Send REST request to Twitter's REST API: https://dev.twitter.com/docs/api/1.1 -// -// args := &url.Values{} -// args.Add("screen_name", "TwitterName") -// data := []TwitterUser{} -// url := &TwitterApiUrl{ -// AccessMethod: "get", -// Url: "https://api.twitter.com/1.1/users/lookup.json", -// } -// go client.Rest(url, args, &data) -// select { -// case err := <-client.Errors: -// log.Fatal(err) -// case <-client.Finished: -// fmt.Printf("%+v", data) -// } -func (s *StreamClient) Rest(stream *TwitterApiUrl, formValues *url.Values, data interface{}) { +// Rest sends a REST request to Twitter's REST API: https://dev.twitter.com/docs/api/1.1 +/* + args := &url.Values{} + args.Add("screen_name", "TwitterName") + data := []TwitterUser{} + url := &TwitterApiUrl{ + AccessMethod: "get", + Url: "https://api.twitter.com/1.1/users/lookup.json", + } + go client.Rest(url, args, &data) + select { + case err := <-client.Errors: + log.Fatal(err) + case <-client.Finished: + fmt.Printf("%+v", data) + } +*/ +func (s *StreamClient) Rest(stream *TwitterAPIURL, formValues *url.Values, data interface{}) { resp, err := s.sendRequest(stream, formValues) if err != nil { s.Errors <- err diff --git a/rest_test.go b/rest_test.go index 033e956..3662a5f 100644 --- a/rest_test.go +++ b/rest_test.go @@ -27,7 +27,7 @@ func TestUserLookupJsonDecode(t *testing.T) { return resp, nil } - testurl := &TwitterApiUrl{ + testurl := &TwitterAPIURL{ AccessMethod: "custom", CustomHandler: handler, } @@ -43,9 +43,9 @@ func TestUserLookupJsonDecode(t *testing.T) { t.Fatal("Data not received on Finished channel") } - testData := []JsonTestData{ - {"Id", data[0].Id, "89409855"}, - {"Id", data[1].Id, "15439395"}, + testData := []JSONTestData{ + {"ID", data[0].ID, "89409855"}, + {"ID", data[1].ID, "15439395"}, } for _, d := range testData { @@ -58,10 +58,10 @@ func TestUserLookupJsonDecode(t *testing.T) { // sendRequest error func TestRestSendsRequestError(t *testing.T) { handler := func(*http.Client, *oauth.Credentials, string, url.Values) (*http.Response, error) { - return &http.Response{}, errors.New("Test error") + return &http.Response{}, errors.New("test error") } - testurl := &TwitterApiUrl{ + testurl := &TwitterAPIURL{ AccessMethod: "custom", CustomHandler: handler, } @@ -70,7 +70,7 @@ func TestRestSendsRequestError(t *testing.T) { go client.Rest(testurl, &url.Values{}, &struct{}{}) select { case err := <-client.Errors: - if err.Error() != "Test error" { + if err.Error() != "test error" { t.Errorf("Expecting error \"Test error\", got %v", err) } break @@ -88,7 +88,7 @@ func TestRestSendsDecodingError(t *testing.T) { return resp, nil } - testurl := &TwitterApiUrl{ + testurl := &TwitterAPIURL{ AccessMethod: "custom", CustomHandler: handler, } @@ -114,7 +114,7 @@ func TestRestSendsFinishedNotification(t *testing.T) { return resp, nil } - testurl := &TwitterApiUrl{ + testurl := &TwitterAPIURL{ AccessMethod: "custom", CustomHandler: handler, } @@ -141,7 +141,7 @@ func TestRestClosesResp(t *testing.T) { return resp, nil } - testurl := &TwitterApiUrl{ + testurl := &TwitterAPIURL{ AccessMethod: "custom", CustomHandler: handler, } diff --git a/stream.go b/stream.go index 6e944be..1d5204d 100644 --- a/stream.go +++ b/stream.go @@ -3,6 +3,7 @@ // Package streamingtwitter provides access to Twitter's streaming API. // See https://dev.twitter.com/docs/api/streaming for more information. + package streamingtwitter import ( @@ -11,46 +12,48 @@ import ( ) var ( - // Streaming API URLs - Streams = make(map[string]*TwitterApiUrl) + // Streams is a map of known Twitter Streaming API URLs. + /* @todo implement fully */ + Streams = make(map[string]*TwitterAPIURL) ) // https://dev.twitter.com/docs/api/streaming func init() { // Public stream URLs - https://dev.twitter.com/docs/streaming-apis/streams/public - Streams["Filter"] = &TwitterApiUrl{ + Streams["Filter"] = &TwitterAPIURL{ AccessMethod: "post", - Url: "https://stream.twitter.com/1.1/statuses/filter.json", + URL: "https://stream.twitter.com/1.1/statuses/filter.json", Type: "stream", } - Streams["Firehose"] = &TwitterApiUrl{ + Streams["Firehose"] = &TwitterAPIURL{ AccessMethod: "get", - Url: "https://stream.twitter.com/1.1/statuses/firehose.json", + URL: "https://stream.twitter.com/1.1/statuses/firehose.json", Type: "stream", } - Streams["Sample"] = &TwitterApiUrl{ + Streams["Sample"] = &TwitterAPIURL{ AccessMethod: "get", - Url: "https://stream.twitter.com/1.1/statuses/sample.json", + URL: "https://stream.twitter.com/1.1/statuses/sample.json", Type: "stream", } } -// Create new Twitter stream. -// -// args := &url.Values{} -// args.Add("track", "Norway") -// go client.Stream(streamingtwitter.Streams["Filter"], args) -// for { -// select { -// case status := <-client.Tweets: -// fmt.Println(status) -// case err := <-client.Errors: -// fmt.Printf("ERROR: '%s'\n", err) -// case <-client.Finished: -// return -// } -// } -func (s *StreamClient) Stream(stream *TwitterApiUrl, formValues *url.Values) { +// Stream creates a new Twitter API stream and sends received tweets on channel client.Tweets +/* + args := &url.Values{} + args.Add("track", "Norway") + go client.Stream(streamingtwitter.Streams["Filter"], args) + for { + select { + case status := <-client.Tweets: + fmt.Println(status) + case err := <-client.Errors: + fmt.Printf("ERROR: '%s'\n", err) + case <-client.Finished: + return + } + } +*/ +func (s *StreamClient) Stream(stream *TwitterAPIURL, formValues *url.Values) { resp, err := s.sendRequest(stream, formValues) if err != nil { s.Errors <- err diff --git a/stream_test.go b/stream_test.go index 8abb3d3..408a3bb 100644 --- a/stream_test.go +++ b/stream_test.go @@ -14,7 +14,7 @@ import ( "time" ) -type JsonTestData struct { +type JSONTestData struct { n string // Variable name v interface{} // Variable value e interface{} // Expected @@ -32,7 +32,7 @@ func TestTweetCreation(t *testing.T) { return resp, nil } - testurl := &TwitterApiUrl{ + testurl := &TwitterAPIURL{ AccessMethod: "custom", CustomHandler: handler, } @@ -47,20 +47,20 @@ func TestTweetCreation(t *testing.T) { t.Fatal("Tweet data not receieve on Tweets channel") } - testData := []JsonTestData{ - {"Id", status.Id, "468728009768579073"}, - {"ReplyToStatusIdStr", status.ReplyToStatusIdStr, ""}, - {"ReplyToUserIdStr", status.ReplyToUserIdStr, ""}, + testData := []JSONTestData{ + {"ID", status.ID, "468728009768579073"}, + {"ReplyToStatusIDStr", status.ReplyToStatusIDStr, ""}, + {"ReplyToUserIDStr", status.ReplyToUserIDStr, ""}, {"ReplyToUserScreenName", status.ReplyToUserScreenName, ""}, {"CreatedAt", status.CreatedAt.T.String(), "2014-05-20 12:20:40 +0000 UTC"}, {"Text", status.Text, "RT @IgorjI4J5: https://t.co/WM4MOusVww #RuinAToy Схема башни московского кремля"}, // TwitterUser - {"User.Id", status.User.Id, "2450810000"}, + {"User.ID", status.User.ID, "2450810000"}, {"User.Name", status.User.Name, "Афанасия Кудряшова"}, {"User.ScreenName", status.User.ScreenName, "AfanasiyaI2E"}, {"User.CreatedAt", status.User.CreatedAt.T.String(), "2014-04-18 04:12:25 +0000 UTC"}, {"User.Location", status.User.Location, ""}, - {"User.Url", status.User.Url, ""}, + {"User.URL", status.User.URL, ""}, {"User.Description", status.User.Description, ""}, {"User.Protected", status.User.Protected, false}, {"User.FollowersCount", status.User.FollowersCount, uint32(7)}, @@ -104,8 +104,8 @@ func TestTweetCreation(t *testing.T) { {"Coordinates.Coordinates[0]", status.Coordinates.Coordinates[0], -74.210251}, {"Coordinates.Coordinates[1]", status.Coordinates.Coordinates[1], 40.422551}, // TwitterPlace - {"Place.Id", status.Place.Id, "27485069891a7938"}, - {"Place.Url", status.Place.Url, "https://api.twitter.com/1.1/geo/id/27485069891a7938.json"}, + {"Place.ID", status.Place.ID, "27485069891a7938"}, + {"Place.URL", status.Place.URL, "https://api.twitter.com/1.1/geo/id/27485069891a7938.json"}, {"Place.PlaceType", status.Place.PlaceType, "city"}, {"Place.Name", status.Place.Name, "New York"}, {"Place.FullName", status.Place.FullName, "New York, NY"}, @@ -122,24 +122,24 @@ func TestTweetCreation(t *testing.T) { {"Entities.Hashtags[0].Indices[0]", status.Entities.Hashtags[0].Indices[0], uint(39)}, {"Entities.Hashtags[0].Indices[1]", status.Entities.Hashtags[0].Indices[1], uint(48)}, // TwitterMedia - {"Entities.Media[0].Id", status.Entities.Media[0].Id, "468831177185710081"}, + {"Entities.Media[0].ID", status.Entities.Media[0].ID, "468831177185710081"}, {"Entities.Media[0].Type", status.Entities.Media[0].Type, "photo"}, - {"Entities.Media[0].Url", status.Entities.Media[0].Url, "http://t.co/Sqk7VYgixB"}, - {"Entities.Media[0].DisplayUrl", status.Entities.Media[0].DisplayUrl, "pic.twitter.com/Sqk7VYgixB"}, - {"Entities.Media[0].ExpandedUrl", status.Entities.Media[0].ExpandedUrl, "http://twitter.com/ShawnWiora/status/468831180297887744/photo/1"}, - {"Entities.Media[0].MediaUrl", status.Entities.Media[0].MediaUrl, "http://pbs.twimg.com/media/BoGfeL_IUAEcnfI.jpg"}, - {"Entities.Media[0].MediaUrlHttps", status.Entities.Media[0].MediaUrlHttps, "https://pbs.twimg.com/media/BoGfeL_IUAEcnfI.jpg"}, + {"Entities.Media[0].URL", status.Entities.Media[0].URL, "http://t.co/Sqk7VYgixB"}, + {"Entities.Media[0].DisplayURL", status.Entities.Media[0].DisplayURL, "pic.twitter.com/Sqk7VYgixB"}, + {"Entities.Media[0].ExpandedURL", status.Entities.Media[0].ExpandedURL, "http://twitter.com/ShawnWiora/status/468831180297887744/photo/1"}, + {"Entities.Media[0].MediaURL", status.Entities.Media[0].MediaURL, "http://pbs.twimg.com/media/BoGfeL_IUAEcnfI.jpg"}, + {"Entities.Media[0].MediaURLHttps", status.Entities.Media[0].MediaURLHttps, "https://pbs.twimg.com/media/BoGfeL_IUAEcnfI.jpg"}, {"Entities.Media[0].Sizes[\"medium\"].(map[string]interface{})[\"w\"]", status.Entities.Media[0].Sizes["medium"].(map[string]interface{})["w"], float64(600)}, {"Entities.Media[0].Indices", status.Entities.Media[0].Indices[0], uint(113)}, {"Entities.Media[0].Indices", status.Entities.Media[0].Indices[1], uint(135)}, // TwitterUrl - {"Entities.Urls[0].Url", status.Entities.Urls[0].Url, "https://t.co/WM4MOusVww"}, - {"Entities.Urls[0].DisplayUrl", status.Entities.Urls[0].DisplayUrl, "docs.google.com/document/d/1Uc\u2026"}, - {"Entities.Urls[0].ExpandedUrl", status.Entities.Urls[0].ExpandedUrl, "https://docs.google.com/document/d/1UcPtaqzHLCpbOlbrOBRAHuWoXsxoc2Ei4hxlNJMR_1I/edit?usp=d"}, - {"Entities.Urls[0].Indices", status.Entities.Urls[0].Indices[0], uint(15)}, - {"Entities.Urls[0].Indices", status.Entities.Urls[0].Indices[1], uint(38)}, + {"Entities.URLs[0].Url", status.Entities.URLs[0].URL, "https://t.co/WM4MOusVww"}, + {"Entities.URLs[0].DisplayURL", status.Entities.URLs[0].DisplayURL, "docs.google.com/document/d/1Uc\u2026"}, + {"Entities.URLs[0].ExpandedURL", status.Entities.URLs[0].ExpandedURL, "https://docs.google.com/document/d/1UcPtaqzHLCpbOlbrOBRAHuWoXsxoc2Ei4hxlNJMR_1I/edit?usp=d"}, + {"Entities.URLs[0].Indices", status.Entities.URLs[0].Indices[0], uint(15)}, + {"Entities.URLs[0].Indices", status.Entities.URLs[0].Indices[1], uint(38)}, // TwitterMention - {"Entities.UserMentions[0].Id", status.Entities.UserMentions[0].Id, "2459254292"}, + {"Entities.UserMentions[0].ID", status.Entities.UserMentions[0].ID, "2459254292"}, {"Entities.UserMentions[0].Name", status.Entities.UserMentions[0].Name, "Игорь Савин"}, {"Entities.UserMentions[0].ScreenName", status.Entities.UserMentions[0].ScreenName, "IgorjI4J5"}, {"Entities.UserMentions[0].Indices", status.Entities.UserMentions[0].Indices[0], uint(3)}, @@ -173,7 +173,7 @@ func TestStreamSendsRequestError(t *testing.T) { return &http.Response{}, errors.New("Test error") } - testurl := &TwitterApiUrl{ + testurl := &TwitterAPIURL{ AccessMethod: "custom", CustomHandler: handler, } @@ -203,7 +203,7 @@ func TestStreamEOFClosesResp(t *testing.T) { return resp, nil } - testurl := &TwitterApiUrl{ + testurl := &TwitterAPIURL{ AccessMethod: "custom", CustomHandler: handler, } @@ -240,7 +240,7 @@ func TestDecodingErrorContinues(t *testing.T) { return resp, nil } - testurl := &TwitterApiUrl{ + testurl := &TwitterAPIURL{ AccessMethod: "custom", CustomHandler: handler, }