From 7e731afde9bb6532ebbb297346f31d5ee6e5eec4 Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Wed, 3 Aug 2016 19:20:42 -0400 Subject: [PATCH] field name updates --- errors.go | 22 ++++----- struct_level.go | 14 +++--- validator.go | 40 ++++++++-------- validator_test.go | 116 ++++++++++++++++++++++++++++------------------ 4 files changed, 111 insertions(+), 81 deletions(-) diff --git a/errors.go b/errors.go index ce46333..87df6f6 100644 --- a/errors.go +++ b/errors.go @@ -86,7 +86,7 @@ type FieldError interface { // // 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 - ActualNamespace() string + StructNamespace() string // returns the fields name with the tag name taking precedence over the // fields actual name. @@ -95,11 +95,11 @@ type FieldError interface { // see ActualField for comparison Field() string - // returns the fields actual name. + // returns the fields actual name from the struct, when able to determine. // // eq. "FirstName" // see Field for comparison - ActualField() string + StructField() string // returns the actual fields value in case needed for creating the error // message @@ -131,9 +131,9 @@ type fieldError struct { tag string actualTag string ns string - actualNs string + structNs string field string - actualField string + structField string value interface{} param interface{} kind reflect.Kind @@ -157,10 +157,10 @@ func (fe *fieldError) Namespace() string { 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. -func (fe *fieldError) ActualNamespace() string { - return fe.actualNs +func (fe *fieldError) StructNamespace() string { + return fe.structNs } // 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 } -// ActualField returns the fields actual name. -func (fe *fieldError) ActualField() string { - return fe.actualField +// returns the fields actual name from the struct, when able to determine. +func (fe *fieldError) StructField() string { + return fe.structField } // Value returns the actual fields value in case needed for creating the error diff --git a/struct_level.go b/struct_level.go index 33bfd59..31c59bf 100644 --- a/struct_level.go +++ b/struct_level.go @@ -10,6 +10,8 @@ type StructLevelFunc func(sl StructLevel) type StructLevel interface { // 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 // 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...) - nsActual := append(v.slActualNs, altName...) + nsActual := append(v.slStructNs, altName...) switch kind { case reflect.Invalid: @@ -114,9 +116,9 @@ func (v *validate) ReportError(field interface{}, fieldName, altName, tag string tag: tag, actualTag: tag, ns: string(ns), - actualNs: string(nsActual), + structNs: string(nsActual), field: fieldName, - actualField: altName, + structField: altName, param: "", kind: kind, }, @@ -129,9 +131,9 @@ func (v *validate) ReportError(field interface{}, fieldName, altName, tag string tag: tag, actualTag: tag, ns: string(ns), - actualNs: string(nsActual), + structNs: string(nsActual), field: fieldName, - actualField: altName, + structField: altName, value: fv.Interface(), param: "", kind: kind, @@ -152,7 +154,7 @@ func (v *validate) ReportValidationErrors(relativeNamespace, relativeActualNames err = errs[i].(*fieldError) 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) } diff --git a/validator.go b/validator.go index dec2bd3..b97817e 100644 --- a/validator.go +++ b/validator.go @@ -25,13 +25,13 @@ type validate struct { slflParent reflect.Value slCurrent reflect.Value slNs []byte - slActualNs []byte + slStructNs []byte flField reflect.Value flParam string } // 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 @@ -45,8 +45,8 @@ func (v *validate) validateStruct(parent reflect.Value, current reflect.Value, t ns = append(ns, cs.Name...) ns = append(ns, '.') - actualNs = append(actualNs, cs.Name...) - actualNs = append(actualNs, '.') + structNs = append(structNs, cs.Name...) + structNs = append(structNs, '.') } // 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.slCurrent = current v.slNs = ns - v.slActualNs = actualNs + v.slStructNs = structNs 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 -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 kind reflect.Kind @@ -111,9 +111,9 @@ func (v *validate) traverseField(parent reflect.Value, current reflect.Value, ns tag: ct.aliasTag, actualTag: ct.tag, ns: string(append(ns, cf.Name...)), - actualNs: string(append(actualNs, cf.AltName...)), + structNs: string(append(structNs, cf.AltName...)), field: cf.AltName, - actualField: cf.Name, + structField: cf.Name, param: ct.param, kind: kind, }, @@ -127,9 +127,9 @@ func (v *validate) traverseField(parent reflect.Value, current reflect.Value, ns tag: ct.aliasTag, actualTag: ct.tag, ns: string(append(ns, cf.Name...)), - actualNs: string(append(actualNs, cf.AltName...)), + structNs: string(append(structNs, cf.AltName...)), field: cf.AltName, - actualField: cf.Name, + structField: cf.Name, value: current.Interface(), param: ct.param, kind: kind, @@ -154,7 +154,7 @@ func (v *validate) traverseField(parent reflect.Value, current reflect.Value, ns 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 } } @@ -201,12 +201,12 @@ OUTER: case reflect.Slice, reflect.Array: 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: 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: @@ -257,9 +257,9 @@ OUTER: tag: ct.aliasTag, actualTag: ct.actualAliasTag, ns: string(append(ns, cf.Name...)), - actualNs: string(append(actualNs, cf.AltName...)), + structNs: string(append(structNs, cf.AltName...)), field: cf.AltName, - actualField: cf.Name, + structField: cf.Name, value: current.Interface(), param: ct.param, kind: kind, @@ -274,9 +274,9 @@ OUTER: tag: errTag[1:], actualTag: errTag[1:], ns: string(append(ns, cf.Name...)), - actualNs: string(append(actualNs, cf.AltName...)), + structNs: string(append(structNs, cf.AltName...)), field: cf.AltName, - actualField: cf.Name, + structField: cf.Name, value: current.Interface(), param: ct.param, kind: kind, @@ -305,9 +305,9 @@ OUTER: tag: ct.aliasTag, actualTag: ct.tag, ns: string(append(ns, cf.Name...)), - actualNs: string(append(actualNs, cf.AltName...)), + structNs: string(append(structNs, cf.AltName...)), field: cf.AltName, - actualField: cf.Name, + structField: cf.Name, value: current.Interface(), param: ct.param, kind: kind, diff --git a/validator_test.go b/validator_test.go index 30b2fab..41ee070 100644 --- a/validator_test.go +++ b/validator_test.go @@ -2,7 +2,9 @@ package validator import ( "database/sql/driver" + "fmt" "reflect" + "strings" "testing" . "gopkg.in/go-playground/assert.v1" @@ -96,7 +98,7 @@ type TestString struct { // 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) @@ -114,9 +116,26 @@ func AssertError(t *testing.T, err error, key, field, expectedTag string) { EqualSkip(t, 2, found, true) NotEqualSkip(t, 2, fe, nil) EqualSkip(t, 2, fe.Field(), field) + EqualSkip(t, 2, fe.StructField(), actualField) 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 { Name string } @@ -301,50 +320,59 @@ func StructValidationTestStruct(sl StructLevel) { // Inner1 *TestStructReturnValidationErrorsInner1 `json:"Inner1JSON"` // } -// type Inner2Namespace struct { -// String []string `validate:"dive,required" json:"JSONString"` -// } +func TestNameNamespace(t *testing.T) { -// type Inner1Namespace struct { -// Inner2 *Inner2Namespace `json:"Inner2JSON"` -// } + type Inner2Namespace struct { + String []string `validate:"dive,required" json:"JSONString"` + } -// type Namespace struct { -// Inner1 *Inner1Namespace `json:"Inner1JSON"` -// } + type Inner1Namespace struct { + Inner2 *Inner2Namespace `json:"Inner2JSON"` + } -// func TestNameNamespace(t *testing.T) { + type Namespace struct { + Inner1 *Inner1Namespace `json:"Inner1JSON"` + } -// config := &Config{ -// TagName: "validate", -// FieldNameTag: "json", -// } + validate := New() + validate.RegisterTagNameFunc(func(fld reflect.StructField) string { + name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0] -// v1 := New(config) -// i2 := &Inner2Namespace{String: []string{"ok", "ok", "ok"}} -// i1 := &Inner1Namespace{Inner2: i2} -// ns := &Namespace{Inner1: i1} + if name == "-" { + return "" + } -// errs := v1.Struct(ns) -// Equal(t, errs, nil) + return name + }) -// i2.String[1] = "" + i2 := &Inner2Namespace{String: []string{"ok", "ok", "ok"}} + i1 := &Inner1Namespace{Inner2: i2} + ns := &Namespace{Inner1: i1} -// errs = v1.Struct(ns) -// NotEqual(t, errs, nil) + errs := validate.Struct(ns) + Equal(t, errs, nil) -// ve := errs.(ValidationErrors) -// Equal(t, len(ve), 1) -// AssertError(t, errs, "Namespace.Inner1.Inner2.String[1]", "String[1]", "required") + i2.String[1] = "" -// fe, ok := ve["Namespace.Inner1.Inner2.String[1]"] -// Equal(t, ok, true) + errs = validate.Struct(ns) + NotEqual(t, errs, nil) -// Equal(t, fe.Field, "String[1]") -// Equal(t, fe.FieldNamespace, "Namespace.Inner1.Inner2.String[1]") -// Equal(t, fe.Name, "JSONString[1]") -// Equal(t, fe.NameNamespace, "Namespace.Inner1JSON.Inner2JSON.JSONString[1]") -// } + ve := errs.(ValidationErrors) + Equal(t, len(ve), 1) + AssertError(t, errs, "Namespace.Inner1.Inner2.String[1]", "JSONString[1]", "String[1]", "required") + + 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) { @@ -5667,16 +5695,16 @@ func TestStructInt32Validation(t *testing.T) { Equal(t, len(errs.(ValidationErrors)), 10) // Assert Fields - AssertError(t, errs, "TestInt32.Required", "Required", "required") - AssertError(t, errs, "TestInt32.Len", "Len", "len") - AssertError(t, errs, "TestInt32.Min", "Min", "min") - AssertError(t, errs, "TestInt32.Max", "Max", "max") - AssertError(t, errs, "TestInt32.MinMax", "MinMax", "min") - AssertError(t, errs, "TestInt32.Lt", "Lt", "lt") - AssertError(t, errs, "TestInt32.Lte", "Lte", "lte") - AssertError(t, errs, "TestInt32.Gt", "Gt", "gt") - AssertError(t, errs, "TestInt32.Gte", "Gte", "gte") - AssertError(t, errs, "TestInt32.OmitEmpty", "OmitEmpty", "max") + AssertError(t, errs, "TestInt32.Required", "Required", "Required", "required") + AssertError(t, errs, "TestInt32.Len", "Len", "Len", "len") + AssertError(t, errs, "TestInt32.Min", "Min", "Min", "min") + AssertError(t, errs, "TestInt32.Max", "Max", "Max", "max") + AssertError(t, errs, "TestInt32.MinMax", "MinMax", "MinMax", "min") + AssertError(t, errs, "TestInt32.Lt", "Lt", "Lt", "lt") + AssertError(t, errs, "TestInt32.Lte", "Lte", "Lte", "lte") + AssertError(t, errs, "TestInt32.Gt", "Gt", "Gt", "gt") + AssertError(t, errs, "TestInt32.Gte", "Gte", "Gte", "gte") + AssertError(t, errs, "TestInt32.OmitEmpty", "OmitEmpty", "OmitEmpty", "max") } // func TestStructUint64Validation(t *testing.T) {