-
Notifications
You must be signed in to change notification settings - Fork 1
/
example_partial_entity_validation_test.go
157 lines (134 loc) · 4.3 KB
/
example_partial_entity_validation_test.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
package validation_test
import (
"bytes"
"context"
"fmt"
"path/filepath"
"strings"
"github.com/muonsoft/validation"
"github.com/muonsoft/validation/it"
"github.com/muonsoft/validation/validator"
)
type File struct {
Name string
Data []byte
}
// This validation will always check that file is valid.
// Partial validation will be applied by AllowedFileExtensionConstraint
// and AllowedFileSizeConstraint.
func (f File) Validate(ctx context.Context, validator *validation.Validator) error {
return validator.Validate(
ctx,
validation.StringProperty("name", f.Name, it.HasLengthBetween(5, 50)),
)
}
type FileUploadRequest struct {
Section string
File *File
}
type FileConstraint interface {
ValidateFile(ctx context.Context, validator *validation.Validator, file *File) error
}
func ValidFile(file *File, constraints ...FileConstraint) validation.ValidatorArgument {
return validation.NewArgument(func(ctx context.Context, validator *validation.Validator) (*validation.ViolationList, error) {
violations := validation.NewViolationList()
for _, constraint := range constraints {
err := violations.AppendFromError(constraint.ValidateFile(ctx, validator, file))
if err != nil {
return nil, err
}
}
return violations, nil
})
}
// AllowedFileExtensionConstraint used to check that file has one of allowed extensions.
// This constraint can be used for partial validation.
type AllowedFileExtensionConstraint struct {
extensions []string
}
func FileHasAllowedExtension(extensions ...string) AllowedFileExtensionConstraint {
return AllowedFileExtensionConstraint{extensions: extensions}
}
func (c AllowedFileExtensionConstraint) ValidateFile(ctx context.Context, validator *validation.Validator, file *File) error {
if file == nil {
return nil
}
extension := strings.ReplaceAll(filepath.Ext(file.Name), ".", "")
return validator.AtProperty("name").Validate(
ctx,
validation.Comparable[string](
extension,
it.IsOneOf(c.extensions...).WithMessage("Not allowed extension. Must be one of: {{ choices }}."),
),
)
}
// AllowedFileSizeConstraint used to check that file has limited size.
// This constraint can be used for partial validation.
type AllowedFileSizeConstraint struct {
minSize int
maxSize int
}
func FileHasAllowedSize(min, max int) AllowedFileSizeConstraint {
return AllowedFileSizeConstraint{minSize: min, maxSize: max}
}
func (c AllowedFileSizeConstraint) ValidateFile(ctx context.Context, validator *validation.Validator, file *File) error {
if file == nil {
return nil
}
size := len(file.Data)
return validator.Validate(
ctx,
validation.Number[int](
size,
it.IsGreaterThan(c.minSize).WithMessage("File size is too small."),
it.IsLessThan(c.maxSize).WithMessage("File size is too large."),
),
)
}
func ExampleValidator_Validate_partialEntityValidation() {
// this constraints will be applied to all files uploaded as avatars
avatarConstraints := []FileConstraint{
FileHasAllowedExtension("jpeg", "jpg", "gif"),
FileHasAllowedSize(100, 1000),
}
// this constraints will be applied to all files uploaded as documents
documentConstraints := []FileConstraint{
FileHasAllowedExtension("doc", "pdf", "txt"),
FileHasAllowedSize(1000, 100000),
}
requests := []FileUploadRequest{
{
Section: "avatars",
File: &File{Name: "avatar.png", Data: bytes.Repeat([]byte{0}, 99)},
},
{
Section: "documents",
File: &File{Name: "sheet.xls", Data: bytes.Repeat([]byte{0}, 100001)},
},
}
for _, request := range requests {
switch request.Section {
case "avatars":
err := validator.Validate(
context.Background(),
// common validation of validatable
validation.Valid(request.File),
// specific validation for file storage section
ValidFile(request.File, avatarConstraints...),
)
fmt.Println(err)
case "documents":
err := validator.Validate(
context.Background(),
// common validation of validatable
validation.Valid(request.File),
// specific validation for file storage section
ValidFile(request.File, documentConstraints...),
)
fmt.Println(err)
}
}
// Output:
// violations: #0 at "name": "Not allowed extension. Must be one of: jpeg, jpg, gif."; #1: "File size is too small."
// violations: #0 at "name": "Not allowed extension. Must be one of: doc, pdf, txt."; #1: "File size is too large."
}