-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathvalidator.go
151 lines (138 loc) · 4.44 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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// Package validator allows simple boundary checking of user input
// either against a struct or by checking values using the provided
// fluent API.
//
// It stores the output of any errors found in a usable structure that lists
// the field names and all validation errors associated with it.
//
// This allows the output to be serialised and returned in 400 responses or equivalent
// with readable and useful messages.
//
// It is designed to be simple to use and comes with a series of built in validation
// functions. You can add your own simply by wrapping the provided ValidationFunc.
package validator
import (
"fmt"
"sort"
"strings"
)
// Validator is an interface that can be implemented on a struct
// to run validation checks against its properties.
//
// This is designed to be used in http handlers or middleware where
// all requests can be checked for this behaviour and evaluated.
//
// type MyStruct struct{
// MyProp string
// ShouldBeTrue bool
// }
//
// func(m *MyStruct) Validate() error{
// return validator.New().
// Validate("myProp", validator.Length(m.MyProp,10,20)).
// Validate("shouldBeTrue", validator.Bool(m.ShouldBeTrue, true))
// }
//
type Validator interface {
Validate() ErrValidation
}
// ValidationFunc defines a simple function that can be wrapped
// and supplied with arguments.
//
// Typical usage is shown:
// func Length(val string, min, max int) ValidationFunc {
// return func() error {
// if len(val) >= min && len(val) <= max {
// return nil
// }
// return fmt.Errorf(validateLength, val, min, max)
// }
// }
type ValidationFunc func() error
// String satisfies the String interface and returns the underlying error
// string that is returned by evaluating the function.
func (v ValidationFunc) String() string {
return v().Error()
}
// ErrValidation contains a list of field names and a list of errors
// found against each. This can then be converted for output to a user.
type ErrValidation map[string][]string
// New will create and return a new ErrValidation which can have Validate functions chained.
func New() ErrValidation {
return map[string][]string{}
}
// Validate will log any errors found when evaluating the list of validation functions
// supplied to it.
func (e ErrValidation) Validate(field string, fns ...ValidationFunc) ErrValidation {
out := make([]string, 0)
for _, fn := range fns {
if err := fn(); err != nil {
out = append(out, err.Error())
}
}
if len(out) > 0 {
e[field] = out
}
return e
}
// Err will return nil if no errors are found, ie all validators return valid
// or ErrValidation if an error has been found.
func (e ErrValidation) Err() error {
if len(e) > 0 {
return e
}
return nil
}
// String implements the Stringer interface and
// will return a string based representation
// of any errors found.
func (e ErrValidation) String() string {
if len(e) == 0 {
return "no validation errors"
}
errs := make([]string, 0)
for k, vv := range e {
errs = append(errs, fmt.Sprintf("[%s: %s]", k, strings.Join(vv, ", ")))
}
sort.Strings(errs)
return strings.Join(errs, ", ")
}
// Error implements the Error interface and ensure that ErrValidation
// can be passed as an error as well and being printable.
func (e ErrValidation) Error() string {
return e.String()
}
// BadRequest implements the err BadRequest behaviour
// from the https://github.com/theflyingcodr/lathos package.
func (e ErrValidation) BadRequest() bool {
return true
}
// NewSingleError is a simple way of creating a one off error
// rather than calling a validate function.
// test, err := thing()
// if err != nil{
// // this error is froma. bad request, convert to a validation error
// return validator.NewSingleError("myField", []string{"this went wrong"})
// }
// This will then be classed as a validation error and handled how you desire.
func NewSingleError(fieldName string, msg []string) error {
return ErrValidation{
fieldName: msg,
}.Err()
}
// NewFromError is a simple way of creating a one off error
// rather than calling a validate function.
// test, err := thing()
// if err != nil{
// // this error is froma. bad request, convert to a validation error
// return validator.NewFromError("myField", err)
// }
// This will then be classed as a validation error and handled how you desire.
func NewFromError(fieldName string, err error) error {
if err == nil {
return nil
}
return ErrValidation{
fieldName: []string{err.Error()},
}.Err()
}