Skip to content

Commit

Permalink
Add Error Interface.
Browse files Browse the repository at this point in the history
  • Loading branch information
mehran-prs committed Jan 30, 2020
1 parent 329f7b1 commit df2b2c7
Show file tree
Hide file tree
Showing 23 changed files with 177 additions and 148 deletions.
13 changes: 4 additions & 9 deletions UPGRADE.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
# Upgrade Instructions

## Upgrade from 3.x to 4.x
* If you want to support i18n for your defined string rules, instead of `NewStringRule` use the `NewStringRuleWithError`
method to define them.
* Change your error message placeholders from the Go `fmt` package `verbs` to the `template variables`.
```go
// 3.x
// Assume Email is your custom rule:
var Email = validation.NewStringRule(govalidator.IsEmail, "must be a valid email address")
// Assume you want to set a custom message for the LengthRule:
var lengthRule= validation.Length(2,10).Error("the length must be between %v and %v")

// 4.x
// ErrValidationIsEmail is the error that returns in case of an invalid email.
var ErrEmail = validation.NewError("validation_is_email", "must be a valid email address")

// Define your rule.
var Email = validation.NewStringRuleWithError(govalidator.IsEmail, ErrEmail)
var lengthRule= validation.Length(2,10).Error("the length must be between {{.min}} and {{.max}}")
```

## Upgrade from 2.x to 3.x
Expand Down
6 changes: 3 additions & 3 deletions date.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ var (
// ErrDateInvalid is the error that returns in case of an invalid date.
ErrDateInvalid = NewError("validation_date_invalid", "must be a valid date")
// ErrDateOutOfRange is the error that returns in case of an invalid date.
ErrDateOutOfRange = NewError("validation_date_out_of_range", "the data is out of range")
ErrDateOutOfRange = NewError("validation_date_out_of_range", "the date is out of range")
)

// DateRule is a validation rule that validates date/time string values.
Expand Down Expand Up @@ -43,7 +43,7 @@ func Date(layout string) DateRule {

// Error sets the error message that is used when the value being validated is not a valid date.
func (r DateRule) Error(message string) DateRule {
r.err.SetMessage(message)
r.err = r.err.SetMessage(message)
return r
}

Expand All @@ -55,7 +55,7 @@ func (r DateRule) ErrorObject(err Error) DateRule {

// RangeError sets the error message that is used when the value being validated is out of the specified Min/Max date range.
func (r DateRule) RangeError(message string) DateRule {
r.rangeErr.SetMessage(message)
r.rangeErr = r.rangeErr.SetMessage(message)
return r
}

Expand Down
22 changes: 11 additions & 11 deletions date_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ func TestDateRule_Error(t *testing.T) {
r := Date(time.RFC3339)
assert.Equal(t, "must be a valid date", r.Validate("0001-01-02T15:04:05Z07:00").Error())
r2 := r.Min(time.Date(2000, 1, 1, 1, 1, 1, 0, time.UTC))
assert.Equal(t, "the data is out of range", r2.Validate("1999-01-02T15:04:05Z").Error())
assert.Equal(t, "the date is out of range", r2.Validate("1999-01-02T15:04:05Z").Error())
r = r.Error("123")
r = r.RangeError("456")
assert.Equal(t, "123", r.err.message)
assert.Equal(t, "456", r.rangeErr.message)
assert.Equal(t, "123", r.err.Message())
assert.Equal(t, "456", r.rangeErr.Message())
}

func TestDateRule_ErrorObject(t *testing.T) {
Expand All @@ -53,19 +53,19 @@ func TestDateRule_ErrorObject(t *testing.T) {

r = r.ErrorObject(NewError("code", "abc"))

assert.Equal(t, "code", r.err.code)
assert.Equal(t, "code", r.err.Code())
assert.Equal(t, "abc", r.Validate("0001-01-02T15:04:05Z07:00").Error())

r2 := r.Min(time.Date(2000, 1, 1, 1, 1, 1, 0, time.UTC))
assert.Equal(t, "the data is out of range", r2.Validate("1999-01-02T15:04:05Z").Error())
assert.Equal(t, "the date is out of range", r2.Validate("1999-01-02T15:04:05Z").Error())

r = r.ErrorObject(NewError("C", "def"))
r = r.RangeErrorObject(NewError("D", "123"))

assert.Equal(t, "C", r.err.code)
assert.Equal(t, "def", r.err.message)
assert.Equal(t, "D", r.rangeErr.code)
assert.Equal(t, "123", r.rangeErr.message)
assert.Equal(t, "C", r.err.Code())
assert.Equal(t, "def", r.err.Message())
assert.Equal(t, "D", r.rangeErr.Code())
assert.Equal(t, "123", r.rangeErr.Message())
}

func TestDateRule_MinMax(t *testing.T) {
Expand All @@ -82,10 +82,10 @@ func TestDateRule_MinMax(t *testing.T) {
assert.Nil(t, r2.Validate("2010-01-02"))
err := r2.Validate("1999-01-02")
if assert.NotNil(t, err) {
assert.Equal(t, "the data is out of range", err.Error())
assert.Equal(t, "the date is out of range", err.Error())
}
err2 := r2.Validate("2021-01-02")
if assert.NotNil(t, err) {
assert.Equal(t, "the data is out of range", err2.Error())
assert.Equal(t, "the date is out of range", err2.Error())
}
}
44 changes: 33 additions & 11 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,19 @@ import (
)

type (
// Error represents an validation error
Error struct {
// Error interface represents an validation error
Error interface {
Error() string
Code() string
Message() string
SetMessage(string) Error
Params() map[string]interface{}
SetParams(map[string]interface{}) Error
}

// ErrorObject is the default validation error
// that implements the Error interface.
ErrorObject struct {
code string
message string
params map[string]interface{}
Expand Down Expand Up @@ -47,42 +58,50 @@ func (e internalError) InternalError() error {
}

// SetCode set the error's translation code.
func (e *Error) SetCode(code string) {
func (e ErrorObject) SetCode(code string) Error {
e.code = code
return e
}

// Code get the error's translation code.
func (e Error) Code() string {
func (e ErrorObject) Code() string {
return e.code
}

// SetParams set the error's params.
func (e *Error) SetParams(params map[string]interface{}) {
func (e ErrorObject) SetParams(params map[string]interface{}) Error {
e.params = params
return e
}

// AddParam add parameter to the error's parameters.
func (e *Error) AddParam(name string, value interface{}) {
func (e ErrorObject) AddParam(name string, value interface{}) Error {
if e.params == nil {
e.params = make(map[string]interface{})
}

e.params[name] = value
return e
}

// Params returns the error's params.
func (e Error) Params() map[string]interface{} {
func (e ErrorObject) Params() map[string]interface{} {
return e.params
}

// SetMessage set the error's message.
func (e *Error) SetMessage(message string) {
func (e ErrorObject) SetMessage(message string) Error {
e.message = message
return e
}

// Message return the error's message.
func (e Error) Message() string {
func (e ErrorObject) Message() string {
return e.message
}

// Error returns the error message.
func (e Error) Error() string {
func (e ErrorObject) Error() string {
if len(e.params) == 0 {
return e.message
}
Expand Down Expand Up @@ -149,8 +168,11 @@ func (es Errors) Filter() error {

// NewError create new validation error.
func NewError(code, message string) Error {
return Error{
return ErrorObject{
code: code,
message: message,
}
}

// Assert that our ErrorObject implements the Error interface.
var _ Error = ErrorObject{}
96 changes: 73 additions & 23 deletions error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,68 +70,118 @@ func TestErrors_Filter(t *testing.T) {
assert.Nil(t, errs.Filter())
}

func TestError_SetCode(t *testing.T) {
err := NewError("A", "msg")
func TestErrorObject_SetCode(t *testing.T) {
err := NewError("A", "msg").(ErrorObject)

assert.Equal(t, err.code, "A")
assert.Equal(t, err.Code(), "A")

err.SetCode("B")
assert.Equal(t, err.code, "B")
err = err.SetCode("B").(ErrorObject)
assert.Equal(t, "B", err.code)
}

func TestError_Code(t *testing.T) {
err := NewError("A", "msg")
func TestErrorObject_Code(t *testing.T) {
err := NewError("A", "msg").(ErrorObject)

assert.Equal(t, err.code, "A")
assert.Equal(t, err.Code(), "A")
}

func TestError_Message(t *testing.T) {
err := NewError("code", "A")
func TestErrorObject_SetMessage(t *testing.T) {
err := NewError("code", "A").(ErrorObject)

assert.Equal(t, err.message, "A")
assert.Equal(t, err.Message(), "A")

err = err.SetMessage("abc").(ErrorObject)
assert.Equal(t, err.message, "abc")
assert.Equal(t, err.Message(), "abc")
}

func TestError_Params(t *testing.T) {
func TestErrorObject_Message(t *testing.T) {
err := NewError("code", "A").(ErrorObject)

assert.Equal(t, err.message, "A")
assert.Equal(t, err.Message(), "A")
}

func TestErrorObject_Params(t *testing.T) {
p := map[string]interface{}{"A": "val1", "AA": "val2"}

err := NewError("code", "A")
err.SetParams(p)
err.SetMessage("B")
err := NewError("code", "A").(ErrorObject)
err = err.SetParams(p).(ErrorObject)
err = err.SetMessage("B").(ErrorObject)

assert.Equal(t, err.params, p)
assert.Equal(t, err.Params(), p)
}

func TestError_AddParam(t *testing.T) {
func TestErrorObject_AddParam2(t *testing.T) {
p := map[string]interface{}{"key": "val"}
err := NewError("code", "A").(ErrorObject)
err = err.AddParam("key", "val").(ErrorObject)

assert.Equal(t, err.params, p)
assert.Equal(t, err.Params(), p)
}

func TestErrorObject_AddParam(t *testing.T) {
p := map[string]interface{}{"A": "val1", "B": "val2"}

err := NewError("code", "A")
err.SetParams(p)
err.AddParam("C", "val3")
err := NewError("code", "A").(ErrorObject)
err = err.SetParams(p).(ErrorObject)
err = err.AddParam("C", "val3").(ErrorObject)

p["C"] = "val3"

assert.Equal(t, err.params, p)
assert.Equal(t, err.Params(), p)
}

func TestError_Code(t *testing.T) {
err := NewError("A", "msg")

assert.Equal(t, err.Code(), "A")
}

func TestError_SetMessage(t *testing.T) {
err := NewError("code", "A")

assert.Equal(t, err.Message(), "A")

err = err.SetMessage("abc")
assert.Equal(t, err.Message(), "abc")
}

func TestError_Message(t *testing.T) {
err := NewError("code", "A")

assert.Equal(t, err.Message(), "A")
}

func TestError_Params(t *testing.T) {
p := map[string]interface{}{"A": "val1", "AA": "val2"}

err := NewError("code", "A")
err = err.SetParams(p)
err = err.SetMessage("B")

assert.Equal(t, err.Params(), p)
}

func TestValidationError(t *testing.T) {
params := map[string]interface{}{
"A": "B",
}

err := NewError("code", "msg")
err.SetParams(params)
err = err.SetParams(params)

assert.Equal(t, err.code, "code")
assert.Equal(t, err.message, "msg")
assert.Equal(t, err.params, params)
assert.Equal(t, err.Code(), "code")
assert.Equal(t, err.Message(), "msg")
assert.Equal(t, err.Params(), params)

params = map[string]interface{}{"min": 1}
err.SetParams(params)
err = err.SetParams(params)

assert.Equal(t, err.params, params)
assert.Equal(t, err.Params(), params)
}
2 changes: 1 addition & 1 deletion in.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (r InRule) Validate(value interface{}) error {

// Error sets the error message for the rule.
func (r InRule) Error(message string) InRule {
r.err.SetMessage(message)
r.err = r.err.SetMessage(message)
return r
}

Expand Down
6 changes: 3 additions & 3 deletions in_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func Test_InRule_Error(t *testing.T) {
val := 4
assert.Equal(t, "must be a valid value", r.Validate(&val).Error())
r = r.Error("123")
assert.Equal(t, "123", r.err.message)
assert.Equal(t, "123", r.err.Message())
}

func TestInRule_ErrorObject(t *testing.T) {
Expand All @@ -52,6 +52,6 @@ func TestInRule_ErrorObject(t *testing.T) {
r = r.ErrorObject(err)

assert.Equal(t, err, r.err)
assert.Equal(t, err.code, r.err.code)
assert.Equal(t, err.message, r.err.message)
assert.Equal(t, err.Code(), r.err.Code())
assert.Equal(t, err.Message(), r.err.Message())
}
6 changes: 2 additions & 4 deletions length.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func (r LengthRule) Validate(value interface{}) error {

// Error sets the error message for the rule.
func (r LengthRule) Error(message string) LengthRule {
r.err.SetMessage(message)
r.err = r.err.SetMessage(message)
return r
}

Expand All @@ -100,7 +100,5 @@ func buildLengthRuleError(min, max int) (err Error) {
err = ErrLengthEmptyRequired
}

err.SetParams(map[string]interface{}{"min": min, "max": max})

return err
return err.SetParams(map[string]interface{}{"min": min, "max": max})
}
Loading

0 comments on commit df2b2c7

Please sign in to comment.