Skip to content

Commit

Permalink
content-type request validation
Browse files Browse the repository at this point in the history
  • Loading branch information
Derek Dowling committed Nov 17, 2015
1 parent bf0c2cb commit a471868
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 24 deletions.
9 changes: 6 additions & 3 deletions list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,19 @@ func TestList(t *testing.T) {

writer := httptest.NewRecorder()
err := Send(writer, req, testList)

So(err, ShouldBeNil)
So(writer.Code, ShouldEqual, http.StatusOK)

contentLength, convErr := strconv.Atoi(writer.HeaderMap.Get("Content-Length"))
So(convErr, ShouldBeNil)
So(contentLength, ShouldBeGreaterThan, 0)
So(writer.HeaderMap.Get("Content-Type"), ShouldEqual, ContentType)

closer := createIOCloser(writer.Body.Bytes())
responseList, err := ParseList(closer)
req, reqErr := testRequest(writer.Body.Bytes())
So(reqErr, ShouldBeNil)

responseList, err := ParseList(req)
So(err, ShouldBeNil)
So(len(responseList), ShouldEqual, 1)
})
})
Expand Down
53 changes: 41 additions & 12 deletions parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io"
"io/ioutil"
"log"
"net/http"
)

const (
Expand Down Expand Up @@ -47,19 +48,18 @@ const (
// http.Error(w, err.Status, err.Detail)
// }
// }
func ParseObject(reader io.ReadCloser) (*Object, SendableError) {
defer closeReader(reader)
func ParseObject(r *http.Request) (*Object, SendableError) {

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

data := struct {
Object Object `json:"data"`
}{}

err = json.Unmarshal(byteData, &data)
err := json.Unmarshal(byteData, &data)
if err != nil {
return nil, ISE(fmt.Sprintf("Unable to parse json: \n%s\nError:%s",
string(byteData),
Expand All @@ -73,19 +73,18 @@ func ParseObject(reader io.ReadCloser) (*Object, SendableError) {

// ParseList returns a JSON List for a given io.ReadCloser containing
// a raw JSON payload
func ParseList(reader io.ReadCloser) ([]*Object, SendableError) {
defer closeReader(reader)
func ParseList(r *http.Request) ([]*Object, SendableError) {

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

data := struct {
List []*Object `json:"data"`
}{List: []*Object{}}

err = json.Unmarshal(byteData, &data)
err := json.Unmarshal(byteData, &data)
if err != nil {
return nil, ISE(fmt.Sprintf("Unable to parse json: \n%s\nError:%s",
string(byteData),
Expand All @@ -103,9 +102,39 @@ func ParseList(reader io.ReadCloser) ([]*Object, SendableError) {
return data.List, nil
}

func loadJSON(r *http.Request) ([]byte, SendableError) {
defer closeReader(r.Body)

validationErr := validateRequest(r)
if validationErr != nil {
return nil, validationErr
}

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

return byteData, nil
}

func closeReader(reader io.ReadCloser) {
err := reader.Close()
if err != nil {
log.Println("Unabled to close request Body")
}
}

func validateRequest(r *http.Request) SendableError {

reqContentType := r.Header.Get("Content-Type")
if reqContentType != ContentType {
return SpecificationError(fmt.Sprintf(
"Expected Content-Type header to be %s, got: %s",
ContentType,
reqContentType,
))
}

return nil
}
45 changes: 36 additions & 9 deletions parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package jsh

import (
"encoding/json"
"net/http"
"testing"

. "github.com/smartystreets/goconvey/convey"
Expand All @@ -11,15 +12,38 @@ func TestParsing(t *testing.T) {

Convey("Request Tests", t, func() {

Convey("->validateRequest()", func() {
req, err := http.NewRequest("GET", "", nil)
So(err, ShouldBeNil)
req.Header.Set("Content-Type", "jpeg")

err = validateRequest(req)
So(err, ShouldNotBeNil)

singleErr := err.(*Error)
So(singleErr.Status, ShouldEqual, http.StatusNotAcceptable)
})

Convey("->loadJSON()", func() {
req, err := http.NewRequest("GET", "", createIOCloser([]byte("1234")))
So(err, ShouldBeNil)
req.Header.Set("Content-Type", ContentType)

bytes, err := loadJSON(req)
So(err, ShouldBeNil)
So(string(bytes), ShouldEqual, "1234")
})

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

Convey("should parse a valid object", func() {

objectJSON := `{"data": {"type": "user", "id": "sweetID123", "attributes": {"ID":"123"}}}`

closer := createIOCloser([]byte(objectJSON))
req, reqErr := testRequest([]byte(objectJSON))
So(reqErr, ShouldBeNil)

object, err := ParseObject(closer)
object, err := ParseObject(req)
So(err, ShouldBeNil)
So(object, ShouldNotBeEmpty)
So(object.Type, ShouldEqual, "user")
Expand All @@ -29,9 +53,11 @@ func TestParsing(t *testing.T) {

Convey("should reject an object with missing attributes", func() {
objectJSON := `{"data": {"id": "sweetID123", "attributes": {"ID":"123"}}}`
closer := createIOCloser([]byte(objectJSON))

_, err := ParseObject(closer)
req, reqErr := testRequest([]byte(objectJSON))
So(reqErr, ShouldBeNil)

_, err := ParseObject(req)
So(err, ShouldNotBeNil)

vErr, ok := err.(*Error)
Expand All @@ -50,10 +76,10 @@ func TestParsing(t *testing.T) {
{"type": "user", "id": "sweetID123", "attributes": {"ID":"123"}},
{"type": "user", "id": "sweetID456", "attributes": {"ID":"456"}}
]}`
req, reqErr := testRequest([]byte(listJSON))
So(reqErr, ShouldBeNil)

closer := createIOCloser([]byte(listJSON))

list, err := ParseList(closer)
list, err := ParseList(req)
So(err, ShouldBeNil)
So(len(list), ShouldEqual, 2)

Expand All @@ -70,9 +96,10 @@ func TestParsing(t *testing.T) {
{"type": "user", "attributes": {"ID":"456"}}
]}`

closer := createIOCloser([]byte(listJSON))
req, reqErr := testRequest([]byte(listJSON))
So(reqErr, ShouldBeNil)

_, err := ParseList(closer)
_, err := ParseList(req)
So(err, ShouldNotBeNil)

vErr, ok := err.(*Error)
Expand Down
11 changes: 11 additions & 0 deletions test_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,20 @@ import (
"bytes"
"io"
"io/ioutil"
"net/http"
)

func createIOCloser(data []byte) io.ReadCloser {
reader := bytes.NewReader(data)
return ioutil.NopCloser(reader)
}

func testRequest(bytes []byte) (*http.Request, error) {
req, err := http.NewRequest("GET", "", createIOCloser(bytes))
if err != nil {
return nil, err
}

req.Header.Set("Content-Type", ContentType)
return req, nil
}

0 comments on commit a471868

Please sign in to comment.