From 13f9f0094fc734d9c446c2ba16c9dee3e0417f7d Mon Sep 17 00:00:00 2001 From: Daniel Lohse Date: Tue, 3 May 2016 15:41:04 +0200 Subject: [PATCH] Invoke all custom validators (fixes #116) --- validator.go | 27 +++++++++++++++++---------- validator_test.go | 17 +++++++++++++++-- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/validator.go b/validator.go index 01852da..2e63620 100644 --- a/validator.go +++ b/validator.go @@ -700,23 +700,30 @@ func typeCheck(v reflect.Value, t reflect.StructField, o reflect.Value) (bool, e } options := parseTag(tag) - for i := range options { - tagOpt := options[i] - tagOptions := strings.Split(tagOpt, "~") - if ok := isValidTag(tagOptions[0]); !ok { + var customTypeErrors Errors + var customTypeValidatorsExist bool + for _, tagOpt := range options { + tagOpts := strings.Split(tagOpt, "~") + if ok := isValidTag(tagOpts[0]); !ok { continue } - if validatefunc, ok := CustomTypeTagMap[tagOptions[0]]; ok { - options = append(options[:i], options[i+1:]...) // we found our custom validator, so remove it from the options + if validatefunc, ok := CustomTypeTagMap[tagOpts[0]]; ok { + customTypeValidatorsExist = true if result := validatefunc(v.Interface(), o.Interface()); !result { - if len(tagOptions) == 2 { - return false, Error{t.Name, fmt.Errorf(tagOptions[1]), true} + if len(tagOpts) == 2 { + customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: fmt.Errorf(tagOpts[1]), CustomErrorMessageExists: true}) + continue } - return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", fmt.Sprint(v), tagOptions[0]), false} + customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: fmt.Errorf("%s does not validate as %s", fmt.Sprint(v), tagOpts[0]), CustomErrorMessageExists: false}) } - return true, nil } } + if customTypeValidatorsExist { + if len(customTypeErrors.Errors()) > 0 { + return false, customTypeErrors + } + return true, nil + } if isEmptyValue(v) { // an empty value is not validated, check only required diff --git a/validator_test.go b/validator_test.go index c947cbd..7bf1bd5 100644 --- a/validator_test.go +++ b/validator_test.go @@ -1924,11 +1924,14 @@ func TestFieldsRequiredByDefaultButExemptOrOptionalStruct(t *testing.T) { type CustomByteArray [6]byte type StructWithCustomByteArray struct { - ID CustomByteArray `valid:"customByteArrayValidator"` - Email string `valid:"email"` + ID CustomByteArray `valid:"customByteArrayValidator,customMinLengthValidator"` + Email string `valid:"email"` + CustomMinLength int `valid:"-"` } func TestStructWithCustomByteArray(t *testing.T) { + t.Parallel() + // add our custom byte array validator that fails when the byte array is pristine (all zeroes) CustomTypeTagMap["customByteArrayValidator"] = CustomTypeValidator(func(i interface{}, o interface{}) bool { switch v := o.(type) { @@ -1952,6 +1955,13 @@ func TestStructWithCustomByteArray(t *testing.T) { } return false }) + CustomTypeTagMap["customMinLengthValidator"] = CustomTypeValidator(func(i interface{}, o interface{}) bool { + switch v := o.(type) { + case StructWithCustomByteArray: + return len(v.ID) >= v.CustomMinLength + } + return false + }) testCustomByteArray := CustomByteArray{'1', '2', '3', '4', '5', '6'} var tests = []struct { param StructWithCustomByteArray @@ -1960,7 +1970,9 @@ func TestStructWithCustomByteArray(t *testing.T) { {StructWithCustomByteArray{}, false}, {StructWithCustomByteArray{Email: "test@example.com"}, false}, {StructWithCustomByteArray{ID: testCustomByteArray, Email: "test@example.com"}, true}, + {StructWithCustomByteArray{ID: testCustomByteArray, Email: "test@example.com", CustomMinLength: 7}, false}, } + SetFieldsRequiredByDefault(true) for _, test := range tests { actual, err := ValidateStruct(test.param) if actual != test.expected { @@ -1970,6 +1982,7 @@ func TestStructWithCustomByteArray(t *testing.T) { } } } + SetFieldsRequiredByDefault(false) } func TestValidateNegationStruct(t *testing.T) {