From 6484d9f2fbdaeea8397377e7abe94e894444d80a Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sun, 29 Sep 2019 14:12:27 -0700 Subject: [PATCH] fix required_with_* --- baked_in.go | 26 +++++++----------- validator_test.go | 68 ++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 71 insertions(+), 23 deletions(-) diff --git a/baked_in.go b/baked_in.go index a2660ef..95d613c 100644 --- a/baked_in.go +++ b/baked_in.go @@ -1314,19 +1314,19 @@ func hasValue(fl FieldLevel) bool { } // requireCheckField is a func for check field kind -func requireCheckFieldKind(fl FieldLevel, param string) bool { +func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue bool) bool { field := fl.Field() var ok bool kind := field.Kind() if len(param) > 0 { field, kind, ok = fl.GetStructFieldOKAdvanced(fl.Parent(), param) if !ok { - return true + return defaultNotFoundValue } } switch kind { case reflect.Invalid: - return true + return defaultNotFoundValue case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func: return !field.IsNil() default: @@ -1339,8 +1339,8 @@ func requireCheckFieldKind(fl FieldLevel, param string) bool { func requiredWith(fl FieldLevel) bool { params := parseOneOfParam2(fl.Param()) for _, param := range params { - if requireCheckFieldKind(fl, param) { - return requireCheckFieldKind(fl, "") + if requireCheckFieldKind(fl, param, false) { + return hasValue(fl) } } return true @@ -1349,19 +1349,13 @@ func requiredWith(fl FieldLevel) bool { // RequiredWithAll is the validation function // The field under validation must be present and not empty only if all of the other specified fields are present. func requiredWithAll(fl FieldLevel) bool { - isValidateCurrentField := true params := parseOneOfParam2(fl.Param()) for _, param := range params { - - if !requireCheckFieldKind(fl, param) { - isValidateCurrentField = false - break + if !requireCheckFieldKind(fl, param, false) { + return true } } - if isValidateCurrentField { - return requireCheckFieldKind(fl, "") - } - return true + return hasValue(fl) } // RequiredWithout is the validation function @@ -1369,7 +1363,7 @@ func requiredWithAll(fl FieldLevel) bool { func requiredWithout(fl FieldLevel) bool { params := parseOneOfParam2(fl.Param()) for _, param := range params { - if !requireCheckFieldKind(fl, param) { + if !requireCheckFieldKind(fl, param, true) { return hasValue(fl) } } @@ -1381,7 +1375,7 @@ func requiredWithout(fl FieldLevel) bool { func requiredWithoutAll(fl FieldLevel) bool { params := parseOneOfParam2(fl.Param()) for _, param := range params { - if requireCheckFieldKind(fl, param) { + if requireCheckFieldKind(fl, param, true) { return true } } diff --git a/validator_test.go b/validator_test.go index 4844537..aab64ac 100644 --- a/validator_test.go +++ b/validator_test.go @@ -8621,17 +8621,22 @@ func TestEndsWithValidation(t *testing.T) { } func TestRequiredWith(t *testing.T) { + type Inner struct { + Field *string + } + fieldVal := "test" test := struct { + Inner *Inner FieldE string `validate:"omitempty" json:"field_e"` FieldER string `validate:"required_with=FieldE" json:"field_er"` Field1 string `validate:"omitempty" json:"field_1"` Field2 *string `validate:"required_with=Field1" json:"field_2"` Field3 map[string]string `validate:"required_with=Field2" json:"field_3"` Field4 interface{} `validate:"required_with=Field3" json:"field_4"` - Field5 string `validate:"required_with=Field3" json:"field_5"` + Field5 string `validate:"required_with=Inner.Field" json:"field_5"` }{ - Field1: "test_field1", + Inner: &Inner{Field: &fieldVal}, Field2: &fieldVal, Field3: map[string]string{"key": "val"}, Field4: "test", @@ -8641,24 +8646,52 @@ func TestRequiredWith(t *testing.T) { validate := New() errs := validate.Struct(test) + Equal(t, errs, nil) - if errs != nil { - t.Fatalf("failed Error: %s", errs) + test2 := struct { + Inner *Inner + Inner2 *Inner + FieldE string `validate:"omitempty" json:"field_e"` + FieldER string `validate:"required_with=FieldE" json:"field_er"` + Field1 string `validate:"omitempty" json:"field_1"` + Field2 *string `validate:"required_with=Field1" json:"field_2"` + Field3 map[string]string `validate:"required_with=Field2" json:"field_3"` + Field4 interface{} `validate:"required_with=Field2" json:"field_4"` + Field5 string `validate:"required_with=Field3" json:"field_5"` + Field6 string `validate:"required_with=Inner.Field" json:"field_6"` + Field7 string `validate:"required_with=Inner2.Field" json:"field_7"` + }{ + Inner: &Inner{Field: &fieldVal}, + Field2: &fieldVal, } + + errs = validate.Struct(test2) + NotEqual(t, errs, nil) + + ve := errs.(ValidationErrors) + Equal(t, len(ve), 3) + AssertError(t, errs, "Field3", "Field3", "Field3", "Field3", "required_with") + AssertError(t, errs, "Field4", "Field4", "Field4", "Field4", "required_with") + AssertError(t, errs, "Field6", "Field6", "Field6", "Field6", "required_with") } func TestRequiredWithAll(t *testing.T) { + type Inner struct { + Field *string + } fieldVal := "test" test := struct { + Inner *Inner FieldE string `validate:"omitempty" json:"field_e"` FieldER string `validate:"required_with_all=FieldE" json:"field_er"` Field1 string `validate:"omitempty" json:"field_1"` Field2 *string `validate:"required_with_all=Field1" json:"field_2"` Field3 map[string]string `validate:"required_with_all=Field2" json:"field_3"` Field4 interface{} `validate:"required_with_all=Field3" json:"field_4"` - Field5 string `validate:"required_with_all=Field3" json:"field_5"` + Field5 string `validate:"required_with_all=Inner.Field" json:"field_5"` }{ + Inner: &Inner{Field: &fieldVal}, Field1: "test_field1", Field2: &fieldVal, Field3: map[string]string{"key": "val"}, @@ -8669,10 +8702,31 @@ func TestRequiredWithAll(t *testing.T) { validate := New() errs := validate.Struct(test) + Equal(t, errs, nil) - if errs != nil { - t.Fatalf("failed Error: %s", errs) + test2 := struct { + Inner *Inner + Inner2 *Inner + FieldE string `validate:"omitempty" json:"field_e"` + FieldER string `validate:"required_with_all=FieldE" json:"field_er"` + Field1 string `validate:"omitempty" json:"field_1"` + Field2 *string `validate:"required_with_all=Field1" json:"field_2"` + Field3 map[string]string `validate:"required_with_all=Field2" json:"field_3"` + Field4 interface{} `validate:"required_with_all=Field1 FieldE" json:"field_4"` + Field5 string `validate:"required_with_all=Inner.Field Field2" json:"field_5"` + Field6 string `validate:"required_with_all=Inner2.Field Field2" json:"field_6"` + }{ + Inner: &Inner{Field: &fieldVal}, + Field2: &fieldVal, } + + errs = validate.Struct(test2) + NotEqual(t, errs, nil) + + ve := errs.(ValidationErrors) + Equal(t, len(ve), 2) + AssertError(t, errs, "Field3", "Field3", "Field3", "Field3", "required_with_all") + AssertError(t, errs, "Field5", "Field5", "Field5", "Field5", "required_with_all") } func TestRequiredWithout(t *testing.T) {