Fixing new errors API

This commit is contained in:
Manu Mtz-Almeida 2015-05-22 16:39:15 +02:00
parent 306da81aaf
commit 37b6f6c179
4 changed files with 150 additions and 61 deletions

View File

@ -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{
func (c *Context) Error(err error) *Error {
var parsedError *Error
switch err.(type) {
case *Error:
parsedError = err.(*Error)
default:
parsedError = &Error{
Err: err,
Flags: ErrorTypePrivate,
Type: 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
}
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)
}
}

View File

@ -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())
}

View File

@ -21,33 +21,37 @@ const (
)
// Used internally to collect errors that occurred during an http request.
type errorMsg struct {
type Error struct {
Err error `json:"error"`
Flags int `json:"-"`
Metadata interface{} `json:"meta"`
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
errors_test.go Normal file
View File

@ -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())
}