Merge branch 'v2' into v2-development

pull/16/head
Dean Karn 10 years ago
commit e0bf69bca2
  1. 10
      README.md
  2. 46
      baked_in.go
  3. 30
      doc.go
  4. 52
      validator.go
  5. 26
      validator_test.go

@ -1,6 +1,6 @@
Package go-validate-yourself Package go-validate-yourself
================ ================
[![Build Status](https://travis-ci.org/joeybloggs/go-validate-yourself.svg?branch=v2-development)](https://travis-ci.org/joeybloggs/go-validate-yourself) [![Build Status](https://travis-ci.org/joeybloggs/go-validate-yourself.svg?branch=v2)](https://travis-ci.org/joeybloggs/go-validate-yourself)
Package validator implements value validations for structs and individual fields based on tags. Package validator implements value validations for structs and individual fields based on tags.
@ -9,20 +9,20 @@ Installation
Just use go get. Just use go get.
go get gopkg.in/joeybloggs/go-validate-yourself.v1 go get gopkg.in/joeybloggs/go-validate-yourself.v2
or to update or to update
go get -u gopkg.in/joeybloggs/go-validate-yourself.v1 go get -u gopkg.in/joeybloggs/go-validate-yourself.v2
And then just import the package into your own code. And then just import the package into your own code.
import "gopkg.in/joeybloggs/go-validate-yourself.v1" import "gopkg.in/joeybloggs/go-validate-yourself.v2"
Usage Usage
===== =====
Please see http://godoc.org/gopkg.in/joeybloggs/go-validate-yourself.v1 for detailed usage docs. Please see http://godoc.org/gopkg.in/joeybloggs/go-validate-yourself.v2 for detailed usage docs.
Contributing Contributing
============ ============

@ -34,7 +34,7 @@ var BakedInValidators = map[string]ValidationFunc{
"uri": isURI, "uri": isURI,
} }
func isURI(field interface{}, param string) bool { func isURI(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -49,7 +49,7 @@ func isURI(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isURL(field interface{}, param string) bool { func isURL(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -72,7 +72,7 @@ func isURL(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isEmail(field interface{}, param string) bool { func isEmail(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -85,7 +85,7 @@ func isEmail(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isHsla(field interface{}, param string) bool { func isHsla(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -98,7 +98,7 @@ func isHsla(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isHsl(field interface{}, param string) bool { func isHsl(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -111,7 +111,7 @@ func isHsl(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isRgba(field interface{}, param string) bool { func isRgba(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -124,7 +124,7 @@ func isRgba(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isRgb(field interface{}, param string) bool { func isRgb(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -137,7 +137,7 @@ func isRgb(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isHexcolor(field interface{}, param string) bool { func isHexcolor(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -150,7 +150,7 @@ func isHexcolor(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isHexadecimal(field interface{}, param string) bool { func isHexadecimal(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -163,7 +163,7 @@ func isHexadecimal(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isNumber(field interface{}, param string) bool { func isNumber(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -176,7 +176,7 @@ func isNumber(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isNumeric(field interface{}, param string) bool { func isNumeric(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -189,7 +189,7 @@ func isNumeric(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isAlphanum(field interface{}, param string) bool { func isAlphanum(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -202,7 +202,7 @@ func isAlphanum(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isAlpha(field interface{}, param string) bool { func isAlpha(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -215,7 +215,7 @@ func isAlpha(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func hasValue(field interface{}, param string) bool { func hasValue(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -229,7 +229,7 @@ func hasValue(field interface{}, param string) bool {
} }
} }
func isGte(field interface{}, param string) bool { func isGte(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -274,7 +274,7 @@ func isGte(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isGt(field interface{}, param string) bool { func isGt(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -318,7 +318,7 @@ func isGt(field interface{}, param string) bool {
// length tests whether a variable's length is equal to a given // length tests whether a variable's length is equal to a given
// value. For strings it tests the number of characters whereas // value. For strings it tests the number of characters whereas
// for maps and slices it tests the number of items. // for maps and slices it tests the number of items.
func hasLengthOf(field interface{}, param string) bool { func hasLengthOf(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -357,12 +357,12 @@ func hasLengthOf(field interface{}, param string) bool {
// number. For number types, it's a simple lesser-than test; for // number. For number types, it's a simple lesser-than test; for
// strings it tests the number of characters whereas for maps // strings it tests the number of characters whereas for maps
// and slices it tests the number of items. // and slices it tests the number of items.
func hasMinOf(field interface{}, param string) bool { func hasMinOf(val interface{}, field interface{}, param string) bool {
return isGte(field, param) return isGte(val, field, param)
} }
func isLte(field interface{}, param string) bool { func isLte(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -407,7 +407,7 @@ func isLte(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isLt(field interface{}, param string) bool { func isLt(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -453,9 +453,9 @@ func isLt(field interface{}, param string) bool {
// value. For numbers, it's a simple lesser-than test; for // value. For numbers, it's a simple lesser-than test; for
// strings it tests the number of characters whereas for maps // strings it tests the number of characters whereas for maps
// and slices it tests the number of items. // and slices it tests the number of items.
func hasMaxOf(field interface{}, param string) bool { func hasMaxOf(val interface{}, field interface{}, param string) bool {
return isLte(field, param) return isLte(val, field, param)
} }
// asInt retuns the parameter as a int64 // asInt retuns the parameter as a int64

@ -82,7 +82,7 @@ Custom Functions
Custom functions can be added Custom functions can be added
//Structure //Structure
func customFunc(field interface{}, param string) bool { func customFunc(val interface{}, field interface{}, param string) bool {
if whatever { if whatever {
return false return false
@ -92,9 +92,35 @@ Custom functions can be added
} }
validator.AddFunction("custom tag name", customFunc) validator.AddFunction("custom tag name", customFunc)
// NOTE: using the same tag name as an existing function // NOTES: using the same tag name as an existing function
// will overwrite the existing one // will overwrite the existing one
Cross Field Validation
Cross Field Validation can be implemented, for example Start & End Date range validation
// NOTE: when calling validator.validateStruct(val) val will be the top level struct passed
// into the function
// when calling validator.ValidateFieldByTagAndValue(val, field, tag) val will be
// whatever you pass, struct, field...
// when calling validator.ValidateFieldByTag(field, tag) val will be nil
//
// Because of the specific requirements and field names within each persons project that
// uses this library it is unlikely that any baked in function for this type of validation
// would be added, but you can add your own custom ones and keep all your validation logic
// in one place.
func isDateRangeValid(val interface{}, field interface{}, param string) bool {
myStruct := val.(myStructType)
if myStruct.Start.After(field.(time.Time)) {
return false
}
return true
}
Custom Tag Name Custom Tag Name
A custom tag name can be set to avoid conficts, or just have a shorter name A custom tag name can be set to avoid conficts, or just have a shorter name

@ -96,8 +96,8 @@ func (e *StructValidationErrors) Flatten() map[string]*FieldValidationError {
return errs return errs
} }
// ValidationFunc that accepts the value of a field and parameter for use in validation (parameter not always used or needed) // ValidationFunc that accepts a value(optional usage), a field and parameter(optional usage) for use in validation
type ValidationFunc func(v interface{}, param string) bool type ValidationFunc func(val interface{}, v interface{}, param string) bool
// Validator implements the Validator Struct // Validator implements the Validator Struct
// NOTE: Fields within are not thread safe and that is on purpose // NOTE: Fields within are not thread safe and that is on purpose
@ -168,6 +168,18 @@ func ValidateStruct(s interface{}) *StructValidationErrors {
// ValidateStruct validates a struct and returns a struct containing the errors // ValidateStruct validates a struct and returns a struct containing the errors
func (v *Validator) ValidateStruct(s interface{}) *StructValidationErrors { func (v *Validator) ValidateStruct(s interface{}) *StructValidationErrors {
structValue := reflect.ValueOf(s)
if structValue.Kind() == reflect.Ptr && !structValue.IsNil() {
return v.ValidateStruct(structValue.Elem().Interface())
}
return v.validateStructRecursive(s, s)
}
// validateStructRecursive validates a struct recursivly and passes the top level struct around for use in validator functions and returns a struct containing the errors
func (v *Validator) validateStructRecursive(top interface{}, s interface{}) *StructValidationErrors {
structValue := reflect.ValueOf(s) structValue := reflect.ValueOf(s)
structType := reflect.TypeOf(s) structType := reflect.TypeOf(s)
structName := structType.Name() structName := structType.Name()
@ -179,7 +191,7 @@ func (v *Validator) ValidateStruct(s interface{}) *StructValidationErrors {
} }
if structValue.Kind() == reflect.Ptr && !structValue.IsNil() { if structValue.Kind() == reflect.Ptr && !structValue.IsNil() {
return v.ValidateStruct(structValue.Elem().Interface()) return v.validateStructRecursive(top, structValue.Elem().Interface())
} }
if structValue.Kind() != reflect.Struct && structValue.Kind() != reflect.Interface { if structValue.Kind() != reflect.Struct && structValue.Kind() != reflect.Interface {
@ -218,7 +230,7 @@ func (v *Validator) ValidateStruct(s interface{}) *StructValidationErrors {
if valueField.Type() == reflect.TypeOf(time.Time{}) { if valueField.Type() == reflect.TypeOf(time.Time{}) {
if fieldError := v.validateFieldByNameAndTag(valueField.Interface(), typeField.Name, tag); fieldError != nil { if fieldError := v.validateFieldByNameAndTagAndValue(top, valueField.Interface(), typeField.Name, tag); fieldError != nil {
validationErrors.Errors[fieldError.Field] = fieldError validationErrors.Errors[fieldError.Field] = fieldError
// free up memory reference // free up memory reference
fieldError = nil fieldError = nil
@ -235,7 +247,7 @@ func (v *Validator) ValidateStruct(s interface{}) *StructValidationErrors {
default: default:
if fieldError := v.validateFieldByNameAndTag(valueField.Interface(), typeField.Name, tag); fieldError != nil { if fieldError := v.validateFieldByNameAndTagAndValue(top, valueField.Interface(), typeField.Name, tag); fieldError != nil {
validationErrors.Errors[fieldError.Field] = fieldError validationErrors.Errors[fieldError.Field] = fieldError
// free up memory reference // free up memory reference
fieldError = nil fieldError = nil
@ -253,30 +265,42 @@ func (v *Validator) ValidateStruct(s interface{}) *StructValidationErrors {
// ValidateFieldByTag allows validation of a single field with the internal validator, still using tag style validation to check multiple errors // ValidateFieldByTag allows validation of a single field with the internal validator, still using tag style validation to check multiple errors
func ValidateFieldByTag(f interface{}, tag string) *FieldValidationError { func ValidateFieldByTag(f interface{}, tag string) *FieldValidationError {
return internalValidator.validateFieldByNameAndTag(f, "", tag) return internalValidator.ValidateFieldByTag(f, tag)
} }
// ValidateFieldByTag allows validation of a single field, still using tag style validation to check multiple errors // ValidateFieldByTag allows validation of a single field, still using tag style validation to check multiple errors
func (v *Validator) ValidateFieldByTag(f interface{}, tag string) *FieldValidationError { func (v *Validator) ValidateFieldByTag(f interface{}, tag string) *FieldValidationError {
return v.validateFieldByNameAndTag(f, "", tag) return v.ValidateFieldByTagAndValue(nil, f, tag)
}
// ValidateFieldByTagAndValue allows validation of a single field with the internal validator, still using tag style validation to check multiple errors
func ValidateFieldByTagAndValue(val interface{}, f interface{}, tag string) *FieldValidationError {
return internalValidator.ValidateFieldByTagAndValue(val, f, tag)
}
// ValidateFieldByTagAndValue allows validation of a single field, still using tag style validation to check multiple errors
func (v *Validator) ValidateFieldByTagAndValue(val interface{}, f interface{}, tag string) *FieldValidationError {
return v.validateFieldByNameAndTagAndValue(val, f, "", tag)
} }
func (v *Validator) validateFieldByNameAndTag(f interface{}, name string, tag string) *FieldValidationError { func (v *Validator) validateFieldByNameAndTagAndValue(val interface{}, f interface{}, name string, tag string) *FieldValidationError {
// This is a double check if coming from ValidateStruct but need to be here in case function is called directly // This is a double check if coming from ValidateStruct but need to be here in case function is called directly
if tag == "-" { if tag == "-" {
return nil return nil
} }
if strings.Contains(tag, omitempty) && !hasValue(f, "") { if strings.Contains(tag, omitempty) && !hasValue(val, f, "") {
return nil return nil
} }
valueField := reflect.ValueOf(f) valueField := reflect.ValueOf(f)
if valueField.Kind() == reflect.Ptr && !valueField.IsNil() { if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
return v.ValidateFieldByTag(valueField.Elem().Interface(), tag) return v.validateFieldByNameAndTagAndValue(val, valueField.Elem().Interface(), name, tag)
} }
switch valueField.Kind() { switch valueField.Kind() {
@ -302,7 +326,7 @@ func (v *Validator) validateFieldByNameAndTag(f interface{}, name string, tag st
for _, val := range orVals { for _, val := range orVals {
valErr, err = v.validateFieldByNameAndSingleTag(f, name, val) valErr, err = v.validateFieldByNameAndSingleTag(val, f, name, val)
if err == nil { if err == nil {
return nil return nil
@ -320,7 +344,7 @@ func (v *Validator) validateFieldByNameAndTag(f interface{}, name string, tag st
return valErr return valErr
} }
if valErr, err = v.validateFieldByNameAndSingleTag(f, name, valTag); err != nil { if valErr, err = v.validateFieldByNameAndSingleTag(val, f, name, valTag); err != nil {
valErr.Kind = valueField.Kind() valErr.Kind = valueField.Kind()
return valErr return valErr
@ -330,7 +354,7 @@ func (v *Validator) validateFieldByNameAndTag(f interface{}, name string, tag st
return nil return nil
} }
func (v *Validator) validateFieldByNameAndSingleTag(f interface{}, name string, valTag string) (*FieldValidationError, error) { func (v *Validator) validateFieldByNameAndSingleTag(val interface{}, f interface{}, name string, valTag string) (*FieldValidationError, error) {
vals := strings.Split(valTag, "=") vals := strings.Split(valTag, "=")
key := strings.Trim(vals[0], " ") key := strings.Trim(vals[0], " ")
@ -361,7 +385,7 @@ func (v *Validator) validateFieldByNameAndSingleTag(f interface{}, name string,
param = strings.Trim(vals[1], " ") param = strings.Trim(vals[1], " ")
} }
if err := valFunc(f, param); !err { if err := valFunc(val, f, param); !err {
valErr.Param = param valErr.Param = param
return valErr, errors.New(key) return valErr, errors.New(key)
} }

@ -124,11 +124,35 @@ func AssertMapFieldError(s map[string]*validator.FieldValidationError, field str
c.Assert(val.ErrorTag, Equals, expectedTag) c.Assert(val.ErrorTag, Equals, expectedTag)
} }
func newValidatorFunc(field interface{}, param string) bool { func newValidatorFunc(val interface{}, field interface{}, param string) bool {
return true return true
} }
func isEqualFunc(val interface{}, field interface{}, param string) bool {
return val.(string) == field.(string)
}
func (ms *MySuite) TestValidateByTagAndValue(c *C) {
val := "test"
field := "test"
err := validator.ValidateFieldByTagAndValue(val, field, "required")
c.Assert(err, IsNil)
validator.AddFunction("isequaltestfunc", isEqualFunc)
err = validator.ValidateFieldByTagAndValue(val, field, "isequaltestfunc")
c.Assert(err, IsNil)
val = "unequal"
err = validator.ValidateFieldByTagAndValue(val, field, "isequaltestfunc")
c.Assert(err, NotNil)
c.Assert(err.ErrorTag, Equals, "isequaltestfunc")
}
func (ms *MySuite) TestAddFunctions(c *C) { func (ms *MySuite) TestAddFunctions(c *C) {
validate := validator.NewValidator("validateme", validator.BakedInValidators) validate := validator.NewValidator("validateme", validator.BakedInValidators)

Loading…
Cancel
Save