|
|
|
@ -2,9 +2,10 @@ package validator |
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"errors" |
|
|
|
|
"fmt" |
|
|
|
|
"log" |
|
|
|
|
"reflect" |
|
|
|
|
"strings" |
|
|
|
|
"unicode" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
// FieldValidationError contains a single fields validation error
|
|
|
|
@ -17,17 +18,18 @@ type FieldValidationError struct { |
|
|
|
|
// NOTE: if a field within a struct is a struct it's errors will not be contained within the current
|
|
|
|
|
// StructValidationErrors but rather a new ArrayValidationErrors is created for each struct
|
|
|
|
|
type StructValidationErrors struct { |
|
|
|
|
Errors []FieldValidationError |
|
|
|
|
Struct string |
|
|
|
|
Errors []*FieldValidationError |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ArrayStructValidationErrors is a struct that contains a 2D flattened list of struct specific StructValidationErrors
|
|
|
|
|
type ArrayStructValidationErrors struct { |
|
|
|
|
// Key = Struct Name
|
|
|
|
|
Errors map[string][]StructValidationErrors |
|
|
|
|
} |
|
|
|
|
// // ArrayStructValidationErrors is a struct that contains a 2D flattened list of struct specific StructValidationErrors
|
|
|
|
|
// type ArrayStructValidationErrors struct {
|
|
|
|
|
// // Key = Struct Name
|
|
|
|
|
// Errors map[string][]StructValidationErrors
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// ValidationFunc that accepts the value of a field and parameter for use in validation (parameter not always used or needed)
|
|
|
|
|
type ValidationFunc func(v interface{}, param string) error |
|
|
|
|
type ValidationFunc func(v interface{}, param string) bool |
|
|
|
|
|
|
|
|
|
// Validator implements the Validator Struct
|
|
|
|
|
// NOTE: Fields within are not thread safe and that is on purpose
|
|
|
|
@ -40,7 +42,7 @@ type Validator struct { |
|
|
|
|
validationFuncs map[string]ValidationFunc |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var bakedInValidators = map[string]ValidationFunc{} |
|
|
|
|
// var bakedInValidators = map[string]ValidationFunc{}
|
|
|
|
|
|
|
|
|
|
var internalValidator = NewValidator("validate", bakedInValidators) |
|
|
|
|
|
|
|
|
@ -89,17 +91,23 @@ func (v *Validator) AddFunction(key string, f ValidationFunc) error { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func ValidateStruct(s interface{}) ArrayStructValidationErrors { |
|
|
|
|
// ValidateStruct validates a struct and returns a struct containing the errors
|
|
|
|
|
func ValidateStruct(s interface{}) []*StructValidationErrors { |
|
|
|
|
|
|
|
|
|
return internalValidator.ValidateStruct(s) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (v *Validator) ValidateStruct(s interface{}) ArrayStructValidationErrors { |
|
|
|
|
|
|
|
|
|
var errorStruct = ArrayStructValidationErrors{} |
|
|
|
|
// ValidateStruct validates a struct and returns a struct containing the errors
|
|
|
|
|
func (v *Validator) ValidateStruct(s interface{}) []*StructValidationErrors { |
|
|
|
|
|
|
|
|
|
errorArray := []*StructValidationErrors{} |
|
|
|
|
structValue := reflect.ValueOf(s) |
|
|
|
|
structType := reflect.TypeOf(s) |
|
|
|
|
structName := structType.Name() |
|
|
|
|
|
|
|
|
|
var currentStructError = &StructValidationErrors{ |
|
|
|
|
Struct: structName, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if structValue.Kind() == reflect.Ptr && !structValue.IsNil() { |
|
|
|
|
return v.ValidateStruct(structValue.Elem().Interface()) |
|
|
|
@ -120,8 +128,179 @@ func (v *Validator) ValidateStruct(s interface{}) ArrayStructValidationErrors { |
|
|
|
|
valueField = valueField.Elem() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fmt.Println(typeField.Name) |
|
|
|
|
tag := typeField.Tag.Get(v.tagName) |
|
|
|
|
|
|
|
|
|
if tag == "-" { |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// if no validation and not a struct (which may containt fields for validation)
|
|
|
|
|
if tag == "" && valueField.Kind() != reflect.Struct { |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
switch valueField.Kind() { |
|
|
|
|
|
|
|
|
|
case reflect.Struct: |
|
|
|
|
|
|
|
|
|
if !unicode.IsUpper(rune(typeField.Name[0])) { |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if structErrors := v.ValidateStruct(valueField.Interface()); structErrors != nil { |
|
|
|
|
errorArray = append(errorArray, structErrors...) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
|
|
if fieldError := v.validateStructFieldByTag(valueField.Interface(), typeField.Name, tag); fieldError != nil { |
|
|
|
|
currentStructError.Errors = append(currentStructError.Errors, fieldError) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if currentStructError.Errors != nil { |
|
|
|
|
errorArray = append(errorArray, currentStructError) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if len(errorArray) == 0 { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return errorArray |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ValidateFieldWithTag validates the given field by the given tag arguments
|
|
|
|
|
func (v *Validator) validateStructFieldByTag(f interface{}, name string, tag string) *FieldValidationError { |
|
|
|
|
|
|
|
|
|
// // This is a double check if coming from ValidateStruct but need to be here in case function is called directly
|
|
|
|
|
// if tag == "-" {
|
|
|
|
|
// return nil
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// valueField := reflect.ValueOf(f)
|
|
|
|
|
//
|
|
|
|
|
// if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
|
|
|
|
|
// return v.validateStructFieldByTag(valueField.Elem().Interface(), name, tag)
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// // fmt.Println(typeField.Name)
|
|
|
|
|
//
|
|
|
|
|
// switch valueField.Kind() {
|
|
|
|
|
//
|
|
|
|
|
// case reflect.Struct, reflect.Invalid:
|
|
|
|
|
// log.Fatal("Invalid field passed to ValidateFieldWithTag")
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// // typeField := reflect.TypeOf(f)
|
|
|
|
|
// // name := ""
|
|
|
|
|
// valTags := strings.Split(tag, ",")
|
|
|
|
|
//
|
|
|
|
|
// for _, valTag := range valTags {
|
|
|
|
|
//
|
|
|
|
|
// vals := strings.Split(valTag, "=")
|
|
|
|
|
// key := strings.Trim(vals[0], " ")
|
|
|
|
|
//
|
|
|
|
|
// if len(key) == 0 {
|
|
|
|
|
// log.Fatalf("Invalid validation tag on field %s", name)
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// valFunc := v.validationFuncs[key]
|
|
|
|
|
// if valFunc == nil {
|
|
|
|
|
// log.Fatalf("Undefined validation function on field %s", name)
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// param := ""
|
|
|
|
|
// if len(vals) > 1 {
|
|
|
|
|
// param = strings.Trim(vals[1], " ")
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// if err := valFunc(f, param); !err {
|
|
|
|
|
//
|
|
|
|
|
// return &FieldValidationError{
|
|
|
|
|
// Field: name,
|
|
|
|
|
// ErrorTag: key,
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// return nil
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
if err := v.validateFieldByNameAndTag(f, name, tag); err != nil { |
|
|
|
|
return &FieldValidationError{ |
|
|
|
|
Field: name, |
|
|
|
|
ErrorTag: err.Error(), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ValidateFieldByTag allows validation of a single field with the internal validator, still using tag style validation to check multiple errors
|
|
|
|
|
func ValidateFieldByTag(f interface{}, tag string) error { |
|
|
|
|
|
|
|
|
|
return internalValidator.validateFieldByNameAndTag(f, "", tag) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ValidateFieldByTag allows validation of a single field, still using tag style validation to check multiple errors
|
|
|
|
|
func (v *Validator) ValidateFieldByTag(f interface{}, tag string) error { |
|
|
|
|
|
|
|
|
|
return v.validateFieldByNameAndTag(f, "", tag) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (v *Validator) validateFieldByNameAndTag(f interface{}, name string, tag string) error { |
|
|
|
|
|
|
|
|
|
// This is a double check if coming from ValidateStruct but need to be here in case function is called directly
|
|
|
|
|
if tag == "-" { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
valueField := reflect.ValueOf(f) |
|
|
|
|
|
|
|
|
|
if valueField.Kind() == reflect.Ptr && !valueField.IsNil() { |
|
|
|
|
return v.ValidateFieldByTag(valueField.Elem().Interface(), tag) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// fmt.Println(typeField.Name)
|
|
|
|
|
|
|
|
|
|
switch valueField.Kind() { |
|
|
|
|
|
|
|
|
|
case reflect.Struct, reflect.Invalid: |
|
|
|
|
log.Fatal("Invalid field passed to ValidateFieldWithTag") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// typeField := reflect.TypeOf(f)
|
|
|
|
|
// name := ""
|
|
|
|
|
valTags := strings.Split(tag, ",") |
|
|
|
|
|
|
|
|
|
for _, valTag := range valTags { |
|
|
|
|
|
|
|
|
|
vals := strings.Split(valTag, "=") |
|
|
|
|
key := strings.Trim(vals[0], " ") |
|
|
|
|
|
|
|
|
|
if len(key) == 0 { |
|
|
|
|
log.Fatalf("Invalid validation tag on field %s", name) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
valFunc := v.validationFuncs[key] |
|
|
|
|
if valFunc == nil { |
|
|
|
|
log.Fatalf("Undefined validation function on field %s", name) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
param := "" |
|
|
|
|
if len(vals) > 1 { |
|
|
|
|
param = strings.Trim(vals[1], " ") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err := valFunc(f, param); !err { |
|
|
|
|
|
|
|
|
|
return errors.New(key) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return errorStruct |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|