2024-09-12 18:13:16 -04:00
|
|
|
package core
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
validation "github.com/go-ozzo/ozzo-validation"
|
|
|
|
"github.com/go-ozzo/ozzo-validation/is"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Validator struct{}
|
|
|
|
type validationResult struct {
|
|
|
|
hasFailed bool
|
|
|
|
errorMessages map[string]string
|
|
|
|
}
|
|
|
|
|
|
|
|
var vr validationResult
|
|
|
|
var v *Validator
|
|
|
|
|
|
|
|
func newValidator() *Validator {
|
|
|
|
v := &Validator{}
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
func resolveValidator() *Validator {
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Validator) Validate(data map[string]interface{}, rules map[string]interface{}) validationResult {
|
|
|
|
vr = validationResult{}
|
|
|
|
vr.hasFailed = false
|
|
|
|
res := map[string]string{}
|
2024-09-28 18:40:42 -04:00
|
|
|
for rule_key, rule_val := range rules {
|
|
|
|
rls, err := parseRules(rule_val)
|
2024-09-12 18:13:16 -04:00
|
|
|
if err != nil {
|
|
|
|
panic(err.Error())
|
|
|
|
}
|
2024-09-28 18:40:42 -04:00
|
|
|
err = validation.Validate(data[rule_key], rls...)
|
2024-09-12 18:13:16 -04:00
|
|
|
if err != nil {
|
2024-09-28 18:40:42 -04:00
|
|
|
res[rule_key] = fmt.Sprintf("%v: %v", rule_key, err.Error())
|
2024-09-12 18:13:16 -04:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(res) != 0 {
|
|
|
|
vr.hasFailed = true
|
|
|
|
vr.errorMessages = res
|
|
|
|
}
|
|
|
|
return vr
|
|
|
|
}
|
|
|
|
|
|
|
|
func (vr *validationResult) Failed() bool {
|
|
|
|
return vr.hasFailed
|
|
|
|
}
|
|
|
|
|
|
|
|
func (vr *validationResult) GetErrorMessagesMap() map[string]string {
|
|
|
|
return vr.errorMessages
|
|
|
|
}
|
|
|
|
|
|
|
|
func (vr *validationResult) GetErrorMessagesJson() string {
|
|
|
|
j, err := json.Marshal(vr.GetErrorMessagesMap())
|
|
|
|
if err != nil {
|
|
|
|
panic("error converting validation error messages to json")
|
|
|
|
}
|
|
|
|
return string(j)
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseRules(rawRules interface{}) ([]validation.Rule, error) {
|
|
|
|
var res []validation.Rule
|
|
|
|
rulesStr, ok := rawRules.(string)
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("invalid validation rule")
|
|
|
|
}
|
|
|
|
rules := strings.Split(rulesStr, "|")
|
|
|
|
for _, rule := range rules {
|
|
|
|
rule = strings.TrimSpace(rule)
|
|
|
|
r, err := getRule(rule)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
res = append(res, r)
|
|
|
|
}
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func getRule(rule string) (validation.Rule, error) {
|
|
|
|
switch {
|
|
|
|
case strings.Contains(rule, "max:"):
|
|
|
|
return getRuleMax(rule)
|
|
|
|
case strings.Contains(rule, "min:"):
|
|
|
|
return getRuleMin(rule)
|
|
|
|
case strings.Contains(rule, "in:"):
|
|
|
|
return getRuleIn(rule)
|
|
|
|
case strings.Contains(rule, "dateLayout:"):
|
|
|
|
return getRuleDateLayout(rule)
|
|
|
|
case strings.Contains(rule, "length:"):
|
|
|
|
return getRuleLength(rule)
|
|
|
|
}
|
|
|
|
|
|
|
|
switch rule {
|
|
|
|
case "required":
|
|
|
|
return validation.Required, nil
|
|
|
|
case "email":
|
|
|
|
return is.Email, nil
|
|
|
|
case "url":
|
|
|
|
return is.URL, nil
|
|
|
|
case "alpha":
|
|
|
|
return is.Alpha, nil
|
|
|
|
case "digit":
|
|
|
|
return is.Digit, nil
|
|
|
|
case "alphaNumeric":
|
|
|
|
return is.Alphanumeric, nil
|
|
|
|
case "lowerCase":
|
|
|
|
return is.LowerCase, nil
|
|
|
|
case "upperCase":
|
|
|
|
return is.UpperCase, nil
|
|
|
|
case "int":
|
|
|
|
return is.Int, nil
|
|
|
|
case "float":
|
|
|
|
return is.Float, nil
|
|
|
|
case "uuid":
|
|
|
|
return is.UUID, nil
|
|
|
|
case "creditCard":
|
|
|
|
return is.CreditCard, nil
|
|
|
|
case "json":
|
|
|
|
return is.JSON, nil
|
|
|
|
case "base64":
|
|
|
|
return is.Base64, nil
|
|
|
|
case "countryCode2":
|
|
|
|
return is.CountryCode2, nil
|
|
|
|
case "countryCode3":
|
|
|
|
return is.CountryCode3, nil
|
|
|
|
case "isoCurrencyCode":
|
|
|
|
return is.CurrencyCode, nil
|
|
|
|
case "mac":
|
|
|
|
return is.MAC, nil
|
|
|
|
case "ip":
|
|
|
|
return is.IP, nil
|
|
|
|
case "ipv4":
|
|
|
|
return is.IPv4, nil
|
|
|
|
case "ipv6":
|
|
|
|
return is.IPv6, nil
|
|
|
|
case "subdomain":
|
|
|
|
return is.Subdomain, nil
|
|
|
|
case "domain":
|
|
|
|
return is.Domain, nil
|
|
|
|
case "dnsName":
|
|
|
|
return is.DNSName, nil
|
|
|
|
case "host":
|
|
|
|
return is.Host, nil
|
|
|
|
case "port":
|
|
|
|
return is.Port, nil
|
|
|
|
case "mongoID":
|
|
|
|
return is.MongoID, nil
|
|
|
|
case "latitude":
|
|
|
|
return is.Latitude, nil
|
|
|
|
case "longitude":
|
|
|
|
return is.Longitude, nil
|
|
|
|
case "ssn":
|
|
|
|
return is.SSN, nil
|
|
|
|
case "semver":
|
|
|
|
return is.Semver, nil
|
|
|
|
default:
|
|
|
|
err := errors.New(fmt.Sprintf("invalid validation rule: %v", rule))
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func getRuleMax(rule string) (validation.Rule, error) {
|
|
|
|
// max: 44
|
|
|
|
rr := strings.ReplaceAll(rule, "max:", "")
|
|
|
|
m := strings.TrimSpace(rr)
|
|
|
|
n, err := strconv.ParseInt(m, 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
err := errors.New("invalid value for validation rule 'max'")
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return validation.Max(n), err
|
|
|
|
}
|
|
|
|
|
|
|
|
func getRuleMin(rule string) (validation.Rule, error) {
|
|
|
|
// min: 33
|
|
|
|
rr := strings.ReplaceAll(rule, "min:", "")
|
|
|
|
m := strings.TrimSpace(rr)
|
|
|
|
n, err := strconv.ParseInt(m, 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
err := errors.New("invalid value for validation rule 'min'")
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return validation.Min(n), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func getRuleIn(rule string) (validation.Rule, error) {
|
|
|
|
// in: first, second, third
|
|
|
|
var readyElms []interface{}
|
|
|
|
rr := strings.ReplaceAll(rule, "in:", "")
|
|
|
|
elms := strings.Split(rr, ",")
|
|
|
|
for _, elm := range elms {
|
|
|
|
readyElms = append(readyElms, strings.TrimSpace(elm))
|
|
|
|
}
|
|
|
|
return validation.In(readyElms...), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// example date layouts: https://programming.guide/go/format-parse-string-time-date-example.html
|
|
|
|
func getRuleDateLayout(rule string) (validation.Rule, error) {
|
|
|
|
// dateLayout: 02 January 2006
|
|
|
|
rr := rule
|
|
|
|
rr = strings.TrimSpace(strings.Replace(rr, "dateLayout:", "", -1))
|
|
|
|
return validation.Date(rr), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func getRuleLength(rule string) (validation.Rule, error) {
|
|
|
|
// length: 3, 7
|
|
|
|
rr := rule
|
|
|
|
rr = strings.Replace(rr, "length:", "", -1)
|
|
|
|
lengthRange := strings.Split(rr, ",")
|
|
|
|
if len(lengthRange) < 0 {
|
|
|
|
err := errors.New("min value is not set for validation rule 'length'")
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
min, err := strconv.Atoi(strings.TrimSpace(lengthRange[0]))
|
|
|
|
if err != nil {
|
|
|
|
err := errors.New("min value is not set for validation rule 'length'")
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if len(lengthRange) < 1 {
|
|
|
|
err := errors.New("max value is not set for validation rule 'length'")
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
max, err := strconv.Atoi(strings.TrimSpace(lengthRange[1]))
|
|
|
|
if err != nil {
|
|
|
|
err := errors.New("max value is not set for validation rule 'length'")
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return validation.Length(min, max), nil
|
|
|
|
}
|