diff options
Diffstat (limited to 'client/go/util')
-rw-r--r-- | client/go/util/http.go | 55 | ||||
-rw-r--r-- | client/go/util/http_test.go | 46 | ||||
-rw-r--r-- | client/go/util/io.go | 39 | ||||
-rw-r--r-- | client/go/util/print.go | 63 |
4 files changed, 203 insertions, 0 deletions
diff --git a/client/go/util/http.go b/client/go/util/http.go new file mode 100644 index 00000000000..24e2416117c --- /dev/null +++ b/client/go/util/http.go @@ -0,0 +1,55 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// A HTTP wrapper which handles some errors and provides a way to replace the HTTP client by a mock. +// Author: bratseth + +package util + +import ( + "net/http" + "net/url" + "strings" + "time" +) + +// Set this to a mock HttpClient instead to unit test HTTP requests +var ActiveHttpClient = CreateClient(time.Second * 10) + +type HttpClient interface { + Do(request *http.Request, timeout time.Duration) (response *http.Response, error error) +} + +type defaultHttpClient struct { + client *http.Client +} + +func (c *defaultHttpClient) Do(request *http.Request, timeout time.Duration) (response *http.Response, error error) { + if c.client.Timeout != timeout { // Create a new client with the right timeout + c.client = &http.Client{Timeout: timeout,} + } + return c.client.Do(request) +} + +func CreateClient(timeout time.Duration) HttpClient { + return &defaultHttpClient{ + client: &http.Client{Timeout: timeout,}, + } +} + +// Convenience function for doing a HTTP GET +func HttpGet(host string, path string, description string) *http.Response { + url, urlError := url.Parse(host + path) + if urlError != nil { + Error("Invalid target url '" + host + path + "'") + return nil + } + return HttpDo(&http.Request{URL: url,}, time.Second * 10, description) +} + +func HttpDo(request *http.Request, timeout time.Duration, description string) *http.Response { + response, error := ActiveHttpClient.Do(request, timeout) + if error != nil { + Error("Could not connect to", strings.ToLower(description), "at", request.URL.Host) + Detail(error.Error()) + } + return response +} diff --git a/client/go/util/http_test.go b/client/go/util/http_test.go new file mode 100644 index 00000000000..03b155488d9 --- /dev/null +++ b/client/go/util/http_test.go @@ -0,0 +1,46 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Basic testing of our HTTP client wrapper +// Author: bratseth + +package util + +import ( + "bytes" + "github.com/stretchr/testify/assert" + "io/ioutil" + "net/http" + "testing" + "time" +) + +type mockHttpClient struct {} + +func (c mockHttpClient) Do(request *http.Request, timeout time.Duration) (response *http.Response, error error) { + var status int + var body string + if request.URL.String() == "http://host/okpath" { + status = 200 + body = "OK body" + } else { + status = 500 + body = "Unexpected url body" + } + + return &http.Response{ + StatusCode: status, + Header: make(http.Header), + Body: ioutil.NopCloser(bytes.NewBufferString(body)), + }, + nil +} + +func TestHttpRequest(t *testing.T) { + ActiveHttpClient = mockHttpClient{} + + response := HttpGet("http://host", "/okpath", "description") + assert.Equal(t, 200, response.StatusCode) + + response = HttpGet("http://host", "/otherpath", "description") + assert.Equal(t, 500, response.StatusCode) +} + diff --git a/client/go/util/io.go b/client/go/util/io.go new file mode 100644 index 00000000000..d7b849ba9a4 --- /dev/null +++ b/client/go/util/io.go @@ -0,0 +1,39 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// File utilities. +// Author: bratseth + +package util + +import ( + "bytes" + "errors" + "io" + "os" + "strings" +) + +// Returns true if the given path exists +func PathExists(path string) bool { + _, err := os.Stat(path) + return ! errors.Is(err, os.ErrNotExist) +} + +// Returns true is the given path points to an existing directory +func IsDirectory(path string) bool { + info, err := os.Stat(path) + return ! errors.Is(err, os.ErrNotExist) && info.IsDir() +} + +// Returns the content of a reader as a string +func ReaderToString(reader io.Reader) string { + buffer := new(strings.Builder) + io.Copy(buffer, reader) + return buffer.String() +} + +// Returns the content of a reader as a byte array +func ReaderToBytes(reader io.Reader) []byte { + buffer := new(bytes.Buffer) + buffer.ReadFrom(reader) + return buffer.Bytes() +} diff --git a/client/go/util/print.go b/client/go/util/print.go new file mode 100644 index 00000000000..91a223a3c3a --- /dev/null +++ b/client/go/util/print.go @@ -0,0 +1,63 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Print functions for color-coded text. +// Author: bratseth + +package util + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "os" +) + +// Set this to have output written somewhere else than os.Stdout +var Out io.Writer + +func init() { + Out = os.Stdout +} + +// Prints in default color +func Print(messages ...string) { + print("", messages) +} + +// Prints in a color appropriate for errors +func Error(messages ...string) { + print("\033[31m", messages) +} + +// Prints in a color appropriate for success messages +func Success(messages ...string) { + print("\033[32m", messages) +} + +// Prints in a color appropriate for detail messages +func Detail(messages ...string) { + print("\033[33m", messages) +} + +// Prints all the text of the given reader +func PrintReader(reader io.Reader) { + bodyBytes := ReaderToBytes(reader) + var prettyJSON bytes.Buffer + parseError := json.Indent(&prettyJSON, bodyBytes, "", " ") + if parseError != nil { // Not JSON: Print plainly + Print(string(bodyBytes)) + } else { + Print(string(prettyJSON.Bytes())) + } +} + +func print(prefix string, messages []string) { + fmt.Fprint(Out, prefix) + for i := 0; i < len(messages); i++ { + fmt.Fprint(Out, messages[i]) + if (i < len(messages) - 1) { + fmt.Fprint(Out, " ") + } + } + fmt.Fprintln(Out, "") +} |