field name updates

pull/256/head
joeybloggs 8 years ago
parent e0e1af6a61
commit 7e731afde9
  1. 22
      errors.go
  2. 14
      struct_level.go
  3. 40
      validator.go
  4. 116
      validator_test.go

@ -86,7 +86,7 @@ type FieldError interface {
// //
// NOTE: this field can be blank when validating a single primitive field // NOTE: this field can be blank when validating a single primitive field
// using validate.Field(...) as there is no way to extract it's name // using validate.Field(...) as there is no way to extract it's name
ActualNamespace() string StructNamespace() string
// returns the fields name with the tag name taking precedence over the // returns the fields name with the tag name taking precedence over the
// fields actual name. // fields actual name.
@ -95,11 +95,11 @@ type FieldError interface {
// see ActualField for comparison // see ActualField for comparison
Field() string Field() string
// returns the fields actual name. // returns the fields actual name from the struct, when able to determine.
// //
// eq. "FirstName" // eq. "FirstName"
// see Field for comparison // see Field for comparison
ActualField() string StructField() string
// returns the actual fields value in case needed for creating the error // returns the actual fields value in case needed for creating the error
// message // message
@ -131,9 +131,9 @@ type fieldError struct {
tag string tag string
actualTag string actualTag string
ns string ns string
actualNs string structNs string
field string field string
actualField string structField string
value interface{} value interface{}
param interface{} param interface{}
kind reflect.Kind kind reflect.Kind
@ -157,10 +157,10 @@ func (fe *fieldError) Namespace() string {
return fe.ns return fe.ns
} }
// ActualNamespace returns the namespace for the field error, with the fields // StructNamespace returns the namespace for the field error, with the fields
// actual name. // actual name.
func (fe *fieldError) ActualNamespace() string { func (fe *fieldError) StructNamespace() string {
return fe.actualNs return fe.structNs
} }
// Field returns the fields name with the tag name taking precedence over the // Field returns the fields name with the tag name taking precedence over the
@ -169,9 +169,9 @@ func (fe *fieldError) Field() string {
return fe.field return fe.field
} }
// ActualField returns the fields actual name. // returns the fields actual name from the struct, when able to determine.
func (fe *fieldError) ActualField() string { func (fe *fieldError) StructField() string {
return fe.actualField return fe.structField
} }
// Value returns the actual fields value in case needed for creating the error // Value returns the actual fields value in case needed for creating the error

@ -10,6 +10,8 @@ type StructLevelFunc func(sl StructLevel)
type StructLevel interface { type StructLevel interface {
// returns the main validation object, in case one want to call validations internally. // returns the main validation object, in case one want to call validations internally.
// this is so you don;t have to use anonymous functoins to get access to the validate
// instance.
Validator() *Validate Validator() *Validate
// returns the top level struct, if any // returns the top level struct, if any
@ -104,7 +106,7 @@ func (v *validate) ReportError(field interface{}, fieldName, altName, tag string
} }
ns := append(v.slNs, fieldName...) ns := append(v.slNs, fieldName...)
nsActual := append(v.slActualNs, altName...) nsActual := append(v.slStructNs, altName...)
switch kind { switch kind {
case reflect.Invalid: case reflect.Invalid:
@ -114,9 +116,9 @@ func (v *validate) ReportError(field interface{}, fieldName, altName, tag string
tag: tag, tag: tag,
actualTag: tag, actualTag: tag,
ns: string(ns), ns: string(ns),
actualNs: string(nsActual), structNs: string(nsActual),
field: fieldName, field: fieldName,
actualField: altName, structField: altName,
param: "", param: "",
kind: kind, kind: kind,
}, },
@ -129,9 +131,9 @@ func (v *validate) ReportError(field interface{}, fieldName, altName, tag string
tag: tag, tag: tag,
actualTag: tag, actualTag: tag,
ns: string(ns), ns: string(ns),
actualNs: string(nsActual), structNs: string(nsActual),
field: fieldName, field: fieldName,
actualField: altName, structField: altName,
value: fv.Interface(), value: fv.Interface(),
param: "", param: "",
kind: kind, kind: kind,
@ -152,7 +154,7 @@ func (v *validate) ReportValidationErrors(relativeNamespace, relativeActualNames
err = errs[i].(*fieldError) err = errs[i].(*fieldError)
err.ns = string(append(append(v.slNs, err.ns...), err.field...)) err.ns = string(append(append(v.slNs, err.ns...), err.field...))
err.actualNs = string(append(append(v.slActualNs, err.actualNs...), err.actualField...)) err.structNs = string(append(append(v.slStructNs, err.structNs...), err.structField...))
v.errs = append(v.errs, err) v.errs = append(v.errs, err)
} }

@ -25,13 +25,13 @@ type validate struct {
slflParent reflect.Value slflParent reflect.Value
slCurrent reflect.Value slCurrent reflect.Value
slNs []byte slNs []byte
slActualNs []byte slStructNs []byte
flField reflect.Value flField reflect.Value
flParam string flParam string
} }
// parent and current will be the same the first run of validateStruct // parent and current will be the same the first run of validateStruct
func (v *validate) validateStruct(parent reflect.Value, current reflect.Value, typ reflect.Type, ns []byte, actualNs []byte, ct *cTag) { func (v *validate) validateStruct(parent reflect.Value, current reflect.Value, typ reflect.Type, ns []byte, structNs []byte, ct *cTag) {
first := len(ns) == 0 first := len(ns) == 0
@ -45,8 +45,8 @@ func (v *validate) validateStruct(parent reflect.Value, current reflect.Value, t
ns = append(ns, cs.Name...) ns = append(ns, cs.Name...)
ns = append(ns, '.') ns = append(ns, '.')
actualNs = append(actualNs, cs.Name...) structNs = append(structNs, cs.Name...)
actualNs = append(actualNs, '.') structNs = append(structNs, '.')
} }
// ct is nil on top level struct, and structs as fields that have no tag info // ct is nil on top level struct, and structs as fields that have no tag info
@ -64,7 +64,7 @@ func (v *validate) validateStruct(parent reflect.Value, current reflect.Value, t
} }
} }
v.traverseField(parent, current.Field(f.Idx), ns, actualNs, f, f.cTags) v.traverseField(parent, current.Field(f.Idx), ns, structNs, f, f.cTags)
} }
} }
@ -76,14 +76,14 @@ func (v *validate) validateStruct(parent reflect.Value, current reflect.Value, t
v.slflParent = parent v.slflParent = parent
v.slCurrent = current v.slCurrent = current
v.slNs = ns v.slNs = ns
v.slActualNs = actualNs v.slStructNs = structNs
cs.fn(v) cs.fn(v)
} }
} }
// traverseField validates any field, be it a struct or single field, ensures it's validity and passes it along to be validated via it's tag options // traverseField validates any field, be it a struct or single field, ensures it's validity and passes it along to be validated via it's tag options
func (v *validate) traverseField(parent reflect.Value, current reflect.Value, ns []byte, actualNs []byte, cf *cField, ct *cTag) { func (v *validate) traverseField(parent reflect.Value, current reflect.Value, ns []byte, structNs []byte, cf *cField, ct *cTag) {
var typ reflect.Type var typ reflect.Type
var kind reflect.Kind var kind reflect.Kind
@ -111,9 +111,9 @@ func (v *validate) traverseField(parent reflect.Value, current reflect.Value, ns
tag: ct.aliasTag, tag: ct.aliasTag,
actualTag: ct.tag, actualTag: ct.tag,
ns: string(append(ns, cf.Name...)), ns: string(append(ns, cf.Name...)),
actualNs: string(append(actualNs, cf.AltName...)), structNs: string(append(structNs, cf.AltName...)),
field: cf.AltName, field: cf.AltName,
actualField: cf.Name, structField: cf.Name,
param: ct.param, param: ct.param,
kind: kind, kind: kind,
}, },
@ -127,9 +127,9 @@ func (v *validate) traverseField(parent reflect.Value, current reflect.Value, ns
tag: ct.aliasTag, tag: ct.aliasTag,
actualTag: ct.tag, actualTag: ct.tag,
ns: string(append(ns, cf.Name...)), ns: string(append(ns, cf.Name...)),
actualNs: string(append(actualNs, cf.AltName...)), structNs: string(append(structNs, cf.AltName...)),
field: cf.AltName, field: cf.AltName,
actualField: cf.Name, structField: cf.Name,
value: current.Interface(), value: current.Interface(),
param: ct.param, param: ct.param,
kind: kind, kind: kind,
@ -154,7 +154,7 @@ func (v *validate) traverseField(parent reflect.Value, current reflect.Value, ns
return return
} }
v.validateStruct(current, current, typ, append(append(ns, cf.Name...), '.'), append(append(actualNs, cf.AltName...), '.'), ct) v.validateStruct(current, current, typ, append(append(ns, cf.Name...), '.'), append(append(structNs, cf.AltName...), '.'), ct)
return return
} }
} }
@ -201,12 +201,12 @@ OUTER:
case reflect.Slice, reflect.Array: case reflect.Slice, reflect.Array:
for i := 0; i < current.Len(); i++ { for i := 0; i < current.Len(); i++ {
v.traverseField(parent, current.Index(i), ns, actualNs, &cField{Name: fmt.Sprintf(arrayIndexFieldName, cf.Name, i), AltName: fmt.Sprintf(arrayIndexFieldName, cf.AltName, i)}, ct) v.traverseField(parent, current.Index(i), ns, structNs, &cField{Name: fmt.Sprintf(arrayIndexFieldName, cf.Name, i), AltName: fmt.Sprintf(arrayIndexFieldName, cf.AltName, i)}, ct)
} }
case reflect.Map: case reflect.Map:
for _, key := range current.MapKeys() { for _, key := range current.MapKeys() {
v.traverseField(parent, current.MapIndex(key), ns, actualNs, &cField{Name: fmt.Sprintf(mapIndexFieldName, cf.Name, key.Interface()), AltName: fmt.Sprintf(mapIndexFieldName, cf.AltName, key.Interface())}, ct) v.traverseField(parent, current.MapIndex(key), ns, structNs, &cField{Name: fmt.Sprintf(mapIndexFieldName, cf.Name, key.Interface()), AltName: fmt.Sprintf(mapIndexFieldName, cf.AltName, key.Interface())}, ct)
} }
default: default:
@ -257,9 +257,9 @@ OUTER:
tag: ct.aliasTag, tag: ct.aliasTag,
actualTag: ct.actualAliasTag, actualTag: ct.actualAliasTag,
ns: string(append(ns, cf.Name...)), ns: string(append(ns, cf.Name...)),
actualNs: string(append(actualNs, cf.AltName...)), structNs: string(append(structNs, cf.AltName...)),
field: cf.AltName, field: cf.AltName,
actualField: cf.Name, structField: cf.Name,
value: current.Interface(), value: current.Interface(),
param: ct.param, param: ct.param,
kind: kind, kind: kind,
@ -274,9 +274,9 @@ OUTER:
tag: errTag[1:], tag: errTag[1:],
actualTag: errTag[1:], actualTag: errTag[1:],
ns: string(append(ns, cf.Name...)), ns: string(append(ns, cf.Name...)),
actualNs: string(append(actualNs, cf.AltName...)), structNs: string(append(structNs, cf.AltName...)),
field: cf.AltName, field: cf.AltName,
actualField: cf.Name, structField: cf.Name,
value: current.Interface(), value: current.Interface(),
param: ct.param, param: ct.param,
kind: kind, kind: kind,
@ -305,9 +305,9 @@ OUTER:
tag: ct.aliasTag, tag: ct.aliasTag,
actualTag: ct.tag, actualTag: ct.tag,
ns: string(append(ns, cf.Name...)), ns: string(append(ns, cf.Name...)),
actualNs: string(append(actualNs, cf.AltName...)), structNs: string(append(structNs, cf.AltName...)),
field: cf.AltName, field: cf.AltName,
actualField: cf.Name, structField: cf.Name,
value: current.Interface(), value: current.Interface(),
param: ct.param, param: ct.param,
kind: kind, kind: kind,

@ -2,7 +2,9 @@ package validator
import ( import (
"database/sql/driver" "database/sql/driver"
"fmt"
"reflect" "reflect"
"strings"
"testing" "testing"
. "gopkg.in/go-playground/assert.v1" . "gopkg.in/go-playground/assert.v1"
@ -96,7 +98,7 @@ type TestString struct {
// var validate = New(&Config{TagName: "validate"}) // var validate = New(&Config{TagName: "validate"})
func AssertError(t *testing.T, err error, key, field, expectedTag string) { func AssertError(t *testing.T, err error, key, field, actualField, expectedTag string) {
errs := err.(ValidationErrors) errs := err.(ValidationErrors)
@ -114,9 +116,26 @@ func AssertError(t *testing.T, err error, key, field, expectedTag string) {
EqualSkip(t, 2, found, true) EqualSkip(t, 2, found, true)
NotEqualSkip(t, 2, fe, nil) NotEqualSkip(t, 2, fe, nil)
EqualSkip(t, 2, fe.Field(), field) EqualSkip(t, 2, fe.Field(), field)
EqualSkip(t, 2, fe.StructField(), actualField)
EqualSkip(t, 2, fe.Tag(), expectedTag) EqualSkip(t, 2, fe.Tag(), expectedTag)
} }
func getError(err error, key string) FieldError {
errs := err.(ValidationErrors)
var fe FieldError
for i := 0; i < len(errs); i++ {
if errs[i].Namespace() == key {
fe = errs[i]
break
}
}
return fe
}
type valuer struct { type valuer struct {
Name string Name string
} }
@ -301,50 +320,59 @@ func StructValidationTestStruct(sl StructLevel) {
// Inner1 *TestStructReturnValidationErrorsInner1 `json:"Inner1JSON"` // Inner1 *TestStructReturnValidationErrorsInner1 `json:"Inner1JSON"`
// } // }
// type Inner2Namespace struct { func TestNameNamespace(t *testing.T) {
// String []string `validate:"dive,required" json:"JSONString"`
// }
// type Inner1Namespace struct { type Inner2Namespace struct {
// Inner2 *Inner2Namespace `json:"Inner2JSON"` String []string `validate:"dive,required" json:"JSONString"`
// } }
// type Namespace struct { type Inner1Namespace struct {
// Inner1 *Inner1Namespace `json:"Inner1JSON"` Inner2 *Inner2Namespace `json:"Inner2JSON"`
// } }
// func TestNameNamespace(t *testing.T) { type Namespace struct {
Inner1 *Inner1Namespace `json:"Inner1JSON"`
}
// config := &Config{ validate := New()
// TagName: "validate", validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
// FieldNameTag: "json", name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
// }
// v1 := New(config) if name == "-" {
// i2 := &Inner2Namespace{String: []string{"ok", "ok", "ok"}} return ""
// i1 := &Inner1Namespace{Inner2: i2} }
// ns := &Namespace{Inner1: i1}
// errs := v1.Struct(ns) return name
// Equal(t, errs, nil) })
// i2.String[1] = "" i2 := &Inner2Namespace{String: []string{"ok", "ok", "ok"}}
i1 := &Inner1Namespace{Inner2: i2}
ns := &Namespace{Inner1: i1}
// errs = v1.Struct(ns) errs := validate.Struct(ns)
// NotEqual(t, errs, nil) Equal(t, errs, nil)
// ve := errs.(ValidationErrors) i2.String[1] = ""
// Equal(t, len(ve), 1)
// AssertError(t, errs, "Namespace.Inner1.Inner2.String[1]", "String[1]", "required")
// fe, ok := ve["Namespace.Inner1.Inner2.String[1]"] errs = validate.Struct(ns)
// Equal(t, ok, true) NotEqual(t, errs, nil)
// Equal(t, fe.Field, "String[1]") ve := errs.(ValidationErrors)
// Equal(t, fe.FieldNamespace, "Namespace.Inner1.Inner2.String[1]") Equal(t, len(ve), 1)
// Equal(t, fe.Name, "JSONString[1]") AssertError(t, errs, "Namespace.Inner1.Inner2.String[1]", "JSONString[1]", "String[1]", "required")
// Equal(t, fe.NameNamespace, "Namespace.Inner1JSON.Inner2JSON.JSONString[1]")
// } fe := getError(ve, "Namespace.Inner1.Inner2.String[1]")
NotEqual(t, fe, nil)
Equal(t, fe.Field(), "JSONString[1]")
Equal(t, fe.StructField(), "String[1]")
fmt.Println(fe.Namespace())
fmt.Println(fe.StructNamespace())
Equal(t, fe.Namespace(), "Namespace.Inner1JSON.Inner2JSON.JSONString[1]")
Equal(t, fe.StructNamespace(), "Namespace.Inner1.Inner2.String[1]")
}
// func TestAnonymous(t *testing.T) { // func TestAnonymous(t *testing.T) {
@ -5667,16 +5695,16 @@ func TestStructInt32Validation(t *testing.T) {
Equal(t, len(errs.(ValidationErrors)), 10) Equal(t, len(errs.(ValidationErrors)), 10)
// Assert Fields // Assert Fields
AssertError(t, errs, "TestInt32.Required", "Required", "required") AssertError(t, errs, "TestInt32.Required", "Required", "Required", "required")
AssertError(t, errs, "TestInt32.Len", "Len", "len") AssertError(t, errs, "TestInt32.Len", "Len", "Len", "len")
AssertError(t, errs, "TestInt32.Min", "Min", "min") AssertError(t, errs, "TestInt32.Min", "Min", "Min", "min")
AssertError(t, errs, "TestInt32.Max", "Max", "max") AssertError(t, errs, "TestInt32.Max", "Max", "Max", "max")
AssertError(t, errs, "TestInt32.MinMax", "MinMax", "min") AssertError(t, errs, "TestInt32.MinMax", "MinMax", "MinMax", "min")
AssertError(t, errs, "TestInt32.Lt", "Lt", "lt") AssertError(t, errs, "TestInt32.Lt", "Lt", "Lt", "lt")
AssertError(t, errs, "TestInt32.Lte", "Lte", "lte") AssertError(t, errs, "TestInt32.Lte", "Lte", "Lte", "lte")
AssertError(t, errs, "TestInt32.Gt", "Gt", "gt") AssertError(t, errs, "TestInt32.Gt", "Gt", "Gt", "gt")
AssertError(t, errs, "TestInt32.Gte", "Gte", "gte") AssertError(t, errs, "TestInt32.Gte", "Gte", "Gte", "gte")
AssertError(t, errs, "TestInt32.OmitEmpty", "OmitEmpty", "max") AssertError(t, errs, "TestInt32.OmitEmpty", "OmitEmpty", "OmitEmpty", "max")
} }
// func TestStructUint64Validation(t *testing.T) { // func TestStructUint64Validation(t *testing.T) {

Loading…
Cancel
Save