Merge pull request #544 from go-playground/backport-required-with-fixes

fix required_*
v9
Dean Karn 5 years ago committed by GitHub
commit 6963d3cd8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 24
      baked_in.go
  2. 37
      field_level.go
  3. 7
      util.go
  4. 2
      validator.go
  5. 104
      validator_test.go

@ -1316,11 +1316,11 @@ func hasValue(fl FieldLevel) bool {
// requireCheckField is a func for check field kind // requireCheckField is a func for check field kind
func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue bool) bool { func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue bool) bool {
field := fl.Field() field := fl.Field()
var ok bool
kind := field.Kind() kind := field.Kind()
var nullable, found bool
if len(param) > 0 { if len(param) > 0 {
field, kind, ok = fl.GetStructFieldOKAdvanced(fl.Parent(), param) field, kind, nullable, found = fl.GetStructFieldOKAdvanced2(fl.Parent(), param)
if !ok { if !found {
return defaultNotFoundValue return defaultNotFoundValue
} }
} }
@ -1328,9 +1328,12 @@ func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue boo
case reflect.Invalid: case reflect.Invalid:
return defaultNotFoundValue return defaultNotFoundValue
case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func: case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
return !field.IsNil() return field.IsNil()
default: default:
return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface() if nullable && field.Interface() != nil {
return false
}
return field.IsValid() && field.Interface() == reflect.Zero(field.Type()).Interface()
} }
} }
@ -1339,7 +1342,7 @@ func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue boo
func requiredWith(fl FieldLevel) bool { func requiredWith(fl FieldLevel) bool {
params := parseOneOfParam2(fl.Param()) params := parseOneOfParam2(fl.Param())
for _, param := range params { for _, param := range params {
if requireCheckFieldKind(fl, param, false) { if !requireCheckFieldKind(fl, param, true) {
return hasValue(fl) return hasValue(fl)
} }
} }
@ -1351,7 +1354,7 @@ func requiredWith(fl FieldLevel) bool {
func requiredWithAll(fl FieldLevel) bool { func requiredWithAll(fl FieldLevel) bool {
params := parseOneOfParam2(fl.Param()) params := parseOneOfParam2(fl.Param())
for _, param := range params { for _, param := range params {
if !requireCheckFieldKind(fl, param, false) { if requireCheckFieldKind(fl, param, true) {
return true return true
} }
} }
@ -1361,12 +1364,9 @@ func requiredWithAll(fl FieldLevel) bool {
// RequiredWithout is the validation function // RequiredWithout is the validation function
// The field under validation must be present and not empty only when any of the other specified fields are not present. // The field under validation must be present and not empty only when any of the other specified fields are not present.
func requiredWithout(fl FieldLevel) bool { func requiredWithout(fl FieldLevel) bool {
params := parseOneOfParam2(fl.Param()) if requireCheckFieldKind(fl, strings.TrimSpace(fl.Param()), true) {
for _, param := range params {
if !requireCheckFieldKind(fl, param, true) {
return hasValue(fl) return hasValue(fl)
} }
}
return true return true
} }
@ -1375,7 +1375,7 @@ func requiredWithout(fl FieldLevel) bool {
func requiredWithoutAll(fl FieldLevel) bool { func requiredWithoutAll(fl FieldLevel) bool {
params := parseOneOfParam2(fl.Param()) params := parseOneOfParam2(fl.Param())
for _, param := range params { for _, param := range params {
if requireCheckFieldKind(fl, param, true) { if !requireCheckFieldKind(fl, param, true) {
return true return true
} }
} }

@ -37,11 +37,27 @@ type FieldLevel interface {
// //
// NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field // NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
// could not be retrieved because it didn't exist. // could not be retrieved because it didn't exist.
//
// Deprecated: Use GetStructFieldOK2() instead which also return if the value is nullable.
GetStructFieldOK() (reflect.Value, reflect.Kind, bool) GetStructFieldOK() (reflect.Value, reflect.Kind, bool)
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for // GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
// the field and namespace allowing more extensibility for validators. // the field and namespace allowing more extensibility for validators.
//
// Deprecated: Use GetStructFieldOKAdvanced2() instead which also return if the value is nullable.
GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool) GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool)
// traverses the parent struct to retrieve a specific field denoted by the provided namespace
// in the param and returns the field, field kind, if it's a nullable type and whether is was successful in retrieving
// the field at all.
//
// NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
// could not be retrieved because it didn't exist.
GetStructFieldOK2() (reflect.Value, reflect.Kind, bool, bool)
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
// the field and namespace allowing more extensibility for validators.
GetStructFieldOKAdvanced2(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool, bool)
} }
var _ FieldLevel = new(validate) var _ FieldLevel = new(validate)
@ -52,7 +68,7 @@ func (v *validate) Field() reflect.Value {
} }
// FieldName returns the field's name with the tag // FieldName returns the field's name with the tag
// name takeing precedence over the fields actual name. // name taking precedence over the fields actual name.
func (v *validate) FieldName() string { func (v *validate) FieldName() string {
return v.cf.altName return v.cf.altName
} }
@ -68,12 +84,29 @@ func (v *validate) Param() string {
} }
// GetStructFieldOK returns Param returns param for validation against current field // GetStructFieldOK returns Param returns param for validation against current field
//
// Deprecated: Use GetStructFieldOK2() instead which also return if the value is nullable.
func (v *validate) GetStructFieldOK() (reflect.Value, reflect.Kind, bool) { func (v *validate) GetStructFieldOK() (reflect.Value, reflect.Kind, bool) {
return v.getStructFieldOKInternal(v.slflParent, v.ct.param) current, kind, _, found := v.getStructFieldOKInternal(v.slflParent, v.ct.param)
return current, kind, found
} }
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for // GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
// the field and namespace allowing more extensibility for validators. // the field and namespace allowing more extensibility for validators.
//
// Deprecated: Use GetStructFieldOKAdvanced2() instead which also return if the value is nullable.
func (v *validate) GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool) { func (v *validate) GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool) {
current, kind, _, found := v.GetStructFieldOKAdvanced2(val, namespace)
return current, kind, found
}
// GetStructFieldOK returns Param returns param for validation against current field
func (v *validate) GetStructFieldOK2() (reflect.Value, reflect.Kind, bool, bool) {
return v.getStructFieldOKInternal(v.slflParent, v.ct.param)
}
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
// the field and namespace allowing more extensibility for validators.
func (v *validate) GetStructFieldOKAdvanced2(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool, bool) {
return v.getStructFieldOKInternal(val, namespace) return v.getStructFieldOKInternal(val, namespace)
} }

@ -57,11 +57,10 @@ BEGIN:
// //
// NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field // NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
// could not be retrieved because it didn't exist. // could not be retrieved because it didn't exist.
func (v *validate) getStructFieldOKInternal(val reflect.Value, namespace string) (current reflect.Value, kind reflect.Kind, found bool) { func (v *validate) getStructFieldOKInternal(val reflect.Value, namespace string) (current reflect.Value, kind reflect.Kind, nullable bool, found bool) {
BEGIN: BEGIN:
current, kind, _ = v.ExtractType(val) current, kind, nullable = v.ExtractType(val)
if kind == reflect.Invalid { if kind == reflect.Invalid {
return return
} }
@ -112,7 +111,7 @@ BEGIN:
arrIdx, _ := strconv.Atoi(namespace[idx+1 : idx2]) arrIdx, _ := strconv.Atoi(namespace[idx+1 : idx2])
if arrIdx >= current.Len() { if arrIdx >= current.Len() {
return current, kind, false return
} }
startIdx := idx2 + 1 startIdx := idx2 + 1

@ -7,7 +7,7 @@ import (
"strconv" "strconv"
) )
// per validate contruct // per validate construct
type validate struct { type validate struct {
v *Validate v *Validate
top reflect.Value top reflect.Value

@ -1640,125 +1640,125 @@ func TestCrossNamespaceFieldValidation(t *testing.T) {
v: vd, v: vd,
} }
current, kind, ok := v.getStructFieldOKInternal(val, "Inner.CreatedAt") current, kind, _, ok := v.getStructFieldOKInternal(val, "Inner.CreatedAt")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.Struct) Equal(t, kind, reflect.Struct)
tm, ok := current.Interface().(time.Time) tm, ok := current.Interface().(time.Time)
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, tm, now) Equal(t, tm, now)
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.Slice[1]") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.Slice[1]")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.String) Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2") Equal(t, current.String(), "val2")
current, _, ok = v.getStructFieldOKInternal(val, "Inner.CrazyNonExistantField") current, _, _, ok = v.getStructFieldOKInternal(val, "Inner.CrazyNonExistantField")
Equal(t, ok, false) Equal(t, ok, false)
current, _, ok = v.getStructFieldOKInternal(val, "Inner.Slice[101]") current, _, _, ok = v.getStructFieldOKInternal(val, "Inner.Slice[101]")
Equal(t, ok, false) Equal(t, ok, false)
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.Map[key3]") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.Map[key3]")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.String) Equal(t, kind, reflect.String)
Equal(t, current.String(), "val3") Equal(t, current.String(), "val3")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapMap[key2][key2-1]") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapMap[key2][key2-1]")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.String) Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2") Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapStructs[key2].Name") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapStructs[key2].Name")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.String) Equal(t, kind, reflect.String)
Equal(t, current.String(), "name2") Equal(t, current.String(), "name2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapMapStruct[key3][key3-1].Name") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapMapStruct[key3][key3-1].Name")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.String) Equal(t, kind, reflect.String)
Equal(t, current.String(), "name3") Equal(t, current.String(), "name3")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.SliceSlice[2][0]") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.SliceSlice[2][0]")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.String) Equal(t, kind, reflect.String)
Equal(t, current.String(), "7") Equal(t, current.String(), "7")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.SliceSliceStruct[2][1].Name") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.SliceSliceStruct[2][1].Name")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.String) Equal(t, kind, reflect.String)
Equal(t, current.String(), "name8") Equal(t, current.String(), "name8")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.SliceMap[1][key5]") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.SliceMap[1][key5]")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.String) Equal(t, kind, reflect.String)
Equal(t, current.String(), "val5") Equal(t, current.String(), "val5")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapSlice[key3][2]") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapSlice[key3][2]")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.String) Equal(t, kind, reflect.String)
Equal(t, current.String(), "9") Equal(t, current.String(), "9")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapInt[2]") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapInt[2]")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.String) Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2") Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapInt8[2]") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapInt8[2]")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.String) Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2") Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapInt16[2]") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapInt16[2]")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.String) Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2") Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapInt32[2]") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapInt32[2]")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.String) Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2") Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapInt64[2]") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapInt64[2]")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.String) Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2") Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapUint[2]") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapUint[2]")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.String) Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2") Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapUint8[2]") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapUint8[2]")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.String) Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2") Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapUint16[2]") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapUint16[2]")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.String) Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2") Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapUint32[2]") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapUint32[2]")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.String) Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2") Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapUint64[2]") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapUint64[2]")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.String) Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2") Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapFloat32[3.03]") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapFloat32[3.03]")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.String) Equal(t, kind, reflect.String)
Equal(t, current.String(), "val3") Equal(t, current.String(), "val3")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapFloat64[2.02]") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapFloat64[2.02]")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.String) Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2") Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapBool[true]") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapBool[true]")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.String) Equal(t, kind, reflect.String)
Equal(t, current.String(), "val1") Equal(t, current.String(), "val1")
@ -1784,13 +1784,13 @@ func TestCrossNamespaceFieldValidation(t *testing.T) {
val = reflect.ValueOf(test) val = reflect.ValueOf(test)
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.SliceStructs[2]") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.SliceStructs[2]")
Equal(t, ok, true) Equal(t, ok, true)
Equal(t, kind, reflect.Ptr) Equal(t, kind, reflect.Ptr)
Equal(t, current.String(), "<*validator.SliceStruct Value>") Equal(t, current.String(), "<*validator.SliceStruct Value>")
Equal(t, current.IsNil(), true) Equal(t, current.IsNil(), true)
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.SliceStructs[2].Name") current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.SliceStructs[2].Name")
Equal(t, ok, false) Equal(t, ok, false)
Equal(t, kind, reflect.Ptr) Equal(t, kind, reflect.Ptr)
Equal(t, current.String(), "<*validator.SliceStruct Value>") Equal(t, current.String(), "<*validator.SliceStruct Value>")
@ -8864,3 +8864,53 @@ func TestAbilityToValidateNils(t *testing.T) {
errs = val.Struct(ts) errs = val.Struct(ts)
NotEqual(t, errs, nil) NotEqual(t, errs, nil)
} }
func TestRequiredWithoutPointers(t *testing.T) {
type Lookup struct {
FieldA *bool `json:"fieldA,omitempty" validate:"required_without=FieldB"`
FieldB *bool `json:"fieldB,omitempty" validate:"required_without=FieldA"`
}
b := true
lookup := Lookup{
FieldA: &b,
FieldB: nil,
}
val := New()
errs := val.Struct(lookup)
Equal(t, errs, nil)
b = false
lookup = Lookup{
FieldA: &b,
FieldB: nil,
}
errs = val.Struct(lookup)
Equal(t, errs, nil)
}
func TestRequiredWithoutAllPointers(t *testing.T) {
type Lookup struct {
FieldA *bool `json:"fieldA,omitempty" validate:"required_without_all=FieldB"`
FieldB *bool `json:"fieldB,omitempty" validate:"required_without_all=FieldA"`
}
b := true
lookup := Lookup{
FieldA: &b,
FieldB: nil,
}
val := New()
errs := val.Struct(lookup)
Equal(t, errs, nil)
b = false
lookup = Lookup{
FieldA: &b,
FieldB: nil,
}
errs = val.Struct(lookup)
Equal(t, errs, nil)
}

Loading…
Cancel
Save