Skip to content

Commit

Permalink
Separating different GET requests, leaning on expected response types…
Browse files Browse the repository at this point in the history
… to eliminate extra required API calls
  • Loading branch information
Derek Dowling committed Dec 13, 2015
1 parent 7494127 commit 391d173
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 64 deletions.
42 changes: 21 additions & 21 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,47 +12,41 @@ import (
"github.com/derekdowling/go-json-spec-handler"
)

// Response is a wrapper around an http.Response that allows us to perform
// intelligent actions on them
type Response struct {
*http.Response
}

// GetObject validates the HTTP response and parses out the JSON object from the
// ParseObject validates the HTTP response and parses out the JSON object from the
// body if possible
func (r *Response) GetObject() (*jsh.Object, *jsh.Error) {
obj, objErr := buildParser(r).GetObject()
func ParseObject(response *http.Response) (*jsh.Object, *jsh.Error) {
obj, objErr := buildParser(response).GetObject()
if objErr != nil {
return nil, objErr.(*jsh.Error)
}

return obj, nil
}

// GetList validates the HTTP response and parses out the JSON list from the
// ParseList validates the HTTP response and parses out the JSON list from the
// body if possible
func (r *Response) GetList() (jsh.List, *jsh.Error) {
list, listErr := buildParser(r).GetList()
func ParseList(response *http.Response) (jsh.List, *jsh.Error) {
list, listErr := buildParser(response).GetList()
if listErr != nil {
return nil, listErr.(*jsh.Error)
}

return list, nil
}

// BodyStr is a convenience function that parses the body of the response into a
// string BUT DOESN'T close the ReadCloser
func (r *Response) BodyStr() (string, *jsh.Error) {
// DumpBody is a convenience function that parses the body of the response into a
// string BUT DOESN'T close the ReadCloser. Useful for debugging.
func DumpBody(response *http.Response) (string, *jsh.Error) {

byteData, err := ioutil.ReadAll(r.Body)
byteData, err := ioutil.ReadAll(response.Body)
if err != nil {
return "", jsh.ISE(fmt.Sprintf("Error attempting to read request body: %s", err.Error()))
}

return string(byteData), nil
}

func buildParser(response *Response) *jsh.Parser {
func buildParser(response *http.Response) *jsh.Parser {
return &jsh.Parser{
Method: "",
Headers: response.Header,
Expand Down Expand Up @@ -100,11 +94,11 @@ func objectToPayload(request *http.Request, object *jsh.Object) ([]byte, *jsh.Er

// sendPayloadRequest is required for sending JSON payload related requests
// because by default the http package does not set Content-Length headers
func sendObjectRequest(request *http.Request, object *jsh.Object) (*Response, *jsh.Error) {
func sendObjectRequest(request *http.Request, object *jsh.Object) (*jsh.Object, *http.Response, *jsh.Error) {

payload, err := objectToPayload(request, object)
if err != nil {
return nil, jsh.ISE(fmt.Sprintf("Error converting object to JSON: %s", err.Error()))
return nil, nil, jsh.ISE(fmt.Sprintf("Error converting object to JSON: %s", err.Error()))
}

// prepare payload and corresponding headers
Expand All @@ -114,11 +108,17 @@ func sendObjectRequest(request *http.Request, object *jsh.Object) (*Response, *j

client := &http.Client{}
response, clientErr := client.Do(request)

if clientErr != nil {
return nil, jsh.ISE(fmt.Sprintf(
return nil, nil, jsh.ISE(fmt.Sprintf(
"Error sending %s request: %s", request.Method, clientErr.Error(),
))
}

return &Response{response}, nil
object, objErr := ParseObject(response)
if objErr != nil {
return nil, response, objErr
}

return object, response, nil
}
10 changes: 5 additions & 5 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,19 @@ func TestClientRequest(t *testing.T) {
})
}

func TestClientResponse(t *testing.T) {
func TestResponseParsing(t *testing.T) {

Convey("Client Response Tests", t, func() {
Convey("Response Parsing Tests", t, func() {

Convey("->GetObject()", func() {
Convey("->ParseObject()", func() {

obj, objErr := jsh.NewObject("123", "test", map[string]string{"test": "test"})
So(objErr, ShouldBeNil)
response, err := mockObjectResponse(obj)
So(err, ShouldBeNil)

Convey("should parse successfully", func() {
respObj, err := response.GetObject()
respObj, err := ParseObject(response)
So(err, ShouldBeNil)
So(respObj, ShouldNotBeNil)
})
Expand All @@ -71,7 +71,7 @@ func TestClientResponse(t *testing.T) {
So(err, ShouldBeNil)

Convey("should parse successfully", func() {
respObj, err := response.GetList()
respObj, err := ParseList(response)
So(err, ShouldBeNil)
So(respObj, ShouldNotBeNil)
})
Expand Down
64 changes: 45 additions & 19 deletions client/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,60 @@ import (
"github.com/derekdowling/go-json-spec-handler"
)

// Get allows a user to make an outbound GET /resources(/:id) request.
//
// For a GET request that retrieves multiple resources, pass an empty string for
// the id parameter:
//
// GET "http://apiserver/users
// resp, err := jsh.Get("http://apiserver", "user", "")
// list, err := resp.GetList()
//
// For a GET request on a specific attribute:
//
// GET "http://apiserver/users/2
// resp, err := jsh.Get("http://apiserver", "user", "2")
// obj := resp.GetObject()
//
func Get(urlStr string, resourceType string, id string) (*Response, *jsh.Error) {
// GetObject allows a user to make an outbound GET /resourceTypes/:id
func GetObject(urlStr string, resourceType string, id string) (*jsh.Object, *http.Response, *jsh.Error) {
if id == "" {
return nil, nil, jsh.SpecificationError("ID cannot be empty for GetObject request type")
}

u, err := url.Parse(urlStr)
if err != nil {
return nil, jsh.ISE(fmt.Sprintf("Error parsing URL: %s", err.Error()))
return nil, nil, jsh.ISE(fmt.Sprintf("Error parsing URL: %s", err.Error()))
}

setIDPath(u, resourceType, id)

response, err := http.Get(u.String())
response, getErr := Get(u.String())
if err != nil {
return nil, nil, getErr
}

object, objectErr := ParseObject(response)
if objectErr != nil {
return nil, response, objectErr
}

return object, response, nil
}

// GetList prepares an outbound request for /resourceTypes expecting a list return value.
func GetList(urlStr string, resourceType string) (jsh.List, *http.Response, *jsh.Error) {
u, err := url.Parse(urlStr)
if err != nil {
return nil, nil, jsh.ISE(fmt.Sprintf("Error parsing URL: %s", err.Error()))
}

setPath(u, resourceType)

response, getErr := Get(u.String())
if err != nil {
return nil, nil, getErr
}

list, listErr := ParseList(response)
if listErr != nil {
return nil, response, listErr
}

return list, response, nil
}

// Get performs a Get request for a given URL and returns a basic Response type
func Get(urlStr string) (*http.Response, *jsh.Error) {
response, err := http.Get(urlStr)
if err != nil {
return nil, jsh.ISE(fmt.Sprintf("Error performing GET request: %s", err.Error()))
}

return &Response{response}, nil
return response, nil
}
20 changes: 12 additions & 8 deletions client/get_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package jsc

import (
"net/http"
"net/http/httptest"
"testing"

Expand All @@ -17,21 +18,24 @@ func TestGet(t *testing.T) {
baseURL := server.URL

Convey("->Get()", func() {
resp, err := Get(baseURL + "/tests/1")
So(err, ShouldBeNil)
So(resp.StatusCode, ShouldEqual, http.StatusOK)
})

Convey("should handle an object listing request", func() {
resp, err := Get(baseURL, "test", "")
So(err, ShouldBeNil)
Convey("->GetList()", func() {

list, err := resp.GetList()
Convey("should handle an object listing request", func() {
list, _, err := GetList(baseURL, "test")
So(err, ShouldBeNil)
So(len(list), ShouldEqual, 1)
})
})

Convey("should handle a specific object request", func() {
resp, err := Get(baseURL, "test", "1")
So(err, ShouldBeNil)
Convey("->GetObject()", func() {

obj, err := resp.GetObject()
Convey("should handle a specific object request", func() {
obj, _, err := GetObject(baseURL, "test", "1")
So(err, ShouldBeNil)
So(obj.ID, ShouldEqual, "1")
})
Expand Down
6 changes: 3 additions & 3 deletions client/patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,18 @@ import (
// resp, _ := jsc.Patch("http://postap.com", obj)
// updatedObj, _ := resp.GetObject()
//
func Patch(urlStr string, object *jsh.Object) (*Response, *jsh.Error) {
func Patch(urlStr string, object *jsh.Object) (*jsh.Object, *http.Response, *jsh.Error) {

u, err := url.Parse(urlStr)
if err != nil {
return nil, jsh.ISE(fmt.Sprintf("Error parsing URL: %s", err.Error()))
return nil, nil, jsh.ISE(fmt.Sprintf("Error parsing URL: %s", err.Error()))
}

setIDPath(u, object.Type, object.ID)

request, err := http.NewRequest("PATCH", u.String(), nil)
if err != nil {
return nil, jsh.ISE(fmt.Sprintf("Error creating PATCH request: %s", err.Error()))
return nil, nil, jsh.ISE(fmt.Sprintf("Error creating PATCH request: %s", err.Error()))
}

return sendObjectRequest(request, object)
Expand Down
6 changes: 3 additions & 3 deletions client/post.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@ import (
// resp, _ := jsh.Post("http://apiserver", obj)
// createdObj := resp.GetObject()
//
func Post(urlStr string, object *jsh.Object) (*Response, *jsh.Error) {
func Post(urlStr string, object *jsh.Object) (*jsh.Object, *http.Response, *jsh.Error) {

u, err := url.Parse(urlStr)
if err != nil {
return nil, jsh.ISE(fmt.Sprintf("Error parsing URL: %s", err.Error()))
return nil, nil, jsh.ISE(fmt.Sprintf("Error parsing URL: %s", err.Error()))
}

// ghetto pluralization, fix when it becomes an issue
setPath(u, object.Type)

request, err := http.NewRequest("POST", u.String(), nil)
if err != nil {
return nil, jsh.ISE(fmt.Sprintf("Error building POST request: %s", err.Error()))
return nil, nil, jsh.ISE(fmt.Sprintf("Error building POST request: %s", err.Error()))
}

return sendObjectRequest(request, object)
Expand Down
10 changes: 5 additions & 5 deletions client/test_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"github.com/derekdowling/go-json-spec-handler"
)

func mockObjectResponse(object *jsh.Object) (*Response, error) {
func mockObjectResponse(object *jsh.Object) (*http.Response, error) {
object.ID = "1"

url := &url.URL{Host: "test"}
Expand All @@ -29,7 +29,7 @@ func mockObjectResponse(object *jsh.Object) (*Response, error) {
return recorderToResponse(recorder), nil
}

func mockListResponse(list jsh.List) (*Response, error) {
func mockListResponse(list jsh.List) (*http.Response, error) {

url := &url.URL{Host: "test"}
setPath(url, list[0].Type)
Expand All @@ -49,10 +49,10 @@ func mockListResponse(list jsh.List) (*Response, error) {
return recorderToResponse(recorder), nil
}

func recorderToResponse(recorder *httptest.ResponseRecorder) *Response {
return &Response{&http.Response{
func recorderToResponse(recorder *httptest.ResponseRecorder) *http.Response {
return &http.Response{
StatusCode: recorder.Code,
Body: jsh.CreateReadCloser(recorder.Body.Bytes()),
Header: recorder.Header(),
}}
}
}

0 comments on commit 391d173

Please sign in to comment.