forked from dghubble/sling
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add initial Sling which decodes Response Body JSON
- Loading branch information
0 parents
commit 3e7592f
Showing
4 changed files
with
129 additions
and
0 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 |
---|---|---|
@@ -0,0 +1,7 @@ | ||
|
||
# Sling | ||
|
||
Sling is a Go REST client library for building and firing requests. | ||
|
||
Sling generalizes ideas from the google/go-github API to make it easy to build Go clients in the same style. | ||
|
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,4 @@ | ||
/* | ||
Sling is a Go REST client library for building and firing API requests. | ||
*/ | ||
package sling |
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,41 @@ | ||
package sling | ||
|
||
import ( | ||
"encoding/json" | ||
"net/http" | ||
) | ||
|
||
type Sling struct { | ||
// http Client for doing requests | ||
httpClient *http.Client | ||
} | ||
|
||
// New returns a new Sling. | ||
func New(httpClient *http.Client) *Sling { | ||
if httpClient == nil { | ||
httpClient = http.DefaultClient | ||
} | ||
return &Sling{httpClient: httpClient} | ||
} | ||
|
||
// Fire sends the HTTP request and decodes the response into the value pointed | ||
// to by 'success'. It wraps http.Client.Do, but handles closing the Response | ||
// Body. The Response and any error making the request are returned. | ||
// | ||
// Note that non-2xx StatusCodes are valid responses, not errors. | ||
func (s *Sling) Fire(req *http.Request, value interface{}) (*http.Response, error) { | ||
resp, err := s.httpClient.Do(req) | ||
if err != nil { | ||
return resp, err | ||
} | ||
// when err is nil, resp contains a non-nil resp.Body which must be closed | ||
defer resp.Body.Close() | ||
err = decodeResponse(resp, value) | ||
return resp, err | ||
} | ||
|
||
// decodeResponse decodes Response Body encoded as JSON into the value pointed | ||
// to by v. Caller should call resp.Body.Close(). | ||
func decodeResponse(resp *http.Response, v interface{}) error { | ||
return json.NewDecoder(resp.Body).Decode(v) | ||
} |
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,77 @@ | ||
package sling | ||
|
||
import ( | ||
"fmt" | ||
"io/ioutil" | ||
"net/http" | ||
"net/http/httptest" | ||
"net/url" | ||
"testing" | ||
) | ||
|
||
func TestNew(t *testing.T) { | ||
developerClient := &http.Client{} | ||
cases := []struct { | ||
input *http.Client | ||
expected *http.Client | ||
}{ | ||
{nil, http.DefaultClient}, | ||
{developerClient, developerClient}, | ||
} | ||
for _, c := range cases { | ||
sling := New(c.input) | ||
if sling.httpClient != c.expected { | ||
t.Errorf("expected %v, got %v", c.expected, sling.httpClient) | ||
} | ||
} | ||
} | ||
|
||
// mockServer returns an httptest.Server which always returns the given body. | ||
// The caller must close the test server. | ||
func mockServer(body string) (*http.Client, *httptest.Server) { | ||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
w.Header().Set("Content-Type", "application/json") | ||
fmt.Fprintln(w, body) | ||
})) | ||
transport := &http.Transport{ | ||
Proxy: func(req *http.Request) (*url.URL, error) { | ||
return url.Parse(server.URL) | ||
}, | ||
} | ||
client := &http.Client{Transport: transport} | ||
return client, server | ||
} | ||
|
||
type FakeModel struct { | ||
Text string `json:"text"` | ||
FavoriteCount int64 `json:"favorite_count"` | ||
} | ||
|
||
func TestFire(t *testing.T) { | ||
expectedText := "Some text" | ||
var expectedFavoriteCount int64 = 24 | ||
client, server := mockServer(`{"text": "Some text", "favorite_count": 24}`) | ||
defer server.Close() | ||
|
||
sling := New(client) | ||
req, _ := http.NewRequest("GET", server.URL, nil) | ||
var model FakeModel | ||
resp, err := sling.Fire(req, &model) | ||
|
||
if err != nil { | ||
t.Errorf("expected nil, got %v", err) | ||
} | ||
if resp.StatusCode != 200 { | ||
t.Errorf("expected %d, got %d", 200, resp.StatusCode) | ||
} | ||
expectedReadError := "http: read on closed response body" | ||
if _, err = ioutil.ReadAll(resp.Body); err == nil || err.Error() != expectedReadError { | ||
t.Errorf("expected %s, got %v", expectedReadError, err) | ||
} | ||
if model.Text != expectedText { | ||
t.Errorf("expected %s, got %s", expectedText, model.Text) | ||
} | ||
if model.FavoriteCount != expectedFavoriteCount { | ||
t.Errorf("expected %d, got %d", expectedFavoriteCount, model.FavoriteCount) | ||
} | ||
} |