diff --git a/Gopkg.lock b/Gopkg.lock index a175c73..f700e3c 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -1,11 +1,17 @@ # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. +[[projects]] + name = "github.com/davecgh/go-spew" + packages = ["spew"] + revision = "346938d642f2ec3594ed81d874461961cd0faa76" + version = "v1.1.0" + [[projects]] name = "github.com/labstack/echo" packages = ["."] - revision = "b338075a0fc6e1a0683dbf03d09b4957a289e26f" - version = "3.2.6" + revision = "6d227dfea4d2e52cb76856120b3c17f758139b4e" + version = "3.3.5" [[projects]] name = "github.com/labstack/gommon" @@ -13,8 +19,8 @@ "color", "log" ] - revision = "57409ada9da0f2afad6664c49502f8c50fbd8476" - version = "0.2.3" + revision = "6fe1405d73ec4bd4cd8a4ac8e2a2b2bf95d03954" + version = "0.2.4" [[projects]] name = "github.com/mattn/go-colorable" @@ -28,6 +34,24 @@ revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39" version = "v0.0.3" +[[projects]] + name = "github.com/pkg/errors" + packages = ["."] + revision = "645ef00459ed84a119197bfb8d8205042c6df63d" + version = "v0.8.0" + +[[projects]] + name = "github.com/pmezard/go-difflib" + packages = ["difflib"] + revision = "792786c7400a136282c1664665ae0a8db921c6c2" + version = "v1.0.0" + +[[projects]] + name = "github.com/stretchr/testify" + packages = ["assert"] + revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" + version = "v1.2.1" + [[projects]] branch = "master" name = "github.com/valyala/bytebufferpool" @@ -47,17 +71,17 @@ "acme", "acme/autocert" ] - revision = "1875d0a70c90e57f11972aefd42276df65e895b9" + revision = "2b6c08872f4b66da917bb4ce98df4f0307330f78" [[projects]] branch = "master" name = "golang.org/x/sys" packages = ["unix"] - revision = "3dbebcf8efb6a5011a60c2b4591c1022a759af8a" + revision = "79b0c6888797020a994db17c8510466c72fe75d9" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "b8fe5a95d3a604eef275ed7e141f7e898522c298352c51a379e7390f9e7804da" + inputs-digest = "9270bbe8382ef12ba264cf489dede9949f81b909d34657daef325f4d7ab4bd8b" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index c54f127..e5bb50a 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -16,10 +16,27 @@ # source = "github.com/myfork/project2" # # [[override]] -# name = "github.com/x/y" -# version = "2.4.0" +# name = "github.com/x/y" +# version = "2.4.0" +# +# [prune] +# non-go = false +# go-tests = true +# unused-packages = true [[constraint]] name = "github.com/labstack/echo" - version = "3.2.6" + version = "3.3.5" + +[[constraint]] + name = "github.com/pkg/errors" + version = "0.8.0" + +[[constraint]] + name = "github.com/stretchr/testify" + version = "1.2.1" + +[prune] + go-tests = true + unused-packages = true diff --git a/ctx_test.go b/ctx_test.go index 350f41c..3354ebf 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -28,7 +28,7 @@ func TestCustomCtx(t *testing.T) { { name: "200 with google json style", givenHandler: func(c echo.Context) error { - return c.(ctx.CustomCtx).GResp(http.StatusOK).Data("hello world").Do() + return c.(ctx.CustomCtx).GResp(http.StatusOK).Data("hello world").Out() }, wantJSON: `{"apiVersion": "1.0", "data": "hello world"}`, }, @@ -46,7 +46,7 @@ func TestCustomCtx(t *testing.T) { ExtendedHelp: "http://help-link", SendReport: "http://report.dajui.com/", }).Append(&ctx.GError{ - Code: 40000001, + Code: 40000002, Domain: "global", Reason: "required", Message: "Required parameter: part", @@ -54,9 +54,9 @@ func TestCustomCtx(t *testing.T) { Location: "part", }) - return c.(ctx.CustomCtx).GResp().Errors(*gerrs...).Do() + return c.(ctx.CustomCtx).GResp().Errors(*gerrs...).Out() }, - wantJSON: `{"apiVersion":"1.0","error":{"code":40000001,"message":"Resources is not exist","errors":[{"extendedHelp":"http://help-link", "sendReport":"http://report.dajui.com/", "domain":"Calendar", "reason":"ResourceNotFoundException", "message":"Resources is not exist", "location":"query", "locationType":"database query"},{"message":"Required parameter: part", "location":"part", "locationType":"parameter", "domain":"global", "reason":"required"}]}}`, + wantJSON: `{"apiVersion":"1.0","error":{"code":40000001,"message":"Resources is not exist","errors":[{"code": 40000001, "extendedHelp":"http://help-link", "sendReport":"http://report.dajui.com/", "domain":"Calendar", "reason":"ResourceNotFoundException", "message":"Resources is not exist", "location":"query", "locationType":"database query"},{"code": 40000002, "message":"Required parameter: part", "location":"part", "locationType":"parameter", "domain":"global", "reason":"required"}]}}`, }, { name: "400 with string errors", @@ -97,7 +97,7 @@ func TestCustomCtx(t *testing.T) { ExtendedHelp: "http://help-link", SendReport: "http://report.dajui.com/", }).Append(&ctx.GError{ - Code: 40000001, + Code: 40000002, Domain: "global", Reason: "required", Message: "Required parameter: part", @@ -107,9 +107,9 @@ func TestCustomCtx(t *testing.T) { gerrs.AppendDomain("handler") - return c.(ctx.CustomCtx).GResp().Errors(*gerrs...).Do() + return c.(ctx.CustomCtx).GResp().Errors(*gerrs...).Out() }, - wantJSON: `{"apiVersion":"1.0","error":{"code":40000001,"message":"Resources is not exist","errors":[{"extendedHelp":"http://help-link", "sendReport":"http://report.dajui.com/", "domain":"handler.Calendar", "reason":"ResourceNotFoundException", "message":"Resources is not exist", "location":"query", "locationType":"database query"},{"message":"Required parameter: part", "location":"part", "locationType":"parameter", "domain":"handler.global", "reason":"required"}]}}`, + wantJSON: `{"apiVersion":"1.0","error":{"code":40000001,"message":"Resources is not exist","errors":[{"code": 40000001, "extendedHelp":"http://help-link", "sendReport":"http://report.dajui.com/", "domain":"handler.Calendar", "reason":"ResourceNotFoundException", "message":"Resources is not exist", "location":"query", "locationType":"database query"},{"code": 40000002, "message":"Required parameter: part", "location":"part", "locationType":"parameter", "domain":"handler.global", "reason":"required"}]}}`, }, } diff --git a/example/ErrorHandler/main.go b/example/ErrorHandler/main.go new file mode 100644 index 0000000..18680c1 --- /dev/null +++ b/example/ErrorHandler/main.go @@ -0,0 +1,89 @@ +package main + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/cutedogspark/echo-custom-context" + "github.com/labstack/echo" + "github.com/pkg/errors" +) + +func HTTPErrorHandler(err error, c echo.Context) { + if he, ok := err.(*ctx.GErrCall); ok { + err = errors.WithStack(he) + b, _ := json.Marshal(he.ResponseParams) + c.JSONBlob(he.HttpStatus, b) + } else if he, ok := err.(*echo.HTTPError); ok { + // warp echo error struct + err = errors.WithStack(he) + gCtx := ctx.CustomCtx{} + gErrs := gCtx.GResp().Errors(&ctx.GError{ + Code: uint(he.Code), + Message: fmt.Sprintf("%+v", he.Message), + }) + b, _ := json.Marshal(gErrs.ResponseParams) + c.JSONBlob(he.Code, b) + } else { + // define unknown error message + err = errors.New("unknown error") + gCtx := ctx.CustomCtx{} + gErrs := gCtx.GResp().Errors(&ctx.GError{ + Code: http.StatusInternalServerError, + Message: err.Error(), + }) + b, _ := json.Marshal(gErrs.ResponseParams) + c.JSONBlob(he.Code, b) + } + c.Logger().Error(err) +} + +func main() { + + e := echo.New() + e.HideBanner = true + e.HTTPErrorHandler = HTTPErrorHandler + e.Use(func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + return next(ctx.CustomCtx{c}) + } + }) + + e.GET("/", func(c echo.Context) error { + // no use error handler function + return c.(ctx.CustomCtx).GResp(http.StatusOK).Data("Service").Out() + }) + + e.GET("/gerr", func(c echo.Context) error { + gerrs := ctx.NewGErrors().Append(&ctx.GError{ + Code: 40000001, + Domain: "Calendar", + Reason: "ResourceNotFoundException", + Message: "Resources is not exist", + LocationType: "database query", + Location: "query", + ExtendedHelp: "http://help-link", + SendReport: "http://report.dajui.com/", + }).Append(&ctx.GError{ + Code: 40000002, + Domain: "global", + Reason: "required", + Message: "Required parameter: part", + LocationType: "parameter", + Location: "part", + }) + return c.(ctx.CustomCtx).GResp().Errors(*gerrs...).Do() + }) + + e.GET("/echo-error", func(c echo.Context) error { + return echo.NewHTTPError(http.StatusBadRequest, "default echo error handler") + }) + + e.GET("/unknown-error", func(c echo.Context) error { + return errors.New("Goodbye") + }) + + // Start server + e.Logger.Fatal(e.Start(":1234")) +} diff --git a/example/GError/main.go b/example/GError/main.go index 45bb1c8..46f0a01 100644 --- a/example/GError/main.go +++ b/example/GError/main.go @@ -1,9 +1,10 @@ package main import ( + "net/http" + "github.com/cutedogspark/echo-custom-context" "github.com/labstack/echo" - "net/http" ) func main() { @@ -18,7 +19,7 @@ func main() { }) e.GET("/", func(c echo.Context) error { - return c.(ctx.CustomCtx).GResp(http.StatusOK).Data("Service").Do() + return c.(ctx.CustomCtx).GResp(http.StatusOK).Data("Service").Out() }) e.GET("/error", func(c echo.Context) error { @@ -29,7 +30,7 @@ func main() { Message: "parameter required : id", Location: "id", LocationType: "parameter", - }).Do() + }).Out() }) e.GET("/errors", func(c echo.Context) error { @@ -52,10 +53,10 @@ func main() { ctxErr.AppendDomain("handler") - return c.(ctx.CustomCtx).GResp().Errors(*ctxErr...).Do() + return c.(ctx.CustomCtx).GResp().Errors(*ctxErr...).Out() }) // Start server - e.Logger.Fatal(e.Start(":1323")) + e.Logger.Fatal(e.Start(":1234")) } diff --git a/gctx.go b/gctx.go index 43f7732..6130889 100644 --- a/gctx.go +++ b/gctx.go @@ -57,7 +57,7 @@ func (r *grespCall) Data(data ...interface{}) *gdataCall { // Response Json Format // - replace string when response raw data // - ex: replace := strings.NewReplacer("{PP_KEY}", encryptionKey) -func (r *gdataCall) Do(replace ...*strings.Replacer) (err error) { +func (r *gdataCall) Out(replace ...*strings.Replacer) (err error) { b, err := json.Marshal(r.responseParams) if err != nil { return err @@ -71,53 +71,65 @@ func (r *gdataCall) Do(replace ...*strings.Replacer) (err error) { } // Google JSON Style error call -type gerrorCall struct { +type GErrCall struct { c echo.Context - httpStatus int - responseParams GErrorResponse + HttpStatus int + ResponseParams GErrResponse } -type gerrorMessage struct { +type GErrMessage struct { Code uint `json:"code"` Message string `json:"message"` Errors []*GError `json:"errors,omitempty"` } -type GErrorResponse struct { - ApiVersion string `json:"apiVersion"` - Error gerrorMessage `json:"error"` +type GErrResponse struct { + ApiVersion string `json:"apiVersion"` + Error GErrMessage `json:"error"` } -func (r *grespCall) Errors(errs ...*GError) *gerrorCall { - rs := &gerrorCall{ +func (r *grespCall) Errors(errs ...*GError) *GErrCall { + rs := &GErrCall{ c: r.c, - responseParams: GErrorResponse{ + ResponseParams: GErrResponse{ ApiVersion: apiVersion, - Error: gerrorMessage{}, + Error: GErrMessage{}, }, } if len(errs) > 0 { if len(r.httpStatus) > 0 { - rs.httpStatus = r.httpStatus[0] + rs.HttpStatus = r.httpStatus[0] } else { s, _ := strconv.Atoi(fmt.Sprintf("%d", errs[0].Code)[:3]) - rs.httpStatus = s + rs.HttpStatus = s } - rs.responseParams.Error.Code = errs[0].Code - rs.responseParams.Error.Message = errs[0].Message - rs.responseParams.Error.Errors = errs - - r.c.Set("gerrs", GErrors(errs)) + rs.ResponseParams.Error.Code = errs[0].Code + rs.ResponseParams.Error.Message = errs[0].Message + rs.ResponseParams.Error.Errors = errs } return rs } -func (r *gerrorCall) Do() (err error) { - b, err := json.Marshal(r.responseParams) +// Custom HTTP Error Handler +func (r *GErrCall) Do() (err error) { + return r +} + +// Response Json Out +func (r *GErrCall) Out() (err error) { + b, err := json.Marshal(r.ResponseParams) if err != nil { return err } - return r.c.JSONBlob(r.httpStatus, b) + return r.c.JSONBlob(r.HttpStatus, b) +} + +func (r *GErrCall) Error() string { + b, err := json.Marshal(r.ResponseParams) + if err != nil { + return err.Error() + } + return string(b) } diff --git a/gerr.go b/gerr.go index 53b89cb..50fd0e0 100644 --- a/gerr.go +++ b/gerr.go @@ -1,9 +1,12 @@ package ctx +import "fmt" + type GErrors []*GError type GError struct { - Code uint `json:"-"` + Code uint `json:"code,omitempty"` + Err error `json:"-"` Domain string `json:"domain,omitempty"` Reason string `json:"reason,omitempty"` Message string `json:"message,omitempty"` @@ -13,6 +16,15 @@ type GError struct { SendReport string `json:"sendReport,omitempty"` } +func (g *GError) Error() string { + return fmt.Sprintf("%s", g.Reason) +} + +func (g *GError) AppendDomain(domain string) error { + g.Domain = domain + "." + g.Domain + return g +} + func (c *GErrors) Append(gErr *GError) *GErrors { *c = append(*c, gErr) return c