From 39cd6532b25327a916337fa59a8921a8930e61bf Mon Sep 17 00:00:00 2001 From: Cory Redmond Date: Wed, 19 Sep 2018 21:44:02 +0100 Subject: [PATCH] More methods and crud --- .idea/workspace.xml | 254 ++++++++++++++++++++++++---------------- cmd/perkboxtest/main.go | 26 ++++ config/config.go | 8 +- web/coupons.go | 101 ++++++++++------ web/webutil.go | 29 +++++ 5 files changed, 274 insertions(+), 144 deletions(-) create mode 100644 cmd/perkboxtest/main.go create mode 100644 web/webutil.go diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 777ad5f..dc33752 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,7 +2,11 @@ - + + + + + - + - - - - - + + - + - - + + - + - - + + - + - - + + + + + - + - - + + - + - + - - + + - + - + + + + + + + + + + @@ -135,40 +154,25 @@ - + - - + + - + - - - - - - - - - - - - - - - - - - - - + + - - + + + + + @@ -186,6 +190,11 @@ true @@ -199,7 +208,6 @@ @@ -283,7 +294,7 @@ - + @@ -296,8 +307,24 @@ + + + + + + + + + + + + + + + + @@ -315,21 +342,24 @@ + + + - + - + - + @@ -359,20 +389,6 @@ - - - - - - - - - - - - - - @@ -404,13 +420,6 @@ - - - - - - - @@ -458,52 +467,93 @@ - + + + + + + + + + + + + + + + + + + + + - - + + - + - - + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - - - - - - + + + + + + + diff --git a/cmd/perkboxtest/main.go b/cmd/perkboxtest/main.go new file mode 100644 index 0000000..e611125 --- /dev/null +++ b/cmd/perkboxtest/main.go @@ -0,0 +1,26 @@ +package main + +import ( + "github.com/SilverCory/PerkboxTest/config" + "github.com/SilverCory/PerkboxTest/data" + "github.com/SilverCory/PerkboxTest/web" +) + +func main() { + conf := new(config.Config) + e(conf.Load()) + + handler, err := data.NewHandler(&conf.SQL) + e(err) + + e(handler.Migrate()) + + w := web.NewWeb(conf.Web, handler) + e(w.Start()) +} + +func e(err error) { + if err != nil { + panic(err) + } +} diff --git a/config/config.go b/config/config.go index bb5f65f..a3795f5 100644 --- a/config/config.go +++ b/config/config.go @@ -4,9 +4,9 @@ import "os" // Config the main configuration. type Config struct { - Data `json:"-"` - MySQL SQL `json:"sql"` - Web Web `json:"web"` + Data `json:"-"` + SQL SQL `json:"sql"` + Web Web `json:"web"` } // SQL contains all the apropriate information for the SQL connection. @@ -22,7 +22,7 @@ type Web struct { // DefaultConfig the default configuration to save. var DefaultConfig = Config{ Data: Data{}, - MySQL: SQL{ + SQL: SQL{ URI: "username:password@tcp(127.0.0.1:3306)/perkbox?charset=utf8&parseTime=True&loc=Local", }, Web: Web{ diff --git a/web/coupons.go b/web/coupons.go index adfd392..96cfcbf 100644 --- a/web/coupons.go +++ b/web/coupons.go @@ -3,27 +3,35 @@ package web import ( "encoding/json" "errors" - "io/ioutil" "net/http" "strconv" "strings" + "github.com/jinzhu/gorm" + "github.com/SilverCory/PerkboxTest/data" ) -// CouponHandler is the parent handler and allows for the URL to be /api/coupon/:id -func (web *Web) CouponHandler(w http.ResponseWriter, r *http.Request) { - idStr := strings.TrimPrefix(r.URL.Path, "/api/coupon/") - id, err := strconv.Atoi(idStr) - if err != nil { - web.WriteError(w, 400, errors.New("expected coupon ID in url")) +func (web *Web) CouponList(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + web.WriteError(w, 405, errors.New("method not allowed, get only")) return } - if r.Method == http.MethodPatch { - web.CouponUpdate(w, r, uint(id)) + var coupons []data.Coupon + if err := web.data.Engine.Model(&data.Coupon{}).Find(coupons).Error; err != nil { + web.WriteError(w, 500, errors.New("error fetching coupons: "+err.Error())) + return + } + + bytes, err := json.Marshal(coupons) + if err != nil { + web.WriteError(w, 500, errors.New("unable to marshal coupons: "+err.Error())) + return } + w.Write(bytes) + w.WriteHeader(200) } func (web *Web) CouponCreate(w http.ResponseWriter, r *http.Request) { @@ -32,23 +40,8 @@ func (web *Web) CouponCreate(w http.ResponseWriter, r *http.Request) { return } - if r.Header.Get("Content-Type") != "application/json" { - web.WriteError(w, 400, errors.New("invalid content type")) - return - } - - defer r.Body.Close() - bytes, err := ioutil.ReadAll(r.Body) - if err != nil { - web.WriteError(w, 400, errors.New("data sent was invalid")) - return - } - var coupon data.Coupon - if err := json.Unmarshal(bytes, &coupon); err != nil { - web.WriteError(w, 400, errors.New("data sent was invalid: "+err.Error())) - return - } + web.getJson(w, r, &coupon) if err := coupon.Create(web.data); err != nil { web.WriteError(w, 500, err) @@ -57,34 +50,66 @@ func (web *Web) CouponCreate(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) } -func (web *Web) CouponUpdate(w http.ResponseWriter, r *http.Request, id uint) { - - if r.Header.Get("Content-Type") != "application/json" { - web.WriteError(w, 400, errors.New("invalid content type")) +// CouponHandler is the parent handler and allows for the URL to be /api/coupon/:id +func (web *Web) CouponHandler(w http.ResponseWriter, r *http.Request) { + idStr := strings.TrimPrefix(r.URL.Path, "/api/coupon/") + id, err := strconv.Atoi(idStr) + if err != nil { + web.WriteError(w, 400, errors.New("expected coupon ID in url")) return } - defer r.Body.Close() - bytes, err := ioutil.ReadAll(r.Body) - if err != nil { - web.WriteError(w, 400, errors.New("data sent was invalid")) - return + switch r.Method { + case http.MethodPatch: + web.couponUpdate(w, r, uint(id)) + break + + case http.MethodGet: + web.couponGet(w, r, uint(id)) + break + + default: + web.WriteError(w, 405, errors.New("bad method")) } +} + +// couponUpdate allows for updates to be made the coupon via a patch request. +func (web *Web) couponUpdate(w http.ResponseWriter, r *http.Request, id uint) { var updates map[string]interface{} - if err := json.Unmarshal(bytes, updates); err != nil { - web.WriteError(w, 400, errors.New("data sent was invalid: "+err.Error())) + web.getJson(w, r, &updates) + + coupon := new(data.Coupon) + coupon.ID = uint(id) + + err := web.data.Engine.Model(coupon).Update(updates).Error + if err != nil { + web.WriteError(w, 500, errors.New("unable to update: "+err.Error())) return } + w.WriteHeader(200) +} + +func (web *Web) couponGet(w http.ResponseWriter, r *http.Request, id uint) { coupon := new(data.Coupon) coupon.ID = uint(id) - err = web.data.Engine.Model(coupon).Update(updates).Error - if err != nil { + err := web.data.Engine.First(coupon).Error + if err == gorm.ErrRecordNotFound { + web.WriteError(w, 404, errors.New("coupon not found")) + return + } else if err != nil { web.WriteError(w, 500, errors.New("unable to update: "+err.Error())) return } + bytes, err := json.Marshal(coupon) + if err != nil { + web.WriteError(w, 500, errors.New("unable to marshal coupon: "+err.Error())) + return + } + + w.Write(bytes) w.WriteHeader(200) } diff --git a/web/webutil.go b/web/webutil.go new file mode 100644 index 0000000..657e383 --- /dev/null +++ b/web/webutil.go @@ -0,0 +1,29 @@ +package web + +import ( + "encoding/json" + "errors" + "io/ioutil" + "net/http" +) + +func (web *Web) getJson(w http.ResponseWriter, r *http.Request, i interface{}) error { + if r.Header.Get("Content-Type") != "application/json" { + web.WriteError(w, 400, errors.New("invalid content type")) + return errors.New("invalid content type") + } + + defer r.Body.Close() + bytes, err := ioutil.ReadAll(r.Body) + if err != nil { + web.WriteError(w, 400, errors.New("data sent was invalid")) + return err + } + + if err := json.Unmarshal(bytes, i); err != nil { + web.WriteError(w, 400, errors.New("data sent was invalid: "+err.Error())) + return err + } + + return nil +}