Skip to content

Commit

Permalink
Merge pull request #9 from derekdowling/dd-jsc-headers
Browse files Browse the repository at this point in the history
Adding support for intermediate client requests in jsc, allows custom…
  • Loading branch information
Derek Dowling committed Jan 5, 2016
2 parents b4f0fb1 + fde91ab commit a43bd0e
Show file tree
Hide file tree
Showing 11 changed files with 198 additions and 43 deletions.
4 changes: 4 additions & 0 deletions Godeps/Godeps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions Godeps/_workspace/src/github.com/derekdowling/go-stdlogger/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 24 additions & 24 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,49 +72,49 @@ func setIDPath(url *url.URL, resource string, id string) {
}
}

// objectToPayload first prepares/validates the object to ensure it is JSON
// spec compatible, and then marshals it to JSON
func objectToPayload(request *http.Request, object *jsh.Object) ([]byte, *jsh.Error) {
// prepareBody first prepares/validates the object to ensure it is JSON
// spec compatible, and then marshals it to JSON, sets the request body and
// corresponding attributes
func prepareBody(request *http.Request, object *jsh.Object) error {

err := object.Validate(request, false)
if err != nil {
return nil, jsh.ISE(fmt.Sprintf("Error preparing object: %s", err.Error()))
return fmt.Errorf("Error preparing object: %s", err.Error())
}

doc := jsh.Build(object)

jsonContent, jsonErr := json.MarshalIndent(doc, "", " ")
jsonContent, jsonErr := json.MarshalIndent(doc, "", " ")
if jsonErr != nil {
return nil, jsh.ISE(fmt.Sprintf("Unable to prepare JSON content: %s", jsonErr))
return fmt.Errorf("Unable to prepare JSON content: %s", jsonErr.Error())
}

return jsonContent, nil
}

// sendPayloadRequest is required for sending JSON payload related requests
// because by default the http package does not set Content-Length headers
func doObjectRequest(request *http.Request, object *jsh.Object) (*jsh.Document, *http.Response, *jsh.Error) {
request.Body = jsh.CreateReadCloser(jsonContent)
request.ContentLength = int64(len(jsonContent))

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

// prepare payload and corresponding headers
request.Body = jsh.CreateReadCloser(payload)
request.Header.Add("Content-Type", jsh.ContentType)
// Do sends a the specified request to a JSON API compatible endpoint and
// returns the resulting JSON Document if possible along with the response,
// and any errors that were encountered while sending, or parsing the
// JSON Document.
func Do(request *http.Request) (*jsh.Document, *http.Response, error) {

contentLength := strconv.Itoa(len(payload))
request.ContentLength = int64(len(payload))
request.Header.Set("Content-Length", contentLength)
request.Header.Set("Content-Type", jsh.ContentType)
request.Header.Set("Content-Length", strconv.Itoa(int(request.ContentLength)))

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

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

if request.Method == "DELETE" {
return nil, httpResponse, nil
}

document, err := Document(httpResponse)
Expand Down
28 changes: 19 additions & 9 deletions client/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,28 @@ import (
"github.com/derekdowling/go-json-spec-handler"
)

// Delete allows a user to make an outbound DELETE /resources/:id request:
// Delete allows a user to make an outbound "DELETE /resource/:id" request.
//
// resp, err := jsh.Delete("http://apiserver", "user", "2")
//
func Delete(urlStr string, resourceType string, id string) (*http.Response, *jsh.Error) {
func Delete(urlStr string, resourceType string, id string) (*http.Response, error) {
request, err := DeleteRequest(urlStr, resourceType, id)
if err != nil {
return nil, err
}

_, response, err := Do(request)
if err != nil {
return nil, err
}

return response, nil
}

// DeleteRequest returns a fully formatted request for performing a JSON API DELETE.
// This is useful for if you need to set custom headers on the request. Otherwise
// just use "jsc.Delete".
func DeleteRequest(urlStr string, resourceType string, id string) (*http.Request, error) {
u, err := url.Parse(urlStr)
if err != nil {
return nil, jsh.ISE(fmt.Sprintf("Error parsing URL: %s", err.Error()))
Expand All @@ -27,11 +43,5 @@ func Delete(urlStr string, resourceType string, id string) (*http.Response, *jsh
return nil, jsh.ISE(fmt.Sprintf("Error creating DELETE request: %s", err.Error()))
}

client := &http.Client{}
response, err := client.Do(request)
if err != nil {
return nil, jsh.ISE(fmt.Sprintf("Error sending DELETE request: %s", err.Error()))
}

return response, nil
return request, nil
}
20 changes: 18 additions & 2 deletions client/delete_test.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
package jsc

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

. "github.com/smartystreets/goconvey/convey"
)

func TestDelete(t *testing.T) {

Convey("Delete Tests", t, func() {
Convey("DELETE Tests", t, func() {

})
api := testAPI()
server := httptest.NewServer(api)
defer server.Close()

baseURL := server.URL

Convey("->Delete()", func() {
resp, err := Delete(baseURL, "test", "1")

So(err, ShouldBeNil)
log.Printf("patchErr = %+v\n", err)
log.Printf("resp = %+v\n", resp)
So(resp.StatusCode, ShouldEqual, http.StatusOK)
})
})
}
23 changes: 20 additions & 3 deletions client/patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,34 @@ import (
// updatedObj := json.First()
//
func Patch(baseURL string, object *jsh.Object) (*jsh.Document, *http.Response, error) {
request, err := PatchRequest(baseURL, object)
if err != nil {
return nil, nil, err
}

return Do(request)
}

// PatchRequest returns a fully formatted request with JSON body for performing
// a JSONAPI PATCH. This is useful for if you need to set custom headers on the
// request. Otherwise just use "jsc.Patch".
func PatchRequest(baseURL string, object *jsh.Object) (*http.Request, error) {
u, err := url.Parse(baseURL)
if err != nil {
return nil, nil, fmt.Errorf("Error parsing URL: %s", err.Error())
return nil, fmt.Errorf("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, nil, fmt.Errorf("Error creating PATCH request: %s", err.Error())
return nil, fmt.Errorf("Error creating PATCH request: %s", err.Error())
}

err = prepareBody(request, object)
if err != nil {
return nil, err
}

return doObjectRequest(request, object)
return request, nil
}
2 changes: 0 additions & 2 deletions client/patch_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package jsc

import (
"log"
"net/http"
"net/http/httptest"
"testing"
Expand All @@ -25,7 +24,6 @@ func TestPatch(t *testing.T) {
So(err, ShouldBeNil)

json, resp, patchErr := Patch(baseURL, object)
log.Printf("resp.Request = %+v\n", resp.Request)

So(resp.StatusCode, ShouldEqual, http.StatusOK)
So(patchErr, ShouldBeNil)
Expand Down
22 changes: 19 additions & 3 deletions client/post.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,35 @@ import (
// // does POST http://apiserver/user/123
// json, resp, err := jsh.Post("http://apiserver", obj)
func Post(baseURL string, object *jsh.Object) (*jsh.Document, *http.Response, error) {
request, err := PostRequest(baseURL, object)
if err != nil {
return nil, nil, err
}

return Do(request)
}

// PostRequest returns a fully formatted request with JSON body for performing
// a JSONAPI POST. This is useful for if you need to set custom headers on the
// request. Otherwise just use "jsc.Post".
func PostRequest(baseURL string, object *jsh.Object) (*http.Request, error) {
u, err := url.Parse(baseURL)
if err != nil {
return nil, nil, fmt.Errorf("Error parsing URL: %s", err.Error())
return nil, fmt.Errorf("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, nil, fmt.Errorf("Error building POST request: %s", err.Error())
return nil, fmt.Errorf("Error building POST request: %s", err.Error())
}

err = prepareBody(request, object)
if err != nil {
return nil, err
}

return doObjectRequest(request, object)
return request, nil
}

0 comments on commit a43bd0e

Please sign in to comment.