Skip to content

Commit

Permalink
pangpanglabs#29 Refactory error dispatch way between packages
Browse files Browse the repository at this point in the history
  • Loading branch information
elvinchan committed Mar 13, 2020
1 parent 08fd5f2 commit a548176
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 75 deletions.
41 changes: 21 additions & 20 deletions controllers/discount_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ type DiscountApiController struct {
func (c DiscountApiController) Init(g echoswagger.ApiGroup) {
g.GET("", c.GetAll).AddParamQueryNested(SearchInput{})
g.POST("", c.Create).AddParamBody(DiscountInput{}, "body", "", true)
g.GET("/:id", c.GetOne).AddParamPath("", "id", "")
g.PUT("/:id", c.Update).AddParamBody(DiscountInput{}, "body", "", true)
g.GET("/:id", c.GetOne).AddParamPath(0, "id", "")
g.PUT("/:id", c.Update).AddParamPath(0, "id", "").AddParamBody(DiscountInput{}, "body", "", true)
}

func (DiscountApiController) GetAll(c echo.Context) error {
var v SearchInput
if err := c.Bind(&v); err != nil {
return ReturnApiFail(c, http.StatusBadRequest, ApiErrorParameter, err)
return renderFail(c, factory.ErrorParameter.New(err))
}
if v.MaxResultCount == 0 {
v.MaxResultCount = DefaultMaxResultCount
Expand All @@ -45,7 +46,7 @@ func (DiscountApiController) GetAll(c echo.Context) error {

totalCount, items, err := models.Discount{}.GetAll(c.Request().Context(), v.Sortby, v.Order, v.SkipCount, v.MaxResultCount)
if err != nil {
return ReturnApiFail(c, http.StatusInternalServerError, ApiErrorDB, err)
return renderFail(c, err)
}

// behavior log
Expand All @@ -57,7 +58,7 @@ func (DiscountApiController) GetAll(c echo.Context) error {
}).
Log("SearchComplete")

return ReturnApiSucc(c, http.StatusOK, ArrayResult{
return renderSucc(c, http.StatusOK, ArrayResult{
TotalCount: totalCount,
Items: items,
})
Expand All @@ -66,56 +67,56 @@ func (DiscountApiController) GetAll(c echo.Context) error {
func (DiscountApiController) Create(c echo.Context) error {
var v DiscountInput
if err := c.Bind(&v); err != nil {
return ReturnApiFail(c, http.StatusBadRequest, ApiErrorParameter, err)
return renderFail(c, factory.ErrorParameter.New(err))
}
if err := c.Validate(&v); err != nil {
return ReturnApiFail(c, http.StatusBadRequest, ApiErrorParameter, err)
return renderFail(c, factory.ErrorParameter.New(err))
}
discount, err := v.ToModel()
if err != nil {
return ReturnApiFail(c, http.StatusBadRequest, ApiErrorParameter, err)
return renderFail(c, factory.ErrorParameter.New(err))
}
if _, err := discount.Create(c.Request().Context()); err != nil {
return ReturnApiFail(c, http.StatusInternalServerError, ApiErrorDB, err)
return renderFail(c, err)
}
return ReturnApiSucc(c, http.StatusOK, discount)
return renderSucc(c, http.StatusOK, discount)
}

func (DiscountApiController) GetOne(c echo.Context) error {
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
return ReturnApiFail(c, http.StatusBadRequest, ApiErrorParameter, err)
return renderFail(c, factory.ErrorParameter.New(err))
}
v, err := models.Discount{}.GetById(c.Request().Context(), id)
if err != nil {
return ReturnApiFail(c, http.StatusInternalServerError, ApiErrorDB, err)
return renderFail(c, err)
}
if v == nil {
return ReturnApiFail(c, http.StatusNotFound, ApiErrorNotFound, nil)
return renderFail(c, factory.ErrorNotFound.New(nil))
}
return ReturnApiSucc(c, http.StatusOK, v)
return renderSucc(c, http.StatusOK, v)
}

func (DiscountApiController) Update(c echo.Context) error {
var v DiscountInput
if err := c.Bind(&v); err != nil {
return ReturnApiFail(c, http.StatusBadRequest, ApiErrorParameter, err)
return renderFail(c, factory.ErrorParameter.New(err))
}
if err := c.Validate(&v); err != nil {
return ReturnApiFail(c, http.StatusBadRequest, ApiErrorParameter, err)
return renderFail(c, factory.ErrorParameter.New(err))
}
discount, err := v.ToModel()
if err != nil {
return ReturnApiFail(c, http.StatusBadRequest, ApiErrorParameter, err)
return renderFail(c, factory.ErrorParameter.New(err))
}

id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
return ReturnApiFail(c, http.StatusBadRequest, ApiErrorParameter, err)
return renderFail(c, factory.ErrorParameter.New(err))
}
discount.Id = id
if err := discount.Update(c.Request().Context()); err != nil {
return ReturnApiFail(c, http.StatusInternalServerError, ApiErrorDB, err)
return renderFail(c, err)
}
return ReturnApiSucc(c, http.StatusOK, discount)
return renderSucc(c, http.StatusOK, v)
}
64 changes: 18 additions & 46 deletions controllers/utils.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package controllers

import (
"fmt"
"errors"
"net/http"
"net/url"
"strings"
Expand All @@ -18,66 +18,38 @@ const (
)

type ApiResult struct {
Result interface{} `json:"result"`
Success bool `json:"success"`
Error ApiError `json:"error"`
}

type ApiError struct {
Code int `json:"code,omitempty"`
Message string `json:"message,omitempty"`
Details interface{} `json:"details,omitempty"`
Result interface{} `json:"result"`
Success bool `json:"success"`
Error factory.Error `json:"error"`
}

type ArrayResult struct {
Items interface{} `json:"items"`
TotalCount int64 `json:"totalCount"`
}

var (
// System Error
ApiErrorSystem = ApiError{Code: 10001, Message: "System Error"}
ApiErrorServiceUnavailable = ApiError{Code: 10002, Message: "Service unavailable"}
ApiErrorRemoteService = ApiError{Code: 10003, Message: "Remote service error"}
ApiErrorIPLimit = ApiError{Code: 10004, Message: "IP limit"}
ApiErrorPermissionDenied = ApiError{Code: 10005, Message: "Permission denied"}
ApiErrorIllegalRequest = ApiError{Code: 10006, Message: "Illegal request"}
ApiErrorHTTPMethod = ApiError{Code: 10007, Message: "HTTP method is not suported for this request"}
ApiErrorParameter = ApiError{Code: 10008, Message: "Parameter error"}
ApiErrorMissParameter = ApiError{Code: 10009, Message: "Miss required parameter"}
ApiErrorDB = ApiError{Code: 10010, Message: "DB error, please contact the administator"}
ApiErrorTokenInvaild = ApiError{Code: 10011, Message: "Token invaild"}
ApiErrorMissToken = ApiError{Code: 10012, Message: "Miss token"}
ApiErrorVersion = ApiError{Code: 10013, Message: "API version %s invalid"}
ApiErrorNotFound = ApiError{Code: 10014, Message: "Resource not found"}
// Business Error
ApiErrorUserNotExists = ApiError{Code: 20001, Message: "User does not exists"}
ApiErrorPassword = ApiError{Code: 20002, Message: "Password error"}
)

func ReturnApiFail(c echo.Context, status int, apiError ApiError, err error, v ...interface{}) error {
str := ""
if err != nil {
str = err.Error()
behaviorlog.FromCtx(c.Request().Context()).WithError(err)
func renderFail(c echo.Context, err error) error {
if err == nil {
err = factory.ErrorSystem.New(nil)
}
return c.JSON(status, ApiResult{
Success: false,
Error: ApiError{
Code: apiError.Code,
Message: fmt.Sprintf(apiError.Message, v...),
Details: str,
},
})
behaviorlog.FromCtx(c.Request().Context()).WithError(err)
var apiError *factory.Error
if ok := errors.As(err, &apiError); ok {
return c.JSON(apiError.Status(), ApiResult{
Success: false,
Error: *apiError,
})
}
return err
}

func ReturnApiSucc(c echo.Context, status int, result interface{}) error {
func renderSucc(c echo.Context, status int, result interface{}) error {
req := c.Request()
if req.Method == "POST" || req.Method == "PUT" || req.Method == "DELETE" {
if session, ok := factory.DB(req.Context()).(*xorm.Session); ok {
err := session.Commit()
if err != nil {
return ReturnApiFail(c, http.StatusInternalServerError, ApiErrorDB, err)
return renderFail(c, factory.ErrorDB.New(err))
}
}
}
Expand Down
71 changes: 71 additions & 0 deletions factory/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package factory

import (
"fmt"
"net/http"
)

const WrapErrorMessage = "echosample error"

type Error struct {
Code int `json:"code,omitempty"`
Message string `json:"message,omitempty"`
Details string `json:"details,omitempty"`
err error
status int
}

func (t ErrorTemplate) New(err error, v ...interface{}) *Error {
e := Error{
Code: t.Code,
Message: fmt.Sprintf(t.Message, v...),
err: err,
}
if err != nil {
e.Details = fmt.Sprintf("%s: %s", WrapErrorMessage, err.Error())
}
return &e
}

func (e *Error) Error() string {
if e == nil {
return ""
}
return e.Details
}

func (e *Error) Unwrap() error {
if e == nil {
return nil
}
return e.err
}

func (e *Error) Status() int {
if e == nil || e.status == 0 {
return http.StatusInternalServerError
}
return e.status
}

type ErrorTemplate Error

var (
// System Error
ErrorSystem = ErrorTemplate{Code: 10001, Message: "System Error"}
ErrorServiceUnavailable = ErrorTemplate{Code: 10002, Message: "Service unavailable"}
ErrorRemoteService = ErrorTemplate{Code: 10003, Message: "Remote service error"}
ErrorIPLimit = ErrorTemplate{Code: 10004, Message: "IP limit"}
ErrorPermissionDenied = ErrorTemplate{Code: 10005, Message: "Permission denied", status: http.StatusForbidden}
ErrorIllegalRequest = ErrorTemplate{Code: 10006, Message: "Illegal request", status: http.StatusBadRequest}
ErrorHTTPMethod = ErrorTemplate{Code: 10007, Message: "HTTP method is not suported for this request", status: http.StatusMethodNotAllowed}
ErrorParameter = ErrorTemplate{Code: 10008, Message: "Parameter error", status: http.StatusBadRequest}
ErrorMissParameter = ErrorTemplate{Code: 10009, Message: "Miss required parameter", status: http.StatusBadRequest}
ErrorDB = ErrorTemplate{Code: 10010, Message: "DB error, please contact the administator"}
ErrorTokenInvaild = ErrorTemplate{Code: 10011, Message: "Token invaild", status: http.StatusUnauthorized}
ErrorMissToken = ErrorTemplate{Code: 10012, Message: "Miss token", status: http.StatusUnauthorized}
ErrorVersion = ErrorTemplate{Code: 10013, Message: "API version %s invalid"}
ErrorNotFound = ErrorTemplate{Code: 10014, Message: "Resource not found", status: http.StatusNotFound}
// Business Error
ErrorDiscountNotExists = ErrorTemplate{Code: 20001, Message: "Discount %d does not exists"}
)
41 changes: 32 additions & 9 deletions models/discount.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,23 @@ type Discount struct {
}

func (d *Discount) Create(ctx context.Context) (int64, error) {
return factory.DB(ctx).Insert(d)
affected, err := factory.DB(ctx).Insert(d)
if err != nil {
return 0, factory.ErrorDB.New(err)
}
return affected, nil
}

func (Discount) GetById(ctx context.Context, id int64) (*Discount, error) {
var v Discount
if has, err := factory.DB(ctx).ID(id).Get(&v); err != nil {
return nil, err
return nil, factory.ErrorDB.New(err)
} else if !has {
return nil, nil
}
return &v, nil
}

func (Discount) GetAll(ctx context.Context, sortby, order []string, offset, limit int) (int64, []Discount, error) {
q := factory.DB(ctx)
if err := setSortOrder(q, sortby, order); err != nil {
Expand All @@ -41,16 +47,33 @@ func (Discount) GetAll(ctx context.Context, sortby, order []string, offset, limi
var items []Discount
totalCount, err := q.Limit(limit, offset).FindAndCount(&items)
if err != nil {
return 0, nil, err
return 0, nil, factory.ErrorDB.New(err)
}
return totalCount, items, nil
}
func (d *Discount) Update(ctx context.Context) (err error) {
_, err = factory.DB(ctx).ID(d.Id).Update(d)
return

func (d *Discount) Update(ctx context.Context) error {
if origin, err := d.GetById(ctx, d.Id); err != nil {
return factory.ErrorDB.New(err)
} else if origin == nil {
return factory.ErrorDiscountNotExists.New(err, d.Id)
}

if _, err := factory.DB(ctx).ID(d.Id).Update(d); err != nil {
return factory.ErrorDB.New(err)
}
return nil
}

func (Discount) Delete(ctx context.Context, id int64) (err error) {
_, err = factory.DB(ctx).ID(id).Delete(&Discount{})
return
func (Discount) Delete(ctx context.Context, id int64) error {
if origin, err := (Discount{}).GetById(ctx, id); err != nil {
return factory.ErrorDB.New(err)
} else if origin == nil {
return factory.ErrorDiscountNotExists.New(err, id)
}

if _, err := factory.DB(ctx).ID(id).Delete(&Discount{}); err != nil {
return factory.ErrorDB.New(err)
}
return nil
}

0 comments on commit a548176

Please sign in to comment.