Skip to content

Commit 1152639

Browse files
committed
Fixed missing keys from returned errors in map validation
1 parent a947377 commit 1152639

File tree

2 files changed

+153
-1
lines changed

2 files changed

+153
-1
lines changed

validator_instance.go

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ func (v Validate) ValidateMapCtx(ctx context.Context, data map[string]interface{
182182
errs[field] = errors.New("The field: '" + field + "' is not a map to dive")
183183
}
184184
} else if ruleStr, ok := rule.(string); ok {
185-
err := v.VarCtx(ctx, data[field], ruleStr)
185+
err := v.VarWithKeyCtx(ctx, field, data[field], ruleStr)
186186
if err != nil {
187187
errs[field] = err
188188
}
@@ -708,3 +708,61 @@ func (v *Validate) VarWithValueCtx(ctx context.Context, field interface{}, other
708708
v.pool.Put(vd)
709709
return
710710
}
711+
712+
// VarWithKey validates a single variable with a key to be included in the returned error using tag style validation
713+
// eg.
714+
// var s string
715+
// validate.VarWithKey("email_address", s, "required,email")
716+
//
717+
// WARNING: a struct can be passed for validation eg. time.Time is a struct or
718+
// if you have a custom type and have registered a custom type handler, so must
719+
// allow it; however unforeseen validations will occur if trying to validate a
720+
// struct that is meant to be passed to 'validate.Struct'
721+
//
722+
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
723+
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
724+
// validate Array, Slice and maps fields which may contain more than one error
725+
func (v *Validate) VarWithKey(key string, field interface{}, tag string) error {
726+
return v.VarWithKeyCtx(context.Background(), key, field, tag)
727+
}
728+
729+
// VarWithKeyCtx validates a single variable with a key to be included in the returned error using tag style validation
730+
// and allows passing of contextual validation information via context.Context.
731+
// eg.
732+
// var s string
733+
// validate.VarWithKeyCtx("email_address", s, "required,email")
734+
//
735+
// WARNING: a struct can be passed for validation eg. time.Time is a struct or
736+
// if you have a custom type and have registered a custom type handler, so must
737+
// allow it; however unforeseen validations will occur if trying to validate a
738+
// struct that is meant to be passed to 'validate.Struct'
739+
//
740+
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
741+
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
742+
// validate Array, Slice and maps fields which may contain more than one error
743+
func (v *Validate) VarWithKeyCtx(ctx context.Context, key string, field interface{}, tag string) (err error) {
744+
if len(tag) == 0 || tag == skipValidationTag {
745+
return nil
746+
}
747+
748+
ctag := v.fetchCacheTag(tag)
749+
750+
cField := &cField{
751+
name: key,
752+
altName: key,
753+
namesEqual: true,
754+
}
755+
756+
val := reflect.ValueOf(field)
757+
vd := v.pool.Get().(*validate)
758+
vd.top = val
759+
vd.isPartial = false
760+
vd.traverseField(ctx, val, val, vd.ns[0:0], vd.actualNs[0:0], cField, ctag)
761+
762+
if len(vd.errs) > 0 {
763+
err = vd.errs
764+
vd.errs = nil
765+
}
766+
v.pool.Put(vd)
767+
return
768+
}

validator_test.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13351,6 +13351,100 @@ func TestValidate_ValidateMapCtx(t *testing.T) {
1335113351
}
1335213352
}
1335313353

13354+
func TestValidate_ValidateMapCtxWithKeys(t *testing.T) {
13355+
type args struct {
13356+
data map[string]interface{}
13357+
rules map[string]interface{}
13358+
errors map[string]interface{}
13359+
}
13360+
tests := []struct {
13361+
name string
13362+
args args
13363+
want int
13364+
}{
13365+
{
13366+
name: "test invalid email",
13367+
args: args{
13368+
data: map[string]interface{}{
13369+
"email": "emailaddress",
13370+
},
13371+
rules: map[string]interface{}{
13372+
"email": "required,email",
13373+
},
13374+
errors: map[string]interface{}{
13375+
"email": "Key: 'email' Error:Field validation for 'email' failed on the 'email' tag",
13376+
},
13377+
},
13378+
want: 1,
13379+
},
13380+
{
13381+
name: "test multiple errors with capitalized keys",
13382+
args: args{
13383+
data: map[string]interface{}{
13384+
"Email": "emailaddress",
13385+
"Age": 15,
13386+
},
13387+
rules: map[string]interface{}{
13388+
"Email": "required,email",
13389+
"Age": "number,gt=16",
13390+
},
13391+
errors: map[string]interface{}{
13392+
"Email": "Key: 'Email' Error:Field validation for 'Email' failed on the 'email' tag",
13393+
"Age": "Key: 'Age' Error:Field validation for 'Age' failed on the 'gt' tag",
13394+
},
13395+
},
13396+
want: 2,
13397+
},
13398+
{
13399+
name: "test valid map data",
13400+
args: args{
13401+
data: map[string]interface{}{
13402+
"email": "email@example.com",
13403+
"age": 17,
13404+
},
13405+
rules: map[string]interface{}{
13406+
"email": "required,email",
13407+
"age": "number,gt=16",
13408+
},
13409+
errors: map[string]interface{}{},
13410+
},
13411+
want: 0,
13412+
},
13413+
}
13414+
13415+
for _, tt := range tests {
13416+
t.Run(tt.name, func(t *testing.T) {
13417+
validate := New()
13418+
errs := validate.ValidateMapCtx(context.Background(), tt.args.data, tt.args.rules)
13419+
NotEqual(t, errs, nil)
13420+
Equal(t, len(errs), tt.want)
13421+
for key, err := range errs {
13422+
Equal(t, err.(ValidationErrors)[0].Error(), tt.args.errors[key])
13423+
}
13424+
})
13425+
}
13426+
}
13427+
13428+
func TestValidate_VarWithKey(t *testing.T) {
13429+
validate := New()
13430+
errs := validate.VarWithKey("email", "invalidemail", "required,email")
13431+
NotEqual(t, errs, nil)
13432+
AssertError(t, errs, "email", "email", "email", "email", "email")
13433+
13434+
errs = validate.VarWithKey("email", "email@example.com", "required,email")
13435+
Equal(t, errs, nil)
13436+
}
13437+
13438+
func TestValidate_VarWithKeyCtx(t *testing.T) {
13439+
validate := New()
13440+
errs := validate.VarWithKeyCtx(context.Background(), "age", 15, "required,gt=16")
13441+
NotEqual(t, errs, nil)
13442+
AssertError(t, errs, "age", "age", "age", "age", "gt")
13443+
13444+
errs = validate.VarWithKey("age", 17, "required,gt=16")
13445+
Equal(t, errs, nil)
13446+
}
13447+
1335413448
func TestMongoDBObjectIDFormatValidation(t *testing.T) {
1335513449
tests := []struct {
1335613450
value string `validate:"mongodb"`

0 commit comments

Comments
 (0)