Skip to content

Commit

Permalink
Fixed missing keys from returned errors in map validation
Browse files Browse the repository at this point in the history
  • Loading branch information
angelo1121 committed Jun 21, 2024
1 parent a947377 commit 1152639
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 1 deletion.
60 changes: 59 additions & 1 deletion validator_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ func (v Validate) ValidateMapCtx(ctx context.Context, data map[string]interface{
errs[field] = errors.New("The field: '" + field + "' is not a map to dive")
}
} else if ruleStr, ok := rule.(string); ok {
err := v.VarCtx(ctx, data[field], ruleStr)
err := v.VarWithKeyCtx(ctx, field, data[field], ruleStr)
if err != nil {
errs[field] = err
}
Expand Down Expand Up @@ -708,3 +708,61 @@ func (v *Validate) VarWithValueCtx(ctx context.Context, field interface{}, other
v.pool.Put(vd)
return
}

// VarWithKey validates a single variable with a key to be included in the returned error using tag style validation
// eg.
// var s string
// validate.VarWithKey("email_address", s, "required,email")
//
// WARNING: a struct can be passed for validation eg. time.Time is a struct or
// if you have a custom type and have registered a custom type handler, so must
// allow it; however unforeseen validations will occur if trying to validate a
// struct that is meant to be passed to 'validate.Struct'
//
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
// validate Array, Slice and maps fields which may contain more than one error
func (v *Validate) VarWithKey(key string, field interface{}, tag string) error {
return v.VarWithKeyCtx(context.Background(), key, field, tag)
}

// VarWithKeyCtx validates a single variable with a key to be included in the returned error using tag style validation
// and allows passing of contextual validation information via context.Context.
// eg.
// var s string
// validate.VarWithKeyCtx("email_address", s, "required,email")
//
// WARNING: a struct can be passed for validation eg. time.Time is a struct or
// if you have a custom type and have registered a custom type handler, so must
// allow it; however unforeseen validations will occur if trying to validate a
// struct that is meant to be passed to 'validate.Struct'
//
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
// validate Array, Slice and maps fields which may contain more than one error
func (v *Validate) VarWithKeyCtx(ctx context.Context, key string, field interface{}, tag string) (err error) {
if len(tag) == 0 || tag == skipValidationTag {
return nil
}

ctag := v.fetchCacheTag(tag)

cField := &cField{
name: key,
altName: key,
namesEqual: true,
}

val := reflect.ValueOf(field)
vd := v.pool.Get().(*validate)
vd.top = val
vd.isPartial = false
vd.traverseField(ctx, val, val, vd.ns[0:0], vd.actualNs[0:0], cField, ctag)

if len(vd.errs) > 0 {
err = vd.errs
vd.errs = nil
}
v.pool.Put(vd)
return
}
94 changes: 94 additions & 0 deletions validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13351,6 +13351,100 @@ func TestValidate_ValidateMapCtx(t *testing.T) {
}
}

func TestValidate_ValidateMapCtxWithKeys(t *testing.T) {
type args struct {
data map[string]interface{}
rules map[string]interface{}
errors map[string]interface{}
}
tests := []struct {
name string
args args
want int
}{
{
name: "test invalid email",
args: args{
data: map[string]interface{}{
"email": "emailaddress",
},
rules: map[string]interface{}{
"email": "required,email",
},
errors: map[string]interface{}{
"email": "Key: 'email' Error:Field validation for 'email' failed on the 'email' tag",
},
},
want: 1,
},
{
name: "test multiple errors with capitalized keys",
args: args{
data: map[string]interface{}{
"Email": "emailaddress",
"Age": 15,
},
rules: map[string]interface{}{
"Email": "required,email",
"Age": "number,gt=16",
},
errors: map[string]interface{}{
"Email": "Key: 'Email' Error:Field validation for 'Email' failed on the 'email' tag",
"Age": "Key: 'Age' Error:Field validation for 'Age' failed on the 'gt' tag",
},
},
want: 2,
},
{
name: "test valid map data",
args: args{
data: map[string]interface{}{
"email": "email@example.com",
"age": 17,
},
rules: map[string]interface{}{
"email": "required,email",
"age": "number,gt=16",
},
errors: map[string]interface{}{},
},
want: 0,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
validate := New()
errs := validate.ValidateMapCtx(context.Background(), tt.args.data, tt.args.rules)
NotEqual(t, errs, nil)
Equal(t, len(errs), tt.want)
for key, err := range errs {
Equal(t, err.(ValidationErrors)[0].Error(), tt.args.errors[key])
}
})
}
}

func TestValidate_VarWithKey(t *testing.T) {
validate := New()
errs := validate.VarWithKey("email", "invalidemail", "required,email")
NotEqual(t, errs, nil)
AssertError(t, errs, "email", "email", "email", "email", "email")

errs = validate.VarWithKey("email", "email@example.com", "required,email")
Equal(t, errs, nil)
}

func TestValidate_VarWithKeyCtx(t *testing.T) {
validate := New()
errs := validate.VarWithKeyCtx(context.Background(), "age", 15, "required,gt=16")
NotEqual(t, errs, nil)
AssertError(t, errs, "age", "age", "age", "age", "gt")

errs = validate.VarWithKey("age", 17, "required,gt=16")
Equal(t, errs, nil)
}

func TestMongoDBObjectIDFormatValidation(t *testing.T) {
tests := []struct {
value string `validate:"mongodb"`
Expand Down

0 comments on commit 1152639

Please sign in to comment.