From b891b1cf3cd763bbd7e8e7137d8e8e8fb04d2792 Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Fri, 8 May 2015 11:59:48 -0400 Subject: [PATCH 1/4] add eq baked in function + tests add eqfield baked in function + tests for issue #35 --- baked_in.go | 115 +++++++++++++++++++++++++++++++++++++++++++++ doc.go | 12 +++++ validator.go | 2 +- validator_test.go | 116 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 244 insertions(+), 1 deletion(-) diff --git a/baked_in.go b/baked_in.go index b733fd0..767f280 100644 --- a/baked_in.go +++ b/baked_in.go @@ -16,10 +16,12 @@ var BakedInValidators = map[string]Func{ "len": hasLengthOf, "min": hasMinOf, "max": hasMaxOf, + "eq": isEq, "lt": isLt, "lte": isLte, "gt": isGt, "gte": isGte, + "eqfield": isEqField, "gtefield": isGteField, "gtfield": isGtField, "ltefield": isLteField, @@ -40,6 +42,119 @@ var BakedInValidators = map[string]Func{ "base64": isBase64, } +func isEqField(top interface{}, current interface{}, field interface{}, param string) bool { + + if current == nil { + panic("struct not passed for cross validation") + } + + currentVal := reflect.ValueOf(current) + + if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() { + currentVal = reflect.ValueOf(currentVal.Elem().Interface()) + } + + var currentFielVal reflect.Value + + switch currentVal.Kind() { + + case reflect.Struct: + + if currentVal.Type() == reflect.TypeOf(time.Time{}) { + currentFielVal = currentVal + break + } + + f := currentVal.FieldByName(param) + + if f.Kind() == reflect.Invalid { + panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) + } + + currentFielVal = f + + default: + + currentFielVal = currentVal + } + + if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() { + + currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface()) + } + + fv := reflect.ValueOf(field) + + switch fv.Kind() { + + case reflect.String: + return fv.String() == currentFielVal.String() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + return fv.Int() == currentFielVal.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + + return fv.Uint() == currentFielVal.Uint() + + case reflect.Float32, reflect.Float64: + + return fv.Float() == currentFielVal.Float() + case reflect.Slice, reflect.Map, reflect.Array: + + return int64(fv.Len()) == int64(currentFielVal.Len()) + case reflect.Struct: + + if fv.Type() == reflect.TypeOf(time.Time{}) { + + if currentFielVal.Type() != reflect.TypeOf(time.Time{}) { + panic("Bad Top Level field type") + } + + t := currentFielVal.Interface().(time.Time) + fieldTime := field.(time.Time) + + return fieldTime.Equal(t) + } + } + + panic(fmt.Sprintf("Bad field type %T", field)) +} + +func isEq(top interface{}, current interface{}, field interface{}, param string) bool { + + st := reflect.ValueOf(field) + + switch st.Kind() { + + case reflect.String: + + return st.String() == param + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(st.Len()) == p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) + + return st.Int() == p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return st.Uint() == p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return st.Float() == p + } + + panic(fmt.Sprintf("Bad field type %T", field)) +} + func isBase64(top interface{}, current interface{}, field interface{}, param string) bool { return matchesRegex(base64Regex, field) } diff --git a/doc.go b/doc.go index 62b3eb9..d4ddf1b 100644 --- a/doc.go +++ b/doc.go @@ -192,6 +192,11 @@ Here is a list of the current built in validators: the string length is at least that number of characters. For slices, arrays, and maps, validates the number of items. (Usage: min=10) + eq + For strings & numbers, eq will ensure that the value is + equal to the parameter given. For slices, arrays, and maps, + validates the number of items. (Usage: eq=10) + gt For numbers, this will ensure that the value is greater than the parameter given. For strings, it checks that the string length @@ -221,6 +226,13 @@ Here is a list of the current built in validators: For time.Time ensures the time value is less than or equal to time.Now.UTC() (Usage: lte) + eqfield + This will validate the field value against another fields value either within + a struct or passed in field. + usage examples are for validation of a password and confirm password: + Validation on Password field using validate.Struct Usage(eqfield=ConfirmPassword) + Validating by field validate.FieldWithValue(password, confirmpassword, "eqfield") + gtfield Only valid for Numbers and time.Time types, this will validate the field value against another fields value either within a struct or passed in field. diff --git a/validator.go b/validator.go index 6350797..e787ffa 100644 --- a/validator.go +++ b/validator.go @@ -200,7 +200,7 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter } // if no validation and not a struct (which may containt fields for validation) - if tag == "" && valueField.Kind() != reflect.Struct && valueField.Kind() != reflect.Interface { + if tag == "" && ((valueField.Kind() != reflect.Struct && valueField.Kind() != reflect.Interface) || valueField.Type() == reflect.TypeOf(time.Time{})) { continue } diff --git a/validator_test.go b/validator_test.go index 55c5cb6..efdf469 100644 --- a/validator_test.go +++ b/validator_test.go @@ -142,6 +142,122 @@ func isEqualFunc(val interface{}, current interface{}, field interface{}, param return current.(string) == field.(string) } +func (ms *MySuite) TestIsEqFieldValidation(c *C) { + + var j uint64 + var k float64 + s := "abcd" + i := 1 + j = 1 + k = 1.543 + arr := []string{"test"} + now := time.Now().UTC() + + var j2 uint64 + var k2 float64 + s2 := "abcd" + i2 := 1 + j2 = 1 + k2 = 1.543 + arr2 := []string{"test"} + arr3 := []string{"test", "test2"} + now2 := now + + err := validate.FieldWithValue(s, s2, "eqfield") + c.Assert(err, IsNil) + + err = validate.FieldWithValue(i2, i, "eqfield") + c.Assert(err, IsNil) + + err = validate.FieldWithValue(j2, j, "eqfield") + c.Assert(err, IsNil) + + err = validate.FieldWithValue(k2, k, "eqfield") + c.Assert(err, IsNil) + + err = validate.FieldWithValue(arr2, arr, "eqfield") + c.Assert(err, IsNil) + + err = validate.FieldWithValue(now2, now, "eqfield") + c.Assert(err, IsNil) + + err = validate.FieldWithValue(arr3, arr, "eqfield") + c.Assert(err, NotNil) + + type Test struct { + Start *time.Time `validate:"eqfield=End"` + End *time.Time + } + + sv := &Test{ + Start: &now, + End: &now, + } + + errs := validate.Struct(sv) + c.Assert(errs, IsNil) + + now3 := time.Now().UTC() + + sv = &Test{ + Start: &now, + End: &now3, + } + + errs = validate.Struct(sv) + c.Assert(errs, NotNil) + + channel := make(chan string) + + c.Assert(func() { validate.FieldWithValue(nil, 1, "eqfield") }, PanicMatches, "struct not passed for cross validation") + c.Assert(func() { validate.FieldWithValue(5, channel, "eqfield") }, PanicMatches, "Bad field type chan string") + c.Assert(func() { validate.FieldWithValue(5, now, "eqfield") }, PanicMatches, "Bad Top Level field type") + + type Test2 struct { + Start *time.Time `validate:"eqfield=NonExistantField"` + End *time.Time + } + + sv2 := &Test2{ + Start: &now, + End: &now, + } + + c.Assert(func() { validate.Struct(sv2) }, PanicMatches, "Field \"NonExistantField\" not found in struct") +} + +func (ms *MySuite) TestIsEqValidation(c *C) { + + var j uint64 + var k float64 + s := "abcd" + i := 1 + j = 1 + k = 1.543 + arr := []string{"test"} + now := time.Now().UTC() + + err := validate.Field(s, "eq=abcd") + c.Assert(err, IsNil) + + err = validate.Field(i, "eq=1") + c.Assert(err, IsNil) + + err = validate.Field(j, "eq=1") + c.Assert(err, IsNil) + + err = validate.Field(k, "eq=1.543") + c.Assert(err, IsNil) + + err = validate.Field(arr, "eq=1") + c.Assert(err, IsNil) + + err = validate.Field(arr, "eq=2") + c.Assert(err, NotNil) + + c.Assert(func() { validate.Field(now, "eq=now") }, PanicMatches, "Bad field type time.Time") +} + func (ms *MySuite) TestBase64Validation(c *C) { s := "dW5pY29ybg==" From 74d0e52ab309b2269bea82c852c828b6b5345a54 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Fri, 8 May 2015 12:10:16 -0400 Subject: [PATCH 2/4] Update README.md update branch url for travis ci --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 95c6b65..4c1991a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ Package validator ================ -[![Build Status](https://travis-ci.org/bluesuncorp/validator.svg?branch=v5.0.2)](https://travis-ci.org/bluesuncorp/validator) +[![Build Status](https://travis-ci.org/bluesuncorp/validator.svg?branch=v5.1)](https://travis-ci.org/bluesuncorp/validator) [![GoDoc](https://godoc.org/gopkg.in/bluesuncorp/validator.v5?status.svg)](https://godoc.org/gopkg.in/bluesuncorp/validator.v5) Package validator implements value validations for structs and individual fields based on tags. From 23c3fa6b63cf4e1b4a8afb9f4027072175880f50 Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Tue, 19 May 2015 15:20:34 -0400 Subject: [PATCH 3/4] add ne and nefield functions to validator --- baked_in.go | 10 ++++ validator_test.go | 116 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) diff --git a/baked_in.go b/baked_in.go index 767f280..8f3558e 100644 --- a/baked_in.go +++ b/baked_in.go @@ -17,11 +17,13 @@ var BakedInValidators = map[string]Func{ "min": hasMinOf, "max": hasMaxOf, "eq": isEq, + "ne": isNe, "lt": isLt, "lte": isLte, "gt": isGt, "gte": isGte, "eqfield": isEqField, + "nefield": isNeField, "gtefield": isGteField, "gtfield": isGtField, "ltefield": isLteField, @@ -42,6 +44,14 @@ var BakedInValidators = map[string]Func{ "base64": isBase64, } +func isNeField(top interface{}, current interface{}, field interface{}, param string) bool { + return !isEqField(top, current, field, param) +} + +func isNe(top interface{}, current interface{}, field interface{}, param string) bool { + return !isEq(top, current, field, param) +} + func isEqField(top interface{}, current interface{}, field interface{}, param string) bool { if current == nil { diff --git a/validator_test.go b/validator_test.go index efdf469..6544fd0 100644 --- a/validator_test.go +++ b/validator_test.go @@ -142,6 +142,122 @@ func isEqualFunc(val interface{}, current interface{}, field interface{}, param return current.(string) == field.(string) } +func (ms *MySuite) TestIsNeFieldValidation(c *C) { + + var j uint64 + var k float64 + s := "abcd" + i := 1 + j = 1 + k = 1.543 + arr := []string{"test"} + now := time.Now().UTC() + + var j2 uint64 + var k2 float64 + s2 := "abcdef" + i2 := 3 + j2 = 2 + k2 = 1.5434456 + arr2 := []string{"test", "test2"} + arr3 := []string{"test"} + now2 := now + + err := validate.FieldWithValue(s, s2, "nefield") + c.Assert(err, IsNil) + + err = validate.FieldWithValue(i2, i, "nefield") + c.Assert(err, IsNil) + + err = validate.FieldWithValue(j2, j, "nefield") + c.Assert(err, IsNil) + + err = validate.FieldWithValue(k2, k, "nefield") + c.Assert(err, IsNil) + + err = validate.FieldWithValue(arr2, arr, "nefield") + c.Assert(err, IsNil) + + err = validate.FieldWithValue(now2, now, "nefield") + c.Assert(err, NotNil) + + err = validate.FieldWithValue(arr3, arr, "nefield") + c.Assert(err, NotNil) + + type Test struct { + Start *time.Time `validate:"nefield=End"` + End *time.Time + } + + sv := &Test{ + Start: &now, + End: &now, + } + + errs := validate.Struct(sv) + c.Assert(errs, NotNil) + + now3 := time.Now().UTC() + + sv = &Test{ + Start: &now, + End: &now3, + } + + errs = validate.Struct(sv) + c.Assert(errs, IsNil) + + channel := make(chan string) + + c.Assert(func() { validate.FieldWithValue(nil, 1, "nefield") }, PanicMatches, "struct not passed for cross validation") + c.Assert(func() { validate.FieldWithValue(5, channel, "nefield") }, PanicMatches, "Bad field type chan string") + c.Assert(func() { validate.FieldWithValue(5, now, "nefield") }, PanicMatches, "Bad Top Level field type") + + type Test2 struct { + Start *time.Time `validate:"nefield=NonExistantField"` + End *time.Time + } + + sv2 := &Test2{ + Start: &now, + End: &now, + } + + c.Assert(func() { validate.Struct(sv2) }, PanicMatches, "Field \"NonExistantField\" not found in struct") +} + +func (ms *MySuite) TestIsNeValidation(c *C) { + + var j uint64 + var k float64 + s := "abcdef" + i := 3 + j = 2 + k = 1.5434 + arr := []string{"test"} + now := time.Now().UTC() + + err := validate.Field(s, "ne=abcd") + c.Assert(err, IsNil) + + err = validate.Field(i, "ne=1") + c.Assert(err, IsNil) + + err = validate.Field(j, "ne=1") + c.Assert(err, IsNil) + + err = validate.Field(k, "ne=1.543") + c.Assert(err, IsNil) + + err = validate.Field(arr, "ne=2") + c.Assert(err, IsNil) + + err = validate.Field(arr, "ne=1") + c.Assert(err, NotNil) + + c.Assert(func() { validate.Field(now, "ne=now") }, PanicMatches, "Bad field type time.Time") +} + func (ms *MySuite) TestIsEqFieldValidation(c *C) { var j uint64 From f7f779fb3158481370b44899c92956aa8976ea97 Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Tue, 19 May 2015 15:29:26 -0400 Subject: [PATCH 4/4] add documentation for ne and nefield validation function #38 --- doc.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc.go b/doc.go index d4ddf1b..dfbece5 100644 --- a/doc.go +++ b/doc.go @@ -197,6 +197,11 @@ Here is a list of the current built in validators: equal to the parameter given. For slices, arrays, and maps, validates the number of items. (Usage: eq=10) + ne + For strings & numbers, eq will ensure that the value is not + equal to the parameter given. For slices, arrays, and maps, + validates the number of items. (Usage: eq=10) + gt For numbers, this will ensure that the value is greater than the parameter given. For strings, it checks that the string length @@ -233,6 +238,13 @@ Here is a list of the current built in validators: Validation on Password field using validate.Struct Usage(eqfield=ConfirmPassword) Validating by field validate.FieldWithValue(password, confirmpassword, "eqfield") + nefield + This will validate the field value against another fields value either within + a struct or passed in field. + usage examples are for ensuring two colors are not the same: + Validation on Color field using validate.Struct Usage(nefield=Color2) + Validating by field validate.FieldWithValue(color1, color2, "nefield") + gtfield Only valid for Numbers and time.Time types, this will validate the field value against another fields value either within a struct or passed in field.