1
16
17 package validation
18
19 import (
20 "reflect"
21 "sort"
22
23 "k8s.io/kube-openapi/pkg/util/proto"
24 )
25
26 type validationItem interface {
27 proto.SchemaVisitor
28
29 Errors() []error
30 Path() *proto.Path
31 }
32
33 type baseItem struct {
34 errors errors
35 path proto.Path
36 }
37
38
39 func (item *baseItem) Errors() []error {
40 return item.errors.Errors()
41 }
42
43
44
45 func (item *baseItem) AddValidationError(err error) {
46 item.errors.AppendErrors(ValidationError{Path: item.path.String(), Err: err})
47 }
48
49
50 func (item *baseItem) AddError(err error) {
51 item.errors.AppendErrors(err)
52 }
53
54
55
56 func (item *baseItem) CopyErrors(errs []error) {
57 item.errors.AppendErrors(errs...)
58 }
59
60
61 func (item *baseItem) Path() *proto.Path {
62 return &item.path
63 }
64
65
66 type mapItem struct {
67 baseItem
68
69 Map map[string]interface{}
70 }
71
72 func (item *mapItem) sortedKeys() []string {
73 sortedKeys := []string{}
74 for key := range item.Map {
75 sortedKeys = append(sortedKeys, key)
76 }
77 sort.Strings(sortedKeys)
78 return sortedKeys
79 }
80
81 var _ validationItem = &mapItem{}
82
83 func (item *mapItem) VisitPrimitive(schema *proto.Primitive) {
84 item.AddValidationError(InvalidTypeError{Path: schema.GetPath().String(), Expected: schema.Type, Actual: "map"})
85 }
86
87 func (item *mapItem) VisitArray(schema *proto.Array) {
88 item.AddValidationError(InvalidTypeError{Path: schema.GetPath().String(), Expected: "array", Actual: "map"})
89 }
90
91 func (item *mapItem) VisitMap(schema *proto.Map) {
92 for _, key := range item.sortedKeys() {
93 subItem, err := itemFactory(item.Path().FieldPath(key), item.Map[key])
94 if err != nil {
95 item.AddError(err)
96 continue
97 }
98 schema.SubType.Accept(subItem)
99 item.CopyErrors(subItem.Errors())
100 }
101 }
102
103 func (item *mapItem) VisitKind(schema *proto.Kind) {
104
105 for _, key := range item.sortedKeys() {
106 if item.Map[key] == nil {
107 continue
108 }
109 subItem, err := itemFactory(item.Path().FieldPath(key), item.Map[key])
110 if err != nil {
111 item.AddError(err)
112 continue
113 }
114 if _, ok := schema.Fields[key]; !ok {
115 item.AddValidationError(UnknownFieldError{Path: schema.GetPath().String(), Field: key})
116 continue
117 }
118 schema.Fields[key].Accept(subItem)
119 item.CopyErrors(subItem.Errors())
120 }
121
122
123 for _, required := range schema.RequiredFields {
124 if v, ok := item.Map[required]; !ok || v == nil {
125 item.AddValidationError(MissingRequiredFieldError{Path: schema.GetPath().String(), Field: required})
126 }
127 }
128 }
129
130 func (item *mapItem) VisitArbitrary(schema *proto.Arbitrary) {
131 }
132
133 func (item *mapItem) VisitReference(schema proto.Reference) {
134
135 schema.SubSchema().Accept(item)
136 }
137
138
139 type arrayItem struct {
140 baseItem
141
142 Array []interface{}
143 }
144
145 var _ validationItem = &arrayItem{}
146
147 func (item *arrayItem) VisitPrimitive(schema *proto.Primitive) {
148 item.AddValidationError(InvalidTypeError{Path: schema.GetPath().String(), Expected: schema.Type, Actual: "array"})
149 }
150
151 func (item *arrayItem) VisitArray(schema *proto.Array) {
152 for i, v := range item.Array {
153 path := item.Path().ArrayPath(i)
154 if v == nil {
155 item.AddValidationError(InvalidObjectTypeError{Type: "nil", Path: path.String()})
156 continue
157 }
158 subItem, err := itemFactory(path, v)
159 if err != nil {
160 item.AddError(err)
161 continue
162 }
163 schema.SubType.Accept(subItem)
164 item.CopyErrors(subItem.Errors())
165 }
166 }
167
168 func (item *arrayItem) VisitMap(schema *proto.Map) {
169 item.AddValidationError(InvalidTypeError{Path: schema.GetPath().String(), Expected: "map", Actual: "array"})
170 }
171
172 func (item *arrayItem) VisitKind(schema *proto.Kind) {
173 item.AddValidationError(InvalidTypeError{Path: schema.GetPath().String(), Expected: "map", Actual: "array"})
174 }
175
176 func (item *arrayItem) VisitArbitrary(schema *proto.Arbitrary) {
177 }
178
179 func (item *arrayItem) VisitReference(schema proto.Reference) {
180
181 schema.SubSchema().Accept(item)
182 }
183
184
185 type primitiveItem struct {
186 baseItem
187
188 Value interface{}
189 Kind string
190 }
191
192 var _ validationItem = &primitiveItem{}
193
194 func (item *primitiveItem) VisitPrimitive(schema *proto.Primitive) {
195
196
197
198
199
200 switch schema.Type {
201 case proto.Boolean:
202 switch item.Kind {
203 case proto.Boolean:
204 return
205 }
206 case proto.Integer:
207 switch item.Kind {
208 case proto.Integer, proto.Number:
209 return
210 }
211 case proto.Number:
212 switch item.Kind {
213 case proto.Integer, proto.Number:
214 return
215 }
216 case proto.String:
217 return
218 }
219
220
221 item.AddValidationError(InvalidTypeError{Path: schema.GetPath().String(), Expected: schema.Type, Actual: item.Kind})
222 }
223
224 func (item *primitiveItem) VisitArray(schema *proto.Array) {
225 item.AddValidationError(InvalidTypeError{Path: schema.GetPath().String(), Expected: "array", Actual: item.Kind})
226 }
227
228 func (item *primitiveItem) VisitMap(schema *proto.Map) {
229 item.AddValidationError(InvalidTypeError{Path: schema.GetPath().String(), Expected: "map", Actual: item.Kind})
230 }
231
232 func (item *primitiveItem) VisitKind(schema *proto.Kind) {
233 item.AddValidationError(InvalidTypeError{Path: schema.GetPath().String(), Expected: "map", Actual: item.Kind})
234 }
235
236 func (item *primitiveItem) VisitArbitrary(schema *proto.Arbitrary) {
237 }
238
239 func (item *primitiveItem) VisitReference(schema proto.Reference) {
240
241 schema.SubSchema().Accept(item)
242 }
243
244
245 func itemFactory(path proto.Path, v interface{}) (validationItem, error) {
246
247 if v == nil {
248 return nil, InvalidObjectTypeError{Type: "nil", Path: path.String()}
249 }
250 kind := reflect.TypeOf(v).Kind()
251 switch kind {
252 case reflect.Bool:
253 return &primitiveItem{
254 baseItem: baseItem{path: path},
255 Value: v,
256 Kind: proto.Boolean,
257 }, nil
258 case reflect.Int,
259 reflect.Int8,
260 reflect.Int16,
261 reflect.Int32,
262 reflect.Int64,
263 reflect.Uint,
264 reflect.Uint8,
265 reflect.Uint16,
266 reflect.Uint32,
267 reflect.Uint64:
268 return &primitiveItem{
269 baseItem: baseItem{path: path},
270 Value: v,
271 Kind: proto.Integer,
272 }, nil
273 case reflect.Float32,
274 reflect.Float64:
275 return &primitiveItem{
276 baseItem: baseItem{path: path},
277 Value: v,
278 Kind: proto.Number,
279 }, nil
280 case reflect.String:
281 return &primitiveItem{
282 baseItem: baseItem{path: path},
283 Value: v,
284 Kind: proto.String,
285 }, nil
286 case reflect.Array,
287 reflect.Slice:
288 return &arrayItem{
289 baseItem: baseItem{path: path},
290 Array: v.([]interface{}),
291 }, nil
292 case reflect.Map:
293 return &mapItem{
294 baseItem: baseItem{path: path},
295 Map: v.(map[string]interface{}),
296 }, nil
297 }
298 return nil, InvalidObjectTypeError{Type: kind.String(), Path: path.String()}
299 }
300
View as plain text