Sfoglia il codice sorgente

Fixing new errors API

Manu Mtz-Almeida 10 anni fa
parent
commit
37b6f6c179
4 ha cambiato i file con 150 aggiunte e 61 eliminazioni
  1. 15 17
      context.go
  2. 21 27
      context_test.go
  3. 24 17
      errors.go
  4. 90 0
      errors_test.go

+ 15 - 17
context.go

@@ -126,7 +126,7 @@ func (c *Context) AbortWithStatus(code int) {
 	c.Abort()
 }
 
-func (c *Context) AbortWithError(code int, err error) *errorMsg {
+func (c *Context) AbortWithError(code int, err error) *Error {
 	c.AbortWithStatus(code)
 	return c.Error(err)
 }
@@ -142,21 +142,19 @@ func (c *Context) IsAborted() bool {
 // Attaches an error to the current context. The error is pushed to a list of errors.
 // It's a good idea to call Error for each error that occurred during the resolution of a request.
 // A middleware can be used to collect all the errors and push them to a database together, print a log, or append it in the HTTP response.
-func (c *Context) Error(err error) *errorMsg {
-	newError := &errorMsg{
-		Err:   err,
-		Flags: ErrorTypePrivate,
-	}
-	c.Errors = append(c.Errors, newError)
-	return newError
-}
-
-func (c *Context) LastError() error {
-	nuErrors := len(c.Errors)
-	if nuErrors > 0 {
-		return c.Errors[nuErrors-1].Err
+func (c *Context) Error(err error) *Error {
+	var parsedError *Error
+	switch err.(type) {
+	case *Error:
+		parsedError = err.(*Error)
+	default:
+		parsedError = &Error{
+			Err:  err,
+			Type: ErrorTypePrivate,
+		}
 	}
-	return nil
+	c.Errors = append(c.Errors, parsedError)
+	return parsedError
 }
 
 /************************************/
@@ -270,7 +268,7 @@ func (c *Context) BindJSON(obj interface{}) error {
 
 func (c *Context) BindWith(obj interface{}, b binding.Binding) error {
 	if err := b.Bind(c.Request, obj); err != nil {
-		c.AbortWithError(400, err).Type(ErrorTypeBind)
+		c.AbortWithError(400, err).SetType(ErrorTypeBind)
 		return err
 	}
 	return nil
@@ -309,7 +307,7 @@ func (c *Context) Render(code int, r render.Render) {
 	c.Writer.WriteHeader(code)
 	if err := r.Write(c.Writer); err != nil {
 		debugPrintError(err)
-		c.AbortWithError(500, err).Type(ErrorTypeRender)
+		c.AbortWithError(500, err).SetType(ErrorTypeRender)
 	}
 }
 

+ 21 - 27
context_test.go

@@ -349,56 +349,50 @@ func TestContextAbortWithStatus(t *testing.T) {
 
 func TestContextError(t *testing.T) {
 	c, _, _ := createTestContext()
-	assert.Nil(t, c.LastError())
-	assert.Empty(t, c.Errors.String())
+	assert.Empty(t, c.Errors)
 
-	c.Error(errors.New("first error")).Meta("some data")
-	assert.Equal(t, c.LastError().Error(), "first error")
+	c.Error(errors.New("first error"))
 	assert.Len(t, c.Errors, 1)
-	assert.Equal(t, c.Errors.String(), "Error #01: first error\n     Meta: some data\n")
+	assert.Equal(t, c.Errors.String(), "Error #01: first error\n")
 
-	c.Error(errors.New("second error")).Meta("some data 2")
-	assert.Equal(t, c.LastError().Error(), "second error")
+	c.Error(&Error{
+		Err:  errors.New("second error"),
+		Meta: "some data 2",
+		Type: ErrorTypePublic,
+	})
 	assert.Len(t, c.Errors, 2)
-	assert.Equal(t, c.Errors.String(), "Error #01: first error\n     Meta: some data\n"+
-		"Error #02: second error\n     Meta: some data 2\n")
 
 	assert.Equal(t, c.Errors[0].Err, errors.New("first error"))
-	assert.Equal(t, c.Errors[0].Metadata, "some data")
-	assert.Equal(t, c.Errors[0].Flags, ErrorTypePrivate)
+	assert.Nil(t, c.Errors[0].Meta)
+	assert.Equal(t, c.Errors[0].Type, ErrorTypePrivate)
 
 	assert.Equal(t, c.Errors[1].Err, errors.New("second error"))
-	assert.Equal(t, c.Errors[1].Metadata, "some data 2")
-	assert.Equal(t, c.Errors[1].Flags, ErrorTypePrivate)
+	assert.Equal(t, c.Errors[1].Meta, "some data 2")
+	assert.Equal(t, c.Errors[1].Type, ErrorTypePublic)
+
+	assert.Equal(t, c.Errors.Last(), c.Errors[1])
 }
 
 func TestContextTypedError(t *testing.T) {
 	c, _, _ := createTestContext()
-	c.Error(errors.New("externo 0")).Type(ErrorTypePublic)
-	c.Error(errors.New("externo 1")).Type(ErrorTypePublic)
-	c.Error(errors.New("interno 0")).Type(ErrorTypePrivate)
-	c.Error(errors.New("externo 2")).Type(ErrorTypePublic)
-	c.Error(errors.New("interno 1")).Type(ErrorTypePrivate)
-	c.Error(errors.New("interno 2")).Type(ErrorTypePrivate)
+	c.Error(errors.New("externo 0")).SetType(ErrorTypePublic)
+	c.Error(errors.New("interno 0")).SetType(ErrorTypePrivate)
 
 	for _, err := range c.Errors.ByType(ErrorTypePublic) {
-		assert.Equal(t, err.Flags, ErrorTypePublic)
+		assert.Equal(t, err.Type, ErrorTypePublic)
 	}
-
 	for _, err := range c.Errors.ByType(ErrorTypePrivate) {
-		assert.Equal(t, err.Flags, ErrorTypePrivate)
+		assert.Equal(t, err.Type, ErrorTypePrivate)
 	}
-
-	assert.Equal(t, c.Errors.Errors(), []string{"externo 0", "externo 1", "interno 0", "externo 2", "interno 1", "interno 2"})
+	assert.Equal(t, c.Errors.Errors(), []string{"externo 0", "interno 0"})
 }
 
-func TestContextFail(t *testing.T) {
+func TestContextAbortWithError(t *testing.T) {
 	c, w, _ := createTestContext()
-	c.AbortWithError(401, errors.New("bad input"))
+	c.AbortWithError(401, errors.New("bad input")).SetMeta("some input")
 	c.Writer.WriteHeaderNow()
 
 	assert.Equal(t, w.Code, 401)
-	assert.Equal(t, c.LastError().Error(), "bad input")
 	assert.Equal(t, c.index, AbortIndex)
 	assert.True(t, c.IsAborted())
 }

+ 24 - 17
errors.go

@@ -21,33 +21,37 @@ const (
 )
 
 // Used internally to collect errors that occurred during an http request.
-type errorMsg struct {
-	Err      error       `json:"error"`
-	Flags    int         `json:"-"`
-	Metadata interface{} `json:"meta"`
+type Error struct {
+	Err  error       `json:"error"`
+	Type int         `json:"-"`
+	Meta interface{} `json:"meta"`
 }
 
-func (msg *errorMsg) Type(flags int) *errorMsg {
-	msg.Flags = flags
+var _ error = &Error{}
+
+func (msg *Error) SetType(flags int) *Error {
+	msg.Type = flags
 	return msg
 }
 
-func (msg *errorMsg) Meta(data interface{}) *errorMsg {
-	msg.Metadata = data
+func (msg *Error) SetMeta(data interface{}) *Error {
+	msg.Meta = data
 	return msg
 }
 
-func (msg *errorMsg) JSON() interface{} {
+func (msg *Error) JSON() interface{} {
 	json := H{}
-	if msg.Metadata != nil {
-		value := reflect.ValueOf(msg.Metadata)
+	if msg.Meta != nil {
+		value := reflect.ValueOf(msg.Meta)
 		switch value.Kind() {
 		case reflect.Struct:
-			return msg.Metadata
+			return msg.Meta
 		case reflect.Map:
 			for _, key := range value.MapKeys() {
 				json[key.String()] = value.MapIndex(key).Interface()
 			}
+		default:
+			json["meta"] = msg.Meta
 		}
 	}
 	if _, ok := json["error"]; !ok {
@@ -56,11 +60,11 @@ func (msg *errorMsg) JSON() interface{} {
 	return json
 }
 
-func (msg *errorMsg) Error() string {
+func (msg *Error) Error() string {
 	return msg.Err.Error()
 }
 
-type errorMsgs []*errorMsg
+type errorMsgs []*Error
 
 func (a errorMsgs) ByType(typ int) errorMsgs {
 	if len(a) == 0 {
@@ -68,14 +72,14 @@ func (a errorMsgs) ByType(typ int) errorMsgs {
 	}
 	result := make(errorMsgs, 0, len(a))
 	for _, msg := range a {
-		if msg.Flags&typ > 0 {
+		if msg.Type&typ > 0 {
 			result = append(result, msg)
 		}
 	}
 	return result
 }
 
-func (a errorMsgs) Last() *errorMsg {
+func (a errorMsgs) Last() *Error {
 	length := len(a)
 	if length == 0 {
 		return nil
@@ -115,7 +119,10 @@ func (a errorMsgs) String() string {
 	}
 	var buffer bytes.Buffer
 	for i, msg := range a {
-		fmt.Fprintf(&buffer, "Error #%02d: %s\n     Meta: %v\n", (i + 1), msg.Err, msg.Metadata)
+		fmt.Fprintf(&buffer, "Error #%02d: %s\n", (i + 1), msg.Err)
+		if msg.Meta != nil {
+			fmt.Fprintf(&buffer, "     Meta: %v\n", msg.Meta)
+		}
 	}
 	return buffer.String()
 }

+ 90 - 0
errors_test.go

@@ -0,0 +1,90 @@
+// Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package gin
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestError(t *testing.T) {
+	baseError := errors.New("test error")
+	err := &Error{
+		Err:  baseError,
+		Type: ErrorTypePrivate,
+	}
+	assert.Equal(t, err.Error(), baseError.Error())
+	assert.Equal(t, err.JSON(), H{"error": baseError.Error()})
+
+	assert.Equal(t, err.SetType(ErrorTypePublic), err)
+	assert.Equal(t, err.Type, ErrorTypePublic)
+
+	assert.Equal(t, err.SetMeta("some data"), err)
+	assert.Equal(t, err.Meta, "some data")
+	assert.Equal(t, err.JSON(), H{
+		"error": baseError.Error(),
+		"meta":  "some data",
+	})
+
+	err.SetMeta(H{
+		"status": "200",
+		"data":   "some data",
+	})
+	assert.Equal(t, err.JSON(), H{
+		"error":  baseError.Error(),
+		"status": "200",
+		"data":   "some data",
+	})
+
+	err.SetMeta(H{
+		"error":  "custom error",
+		"status": "200",
+		"data":   "some data",
+	})
+	assert.Equal(t, err.JSON(), H{
+		"error":  "custom error",
+		"status": "200",
+		"data":   "some data",
+	})
+}
+
+func TestErrorSlice(t *testing.T) {
+	errs := errorMsgs{
+		{Err: errors.New("first"), Type: ErrorTypePrivate},
+		{Err: errors.New("second"), Type: ErrorTypePrivate, Meta: "some data"},
+		{Err: errors.New("third"), Type: ErrorTypePublic, Meta: H{"status": "400"}},
+	}
+
+	assert.Equal(t, errs.Last().Error(), "third")
+	assert.Equal(t, errs.Errors(), []string{"first", "second", "third"})
+	assert.Equal(t, errs.ByType(ErrorTypePublic).Errors(), []string{"third"})
+	assert.Equal(t, errs.ByType(ErrorTypePrivate).Errors(), []string{"first", "second"})
+	assert.Equal(t, errs.ByType(ErrorTypePublic|ErrorTypePrivate).Errors(), []string{"first", "second", "third"})
+	assert.Empty(t, errs.ByType(ErrorTypeBind))
+
+	assert.Equal(t, errs.String(), `Error #01: first
+Error #02: second
+     Meta: some data
+Error #03: third
+     Meta: map[status:400]
+`)
+	assert.Equal(t, errs.JSON(), []interface{}{
+		H{"error": "first"},
+		H{"error": "second", "meta": "some data"},
+		H{"error": "third", "status": "400"},
+	})
+
+	errs = errorMsgs{
+		{Err: errors.New("first"), Type: ErrorTypePrivate},
+	}
+	assert.Equal(t, errs.JSON(), H{"error": "first"})
+
+	errs = errorMsgs{}
+	assert.Nil(t, errs.Last())
+	assert.Nil(t, errs.JSON())
+	assert.Empty(t, errs.String())
+}