Skip to content

Commit 391d173

Browse files
author
Derek Dowling
committed
Separating different GET requests, leaning on expected response types to eliminate extra required API calls
1 parent 7494127 commit 391d173

File tree

7 files changed

+94
-64
lines changed

7 files changed

+94
-64
lines changed

client/client.go

+21-21
Original file line numberDiff line numberDiff line change
@@ -12,47 +12,41 @@ import (
1212
"github.com/derekdowling/go-json-spec-handler"
1313
)
1414

15-
// Response is a wrapper around an http.Response that allows us to perform
16-
// intelligent actions on them
17-
type Response struct {
18-
*http.Response
19-
}
20-
21-
// GetObject validates the HTTP response and parses out the JSON object from the
15+
// ParseObject validates the HTTP response and parses out the JSON object from the
2216
// body if possible
23-
func (r *Response) GetObject() (*jsh.Object, *jsh.Error) {
24-
obj, objErr := buildParser(r).GetObject()
17+
func ParseObject(response *http.Response) (*jsh.Object, *jsh.Error) {
18+
obj, objErr := buildParser(response).GetObject()
2519
if objErr != nil {
2620
return nil, objErr.(*jsh.Error)
2721
}
2822

2923
return obj, nil
3024
}
3125

32-
// GetList validates the HTTP response and parses out the JSON list from the
26+
// ParseList validates the HTTP response and parses out the JSON list from the
3327
// body if possible
34-
func (r *Response) GetList() (jsh.List, *jsh.Error) {
35-
list, listErr := buildParser(r).GetList()
28+
func ParseList(response *http.Response) (jsh.List, *jsh.Error) {
29+
list, listErr := buildParser(response).GetList()
3630
if listErr != nil {
3731
return nil, listErr.(*jsh.Error)
3832
}
3933

4034
return list, nil
4135
}
4236

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

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

5246
return string(byteData), nil
5347
}
5448

55-
func buildParser(response *Response) *jsh.Parser {
49+
func buildParser(response *http.Response) *jsh.Parser {
5650
return &jsh.Parser{
5751
Method: "",
5852
Headers: response.Header,
@@ -100,11 +94,11 @@ func objectToPayload(request *http.Request, object *jsh.Object) ([]byte, *jsh.Er
10094

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

10599
payload, err := objectToPayload(request, object)
106100
if err != nil {
107-
return nil, jsh.ISE(fmt.Sprintf("Error converting object to JSON: %s", err.Error()))
101+
return nil, nil, jsh.ISE(fmt.Sprintf("Error converting object to JSON: %s", err.Error()))
108102
}
109103

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

115109
client := &http.Client{}
116110
response, clientErr := client.Do(request)
111+
117112
if clientErr != nil {
118-
return nil, jsh.ISE(fmt.Sprintf(
113+
return nil, nil, jsh.ISE(fmt.Sprintf(
119114
"Error sending %s request: %s", request.Method, clientErr.Error(),
120115
))
121116
}
122117

123-
return &Response{response}, nil
118+
object, objErr := ParseObject(response)
119+
if objErr != nil {
120+
return nil, response, objErr
121+
}
122+
123+
return object, response, nil
124124
}

client/client_test.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -41,19 +41,19 @@ func TestClientRequest(t *testing.T) {
4141
})
4242
}
4343

44-
func TestClientResponse(t *testing.T) {
44+
func TestResponseParsing(t *testing.T) {
4545

46-
Convey("Client Response Tests", t, func() {
46+
Convey("Response Parsing Tests", t, func() {
4747

48-
Convey("->GetObject()", func() {
48+
Convey("->ParseObject()", func() {
4949

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

5555
Convey("should parse successfully", func() {
56-
respObj, err := response.GetObject()
56+
respObj, err := ParseObject(response)
5757
So(err, ShouldBeNil)
5858
So(respObj, ShouldNotBeNil)
5959
})
@@ -71,7 +71,7 @@ func TestClientResponse(t *testing.T) {
7171
So(err, ShouldBeNil)
7272

7373
Convey("should parse successfully", func() {
74-
respObj, err := response.GetList()
74+
respObj, err := ParseList(response)
7575
So(err, ShouldBeNil)
7676
So(respObj, ShouldNotBeNil)
7777
})

client/get.go

+45-19
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,60 @@ import (
88
"github.com/derekdowling/go-json-spec-handler"
99
)
1010

11-
// Get allows a user to make an outbound GET /resources(/:id) request.
12-
//
13-
// For a GET request that retrieves multiple resources, pass an empty string for
14-
// the id parameter:
15-
//
16-
// GET "http://apiserver/users
17-
// resp, err := jsh.Get("http://apiserver", "user", "")
18-
// list, err := resp.GetList()
19-
//
20-
// For a GET request on a specific attribute:
21-
//
22-
// GET "http://apiserver/users/2
23-
// resp, err := jsh.Get("http://apiserver", "user", "2")
24-
// obj := resp.GetObject()
25-
//
26-
func Get(urlStr string, resourceType string, id string) (*Response, *jsh.Error) {
11+
// GetObject allows a user to make an outbound GET /resourceTypes/:id
12+
func GetObject(urlStr string, resourceType string, id string) (*jsh.Object, *http.Response, *jsh.Error) {
13+
if id == "" {
14+
return nil, nil, jsh.SpecificationError("ID cannot be empty for GetObject request type")
15+
}
2716

2817
u, err := url.Parse(urlStr)
2918
if err != nil {
30-
return nil, jsh.ISE(fmt.Sprintf("Error parsing URL: %s", err.Error()))
19+
return nil, nil, jsh.ISE(fmt.Sprintf("Error parsing URL: %s", err.Error()))
3120
}
3221

3322
setIDPath(u, resourceType, id)
3423

35-
response, err := http.Get(u.String())
24+
response, getErr := Get(u.String())
25+
if err != nil {
26+
return nil, nil, getErr
27+
}
28+
29+
object, objectErr := ParseObject(response)
30+
if objectErr != nil {
31+
return nil, response, objectErr
32+
}
33+
34+
return object, response, nil
35+
}
36+
37+
// GetList prepares an outbound request for /resourceTypes expecting a list return value.
38+
func GetList(urlStr string, resourceType string) (jsh.List, *http.Response, *jsh.Error) {
39+
u, err := url.Parse(urlStr)
40+
if err != nil {
41+
return nil, nil, jsh.ISE(fmt.Sprintf("Error parsing URL: %s", err.Error()))
42+
}
43+
44+
setPath(u, resourceType)
45+
46+
response, getErr := Get(u.String())
47+
if err != nil {
48+
return nil, nil, getErr
49+
}
50+
51+
list, listErr := ParseList(response)
52+
if listErr != nil {
53+
return nil, response, listErr
54+
}
55+
56+
return list, response, nil
57+
}
58+
59+
// Get performs a Get request for a given URL and returns a basic Response type
60+
func Get(urlStr string) (*http.Response, *jsh.Error) {
61+
response, err := http.Get(urlStr)
3662
if err != nil {
3763
return nil, jsh.ISE(fmt.Sprintf("Error performing GET request: %s", err.Error()))
3864
}
3965

40-
return &Response{response}, nil
66+
return response, nil
4167
}

client/get_test.go

+12-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package jsc
22

33
import (
4+
"net/http"
45
"net/http/httptest"
56
"testing"
67

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

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

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

25-
list, err := resp.GetList()
28+
Convey("should handle an object listing request", func() {
29+
list, _, err := GetList(baseURL, "test")
2630
So(err, ShouldBeNil)
2731
So(len(list), ShouldEqual, 1)
2832
})
33+
})
2934

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

34-
obj, err := resp.GetObject()
37+
Convey("should handle a specific object request", func() {
38+
obj, _, err := GetObject(baseURL, "test", "1")
3539
So(err, ShouldBeNil)
3640
So(obj.ID, ShouldEqual, "1")
3741
})

client/patch.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,18 @@ import (
1515
// resp, _ := jsc.Patch("http://postap.com", obj)
1616
// updatedObj, _ := resp.GetObject()
1717
//
18-
func Patch(urlStr string, object *jsh.Object) (*Response, *jsh.Error) {
18+
func Patch(urlStr string, object *jsh.Object) (*jsh.Object, *http.Response, *jsh.Error) {
1919

2020
u, err := url.Parse(urlStr)
2121
if err != nil {
22-
return nil, jsh.ISE(fmt.Sprintf("Error parsing URL: %s", err.Error()))
22+
return nil, nil, jsh.ISE(fmt.Sprintf("Error parsing URL: %s", err.Error()))
2323
}
2424

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

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

3232
return sendObjectRequest(request, object)

client/post.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,19 @@ import (
1414
// resp, _ := jsh.Post("http://apiserver", obj)
1515
// createdObj := resp.GetObject()
1616
//
17-
func Post(urlStr string, object *jsh.Object) (*Response, *jsh.Error) {
17+
func Post(urlStr string, object *jsh.Object) (*jsh.Object, *http.Response, *jsh.Error) {
1818

1919
u, err := url.Parse(urlStr)
2020
if err != nil {
21-
return nil, jsh.ISE(fmt.Sprintf("Error parsing URL: %s", err.Error()))
21+
return nil, nil, jsh.ISE(fmt.Sprintf("Error parsing URL: %s", err.Error()))
2222
}
2323

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

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

3232
return sendObjectRequest(request, object)

client/test_util.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
"github.com/derekdowling/go-json-spec-handler"
99
)
1010

11-
func mockObjectResponse(object *jsh.Object) (*Response, error) {
11+
func mockObjectResponse(object *jsh.Object) (*http.Response, error) {
1212
object.ID = "1"
1313

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

32-
func mockListResponse(list jsh.List) (*Response, error) {
32+
func mockListResponse(list jsh.List) (*http.Response, error) {
3333

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

52-
func recorderToResponse(recorder *httptest.ResponseRecorder) *Response {
53-
return &Response{&http.Response{
52+
func recorderToResponse(recorder *httptest.ResponseRecorder) *http.Response {
53+
return &http.Response{
5454
StatusCode: recorder.Code,
5555
Body: jsh.CreateReadCloser(recorder.Body.Bytes()),
5656
Header: recorder.Header(),
57-
}}
57+
}
5858
}

0 commit comments

Comments
 (0)