Mercurial > gemma
changeset 237:3771788d3dae
Reduce boilerplate code when writing JSON parsing/generating endpoints.
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Thu, 26 Jul 2018 17:07:03 +0200 |
parents | 664fe6536141 |
children | 2b39bf2bf1fd |
files | auth/middleware.go controllers/json.go controllers/routes.go controllers/user.go |
diffstat | 4 files changed, 182 insertions(+), 122 deletions(-) [+] |
line wrap: on
line diff
--- a/auth/middleware.go Thu Jul 26 15:43:19 2018 +0200 +++ b/auth/middleware.go Thu Jul 26 17:07:03 2018 +0200 @@ -74,8 +74,8 @@ } } -func EnsureRole(roles ...string) func(func(http.ResponseWriter, *http.Request)) http.Handler { - return func(fn func(http.ResponseWriter, *http.Request)) http.Handler { - return SessionMiddleware(SessionChecker(http.HandlerFunc(fn), HasRole(roles...))) +func EnsureRole(roles ...string) func(http.Handler) http.Handler { + return func(handler http.Handler) http.Handler { + return SessionMiddleware(SessionChecker(handler, HasRole(roles...))) } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/controllers/json.go Thu Jul 26 17:07:03 2018 +0200 @@ -0,0 +1,94 @@ +package controllers + +import ( + "database/sql" + "encoding/json" + "fmt" + "log" + "net/http" + + "gemma.intevation.de/gemma/auth" + "github.com/jackc/pgx" +) + +type JSONResult struct { + Code int + Result interface{} +} + +type JSONHandler struct { + Input func() interface{} + Process func(http.ResponseWriter, *http.Request, interface{}, *sql.DB) (JSONResult, error) +} + +type JSONError struct { + Code int + Message string +} + +func (je JSONError) Error() string { + return fmt.Sprintf("%d: %s", je.Code, je.Message) +} + +func (j *JSONHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + + var input interface{} + if j.Input != nil { + input = j.Input() + defer req.Body.Close() + if err := json.NewDecoder(req.Body).Decode(input); err != nil { + http.Error(rw, "error: "+err.Error(), http.StatusBadRequest) + return + } + } + + token, _ := auth.GetToken(req) + var jr JSONResult + err := auth.ConnPool.Do(token, func(db *sql.DB) (err error) { + jr, err = j.Process(rw, req, input, db) + return err + }) + + if err != nil { + switch e := err.(type) { + case pgx.PgError: + var res = struct { + Result string `json:"result"` + Code string `json:"code,omitempty"` + Message string `json:"message,omitempty"` + }{ + Result: "failure", + Code: e.Code, + Message: e.Message, + } + rw.Header().Set("Content-Type", "application/json") + rw.WriteHeader(http.StatusInternalServerError) + if err := json.NewEncoder(rw).Encode(&res); err != nil { + log.Printf("error: %v\n", err) + } + case JSONError: + rw.Header().Set("Content-Type", "application/json") + rw.WriteHeader(http.StatusInternalServerError) + var res = struct { + Message string `json:"message"` + }{ + Message: e.Message, + } + if err := json.NewEncoder(rw).Encode(&res); err != nil { + log.Printf("error: %v\n", err) + } + default: + log.Printf("err: %v\n", err) + http.Error(rw, + "error: "+err.Error(), + http.StatusInternalServerError) + } + return + } + + rw.Header().Set("Content-Type", "application/json") + rw.WriteHeader(jr.Code) + if err := json.NewEncoder(rw).Encode(jr.Result); err != nil { + log.Printf("error: %v\n", err) + } +}
--- a/controllers/routes.go Thu Jul 26 15:43:19 2018 +0200 +++ b/controllers/routes.go Thu Jul 26 17:07:03 2018 +0200 @@ -14,8 +14,15 @@ sysAdmin := auth.EnsureRole("sys_admin") - api.Handle("/users/{user}", sysAdmin(updateUser)).Methods(http.MethodPut) - api.Handle("/users", sysAdmin(createUser)).Methods(http.MethodPost) + api.Handle("/users", sysAdmin(&JSONHandler{ + Input: func() interface{} { return new(User) }, + Process: createUser, + })).Methods(http.MethodPost) + + api.Handle("/users/{user}", sysAdmin(&JSONHandler{ + Input: func() interface{} { return new(User) }, + Process: updateUser, + })).Methods(http.MethodPut) api.HandleFunc("/login", login). Methods(http.MethodGet, http.MethodPost)
--- a/controllers/user.go Thu Jul 26 15:43:19 2018 +0200 +++ b/controllers/user.go Thu Jul 26 17:07:03 2018 +0200 @@ -4,14 +4,11 @@ "database/sql" "encoding/json" "errors" - "log" "net/http" "regexp" "strings" - "gemma.intevation.de/gemma/auth" "github.com/gorilla/mux" - "github.com/jackc/pgx" ) type ( @@ -120,138 +117,100 @@ return errNoValidRole } -func updateUser(rw http.ResponseWriter, req *http.Request) { +func updateUser( + rw http.ResponseWriter, req *http.Request, + input interface{}, + db *sql.DB, +) (jr JSONResult, err error) { user := mux.Vars(req)["user"] if user == "" { - http.Error(rw, "error: user empty", http.StatusBadRequest) - return - } - - var newUser User - - defer req.Body.Close() - if err := json.NewDecoder(req.Body).Decode(&newUser); err != nil { - http.Error(rw, "error: "+err.Error(), http.StatusBadRequest) + err = JSONError{http.StatusBadRequest, "error: user empty"} return } - token, _ := auth.GetToken(req) - err := auth.ConnPool.Do(token, func(db *sql.DB) (err error) { - if newUser.Extent == nil { - _, err = db.Exec( - updateUserSQL, - user, - string(newUser.Role), - newUser.User, - newUser.Password, - string(newUser.Country), - string(newUser.Email), - ) - } else { - _, err = db.Exec( - updateUserExtentSQL, - user, - string(newUser.Role), - newUser.User, - newUser.Password, - string(newUser.Country), - newUser.Extent.X1, newUser.Extent.Y1, - newUser.Extent.X2, newUser.Extent.Y2, - string(newUser.Email), - ) - } - return - }) + newUser := input.(*User) - var res struct { - Result string `json:"result"` - Code string `json:"code,omitempty"` - Message string `json:"message,omitempty"` + if newUser.Extent == nil { + _, err = db.Exec( + updateUserSQL, + user, + string(newUser.Role), + newUser.User, + newUser.Password, + string(newUser.Country), + string(newUser.Email), + ) + } else { + _, err = db.Exec( + updateUserExtentSQL, + user, + string(newUser.Role), + newUser.User, + newUser.Password, + string(newUser.Country), + newUser.Extent.X1, newUser.Extent.Y1, + newUser.Extent.X2, newUser.Extent.Y2, + string(newUser.Email), + ) } if err != nil { - if pgErr, ok := err.(pgx.PgError); ok { - res.Result = "failure" - res.Code = pgErr.Code - res.Message = pgErr.Message - } else { - log.Printf("err: %v\n", err) - http.Error(rw, - "error: "+err.Error(), - http.StatusInternalServerError) - return - } - } else { - res.Result = "success" - } - - rw.Header().Set("Content-Type", "application/json") - if err := json.NewEncoder(rw).Encode(&res); err != nil { - log.Printf("error: %v\n", err) - } -} - -func createUser(rw http.ResponseWriter, req *http.Request) { - - var user User - - defer req.Body.Close() - if err := json.NewDecoder(req.Body).Decode(&user); err != nil { - http.Error(rw, "error: "+err.Error(), http.StatusBadRequest) return } - token, _ := auth.GetToken(req) - err := auth.ConnPool.Do(token, func(db *sql.DB) (err error) { - if user.Extent == nil { - _, err = db.Exec( - createUserSQL, - string(user.Role), - user.User, - user.Password, - string(user.Country), - string(user.Email), - ) - } else { - _, err = db.Exec( - createUserExtentSQL, - string(user.Role), - user.User, - user.Password, - string(user.Country), - user.Extent.X1, user.Extent.Y1, - user.Extent.X2, user.Extent.Y2, - string(user.Email), - ) - } - return - }) + jr = JSONResult{ + Code: http.StatusCreated, + Result: struct { + Result string `json:"result"` + }{ + Result: "success", + }, + } + return +} + +func createUser( + rw http.ResponseWriter, req *http.Request, + input interface{}, + db *sql.DB, +) (jr JSONResult, err error) { + + user := input.(*User) - var res struct { - Result string `json:"result"` - Code string `json:"code,omitempty"` - Message string `json:"message,omitempty"` + if user.Extent == nil { + _, err = db.Exec( + createUserSQL, + string(user.Role), + user.User, + user.Password, + string(user.Country), + string(user.Email), + ) + } else { + _, err = db.Exec( + createUserExtentSQL, + string(user.Role), + user.User, + user.Password, + string(user.Country), + user.Extent.X1, user.Extent.Y1, + user.Extent.X2, user.Extent.Y2, + string(user.Email), + ) } if err != nil { - if pgErr, ok := err.(pgx.PgError); ok { - res.Result = "failure" - res.Code = pgErr.Code - res.Message = pgErr.Message - } else { - log.Printf("err: %v\n", err) - http.Error(rw, - "error: "+err.Error(), - http.StatusInternalServerError) - return - } - } else { - res.Result = "success" + return } - rw.Header().Set("Content-Type", "application/json") - if err := json.NewEncoder(rw).Encode(&res); err != nil { - log.Printf("error: %v\n", err) + jr = JSONResult{ + Code: http.StatusCreated, + Result: struct { + Result string `json:"result"` + }{ + Result: "success", + }, } + return }