This repository has been archived by the owner on Nov 5, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #20 from Fallenstedt/0.3.2
0.3.2
- Loading branch information
Showing
17 changed files
with
366 additions
and
233 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
0.3.1 | ||
0.3.2 |
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
module github.com/fallenstedt/twitter-stream | ||
|
||
go 1.15 | ||
go 1.16 |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package httpclient | ||
|
||
import "net/http" | ||
|
||
type mockHttpClient struct { | ||
token string | ||
MockNewHttpRequest func(opts *RequestOpts) (*http.Response, error) | ||
MockGetSearchStream func(queryParams string) (*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) | ||
} | ||
|
||
func NewHttpClientMock(token string) *mockHttpClient { | ||
return &mockHttpClient{token: token} | ||
} | ||
|
||
func (t *mockHttpClient) GenerateUrl(name string, queryParams string) (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) { | ||
return t.MockAddRules(queryParams, body) | ||
} | ||
|
||
func (t *mockHttpClient) GetSearchStream(queryParams string) (*http.Response, error) { | ||
return t.MockGetSearchStream(queryParams) | ||
} | ||
|
||
func (t *mockHttpClient) NewHttpRequest(opts *RequestOpts) (*http.Response, error) { | ||
return t.MockNewHttpRequest(opts) | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package httpclient | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"io/ioutil" | ||
"log" | ||
"math" | ||
"net/http" | ||
"time" | ||
) | ||
|
||
// httpResponseParser is a struct that will retry network requests if the response has a status code of 429. | ||
type httpResponseParser struct{} | ||
|
||
func (h httpResponseParser) handleResponse(resp *http.Response, opts *RequestOpts, fn func(opts *RequestOpts) (*http.Response, error)) (*http.Response, error) { | ||
// Retry with backoff if 429 | ||
if resp.StatusCode == 429 { | ||
log.Printf("Retrying network request %s with backoff", opts.Url) | ||
|
||
delay := h.getBackOffTime(opts.Retries) | ||
log.Printf("Sleeping for %v seconds", delay) | ||
time.Sleep(delay) | ||
|
||
opts.Retries += 1 | ||
|
||
return fn(opts) | ||
} | ||
|
||
// Reject if 400 or greater | ||
if resp.StatusCode >= 400 { | ||
log.Printf("Network Request at %s failed: %v", opts.Url, resp.StatusCode) | ||
|
||
var msg string | ||
if resp.Body != nil { | ||
body, _ := ioutil.ReadAll(resp.Body) | ||
msg = "Network request failed: " + string(body) | ||
} else { | ||
msg = "Network request failed with status" + fmt.Sprint(resp.StatusCode) | ||
} | ||
|
||
return nil, errors.New(msg) | ||
} | ||
|
||
return resp, nil | ||
} | ||
|
||
func (h httpResponseParser) getBackOffTime(retries uint8) time.Duration { | ||
exponentialBackoffCeilingSecs := 30 | ||
delaySecs := int(math.Floor((math.Pow(2, float64(retries)) - 1) * 0.5)) | ||
if delaySecs > exponentialBackoffCeilingSecs { | ||
delaySecs = 30 | ||
} | ||
return time.Duration(delaySecs) * time.Second | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package httpclient | ||
|
||
import ( | ||
"net/http" | ||
"testing" | ||
) | ||
|
||
func givenHttpResponseParserInstance() *httpResponseParser { | ||
return new(httpResponseParser) | ||
} | ||
|
||
func givenFakeHttpResponse(statusCode int) *http.Response { | ||
res := new(http.Response) | ||
res.StatusCode = statusCode | ||
return res | ||
} | ||
|
||
func TestHandleResponseShouldReturnIf200(t *testing.T) { | ||
instance := givenHttpResponseParserInstance() | ||
opts := new(RequestOpts) | ||
resp := givenFakeHttpResponse(200) | ||
|
||
result, err := instance.handleResponse(resp, opts, func(o *RequestOpts) (*http.Response, error) { | ||
return nil, nil | ||
}) | ||
|
||
if err != nil { | ||
t.Errorf("Expected not error, got %v", err) | ||
} | ||
|
||
if result.StatusCode != 200 { | ||
t.Errorf("Expected a status code of 200") | ||
} | ||
} | ||
|
||
func TestHandleResponseShouldRetryRequestIf429(t *testing.T) { | ||
instance := givenHttpResponseParserInstance() | ||
opts := new(RequestOpts) | ||
resp := givenFakeHttpResponse(429) | ||
|
||
result, err := instance.handleResponse(resp, opts, func(o *RequestOpts) (*http.Response, error) { | ||
return givenFakeHttpResponse(200), nil | ||
}) | ||
|
||
if opts.Retries != 1 { | ||
t.Errorf("Expected atleast on retry attempt, got %v", opts.Retries) | ||
} | ||
|
||
if err != nil { | ||
t.Errorf("Expected not error, got %v", err) | ||
} | ||
|
||
if result.StatusCode != 200 { | ||
t.Errorf("Expected a status code of 200") | ||
} | ||
} | ||
|
||
func TestHandleResponseShouldRejectIf400OrHigher(t *testing.T) { | ||
instance := givenHttpResponseParserInstance() | ||
opts := new(RequestOpts) | ||
resp := givenFakeHttpResponse(401) | ||
|
||
_, err := instance.handleResponse(resp, opts, func(o *RequestOpts) (*http.Response, error) { | ||
return nil, nil | ||
}) | ||
|
||
if err == nil { | ||
t.Errorf("Expected error, got nil") | ||
} | ||
} |
Oops, something went wrong.