core/validator.go

243 lines
5.5 KiB
Go
Raw Normal View History

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{}
for key, val := range data {
_, ok := rules[key]
if !ok {
continue
}
rls, err := parseRules(rules[key])
if err != nil {
panic(err.Error())
}
err = validation.Validate(val, rls...)
if err != nil {
res[key] = fmt.Sprintf("%v: %v", key, err.Error())
}
}
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
}