Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 25 additions & 58 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand Down
48 changes: 48 additions & 0 deletions examples/custom/custom.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package main

import (
"database/sql"
"database/sql/driver"
"fmt"
"reflect"

"gopkg.in/bluesuncorp/validator.v6"
)

// DbBackedUser User struct
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
}
File renamed without changes.
14 changes: 14 additions & 0 deletions validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,20 @@ func (v *Validate) RegisterValidation(key string, f Func) error {
return nil
}

// 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 _, t := range types {
v.config.CustomTypeFuncs[reflect.TypeOf(t)] = fn
}

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
Expand Down
68 changes: 63 additions & 5 deletions validator_test.go
Original file line number Diff line number Diff line change
@@ -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"
)

Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down