diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..952bdda --- /dev/null +++ b/doc.go @@ -0,0 +1,24 @@ +// Package validator implements value validations +// +// The MIT License (MIT) +// +// Copyright (c) 2015 Dean Karn +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +package validator 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)