From f1f2e3a2d9ddd5ef73ca796a97f0c00d1ae81710 Mon Sep 17 00:00:00 2001 From: Olakunle Arewa Date: Thu, 20 Aug 2020 01:58:10 +0100 Subject: [PATCH] feat: added support for reading and validation queries into structs --- request_test.go | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ requests.go | 42 +++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 request_test.go diff --git a/request_test.go b/request_test.go new file mode 100644 index 0000000..f2c0a07 --- /dev/null +++ b/request_test.go @@ -0,0 +1,56 @@ +package anansi + +import ( + "net/http" + "strconv" + "testing" + + ozzo "github.com/go-ozzo/ozzo-validation/v4" + "syreclabs.com/go/faker" +) + +type userQuery struct { + Name string `json:"name"` + Age int `json:"age"` +} + +func (u *userQuery) Validate() error { + return ozzo.ValidateStruct(u, + ozzo.Field(&u.Name, ozzo.Required), + ozzo.Field(&u.Age, ozzo.Required), + ) +} + +func TestReadQuery(t *testing.T) { + fName := faker.Name().FirstName() + ageOne := faker.Number().Number(2) + ageTwo := faker.Number().Number(2) + + req, err := http.NewRequest("GET", "https://google.com", nil) + if err != nil { + t.Fatal(err) + } + q := req.URL.Query() + + q.Add("name", fName) + q.Add("age", ageOne) + q.Add("age", ageTwo) + + req.URL.RawQuery = q.Encode() + + uQ := new(userQuery) + ReadQuery(req, uQ) + + if uQ.Name != fName { + t.Errorf("Expected name %s. got name %s", fName, uQ.Name) + } + + uQAge, err := strconv.Atoi(ageOne) + if err != nil { + t.Fatal(err) + } + + if uQ.Age != uQAge { + t.Errorf("Expected name %s. got name %s", fName, uQ.Name) + } +} diff --git a/requests.go b/requests.go index 806a4f2..09b8e26 100644 --- a/requests.go +++ b/requests.go @@ -13,6 +13,7 @@ import ( "github.com/go-chi/chi" ozzo "github.com/go-ozzo/ozzo-validation/v4" + "github.com/mitchellh/mapstructure" ) // ReadBody extracts the bytes in a request body without destroying the contents of the body @@ -84,6 +85,47 @@ func ReadJSON(r *http.Request, v interface{}) { } } +func ReadQuery(r *http.Request, v interface{}) { + raw := r.URL.Query() + qMap := make(map[string]string) + + for k := range raw { + qMap[k] = raw.Get(k) + } + + // convert claims data map to struct + config := &mapstructure.DecoderConfig{ + Result: v, + TagName: "json", + WeaklyTypedInput: true, + } + decoder, err := mapstructure.NewDecoder(config) + + if err != nil { + panic(APIError{ + Code: http.StatusBadRequest, + Message: "We cannot parse your request body.", + Err: err, + }) + } + + if err := decoder.Decode(qMap); err != nil { + panic(APIError{ + Code: http.StatusBadRequest, + Message: "We cannot parse your request body.", + Err: err, + }) + } + + if err := ozzo.Validate(v); err != nil { + panic(APIError{ + Code: http.StatusBadRequest, + Message: "We could not validate your request.", + Meta: err, + }) + } +} + // IDParam extracts a uint URL parameter from the given request func IDParam(r *http.Request, name string) uint { param := chi.URLParam(r, name)