diff --git a/validator.go b/validator.go index 2664663..5b53206 100644 --- a/validator.go +++ b/validator.go @@ -31,7 +31,7 @@ type FieldValidationError struct { // This is intended for use in development + debugging and not intended to be a production error message. // it also allows FieldValidationError to be used as an Error interface -func (e FieldValidationError) Error() string { +func (e *FieldValidationError) Error() string { return fmt.Sprintf(validationFieldErrMsg, e.Field, e.ErrorTag) } @@ -48,7 +48,7 @@ type StructValidationErrors struct { // This is intended for use in development + debugging and not intended to be a production error message. // it also allows StructValidationErrors to be used as an Error interface -func (e StructValidationErrors) Error() string { +func (e *StructValidationErrors) Error() string { s := fmt.Sprintf(validationStructErrMsg, e.Struct) @@ -63,6 +63,35 @@ func (e StructValidationErrors) Error() string { return fmt.Sprintf("%s\n\n", s) } +// Flatten flattens the StructValidationErrors hierarchical sctructure into a flat namespace style field name +// for those that want/need it +func (e *StructValidationErrors) Flatten() map[string]*FieldValidationError { + + if e == nil { + return nil + } + + errs := map[string]*FieldValidationError{} + + for _, f := range e.Errors { + + errs[f.Field] = f + } + + for key, val := range e.StructErrors { + + otherErrs := val.Flatten() + + for _, f2 := range otherErrs { + + f2.Field = fmt.Sprintf("%s.%s", key, f2.Field) + errs[f2.Field] = f2 + } + } + + return errs +} + // ValidationFunc that accepts the value of a field and parameter for use in validation (parameter not always used or needed) type ValidationFunc func(v interface{}, param string) bool diff --git a/validator_test.go b/validator_test.go index 58375b4..e3a1074 100644 --- a/validator_test.go +++ b/validator_test.go @@ -87,6 +87,72 @@ func AssertFieldError(s *validator.StructValidationErrors, field string, expecte c.Assert(val.ErrorTag, Equals, expectedTag) } +func AssertMapFieldError(s map[string]*validator.FieldValidationError, field string, expectedTag string, c *C) { + + val, ok := s[field] + c.Assert(ok, Equals, true) + c.Assert(val, NotNil) + c.Assert(val.Field, Equals, field) + c.Assert(val.ErrorTag, Equals, expectedTag) +} + +func (ms *MySuite) TestFlattening(c *C) { + + tSuccess := &TestString{ + Required: "Required", + Len: "length==10", + Min: "min=1", + Max: "1234567890", + MinMax: "12345", + OmitEmpty: "", + Sub: &SubTest{ + Test: "1", + }, + SubIgnore: &SubTest{ + Test: "", + }, + Anonymous: struct { + A string `validate:"required"` + }{ + A: "1", + }, + } + + err1 := validator.ValidateStruct(tSuccess).Flatten() + c.Assert(err1, IsNil) + + tFail := &TestString{ + Required: "", + Len: "", + Min: "", + Max: "12345678901", + MinMax: "", + OmitEmpty: "12345678901", + Sub: &SubTest{ + Test: "", + }, + Anonymous: struct { + A string `validate:"required"` + }{ + A: "", + }, + } + + err2 := validator.ValidateStruct(tFail).Flatten() + + // Assert Top Level + c.Assert(err2, NotNil) + + // Assert Fields + AssertMapFieldError(err2, "Len", "len", c) + + // Assert Struct Field + AssertMapFieldError(err2, "Sub.Test", "required", c) + + // Assert Anonymous Struct Field + AssertMapFieldError(err2, "Anonymous.A", "required", c) +} + func (ms *MySuite) TestStructStringValidation(c *C) { tSuccess := &TestString{ @@ -132,6 +198,7 @@ func (ms *MySuite) TestStructStringValidation(c *C) { err = validator.ValidateStruct(tFail) // Assert Top Level + c.Assert(err, NotNil) c.Assert(err.Struct, Equals, "TestString") c.Assert(len(err.Errors), Equals, 6) c.Assert(len(err.StructErrors), Equals, 2) @@ -181,6 +248,7 @@ func (ms *MySuite) TestStructInt32Validation(c *C) { err = validator.ValidateStruct(tFail) // Assert Top Level + c.Assert(err, NotNil) c.Assert(err.Struct, Equals, "TestInt32") c.Assert(len(err.Errors), Equals, 6) c.Assert(len(err.StructErrors), Equals, 0) @@ -220,6 +288,7 @@ func (ms *MySuite) TestStructUint64Validation(c *C) { err = validator.ValidateStruct(tFail) // Assert Top Level + c.Assert(err, NotNil) c.Assert(err.Struct, Equals, "TestUint64") c.Assert(len(err.Errors), Equals, 6) c.Assert(len(err.StructErrors), Equals, 0) @@ -259,6 +328,7 @@ func (ms *MySuite) TestStructFloat64Validation(c *C) { err = validator.ValidateStruct(tFail) // Assert Top Level + c.Assert(err, NotNil) c.Assert(err.Struct, Equals, "TestFloat64") c.Assert(len(err.Errors), Equals, 6) c.Assert(len(err.StructErrors), Equals, 0) @@ -298,6 +368,7 @@ func (ms *MySuite) TestStructSliceValidation(c *C) { err = validator.ValidateStruct(tFail) // Assert Top Level + c.Assert(err, NotNil) c.Assert(err.Struct, Equals, "TestSlice") c.Assert(len(err.Errors), Equals, 6) c.Assert(len(err.StructErrors), Equals, 0)