From 4c635ffbe8d8bc7353eea2103a1429fcc446b2ae Mon Sep 17 00:00:00 2001 From: Dan Jones Date: Fri, 19 Jan 2024 21:56:30 -0600 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20middleware?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 3 +- go.sum | 5 ++- middleware.go | 41 +++++++++++++++++++ middleware_test.go | 100 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 middleware.go create mode 100644 middleware_test.go diff --git a/go.mod b/go.mod index f7a75c4..1133a88 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,9 @@ module codeberg.org/danjones000/gin-error-handler go 1.21.5 require ( + codeberg.org/danjones000/responsable-errors v0.1.1 github.com/gin-gonic/gin v1.9.1 - github.com/stretchr/testify v1.8.3 + github.com/stretchr/testify v1.8.4 ) require ( diff --git a/go.sum b/go.sum index 1a77fa1..55e6acd 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +codeberg.org/danjones000/responsable-errors v0.1.1 h1:WTWo0egPNsp+kEKRK8R+yc/bfdEDUbwEjMnqgA7IklA= +codeberg.org/danjones000/responsable-errors v0.1.1/go.mod h1:susEj39A/bflyej4tRirtuVKkmfUdhR2Skljwd/1ndI= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= @@ -54,8 +56,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= diff --git a/middleware.go b/middleware.go new file mode 100644 index 0000000..27a8c1c --- /dev/null +++ b/middleware.go @@ -0,0 +1,41 @@ +package handler + +import ( + "errors" + + rErrors "codeberg.org/danjones000/responsable-errors" + "github.com/gin-gonic/gin" +) + +func ErrorMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + c.Next() + err := c.Errors.Last() + if err == nil { + return + } + + var re rErrors.ResponsableError + errors.As(err, &re) + for _, err = range c.Errors { + errors.As(err, &re) + if re != nil { + break + } + } + + // @todo we need to add some way to do custom handling + + // @todo Refactor this with 👆 + if re == nil { + switch err.Type { + case gin.ErrorTypePrivate: + re = rErrors.NewInternalError("%w", err) + default: + re = rErrors.NewBadRequest("%w", err) + } + } + + c.JSON(re.GetStatus(), gin.H{"error": re.GetMsg()}) + } +} diff --git a/middleware_test.go b/middleware_test.go new file mode 100644 index 0000000..31737cd --- /dev/null +++ b/middleware_test.go @@ -0,0 +1,100 @@ +package handler + +import ( + "errors" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + rErrors "codeberg.org/danjones000/responsable-errors" + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/suite" +) + +func TestMiddleware(t *testing.T) { + suite.Run(t, new(MiddlewareTestSuite)) +} + +type MiddlewareTestSuite struct { + suite.Suite + w *httptest.ResponseRecorder + ctx *gin.Context +} + +func (s *MiddlewareTestSuite) SetupTest() { + gin.SetMode(gin.TestMode) + s.w = httptest.NewRecorder() + s.ctx, _ = gin.CreateTestContext(s.w) + s.ctx.Request = new(http.Request) +} + +func (s *MiddlewareTestSuite) TearDownTest() { + s.w = nil + s.ctx = nil +} + +func (s *MiddlewareTestSuite) do(err ...error) { + for _, e := range err { + s.ctx.Error(e) + } + ErrorMiddleware()(s.ctx) +} + +func (s *MiddlewareTestSuite) doParse(err ...error) map[string]any { + s.do(err...) + var out map[string]any + jsonErr := json.Unmarshal(s.w.Body.Bytes(), &out) + s.Assert().Nil(jsonErr) + + return out +} + +func (s *MiddlewareTestSuite) TestNoError() { + s.do() + s.Assert().Equal("", s.w.Body.String()) +} + +func (s *MiddlewareTestSuite) TestResError() { + msg := "I can't find it" + err := rErrors.NewNotFound(msg) + out := s.doParse(err) + + outMsg, ok := out["error"].(string) + s.Assert().True(ok) + s.Assert().Equal(msg, outMsg) + s.Assert().Equal(http.StatusNotFound, s.w.Code) +} + +func (s *MiddlewareTestSuite) TestGinError() { + msg := "I don't like this" + err := &gin.Error{errors.New(msg), gin.ErrorTypePublic, nil} + out := s.doParse(err) + + outMsg, ok := out["error"].(string) + s.Assert().True(ok) + s.Assert().Equal(msg, outMsg) + s.Assert().Equal(http.StatusBadRequest, s.w.Code) +} + +func (s *MiddlewareTestSuite) TestGinErrorPrivate() { + msg := "Don't do this" + err := &gin.Error{errors.New(msg), gin.ErrorTypePrivate, nil} + out := s.doParse(err) + + outMsg, ok := out["error"].(string) + s.Assert().True(ok) + s.Assert().Equal("Unknown Error", outMsg) + s.Assert().Equal(http.StatusInternalServerError, s.w.Code) +} + +func (s *MiddlewareTestSuite) TestOtherError() { + msg := "Don't do this" + err := errors.New(msg) + out := s.doParse(err) + + outMsg, ok := out["error"].(string) + s.Assert().True(ok) + s.Assert().Equal("Unknown Error", outMsg) + s.Assert().Equal(http.StatusInternalServerError, s.w.Code) +}