From a87651c07b5f08efec91dd323b7bf8e9b6841565 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Mon, 20 Jul 2015 08:54:31 -0400 Subject: [PATCH 1/4] Update README.md fix badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 96aa669..c4da807 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Package validator ================ [![Join the chat at https://gitter.im/bluesuncorp/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/bluesuncorp/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Build Status](https://semaphoreci.com/api/v1/projects/ec20115f-ef1b-4c7d-9393-cc76aba74eb4/487383/badge.svg)](https://semaphoreci.com/joeybloggs/validator) +[![Build Status](https://semaphoreci.com/api/v1/projects/ec20115f-ef1b-4c7d-9393-cc76aba74eb4/487374/badge.svg)](https://semaphoreci.com/joeybloggs/validator) [![Coverage Status](https://coveralls.io/repos/bluesuncorp/validator/badge.svg?branch=v6)](https://coveralls.io/r/bluesuncorp/validator?branch=v6) [![GoDoc](https://godoc.org/gopkg.in/bluesuncorp/validator.v6?status.svg)](https://godoc.org/gopkg.in/bluesuncorp/validator.v6) From 790122c21c381a67d664763e8d50922eeb3b345e Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Thu, 30 Jul 2015 22:37:45 -0400 Subject: [PATCH 2/4] Update simple.go tmp blank out of file --- examples/simple.go | 242 ++++++++++++++++++++++----------------------- 1 file changed, 121 insertions(+), 121 deletions(-) diff --git a/examples/simple.go b/examples/simple.go index fd503c4..e58414e 100644 --- a/examples/simple.go +++ b/examples/simple.go @@ -1,150 +1,150 @@ package main -import ( - "errors" - "fmt" - "reflect" - - sql "database/sql/driver" - - "gopkg.in/bluesuncorp/validator.v6" -) - -// User contains user information -type User struct { - FirstName string `validate:"required"` - LastName string `validate:"required"` - Age uint8 `validate:"gte=0,lte=130"` - Email string `validate:"required,email"` - FavouriteColor string `validate:"hexcolor|rgb|rgba"` - Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage... -} +// import ( +// "errors" +// "fmt" +// "reflect" + +// sql "database/sql/driver" + +// "gopkg.in/bluesuncorp/validator.v6" +// ) + +// // User contains user information +// type User struct { +// FirstName string `validate:"required"` +// LastName string `validate:"required"` +// Age uint8 `validate:"gte=0,lte=130"` +// Email string `validate:"required,email"` +// FavouriteColor string `validate:"hexcolor|rgb|rgba"` +// Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage... +// } + +// // Address houses a users address information +// type Address struct { +// Street string `validate:"required"` +// City string `validate:"required"` +// Planet string `validate:"required"` +// Phone string `validate:"required"` +// } + +// var validate *validator.Validate + +func main() { + +// config := validator.Config{ +// TagName: "validate", +// ValidationFuncs: validator.BakedInValidators, +// } -// Address houses a users address information -type Address struct { - Street string `validate:"required"` - City string `validate:"required"` - Planet string `validate:"required"` - Phone string `validate:"required"` +// validate = validator.New(config) + +// validateStruct() +// validateField() } -var validate *validator.Validate +// func validateStruct() { -func main() { +// address := &Address{ +// Street: "Eavesdown Docks", +// Planet: "Persphone", +// Phone: "none", +// } - config := validator.Config{ - TagName: "validate", - ValidationFuncs: validator.BakedInValidators, - } +// user := &User{ +// FirstName: "Badger", +// LastName: "Smith", +// Age: 135, +// Email: "Badger.Smith@gmail.com", +// FavouriteColor: "#000", +// Addresses: []*Address{address}, +// } - validate = validator.New(config) +// // returns nil or ValidationErrors ( map[string]*FieldError ) +// errs := validate.Struct(user) - validateStruct() - validateField() -} +// if errs != nil { -func validateStruct() { - - address := &Address{ - Street: "Eavesdown Docks", - Planet: "Persphone", - Phone: "none", - } - - user := &User{ - FirstName: "Badger", - LastName: "Smith", - Age: 135, - Email: "Badger.Smith@gmail.com", - FavouriteColor: "#000", - Addresses: []*Address{address}, - } - - // returns nil or ValidationErrors ( map[string]*FieldError ) - errs := validate.Struct(user) - - if errs != nil { - - fmt.Println(errs) // output: Key: "User.Age" Error:Field validation for "Age" failed on the "lte" tag - // Key: "User.Addresses[0].City" Error:Field validation for "City" failed on the "required" tag - err := errs["User.Addresses[0].City"] - fmt.Println(err.Field) // output: City - fmt.Println(err.Tag) // output: required - fmt.Println(err.Kind) // output: string - fmt.Println(err.Type) // output: string - fmt.Println(err.Param) // output: - fmt.Println(err.Value) // output: - - // from here you can create your own error messages in whatever language you wish - return - } - - // save user to database -} +// fmt.Println(errs) // output: Key: "User.Age" Error:Field validation for "Age" failed on the "lte" tag +// // Key: "User.Addresses[0].City" Error:Field validation for "City" failed on the "required" tag +// err := errs["User.Addresses[0].City"] +// fmt.Println(err.Field) // output: City +// fmt.Println(err.Tag) // output: required +// fmt.Println(err.Kind) // output: string +// fmt.Println(err.Type) // output: string +// fmt.Println(err.Param) // output: +// fmt.Println(err.Value) // output: -func validateField() { - myEmail := "joeybloggs.gmail.com" +// // from here you can create your own error messages in whatever language you wish +// return +// } - errs := validate.Field(myEmail, "required,email") +// // save user to database +// } - if errs != nil { - fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "email" tag - return - } +// func validateField() { +// myEmail := "joeybloggs.gmail.com" - // email ok, move on -} +// errs := validate.Field(myEmail, "required,email") -var validate2 *validator.Validate +// if errs != nil { +// fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "email" tag +// return +// } -type valuer struct { - Name string -} +// // email ok, move on +// } -func (v valuer) Value() (sql.Value, error) { +// var validate2 *validator.Validate - if v.Name == "errorme" { - return nil, errors.New("some kind of error") - } +// type valuer struct { +// Name string +// } - if v.Name == "blankme" { - return "", nil - } +// func (v valuer) Value() (sql.Value, error) { - if len(v.Name) == 0 { - return nil, nil - } +// if v.Name == "errorme" { +// return nil, errors.New("some kind of error") +// } - return v.Name, nil -} +// if v.Name == "blankme" { +// return "", nil +// } -func main2() { +// if len(v.Name) == 0 { +// return nil, nil +// } - customTypes := map[reflect.Type]validator.CustomTypeFunc{} - customTypes[reflect.TypeOf((*sql.Valuer)(nil))] = ValidateValuerType - customTypes[reflect.TypeOf(valuer{})] = ValidateValuerType +// return v.Name, nil +// } - config := validator.Config{ - TagName: "validate", - ValidationFuncs: validator.BakedInValidators, - CustomTypeFuncs: customTypes, - } +// func main2() { - validate2 = validator.New(config) +// customTypes := map[reflect.Type]validator.CustomTypeFunc{} +// customTypes[reflect.TypeOf((*sql.Valuer)(nil))] = ValidateValuerType +// customTypes[reflect.TypeOf(valuer{})] = ValidateValuerType - validateCustomFieldType() -} +// config := validator.Config{ +// TagName: "validate", +// ValidationFuncs: validator.BakedInValidators, +// CustomTypeFuncs: customTypes, +// } -func validateCustomFieldType() { - val := valuer{ - Name: "blankme", - } +// validate2 = validator.New(config) - errs := validate2.Field(val, "required") - if errs != nil { - fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "required" tag - return - } +// validateCustomFieldType() +// } - // all ok -} +// func validateCustomFieldType() { +// val := valuer{ +// Name: "blankme", +// } + +// errs := validate2.Field(val, "required") +// if errs != nil { +// fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "required" tag +// return +// } + +// // all ok +// } From 7c844893e1d184bc159ae7f4e8a27f30674c6439 Mon Sep 17 00:00:00 2001 From: Kevin Harrington Date: Fri, 31 Jul 2015 19:33:07 -0400 Subject: [PATCH 3/4] Added RegisterCustomTypeFunc method and usage example. --- examples/custom.go | 47 ++++++++++++++++++++++++++++++++++++++++++++++ validator.go | 11 +++++++++++ 2 files changed, 58 insertions(+) create mode 100644 examples/custom.go diff --git a/examples/custom.go b/examples/custom.go new file mode 100644 index 0000000..d7eb3b2 --- /dev/null +++ b/examples/custom.go @@ -0,0 +1,47 @@ +package main + +import ( + "database/sql" + "database/sql/driver" + "fmt" + "reflect" + + validator "gopkg.in/bluesuncorp/validator.v6" +) + +type DbBackedUser struct { + Name sql.NullString `validate:"required"` + Age sql.NullInt64 `validate:"required"` +} + +func main() { + + config := validator.Config{ + TagName: "validate", + ValidationFuncs: validator.BakedInValidators, + } + + validate := validator.New(config) + + // register all sql.Null* types to use the ValidateValuer CustomTypeFunc + validate.RegisterCustomTypeFunc(ValidateValuer, sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{}) + + x := DbBackedUser{Name: sql.NullString{String: "", Valid: true}, Age: sql.NullInt64{Int64: 0, Valid: false}} + errs := validate.Struct(x) + + if len(errs) > 0 { + fmt.Printf("Errs:\n%+v\n", errs) + } +} + +// ValidateValuer implements validator.CustomTypeFunc +func ValidateValuer(field reflect.Value) interface{} { + if valuer, ok := field.Interface().(driver.Valuer); ok { + val, err := valuer.Value() + if err == nil { + return val + } + // handle the error how you want + } + return nil +} diff --git a/validator.go b/validator.go index e557d28..9fb4dcf 100644 --- a/validator.go +++ b/validator.go @@ -157,6 +157,17 @@ func (v *Validate) RegisterValidation(key string, f Func) error { return nil } +// RegisterCustomTypeFunc registers types w/a custom type handler function. +func (v *Validate) RegisterCustomTypeFunc(f CustomTypeFunc, sampleTypeValues ...interface{}) { + if v.config.CustomTypeFuncs == nil { + v.config.CustomTypeFuncs = map[reflect.Type]CustomTypeFunc{} + } + for _, sample := range sampleTypeValues { + v.config.CustomTypeFuncs[reflect.TypeOf(sample)] = f + } + v.config.hasCustomFuncs = true +} + // Field validates a single field using tag style validation and returns ValidationErrors // NOTE: it returns ValidationErrors instead of a single FieldError because this can also // validate Array, Slice and maps fields which may contain more than one error From d2ea21ad1513b0e0da4c19fb205f38491df611bc Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Sat, 1 Aug 2015 20:54:40 -0400 Subject: [PATCH 4/4] Add RegisterCustomTypeFunc for easier adding of CustomTypeFunc Thanks @johnniedoe for ths pull request! --- README.md | 83 ++++++++++----------------------- examples/{ => custom}/custom.go | 3 +- examples/{ => simple}/simple.go | 0 validator.go | 11 +++-- validator_test.go | 68 +++++++++++++++++++++++++-- 5 files changed, 97 insertions(+), 68 deletions(-) rename examples/{ => custom}/custom.go (94%) rename examples/{ => simple}/simple.go (100%) diff --git a/README.md b/README.md index d64138a..8c40597 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Package validator ================ [![Join the chat at https://gitter.im/bluesuncorp/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/bluesuncorp/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Build Status](https://semaphoreci.com/api/v1/projects/ec20115f-ef1b-4c7d-9393-cc76aba74eb4/487374/badge.svg)](https://semaphoreci.com/joeybloggs/validator) +[![Build Status](https://semaphoreci.com/api/v1/projects/ec20115f-ef1b-4c7d-9393-cc76aba74eb4/487383/badge.svg)](https://semaphoreci.com/joeybloggs/validator) [![Coverage Status](https://coveralls.io/repos/bluesuncorp/validator/badge.svg?branch=v6)](https://coveralls.io/r/bluesuncorp/validator?branch=v6) [![GoDoc](https://godoc.org/gopkg.in/bluesuncorp/validator.v6?status.svg)](https://godoc.org/gopkg.in/bluesuncorp/validator.v6) @@ -138,84 +138,51 @@ Custom Field Type package main import ( - "errors" + "database/sql" + "database/sql/driver" "fmt" "reflect" - sql "database/sql/driver" - "gopkg.in/bluesuncorp/validator.v6" ) -var validate *validator.Validate - -type valuer struct { - Name string -} - -func (v valuer) Value() (sql.Value, error) { - - if v.Name == "errorme" { - return nil, errors.New("some kind of error") - } - - if v.Name == "blankme" { - return "", nil - } - - if len(v.Name) == 0 { - return nil, nil - } - - return v.Name, nil -} - -// ValidateValuerType implements validator.CustomTypeFunc -func ValidateValuerType(field reflect.Value) interface{} { - if valuer, ok := field.Interface().(sql.Valuer); ok { - val, err := valuer.Value() - if err != nil { - // handle the error how you want - return nil - } - - return val - } - - return nil +// DbBackedUser User struct +type DbBackedUser struct { + Name sql.NullString `validate:"required"` + Age sql.NullInt64 `validate:"required"` } func main() { - customTypes := map[reflect.Type]validator.CustomTypeFunc{} - customTypes[reflect.TypeOf((*sql.Valuer)(nil))] = ValidateValuerType - customTypes[reflect.TypeOf(valuer{})] = ValidateValuerType - config := validator.Config{ TagName: "validate", ValidationFuncs: validator.BakedInValidators, - CustomTypeFuncs: customTypes, } - validate = validator.New(config) + validate := validator.New(config) - validateCustomFieldType() -} + // register all sql.Null* types to use the ValidateValuer CustomTypeFunc + validate.RegisterCustomTypeFunc(ValidateValuer, sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{}) -func validateCustomFieldType() { - val := valuer{ - Name: "blankme", - } + x := DbBackedUser{Name: sql.NullString{String: "", Valid: true}, Age: sql.NullInt64{Int64: 0, Valid: false}} + errs := validate.Struct(x) - errs := validate.Field(val, "required") - if errs != nil { - fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "required" tag - return + if len(errs) > 0 { + fmt.Printf("Errs:\n%+v\n", errs) } - - // all ok } +// ValidateValuer implements validator.CustomTypeFunc +func ValidateValuer(field reflect.Value) interface{} { + if valuer, ok := field.Interface().(driver.Valuer); ok { + val, err := valuer.Value() + if err == nil { + return val + } + // handle the error how you want + } + return nil +} ``` Benchmarks diff --git a/examples/custom.go b/examples/custom/custom.go similarity index 94% rename from examples/custom.go rename to examples/custom/custom.go index d7eb3b2..897b17f 100644 --- a/examples/custom.go +++ b/examples/custom/custom.go @@ -6,9 +6,10 @@ import ( "fmt" "reflect" - validator "gopkg.in/bluesuncorp/validator.v6" + "gopkg.in/bluesuncorp/validator.v6" ) +// DbBackedUser User struct type DbBackedUser struct { Name sql.NullString `validate:"required"` Age sql.NullInt64 `validate:"required"` diff --git a/examples/simple.go b/examples/simple/simple.go similarity index 100% rename from examples/simple.go rename to examples/simple/simple.go diff --git a/validator.go b/validator.go index 9fb4dcf..d8f35b6 100644 --- a/validator.go +++ b/validator.go @@ -157,14 +157,17 @@ func (v *Validate) RegisterValidation(key string, f Func) error { return nil } -// RegisterCustomTypeFunc registers types w/a custom type handler function. -func (v *Validate) RegisterCustomTypeFunc(f CustomTypeFunc, sampleTypeValues ...interface{}) { +// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types +func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{}) { + if v.config.CustomTypeFuncs == nil { v.config.CustomTypeFuncs = map[reflect.Type]CustomTypeFunc{} } - for _, sample := range sampleTypeValues { - v.config.CustomTypeFuncs[reflect.TypeOf(sample)] = f + + for _, t := range types { + v.config.CustomTypeFuncs[reflect.TypeOf(t)] = fn } + v.config.hasCustomFuncs = true } diff --git a/validator_test.go b/validator_test.go index 37b6dee..4b93f92 100644 --- a/validator_test.go +++ b/validator_test.go @@ -1,14 +1,14 @@ package validator import ( + "database/sql" + "database/sql/driver" "errors" "fmt" "reflect" "testing" "time" - sql "database/sql/driver" - . "gopkg.in/bluesuncorp/assert.v1" ) @@ -126,7 +126,7 @@ type valuer struct { Name string } -func (v valuer) Value() (sql.Value, error) { +func (v valuer) Value() (driver.Value, error) { if v.Name == "errorme" { return nil, errors.New("some kind of error") @@ -178,7 +178,7 @@ type CustomMadeUpStruct struct { } func ValidateValuerType(field reflect.Value) interface{} { - if valuer, ok := field.Interface().(sql.Valuer); ok { + if valuer, ok := field.Interface().(driver.Valuer); ok { val, err := valuer.Value() if err != nil { // handle the error how you want @@ -191,10 +191,68 @@ func ValidateValuerType(field reflect.Value) interface{} { return nil } +func TestSQLValue2Validation(t *testing.T) { + + config := Config{ + TagName: "validate", + ValidationFuncs: BakedInValidators, + } + + validate := New(config) + validate.RegisterCustomTypeFunc(ValidateValuerType, valuer{}, (*driver.Valuer)(nil), sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{}) + validate.RegisterCustomTypeFunc(ValidateCustomType, MadeUpCustomType{}) + validate.RegisterCustomTypeFunc(OverrideIntTypeForSomeReason, 1) + + val := valuer{ + Name: "", + } + + errs := validate.Field(val, "required") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "required") + + val.Name = "Valid Name" + errs = validate.Field(val, "required") + Equal(t, errs, nil) + + val.Name = "errorme" + + PanicMatches(t, func() { errs = validate.Field(val, "required") }, "SQL Driver Valuer error: some kind of error") + + type myValuer valuer + + myVal := valuer{ + Name: "", + } + + errs = validate.Field(myVal, "required") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "required") + + cust := MadeUpCustomType{ + FirstName: "Joey", + LastName: "Bloggs", + } + + c := CustomMadeUpStruct{MadeUp: cust, OverriddenInt: 2} + + errs = validate.Struct(c) + Equal(t, errs, nil) + + c.MadeUp.FirstName = "" + c.OverriddenInt = 1 + + errs = validate.Struct(c) + NotEqual(t, errs, nil) + Equal(t, len(errs), 2) + AssertError(t, errs, "CustomMadeUpStruct.MadeUp", "MadeUp", "required") + AssertError(t, errs, "CustomMadeUpStruct.OverriddenInt", "OverriddenInt", "gt") +} + func TestSQLValueValidation(t *testing.T) { customTypes := map[reflect.Type]CustomTypeFunc{} - customTypes[reflect.TypeOf((*sql.Valuer)(nil))] = ValidateValuerType + customTypes[reflect.TypeOf((*driver.Valuer)(nil))] = ValidateValuerType customTypes[reflect.TypeOf(valuer{})] = ValidateValuerType customTypes[reflect.TypeOf(MadeUpCustomType{})] = ValidateCustomType customTypes[reflect.TypeOf(1)] = OverrideIntTypeForSomeReason