-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathvalidator.go
133 lines (108 loc) · 3.34 KB
/
validator.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package webutils
import (
"fmt"
"reflect"
"regexp"
"strings"
qerrors "github.com/cyberhorsey/errors"
"github.com/go-playground/locales/en"
ut "github.com/go-playground/universal-translator"
govalidator "github.com/go-playground/validator/v10"
en_translations "github.com/go-playground/validator/v10/translations/en"
"github.com/labstack/gommon/log"
)
// Use a single instance of the validator structs to cache the structs
var (
Validator *govalidator.Validate
utranslator *ut.UniversalTranslator
translator ut.Translator
)
func init() {
en := en.New()
utranslator = ut.New(en, en)
translator, _ = utranslator.GetTranslator("en")
Validator = govalidator.New()
_ = en_translations.RegisterDefaultTranslations(Validator, translator)
Validator.RegisterTagNameFunc(jsonTagName)
}
func jsonTagName(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
if name == "-" {
return ""
}
return name
}
// Validate data using the provided validate struct tags per github.com/go-playground/validator
func Validate(data interface{}) []error {
errs := make([]error, 0)
err := Validator.Struct(data)
if err == nil {
return nil
}
for _, fieldErr := range err.(govalidator.ValidationErrors) {
log.Errorf("validation error: %v", err)
errs = append(errs, qerrors.Validation.NewWithDetail(ValidationErrorDetail(fieldErr)))
}
return errs
}
// ValidationErrorDetail returns the validation error description for fieldErr
func ValidationErrorDetail(fieldErr govalidator.FieldError) string {
tag := fieldErr.Tag()
msg := ""
switch tag {
case "required":
msg = fmt.Sprintf("%v is required", fieldErr.Field())
case "required_without":
msg = fmt.Sprintf(
"%v is required unless %v is provided",
underscore(fieldErr.Field()),
underscore(fieldErr.Param()),
)
case "required_if":
var requiredIfCondition string
// required_if=SomeField value SomeOtherField value2
if params := strings.Split(fieldErr.Param(), " "); len(params) > 1 {
var requiredIfConditions []string
for i := 0; i < len(params); i += 2 {
requiredIfConditions = append(
requiredIfConditions,
fmt.Sprintf("%v is %v", underscore(params[i]), params[i+1]),
)
}
requiredIfCondition = strings.Join(requiredIfConditions, ", ")
} else {
requiredIfCondition = fieldErr.Param()
}
msg = fmt.Sprintf(
"%v is required if %v",
underscore(fieldErr.Field()),
requiredIfCondition,
)
default:
msg = fieldErr.Translate(translator)
}
return msg
}
// Underscore a camel case string
// Inspired by: https://play.golang.org/p/z3_83edIpG
func underscore(s string) string {
// remove all spaces/tabs followed by uppercase character
spaceRegexp := regexp.MustCompile("[[:space:][:blank:]]([A-Z]+)")
spaceless := spaceRegexp.ReplaceAll([]byte(s), []byte("$1"))
s = string(spaceless)
// convert all spaces/tabs not followed by capital letter to underscore
spaceRegexp = regexp.MustCompile("[[:space:][:blank:]]([^A-Z]+)")
spaceless = spaceRegexp.ReplaceAll([]byte(s), []byte("_$1"))
s = string(spaceless)
camelRegexp := regexp.MustCompile("(^[^A-Z]*|[A-Z]*)([A-Z][^A-Z]+|$)")
var humps []string
for _, sub := range camelRegexp.FindAllStringSubmatch(s, -1) {
if sub[1] != "" {
humps = append(humps, sub[1])
}
if sub[2] != "" {
humps = append(humps, sub[2])
}
}
return strings.ToLower(strings.Join(humps, "_"))
}