improve empty value validation for required-with* patterns for package gvalid

This commit is contained in:
jflyfox
2021-04-26 20:37:36 +08:00
parent c4fc9f9618
commit 524e3bedc7
5 changed files with 102 additions and 16 deletions

View File

@ -9,6 +9,7 @@ package empty
import (
"reflect"
"time"
)
// apiString is used for type assert api for String().
@ -26,6 +27,11 @@ type apiMapStrAny interface {
MapStrAny() map[string]interface{}
}
type apiTime interface {
Date() (year int, month time.Month, day int)
IsZero() bool
}
// IsEmpty checks whether given `value` empty.
// It returns true if `value` is in: 0, nil, false, "", len(slice/map/chan) == 0,
// or else it returns false.
@ -80,6 +86,12 @@ func IsEmpty(value interface{}) bool {
return len(value) == 0
default:
// Common interfaces checks.
if f, ok := value.(apiTime); ok {
if f == nil {
return true
}
return f.IsZero()
}
if f, ok := value.(apiString); ok {
if f == nil {
return true

View File

@ -220,6 +220,15 @@ func (t *Time) String() string {
return t.Format("Y-m-d H:i:s")
}
// IsZero reports whether t represents the zero time instant,
// January 1, year 1, 00:00:00 UTC.
func (t *Time) IsZero() bool {
if t == nil {
return true
}
return t.Time.IsZero()
}
// Clone returns a new Time object which is a clone of current time object.
func (t *Time) Clone() *Time {
return New(t.Time)

View File

@ -47,13 +47,11 @@ func (v *Validator) doCheck(key string, value interface{}, rules string, message
// It converts value to string and then does the validation.
var (
// Do not trim it as the space is also part of the value.
data = make(map[string]string)
data = make(map[string]interface{})
errorMsgArray = make(map[string]string)
)
if len(params) > 0 {
for k, v := range gconv.Map(params[0]) {
data[k] = gconv.String(v)
}
data = gconv.Map(params[0])
}
// Custom error messages handling.
var (
@ -150,7 +148,7 @@ func (v *Validator) doCheckBuildInRules(
ruleKey string,
rulePattern string,
ruleItems []string,
dataMap map[string]string,
dataMap map[string]interface{},
customMsgMap map[string]string,
) (match bool, err error) {
valueStr := gconv.String(value)
@ -235,7 +233,7 @@ func (v *Validator) doCheckBuildInRules(
// Values of two fields should be equal as string.
case "same":
if v, ok := dataMap[rulePattern]; ok {
if strings.Compare(valueStr, v) == 0 {
if strings.Compare(valueStr, gconv.String(v)) == 0 {
match = true
}
}
@ -250,7 +248,7 @@ func (v *Validator) doCheckBuildInRules(
case "different":
match = true
if v, ok := dataMap[rulePattern]; ok {
if strings.Compare(valueStr, v) == 0 {
if strings.Compare(valueStr, gconv.String(v)) == 0 {
match = false
}
}

View File

@ -7,6 +7,7 @@
package gvalid
import (
"github.com/gogf/gf/internal/empty"
"github.com/gogf/gf/util/gconv"
"reflect"
"strings"
@ -14,7 +15,7 @@ import (
// checkRequired checks `value` using required rules.
// It also supports require checks for `value` of type: slice, map.
func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string, params map[string]string) bool {
func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string, dataMap map[string]interface{}) bool {
required := false
switch ruleKey {
// Required.
@ -31,8 +32,8 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string
for i := 0; i < len(array); {
tk := array[i]
tv := array[i+1]
if v, ok := params[tk]; ok {
if strings.Compare(tv, v) == 0 {
if v, ok := dataMap[tk]; ok {
if strings.Compare(tv, gconv.String(v)) == 0 {
required = true
break
}
@ -51,8 +52,8 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string
for i := 0; i < len(array); {
tk := array[i]
tv := array[i+1]
if v, ok := params[tk]; ok {
if strings.Compare(tv, v) == 0 {
if v, ok := dataMap[tk]; ok {
if strings.Compare(tv, gconv.String(v)) == 0 {
required = false
break
}
@ -67,7 +68,7 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string
required = false
array := strings.Split(rulePattern, ",")
for i := 0; i < len(array); i++ {
if params[array[i]] != "" {
if !empty.IsEmpty(dataMap[array[i]]) {
required = true
break
}
@ -79,7 +80,7 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string
required = true
array := strings.Split(rulePattern, ",")
for i := 0; i < len(array); i++ {
if params[array[i]] == "" {
if empty.IsEmpty(dataMap[array[i]]) {
required = false
break
}
@ -91,7 +92,7 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string
required = false
array := strings.Split(rulePattern, ",")
for i := 0; i < len(array); i++ {
if params[array[i]] == "" {
if empty.IsEmpty(dataMap[array[i]]) {
required = true
break
}
@ -103,7 +104,7 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string
required = true
array := strings.Split(rulePattern, ",")
for i := 0; i < len(array); i++ {
if params[array[i]] != "" {
if !empty.IsEmpty(dataMap[array[i]]) {
required = false
break
}

View File

@ -88,6 +88,72 @@ func Test_RequiredWith(t *testing.T) {
t.AssertNE(err2, nil)
t.AssertNE(err3, nil)
})
// time.Time
gtest.C(t, func(t *gtest.T) {
rule := "required-with:id,time"
val1 := ""
params1 := g.Map{
"age": 18,
}
params2 := g.Map{
"id": 100,
}
params3 := g.Map{
"time": time.Time{},
}
err1 := gvalid.Check(val1, rule, nil, params1)
err2 := gvalid.Check(val1, rule, nil, params2)
err3 := gvalid.Check(val1, rule, nil, params3)
t.Assert(err1, nil)
t.AssertNE(err2, nil)
t.Assert(err3, nil)
})
gtest.C(t, func(t *gtest.T) {
rule := "required-with:id,time"
val1 := ""
params1 := g.Map{
"age": 18,
}
params2 := g.Map{
"id": 100,
}
params3 := g.Map{
"time": time.Now(),
}
err1 := gvalid.Check(val1, rule, nil, params1)
err2 := gvalid.Check(val1, rule, nil, params2)
err3 := gvalid.Check(val1, rule, nil, params3)
t.Assert(err1, nil)
t.AssertNE(err2, nil)
t.AssertNE(err3, nil)
})
// gtime.Time
gtest.C(t, func(t *gtest.T) {
type UserApiSearch struct {
Uid int64 `json:"uid"`
Nickname string `json:"nickname" v:"required-with:Uid"`
StartTime *gtime.Time `json:"start_time" v:"required-with:EndTime"`
EndTime *gtime.Time `json:"end_time" v:"required-with:StartTime"`
}
data := UserApiSearch{
StartTime: nil,
EndTime: nil,
}
t.Assert(gvalid.CheckStruct(data, nil), nil)
})
gtest.C(t, func(t *gtest.T) {
type UserApiSearch struct {
Uid int64 `json:"uid"`
Nickname string `json:"nickname" v:"required-with:Uid"`
StartTime *gtime.Time `json:"start_time" v:"required-with:EndTime"`
EndTime *gtime.Time `json:"end_time" v:"required-with:StartTime"`
}
data := UserApiSearch{
StartTime: nil,
EndTime: gtime.Now(),
}
t.AssertNE(gvalid.CheckStruct(data, nil), nil)
})
}
func Test_RequiredWithAll(t *testing.T) {