1 package jsonschema
2
3 import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "reflect"
8 "strconv"
9
10 jptr "github.com/qri-io/jsonpointer"
11 )
12
13
14 type Items struct {
15 single bool
16 Schemas []*Schema
17 }
18
19
20 func NewItems() Keyword {
21 return &Items{}
22 }
23
24
25 func (it *Items) Register(uri string, registry *SchemaRegistry) {
26 for _, v := range it.Schemas {
27 v.Register(uri, registry)
28 }
29 }
30
31
32 func (it *Items) Resolve(pointer jptr.Pointer, uri string) *Schema {
33 if pointer == nil {
34 return nil
35 }
36 current := pointer.Head()
37 if current == nil {
38 return nil
39 }
40
41 pos, err := strconv.Atoi(*current)
42 if err != nil {
43 return nil
44 }
45
46 if pos < 0 || pos >= len(it.Schemas) {
47 return nil
48 }
49
50 return it.Schemas[pos].Resolve(pointer.Tail(), uri)
51
52 return nil
53 }
54
55
56 func (it Items) ValidateKeyword(ctx context.Context, currentState *ValidationState, data interface{}) {
57 schemaDebug("[Items] Validating")
58 if arr, ok := data.([]interface{}); ok {
59 if it.single {
60 subState := currentState.NewSubState()
61 subState.DescendBase("items")
62 subState.DescendRelative("items")
63 for i, elem := range arr {
64 subState.ClearState()
65 subState.DescendInstanceFromState(currentState, strconv.Itoa(i))
66 it.Schemas[0].ValidateKeyword(ctx, subState, elem)
67 subState.SetEvaluatedIndex(i)
68
69
70 currentState.UpdateEvaluatedPropsAndItems(subState)
71 }
72 } else {
73 subState := currentState.NewSubState()
74 subState.DescendBase("items")
75 for i, vs := range it.Schemas {
76 if i < len(arr) {
77 subState.ClearState()
78 subState.DescendRelativeFromState(currentState, "items", strconv.Itoa(i))
79 subState.DescendInstanceFromState(currentState, strconv.Itoa(i))
80
81 vs.ValidateKeyword(ctx, subState, arr[i])
82 subState.SetEvaluatedIndex(i)
83 currentState.UpdateEvaluatedPropsAndItems(subState)
84 }
85 }
86 }
87 }
88 }
89
90
91 func (it Items) JSONProp(name string) interface{} {
92 idx, err := strconv.Atoi(name)
93 if err != nil {
94 return nil
95 }
96 if idx > len(it.Schemas) || idx < 0 {
97 return nil
98 }
99 return it.Schemas[idx]
100 }
101
102
103 func (it Items) JSONChildren() (res map[string]JSONPather) {
104 res = map[string]JSONPather{}
105 for i, sch := range it.Schemas {
106 res[strconv.Itoa(i)] = sch
107 }
108 return
109 }
110
111
112 func (it *Items) UnmarshalJSON(data []byte) error {
113 s := &Schema{}
114 if err := json.Unmarshal(data, s); err == nil {
115 *it = Items{single: true, Schemas: []*Schema{s}}
116 return nil
117 }
118 ss := []*Schema{}
119 if err := json.Unmarshal(data, &ss); err != nil {
120 return err
121 }
122 *it = Items{Schemas: ss}
123 return nil
124 }
125
126
127 func (it Items) MarshalJSON() ([]byte, error) {
128 if it.single {
129 return json.Marshal(it.Schemas[0])
130 }
131 return json.Marshal([]*Schema(it.Schemas))
132 }
133
134
135 type MaxItems int
136
137
138 func NewMaxItems() Keyword {
139 return new(MaxItems)
140 }
141
142
143 func (m *MaxItems) Register(uri string, registry *SchemaRegistry) {}
144
145
146 func (m *MaxItems) Resolve(pointer jptr.Pointer, uri string) *Schema {
147 return nil
148 }
149
150
151 func (m MaxItems) ValidateKeyword(ctx context.Context, currentState *ValidationState, data interface{}) {
152 schemaDebug("[MaxItems] Validating")
153 if arr, ok := data.([]interface{}); ok {
154 if len(arr) > int(m) {
155 currentState.AddError(data, fmt.Sprintf("array length %d exceeds %d max", len(arr), m))
156 return
157 }
158 }
159 }
160
161
162 type MinItems int
163
164
165 func NewMinItems() Keyword {
166 return new(MinItems)
167 }
168
169
170 func (m *MinItems) Register(uri string, registry *SchemaRegistry) {}
171
172
173 func (m *MinItems) Resolve(pointer jptr.Pointer, uri string) *Schema {
174 return nil
175 }
176
177
178 func (m MinItems) ValidateKeyword(ctx context.Context, currentState *ValidationState, data interface{}) {
179 schemaDebug("[MinItems] Validating")
180 if arr, ok := data.([]interface{}); ok {
181 if len(arr) < int(m) {
182 currentState.AddError(data, fmt.Sprintf("array length %d below %d minimum items", len(arr), m))
183 return
184 }
185 }
186 }
187
188
189 type UniqueItems bool
190
191
192 func NewUniqueItems() Keyword {
193 return new(UniqueItems)
194 }
195
196
197 func (u *UniqueItems) Register(uri string, registry *SchemaRegistry) {}
198
199
200 func (u *UniqueItems) Resolve(pointer jptr.Pointer, uri string) *Schema {
201 return nil
202 }
203
204
205 func (u UniqueItems) ValidateKeyword(ctx context.Context, currentState *ValidationState, data interface{}) {
206 schemaDebug("[UniqueItems] Validating")
207 if arr, ok := data.([]interface{}); ok {
208 found := []interface{}{}
209 for _, elem := range arr {
210 for _, f := range found {
211 if reflect.DeepEqual(f, elem) {
212 currentState.AddError(data, fmt.Sprintf("array items must be unique. duplicated entry: %v", elem))
213 return
214 }
215 }
216 found = append(found, elem)
217 }
218 }
219 }
220
221
222 type Contains Schema
223
224
225 func NewContains() Keyword {
226 return &Contains{}
227 }
228
229
230 func (c *Contains) Register(uri string, registry *SchemaRegistry) {
231 (*Schema)(c).Register(uri, registry)
232 }
233
234
235 func (c *Contains) Resolve(pointer jptr.Pointer, uri string) *Schema {
236 return (*Schema)(c).Resolve(pointer, uri)
237 }
238
239
240 func (c *Contains) ValidateKeyword(ctx context.Context, currentState *ValidationState, data interface{}) {
241 schemaDebug("[Contains] Validating")
242 v := Schema(*c)
243 if arr, ok := data.([]interface{}); ok {
244 valid := false
245 matchCount := 0
246 subState := currentState.NewSubState()
247 subState.ClearState()
248 subState.DescendBase("contains")
249 subState.DescendRelative("contains")
250 for i, elem := range arr {
251 subState.ClearState()
252 subState.DescendInstanceFromState(currentState, strconv.Itoa(i))
253 subState.Errs = &[]KeyError{}
254 v.ValidateKeyword(ctx, subState, elem)
255 if subState.IsValid() {
256 valid = true
257 matchCount++
258 }
259 }
260 if valid {
261 currentState.Misc["containsCount"] = matchCount
262 } else {
263 currentState.AddError(data, fmt.Sprintf("must contain at least one of: %v", c))
264 }
265 }
266 }
267
268
269 func (c Contains) JSONProp(name string) interface{} {
270 return Schema(c).JSONProp(name)
271 }
272
273
274 func (c Contains) JSONChildren() (res map[string]JSONPather) {
275 return Schema(c).JSONChildren()
276 }
277
278
279 func (c *Contains) UnmarshalJSON(data []byte) error {
280 var sch Schema
281 if err := json.Unmarshal(data, &sch); err != nil {
282 return err
283 }
284 *c = Contains(sch)
285 return nil
286 }
287
288
289 type MaxContains int
290
291
292 func NewMaxContains() Keyword {
293 return new(MaxContains)
294 }
295
296
297 func (m *MaxContains) Register(uri string, registry *SchemaRegistry) {}
298
299
300 func (m *MaxContains) Resolve(pointer jptr.Pointer, uri string) *Schema {
301 return nil
302 }
303
304
305 func (m MaxContains) ValidateKeyword(ctx context.Context, currentState *ValidationState, data interface{}) {
306 schemaDebug("[MaxContains] Validating")
307 if arr, ok := data.([]interface{}); ok {
308 if containsCount, ok := currentState.Misc["containsCount"]; ok {
309 if containsCount.(int) > int(m) {
310 currentState.AddError(data, fmt.Sprintf("contained items %d exceeds %d max", len(arr), m))
311 }
312 }
313 }
314 }
315
316
317 type MinContains int
318
319
320 func NewMinContains() Keyword {
321 return new(MinContains)
322 }
323
324
325 func (m *MinContains) Register(uri string, registry *SchemaRegistry) {}
326
327
328 func (m *MinContains) Resolve(pointer jptr.Pointer, uri string) *Schema {
329 return nil
330 }
331
332
333 func (m MinContains) ValidateKeyword(ctx context.Context, currentState *ValidationState, data interface{}) {
334 schemaDebug("[MinContains] Validating")
335 if arr, ok := data.([]interface{}); ok {
336 if containsCount, ok := currentState.Misc["containsCount"]; ok {
337 if containsCount.(int) < int(m) {
338 currentState.AddError(data, fmt.Sprintf("contained items %d bellow %d min", len(arr), m))
339 }
340 }
341 }
342 }
343
344
345 type AdditionalItems Schema
346
347
348 func NewAdditionalItems() Keyword {
349 return &AdditionalItems{}
350 }
351
352
353 func (ai *AdditionalItems) Register(uri string, registry *SchemaRegistry) {
354 (*Schema)(ai).Register(uri, registry)
355 }
356
357
358 func (ai *AdditionalItems) Resolve(pointer jptr.Pointer, uri string) *Schema {
359 return (*Schema)(ai).Resolve(pointer, uri)
360 }
361
362
363 func (ai *AdditionalItems) ValidateKeyword(ctx context.Context, currentState *ValidationState, data interface{}) {
364 schemaDebug("[AdditionalItems] Validating")
365 if arr, ok := data.([]interface{}); ok {
366 if currentState.LastEvaluatedIndex > -1 && currentState.LastEvaluatedIndex < len(arr) {
367 for i := currentState.LastEvaluatedIndex + 1; i < len(arr); i++ {
368 if ai.schemaType == schemaTypeFalse {
369 currentState.AddError(data, "additional items are not allowed")
370 return
371 }
372 subState := currentState.NewSubState()
373 subState.ClearState()
374 subState.SetEvaluatedIndex(i)
375 subState.DescendBase("additionalItems")
376 subState.DescendRelative("additionalItems")
377 subState.DescendInstance(strconv.Itoa(i))
378
379 (*Schema)(ai).ValidateKeyword(ctx, subState, arr[i])
380 currentState.UpdateEvaluatedPropsAndItems(subState)
381 }
382 }
383 }
384 }
385
386
387 func (ai *AdditionalItems) UnmarshalJSON(data []byte) error {
388 sch := &Schema{}
389 if err := json.Unmarshal(data, sch); err != nil {
390 return err
391 }
392 *ai = (AdditionalItems)(*sch)
393 return nil
394 }
395
396
397 type UnevaluatedItems Schema
398
399
400 func NewUnevaluatedItems() Keyword {
401 return &UnevaluatedItems{}
402 }
403
404
405 func (ui *UnevaluatedItems) Register(uri string, registry *SchemaRegistry) {
406 (*Schema)(ui).Register(uri, registry)
407 }
408
409
410 func (ui *UnevaluatedItems) Resolve(pointer jptr.Pointer, uri string) *Schema {
411 return (*Schema)(ui).Resolve(pointer, uri)
412 }
413
414
415 func (ui *UnevaluatedItems) ValidateKeyword(ctx context.Context, currentState *ValidationState, data interface{}) {
416 schemaDebug("[UnevaluatedItems] Validating")
417 if arr, ok := data.([]interface{}); ok {
418 if currentState.LastEvaluatedIndex < len(arr) {
419 for i := currentState.LastEvaluatedIndex + 1; i < len(arr); i++ {
420 if ui.schemaType == schemaTypeFalse {
421 currentState.AddError(data, "unevaluated items are not allowed")
422 return
423 }
424 subState := currentState.NewSubState()
425 subState.ClearState()
426 subState.DescendBase("unevaluatedItems")
427 subState.DescendRelative("unevaluatedItems")
428 subState.DescendInstance(strconv.Itoa(i))
429
430 (*Schema)(ui).ValidateKeyword(ctx, subState, arr[i])
431 }
432 }
433 }
434 }
435
436
437 func (ui *UnevaluatedItems) UnmarshalJSON(data []byte) error {
438 sch := &Schema{}
439 if err := json.Unmarshal(data, sch); err != nil {
440 return err
441 }
442 *ui = (UnevaluatedItems)(*sch)
443 return nil
444 }
445
View as plain text