-
Notifications
You must be signed in to change notification settings - Fork 11
/
check.go
195 lines (161 loc) · 4.66 KB
/
check.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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
// Package check allows data validation of values in different types
package check
import (
"fmt"
"reflect"
"strings"
)
var (
// ErrorMessages contains default error messages
ErrorMessages = map[string]string{
"nonZero": "value cannot be empty",
"before": "%v is not before %v",
"after": "%v is not after %v",
"lowerThan": "%v is not lower than %v",
"greaterThan": "%v is not greater than %v",
"minChar": "too short, minimum %v characters",
"maxChar": "too long, minimum %v characters",
"email": "'%v' is an invalid email address",
"regex": "'%v' does not match '%v'",
"uuid": "'%v' is an invalid uuid",
}
)
// Validator is an interface for constraint types with a method of validate()
type Validator interface {
// Validate check value against constraints
Validate(v interface{}) Error
}
// Struct allows validation of structs
type Struct map[string]Validator
// Validate execute validation using the validators.
func (s Struct) Validate(v interface{}) StructError {
val := reflect.ValueOf(v)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
if val.Kind() != reflect.Struct {
panic("not a struct")
}
e := StructError{}
for fieldname, validator := range s {
field := val.FieldByName(fieldname)
if field.Kind() == reflect.Invalid {
panic("invalid struct field name \"" + fieldname + "\".")
}
if err := validator.Validate(field.Interface()); err != nil {
if _, ok := e[fieldname]; !ok {
e[fieldname] = make([]Error, 0)
}
e[fieldname] = append(e[fieldname], err)
}
}
return e
}
// Composite allows adding multiple validators to the same value
type Composite []Validator
// Validate implements Validator
func (c Composite) Validate(v interface{}) Error {
e := ValidationError{
errorMap: make(map[string][]interface{}),
}
for _, validator := range c {
if err := validator.Validate(v); err != nil {
errMap := err.ErrorMap()
for msg, params := range errMap {
e.errorMap[msg] = params
}
}
}
if len(e.errorMap) == 0 {
return nil
}
return e
}
// Error is the default validation error. The Params() method returns the params
// to be used in error messages
type Error interface {
Error() string
ErrorMap() map[string][]interface{}
}
// ValidationError implements Error
type ValidationError struct {
errorMap map[string][]interface{}
}
func (e ValidationError) Error() string {
var errs []string
for msg := range e.errorMap {
errs = append(errs, msg)
}
return strings.Join(errs, "; ")
}
// ErrorMap returns the error map
func (e ValidationError) ErrorMap() map[string][]interface{} { return e.errorMap }
// NewValidationError create and return a ValidationError
func NewValidationError(key string, params ...interface{}) *ValidationError {
return &ValidationError{
map[string][]interface{}{
key: params,
},
}
}
// StructError is a map with validation errors
type StructError map[string][]Error
// HasErrors return true if the ErrorMap has errors
func (e StructError) HasErrors() bool {
return len(e) > 0
}
// Error satisfy error interface. Will return a concatenated
// strings of errors separated by "\n"
func (e StructError) Error() string {
var s []string
for _, v := range e {
for _, err := range v {
s = append(s, err.Error())
}
}
return strings.Join(s, "\n")
}
// GetErrorsByKey return all errors for a specificed key
func (e StructError) GetErrorsByKey(key string) ([]Error, bool) {
v, ok := e[key]
return v, ok
}
// ToMessages convert StructError to a map of field and their validation key with proper error messages
func (e StructError) ToMessages() map[string]map[string]string {
errMessages := make(map[string]map[string]string)
for field, validationErrors := range e {
errMessages[field] = make(map[string]string)
for _, err := range validationErrors {
for key, params := range err.ErrorMap() {
msg, ok := ErrorMessages[key]
if !ok {
errMessages[field][key] = "invalid data"
} else {
if len(params) > 0 {
errMessages[field][key] = fmt.Sprintf(msg, params...)
} else {
errMessages[field][key] = msg
}
}
}
}
}
return errMessages
}
// NonEmpty check that the value is not a zeroed value depending on its type
type NonEmpty struct{}
// Validate value to not be a zeroed value, return error and empty slice of strings
func (validator NonEmpty) Validate(v interface{}) Error {
t := reflect.TypeOf(v)
switch t.Kind() {
default:
if reflect.DeepEqual(reflect.Zero(t).Interface(), v) {
return NewValidationError("nonZero")
}
case reflect.Array, reflect.Slice, reflect.Map, reflect.Chan, reflect.String:
if reflect.ValueOf(v).Len() == 0 {
return NewValidationError("nonZero")
}
}
return nil
}