github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/f/validator.go (about)

     1  package f
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"regexp"
     7  	"sort"
     8  	"strconv"
     9  	"strings"
    10  )
    11  
    12  const maxURLRuneCount = 2083
    13  const minURLRuneCount = 3
    14  const RF3339WithoutZone = "2006-01-02T15:04:05"
    15  
    16  var (
    17  	fieldsRequiredByDefault bool
    18  	nilPtrAllowedByRequired = false
    19  	notNumberRegexp         = regexp.MustCompile("[^0-9]+")
    20  	whiteSpacesAndMinus     = regexp.MustCompile(`[\s-]+`)
    21  	paramsRegexp            = regexp.MustCompile(`\(.*\)$`)
    22  )
    23  
    24  // ValidateMap use validation map for fields.
    25  // result will be equal to `false` if there are any errors.
    26  // m is the validation map in the form
    27  // map[string]interface{}{"name":"required,alpha","address":map[string]interface{}{"line1":"required,alphanum"}}
    28  func ValidateMap(s map[string]interface{}, m map[string]interface{}) (bool, error) {
    29  	if s == nil {
    30  		return true, nil
    31  	}
    32  	result := true
    33  	var err error
    34  	var errs Errors
    35  	var index int
    36  	val := reflect.ValueOf(s)
    37  	for key, value := range s {
    38  		presentResult := true
    39  		validator, ok := m[key]
    40  		if !ok {
    41  			presentResult = false
    42  			var err error
    43  			err = fmt.Errorf("all map keys has to be present in the validation map; got %s", key)
    44  			err = PrependPathToErrors(err, key)
    45  			errs = append(errs, err)
    46  		}
    47  		valueField := reflect.ValueOf(value)
    48  		mapResult := true
    49  		typeResult := true
    50  		structResult := true
    51  		resultField := true
    52  		switch subValidator := validator.(type) {
    53  		case map[string]interface{}:
    54  			var err error
    55  			if v, ok := value.(map[string]interface{}); !ok {
    56  				mapResult = false
    57  				err = fmt.Errorf("map validator has to be for the map type only; got %s", valueField.Type().String())
    58  				err = PrependPathToErrors(err, key)
    59  				errs = append(errs, err)
    60  			} else {
    61  				mapResult, err = ValidateMap(v, subValidator)
    62  				if err != nil {
    63  					mapResult = false
    64  					err = PrependPathToErrors(err, key)
    65  					errs = append(errs, err)
    66  				}
    67  			}
    68  		case string:
    69  			if (valueField.Kind() == reflect.Struct ||
    70  				(valueField.Kind() == reflect.Ptr && valueField.Elem().Kind() == reflect.Struct)) &&
    71  				subValidator != "-" {
    72  				var err error
    73  				structResult, err = ValidateStruct(valueField.Interface())
    74  				if err != nil {
    75  					err = PrependPathToErrors(err, key)
    76  					errs = append(errs, err)
    77  				}
    78  			}
    79  			resultField, err = typeCheck(valueField, reflect.StructField{
    80  				Name:      key,
    81  				PkgPath:   "",
    82  				Type:      val.Type(),
    83  				Tag:       reflect.StructTag(fmt.Sprintf("%s:%q", RxTagName, subValidator)),
    84  				Offset:    0,
    85  				Index:     []int{index},
    86  				Anonymous: false,
    87  			}, val, nil)
    88  			if err != nil {
    89  				errs = append(errs, err)
    90  			}
    91  		case nil:
    92  			// already handlerd when checked before
    93  		default:
    94  			typeResult = false
    95  			err = fmt.Errorf("map validator has to be either map[string]interface{} or string; got %s", valueField.Type().String())
    96  			err = PrependPathToErrors(err, key)
    97  			errs = append(errs, err)
    98  		}
    99  		result = result && presentResult && typeResult && resultField && structResult && mapResult
   100  		index++
   101  	}
   102  	// check required keys
   103  	requiredResult := true
   104  	for key, value := range m {
   105  		if schema, ok := value.(string); ok {
   106  			tags := parseTagIntoMap(schema)
   107  			if required, ok := tags["required"]; ok {
   108  				if _, ok := s[key]; !ok {
   109  					requiredResult = false
   110  					if required.customErrorMessage != "" {
   111  						err = Error{key, fmt.Errorf(required.customErrorMessage), true, "required", []string{}}
   112  					} else {
   113  						err = Error{key, fmt.Errorf("required field missing"), false, "required", []string{}}
   114  					}
   115  					errs = append(errs, err)
   116  				}
   117  			}
   118  		}
   119  	}
   120  
   121  	if len(errs) > 0 {
   122  		err = errs
   123  	}
   124  	return result && requiredResult, err
   125  }
   126  
   127  // ValidateStruct use tags for fields.
   128  // result will be equal to `false` if there are any errors.
   129  // to-do currently there is no guarantee that errors will be returned in predictable order (tests may to fail)
   130  func ValidateStruct(s interface{}) (bool, error) {
   131  	if s == nil {
   132  		return true, nil
   133  	}
   134  	result := true
   135  	var err error
   136  	val := reflect.ValueOf(s)
   137  	if val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr {
   138  		val = val.Elem()
   139  	}
   140  	// we only accept structs
   141  	if val.Kind() != reflect.Struct {
   142  		return false, fmt.Errorf("function only accepts structs; got %s", val.Kind())
   143  	}
   144  	var errs Errors
   145  	for i := 0; i < val.NumField(); i++ {
   146  		valueField := val.Field(i)
   147  		typeField := val.Type().Field(i)
   148  		if typeField.PkgPath != "" {
   149  			continue // Private field
   150  		}
   151  		structResult := true
   152  		if valueField.Kind() == reflect.Interface {
   153  			valueField = valueField.Elem()
   154  		}
   155  		if (valueField.Kind() == reflect.Struct ||
   156  			(valueField.Kind() == reflect.Ptr && valueField.Elem().Kind() == reflect.Struct)) &&
   157  			typeField.Tag.Get(RxTagName) != "-" {
   158  			var err error
   159  			structResult, err = ValidateStruct(valueField.Interface())
   160  			if err != nil {
   161  				err = PrependPathToErrors(err, typeField.Name)
   162  				errs = append(errs, err)
   163  			}
   164  		}
   165  		resultField, err2 := typeCheck(valueField, typeField, val, nil)
   166  		if err2 != nil {
   167  
   168  			// Replace structure name with JSON name if there is a tag on the variable
   169  			jsonTag := toJSONName(typeField.Tag.Get("json"))
   170  			if jsonTag != "" {
   171  				switch jsonError := err2.(type) {
   172  				case Error:
   173  					jsonError.Name = jsonTag
   174  					err2 = jsonError
   175  				case Errors:
   176  					for i2, err3 := range jsonError {
   177  						switch customErr := err3.(type) {
   178  						case Error:
   179  							customErr.Name = jsonTag
   180  							jsonError[i2] = customErr
   181  						}
   182  					}
   183  
   184  					err2 = jsonError
   185  				}
   186  			}
   187  
   188  			errs = append(errs, err2)
   189  		}
   190  		result = result && resultField && structResult
   191  	}
   192  	if len(errs) > 0 {
   193  		err = errs
   194  	}
   195  	return result, err
   196  }
   197  
   198  // parseTagIntoMap parses a struct tag `valid:required~Some error message,length(2|3)` into map[string]string{"required": "Some error message", "length(2|3)": ""}
   199  func parseTagIntoMap(tag string) tagOptionsMap {
   200  	optionsMap := make(tagOptionsMap)
   201  	options := strings.Split(tag, ",")
   202  
   203  	for i, option := range options {
   204  		option = strings.TrimSpace(option)
   205  
   206  		validationOptions := strings.Split(option, "~")
   207  		if !isValidTag(validationOptions[0]) {
   208  			continue
   209  		}
   210  		if len(validationOptions) == 2 {
   211  			optionsMap[validationOptions[0]] = tagOption{validationOptions[0], validationOptions[1], i}
   212  		} else {
   213  			optionsMap[validationOptions[0]] = tagOption{validationOptions[0], "", i}
   214  		}
   215  	}
   216  	return optionsMap
   217  }
   218  
   219  func checkRequired(v reflect.Value, t reflect.StructField, options tagOptionsMap) (bool, error) {
   220  	if nilPtrAllowedByRequired {
   221  		k := v.Kind()
   222  		if (k == reflect.Ptr || k == reflect.Interface) && v.IsNil() {
   223  			return true, nil
   224  		}
   225  	}
   226  
   227  	if requiredOption, isRequired := options["required"]; isRequired {
   228  		if len(requiredOption.customErrorMessage) > 0 {
   229  			return false, Error{t.Name, fmt.Errorf(requiredOption.customErrorMessage), true, "required", []string{}}
   230  		}
   231  		return false, Error{t.Name, fmt.Errorf("non zero value required"), false, "required", []string{}}
   232  	} else if _, isOptional := options["optional"]; fieldsRequiredByDefault && !isOptional {
   233  		return false, Error{t.Name, fmt.Errorf("Missing required field"), false, "required", []string{}}
   234  	}
   235  	// not required and empty is valid
   236  	return true, nil
   237  }
   238  
   239  func typeCheck(v reflect.Value, t reflect.StructField, o reflect.Value, options tagOptionsMap) (isValid bool, resultErr error) {
   240  	if !v.IsValid() {
   241  		return false, nil
   242  	}
   243  
   244  	tag := t.Tag.Get(RxTagName)
   245  
   246  	// Check if the field should be ignored
   247  	switch tag {
   248  	case "":
   249  		if v.Kind() != reflect.Slice && v.Kind() != reflect.Map {
   250  			if !fieldsRequiredByDefault {
   251  				return true, nil
   252  			}
   253  			return false, Error{t.Name, fmt.Errorf("All fields are required to at least have one validation defined"), false, "required", []string{}}
   254  		}
   255  	case "-":
   256  		return true, nil
   257  	}
   258  
   259  	isRootType := false
   260  	if options == nil {
   261  		isRootType = true
   262  		options = parseTagIntoMap(tag)
   263  	}
   264  
   265  	if !isFieldSet(v) {
   266  		// an empty value is not validated, check only required
   267  		isValid, resultErr = checkRequired(v, t, options)
   268  		for key := range options {
   269  			delete(options, key)
   270  		}
   271  		return isValid, resultErr
   272  	}
   273  
   274  	var customTypeErrors Errors
   275  	optionsOrder := options.orderedKeys()
   276  	for _, validatorName := range optionsOrder {
   277  		validatorStruct := options[validatorName]
   278  		if validatefunc, ok := CustomTypeTagMap.Get(validatorName); ok {
   279  			delete(options, validatorName)
   280  
   281  			if result := validatefunc(v.Interface(), o.Interface()); !result {
   282  				if len(validatorStruct.customErrorMessage) > 0 {
   283  					customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: TruncatingErrorf(validatorStruct.customErrorMessage, fmt.Sprint(v), validatorName), CustomErrorMessageExists: true, Validator: stripParams(validatorName)})
   284  					continue
   285  				}
   286  				customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: fmt.Errorf("%s does not validate as %s", fmt.Sprint(v), validatorName), CustomErrorMessageExists: false, Validator: stripParams(validatorName)})
   287  			}
   288  		}
   289  	}
   290  
   291  	if customTypeErrors != nil && len(customTypeErrors.Errors()) > 0 {
   292  		return false, customTypeErrors
   293  	}
   294  
   295  	if isRootType {
   296  		// Ensure that we've checked the value by all specified validators before report that the value is valid
   297  		defer func() {
   298  			delete(options, "optional")
   299  			delete(options, "required")
   300  
   301  			if isValid && resultErr == nil && len(options) != 0 {
   302  				optionsOrder := options.orderedKeys()
   303  				for _, validator := range optionsOrder {
   304  					isValid = false
   305  					resultErr = Error{t.Name, fmt.Errorf(
   306  						"the following validator is invalid or can't be applied to the field: %q", validator), false, stripParams(validator), []string{}}
   307  					return
   308  				}
   309  			}
   310  		}()
   311  	}
   312  
   313  	for _, validatorSpec := range optionsOrder {
   314  		validatorStruct := options[validatorSpec]
   315  		var negate bool
   316  		validator := validatorSpec
   317  		customMsgExists := len(validatorStruct.customErrorMessage) > 0
   318  
   319  		// Check whether the tag looks like '!something' or 'something'
   320  		if validator[0] == '!' {
   321  			validator = validator[1:]
   322  			negate = true
   323  		}
   324  
   325  		// Check for interface param validators
   326  		for key, value := range InterfaceParamTagRegexMap {
   327  			ps := value.FindStringSubmatch(validator)
   328  			if len(ps) == 0 {
   329  				continue
   330  			}
   331  
   332  			validatefunc, ok := InterfaceParamTagMap[key]
   333  			if !ok {
   334  				continue
   335  			}
   336  
   337  			delete(options, validatorSpec)
   338  
   339  			field := fmt.Sprint(v)
   340  			if result := validatefunc(v.Interface(), ps[1:]...); (!result && !negate) || (result && negate) {
   341  				if customMsgExists {
   342  					return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
   343  				}
   344  				if negate {
   345  					return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
   346  				}
   347  				return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
   348  			}
   349  		}
   350  	}
   351  
   352  	switch v.Kind() {
   353  	case reflect.Bool,
   354  		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
   355  		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
   356  		reflect.Float32, reflect.Float64,
   357  		reflect.String:
   358  		// for each tag option check the map of validator functions
   359  		for _, validatorSpec := range optionsOrder {
   360  			validatorStruct := options[validatorSpec]
   361  			var negate bool
   362  			validator := validatorSpec
   363  			customMsgExists := len(validatorStruct.customErrorMessage) > 0
   364  
   365  			// Check whether the tag looks like '!something' or 'something'
   366  			if validator[0] == '!' {
   367  				validator = validator[1:]
   368  				negate = true
   369  			}
   370  
   371  			// Check for param validators
   372  			for key, value := range ParamTagRegexMap {
   373  				ps := value.FindStringSubmatch(validator)
   374  				if len(ps) == 0 {
   375  					continue
   376  				}
   377  
   378  				validatefunc, ok := ParamTagMap[key]
   379  				if !ok {
   380  					continue
   381  				}
   382  
   383  				delete(options, validatorSpec)
   384  
   385  				switch v.Kind() {
   386  				case reflect.String,
   387  					reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
   388  					reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
   389  					reflect.Float32, reflect.Float64:
   390  
   391  					field := fmt.Sprint(v) // make value into string, then validate with regex
   392  					if result := validatefunc(field, ps[1:]...); (!result && !negate) || (result && negate) {
   393  						if customMsgExists {
   394  							return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
   395  						}
   396  						if negate {
   397  							return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
   398  						}
   399  						return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
   400  					}
   401  				default:
   402  					// type not yet supported, fail
   403  					return false, Error{t.Name, fmt.Errorf("validator %s doesn't support kind %s", validator, v.Kind()), false, stripParams(validatorSpec), []string{}}
   404  				}
   405  			}
   406  
   407  			if validatefunc, ok := TagMap[validator]; ok {
   408  				delete(options, validatorSpec)
   409  
   410  				switch v.Kind() {
   411  				case reflect.String,
   412  					reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
   413  					reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
   414  					reflect.Float32, reflect.Float64:
   415  					field := fmt.Sprint(v) // make value into string, then validate with regex
   416  					if result := validatefunc(field); !result && !negate || result && negate {
   417  						if customMsgExists {
   418  							return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
   419  						}
   420  						if negate {
   421  							return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
   422  						}
   423  						return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
   424  					}
   425  				default:
   426  					//Not Yet Supported Types (Fail here!)
   427  					err := fmt.Errorf("validator %s doesn't support kind %s for value %v", validator, v.Kind(), v)
   428  					return false, Error{t.Name, err, false, stripParams(validatorSpec), []string{}}
   429  				}
   430  			}
   431  		}
   432  		return true, nil
   433  	case reflect.Map:
   434  		if v.Type().Key().Kind() != reflect.String {
   435  			return false, &UnsupportedTypeError{v.Type()}
   436  		}
   437  		var sv stringValues
   438  		sv = v.MapKeys()
   439  		sort.Sort(sv)
   440  		result := true
   441  		for i, k := range sv {
   442  			var resultItem bool
   443  			var err error
   444  			if v.MapIndex(k).Kind() != reflect.Struct {
   445  				resultItem, err = typeCheck(v.MapIndex(k), t, o, options)
   446  				if err != nil {
   447  					return false, err
   448  				}
   449  			} else {
   450  				resultItem, err = ValidateStruct(v.MapIndex(k).Interface())
   451  				if err != nil {
   452  					err = PrependPathToErrors(err, t.Name+"."+sv[i].Interface().(string))
   453  					return false, err
   454  				}
   455  			}
   456  			result = result && resultItem
   457  		}
   458  		return result, nil
   459  	case reflect.Slice, reflect.Array:
   460  		result := true
   461  		for i := 0; i < v.Len(); i++ {
   462  			var resultItem bool
   463  			var err error
   464  			if v.Index(i).Kind() != reflect.Struct {
   465  				resultItem, err = typeCheck(v.Index(i), t, o, options)
   466  				if err != nil {
   467  					return false, err
   468  				}
   469  			} else {
   470  				resultItem, err = ValidateStruct(v.Index(i).Interface())
   471  				if err != nil {
   472  					err = PrependPathToErrors(err, t.Name+"."+strconv.Itoa(i))
   473  					return false, err
   474  				}
   475  			}
   476  			result = result && resultItem
   477  		}
   478  		return result, nil
   479  	case reflect.Interface:
   480  		// If the value is an interface then encode its element
   481  		if v.IsNil() {
   482  			return true, nil
   483  		}
   484  		return ValidateStruct(v.Interface())
   485  	case reflect.Ptr:
   486  		// If the value is a pointer then check its element
   487  		if v.IsNil() {
   488  			return true, nil
   489  		}
   490  		return typeCheck(v.Elem(), t, o, options)
   491  	case reflect.Struct:
   492  		return true, nil
   493  	default:
   494  		return false, &UnsupportedTypeError{v.Type()}
   495  	}
   496  }
   497  
   498  func stripParams(validatorString string) string {
   499  	return paramsRegexp.ReplaceAllString(validatorString, "")
   500  }
   501  
   502  // isFieldSet returns false for nil pointers, interfaces, maps, and slices. For all other values, it returns true.
   503  func isFieldSet(v reflect.Value) bool {
   504  	switch v.Kind() {
   505  	case reflect.Map, reflect.Slice, reflect.Interface, reflect.Ptr:
   506  		return !v.IsNil()
   507  	}
   508  
   509  	return true
   510  }
   511  
   512  // ErrorByField returns error for specified field of the struct
   513  // validated by ValidateStruct or empty string if there are no errors
   514  // or this field doesn't exists or doesn't have any errors.
   515  func ErrorByField(e error, field string) string {
   516  	if e == nil {
   517  		return ""
   518  	}
   519  	return ErrorsByField(e)[field]
   520  }
   521  
   522  // ErrorsByField returns map of errors of the struct validated
   523  // by ValidateStruct or empty map if there are no errors.
   524  func ErrorsByField(e error) map[string]string {
   525  	m := make(map[string]string)
   526  	if e == nil {
   527  		return m
   528  	}
   529  	// prototype for ValidateStruct
   530  
   531  	switch e.(type) {
   532  	case Error:
   533  		m[e.(Error).Name] = e.(Error).Err.Error()
   534  	case Errors:
   535  		for _, item := range e.(Errors).Errors() {
   536  			n := ErrorsByField(item)
   537  			for k, v := range n {
   538  				m[k] = v
   539  			}
   540  		}
   541  	}
   542  
   543  	return m
   544  }
   545  
   546  // Error returns string equivalent for reflect.Type
   547  func (e *UnsupportedTypeError) Error() string {
   548  	return "validator: unsupported type: " + e.Type.String()
   549  }
   550  
   551  func (sv stringValues) Len() int           { return len(sv) }
   552  func (sv stringValues) Swap(i, j int)      { sv[i], sv[j] = sv[j], sv[i] }
   553  func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) }
   554  func (sv stringValues) get(i int) string   { return sv[i].String() }
   555  
   556  // SetFieldsRequiredByDefault causes validation to fail when struct fields
   557  // do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`).
   558  // This struct definition will fail govalidator.ValidateStruct() (and the field values do not matter):
   559  //     type exampleStruct struct {
   560  //         Name  string ``
   561  //         Email string `valid:"email"`
   562  // This, however, will only fail when Email is empty or an invalid email address:
   563  //     type exampleStruct2 struct {
   564  //         Name  string `valid:"-"`
   565  //         Email string `valid:"email"`
   566  // Lastly, this will only fail when Email is an invalid email address but not when it's empty:
   567  //     type exampleStruct2 struct {
   568  //         Name  string `valid:"-"`
   569  //         Email string `valid:"email,optional"`
   570  func SetFieldsRequiredByDefault(value bool) {
   571  	fieldsRequiredByDefault = value
   572  }
   573  
   574  // SetNilPtrAllowedByRequired causes validation to pass for nil ptrs when a field is set to required.
   575  // The validation will still reject ptr fields in their zero value state. Example with this enabled:
   576  //     type exampleStruct struct {
   577  //         Name  *string `valid:"required"`
   578  // With `Name` set to "", this will be considered invalid input and will cause a validation error.
   579  // With `Name` set to nil, this will be considered valid by validation.
   580  // By default this is disabled.
   581  func SetNilPtrAllowedByRequired(value bool) {
   582  	nilPtrAllowedByRequired = value
   583  }