1 package jsonschema
2
3 import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "regexp"
8 "strconv"
9
10 jptr "github.com/qri-io/jsonpointer"
11 )
12
13
14 type Properties map[string]*Schema
15
16
17 func NewProperties() Keyword {
18 return &Properties{}
19 }
20
21
22 func (p *Properties) Register(uri string, registry *SchemaRegistry) {
23 for _, v := range *p {
24 v.Register(uri, registry)
25 }
26 }
27
28
29 func (p *Properties) Resolve(pointer jptr.Pointer, uri string) *Schema {
30 if pointer == nil {
31 return nil
32 }
33 current := pointer.Head()
34 if current == nil {
35 return nil
36 }
37
38 if schema, ok := (*p)[*current]; ok {
39 return schema.Resolve(pointer.Tail(), uri)
40 }
41
42 return nil
43 }
44
45
46 func (p Properties) ValidateKeyword(ctx context.Context, currentState *ValidationState, data interface{}) {
47 schemaDebug("[Properties] Validating")
48 if obj, ok := data.(map[string]interface{}); ok {
49 subState := currentState.NewSubState()
50 for key := range p {
51 if _, ok := obj[key]; ok {
52 currentState.SetEvaluatedKey(key)
53 subState.ClearState()
54 subState.DescendBaseFromState(currentState, "properties", key)
55 subState.DescendRelativeFromState(currentState, "properties", key)
56 subState.DescendInstanceFromState(currentState, key)
57
58 subState.Errs = &[]KeyError{}
59 p[key].ValidateKeyword(ctx, subState, obj[key])
60 currentState.AddSubErrors(*subState.Errs...)
61 if subState.IsValid() {
62 currentState.UpdateEvaluatedPropsAndItems(subState)
63 }
64 }
65 }
66 }
67 }
68
69
70 func (p Properties) JSONProp(name string) interface{} {
71 return p[name]
72 }
73
74
75 func (p Properties) JSONChildren() (res map[string]JSONPather) {
76 res = map[string]JSONPather{}
77 for key, sch := range p {
78 res[key] = sch
79 }
80 return
81 }
82
83
84 type Required []string
85
86
87 func NewRequired() Keyword {
88 return &Required{}
89 }
90
91
92 func (r *Required) Register(uri string, registry *SchemaRegistry) {}
93
94
95 func (r *Required) Resolve(pointer jptr.Pointer, uri string) *Schema {
96 return nil
97 }
98
99
100 func (r Required) ValidateKeyword(ctx context.Context, currentState *ValidationState, data interface{}) {
101 schemaDebug("[Required] Validating")
102 if obj, ok := data.(map[string]interface{}); ok {
103 for _, key := range r {
104 if _, ok := obj[key]; !ok {
105 currentState.AddError(data, fmt.Sprintf(`"%s" value is required`, key))
106 }
107 }
108 }
109 }
110
111
112 func (r Required) JSONProp(name string) interface{} {
113 idx, err := strconv.Atoi(name)
114 if err != nil {
115 return nil
116 }
117 if idx > len(r) || idx < 0 {
118 return nil
119 }
120 return r[idx]
121 }
122
123
124 type MaxProperties int
125
126
127 func NewMaxProperties() Keyword {
128 return new(MaxProperties)
129 }
130
131
132 func (m *MaxProperties) Register(uri string, registry *SchemaRegistry) {}
133
134
135 func (m *MaxProperties) Resolve(pointer jptr.Pointer, uri string) *Schema {
136 return nil
137 }
138
139
140 func (m MaxProperties) ValidateKeyword(ctx context.Context, currentState *ValidationState, data interface{}) {
141 schemaDebug("[MaxProperties] Validating")
142 if obj, ok := data.(map[string]interface{}); ok {
143 if len(obj) > int(m) {
144 currentState.AddError(data, fmt.Sprintf("%d object Properties exceed %d maximum", len(obj), m))
145 }
146 }
147 }
148
149
150 type MinProperties int
151
152
153 func NewMinProperties() Keyword {
154 return new(MinProperties)
155 }
156
157
158 func (m *MinProperties) Register(uri string, registry *SchemaRegistry) {}
159
160
161 func (m *MinProperties) Resolve(pointer jptr.Pointer, uri string) *Schema {
162 return nil
163 }
164
165
166 func (m MinProperties) ValidateKeyword(ctx context.Context, currentState *ValidationState, data interface{}) {
167 schemaDebug("[MinProperties] Validating")
168 if obj, ok := data.(map[string]interface{}); ok {
169 if len(obj) < int(m) {
170 currentState.AddError(data, fmt.Sprintf("%d object Properties below %d minimum", len(obj), m))
171 }
172 }
173 }
174
175
176 type PatternProperties []patternSchema
177
178
179 func NewPatternProperties() Keyword {
180 return &PatternProperties{}
181 }
182
183 type patternSchema struct {
184 key string
185 re *regexp.Regexp
186 schema *Schema
187 }
188
189
190 func (p *PatternProperties) Register(uri string, registry *SchemaRegistry) {
191 for _, v := range *p {
192 v.schema.Register(uri, registry)
193 }
194 }
195
196
197 func (p *PatternProperties) Resolve(pointer jptr.Pointer, uri string) *Schema {
198 if pointer == nil {
199 return nil
200 }
201 current := pointer.Head()
202 if current == nil {
203 return nil
204 }
205
206 patProp := &patternSchema{}
207
208 for _, v := range *p {
209 if v.key == *current {
210 patProp = &v
211 break
212 }
213 }
214
215 return patProp.schema.Resolve(pointer.Tail(), uri)
216 }
217
218
219 func (p PatternProperties) ValidateKeyword(ctx context.Context, currentState *ValidationState, data interface{}) {
220 schemaDebug("[PatternProperties] Validating")
221 if obj, ok := data.(map[string]interface{}); ok {
222 for key, val := range obj {
223 for _, ptn := range p {
224 if ptn.re.Match([]byte(key)) {
225 currentState.SetEvaluatedKey(key)
226 subState := currentState.NewSubState()
227 subState.DescendBase("patternProperties", key)
228 subState.DescendRelative("patternProperties", key)
229 subState.DescendInstance(key)
230
231 subState.Errs = &[]KeyError{}
232 ptn.schema.ValidateKeyword(ctx, subState, val)
233 currentState.AddSubErrors(*subState.Errs...)
234
235 if subState.IsValid() {
236 currentState.UpdateEvaluatedPropsAndItems(subState)
237 }
238 }
239 }
240 }
241 }
242 }
243
244
245 func (p PatternProperties) JSONProp(name string) interface{} {
246 for _, pp := range p {
247 if pp.key == name {
248 return pp.schema
249 }
250 }
251 return nil
252 }
253
254
255 func (p PatternProperties) JSONChildren() (res map[string]JSONPather) {
256 res = map[string]JSONPather{}
257 for i, pp := range p {
258 res[strconv.Itoa(i)] = pp.schema
259 }
260 return
261 }
262
263
264 func (p *PatternProperties) UnmarshalJSON(data []byte) error {
265 var props map[string]*Schema
266 if err := json.Unmarshal(data, &props); err != nil {
267 return err
268 }
269
270 ptn := make(PatternProperties, len(props))
271 i := 0
272 for key, sch := range props {
273 re, err := regexp.Compile(key)
274 if err != nil {
275 return fmt.Errorf("invalid pattern: %s: %s", key, err.Error())
276 }
277 ptn[i] = patternSchema{
278 key: key,
279 re: re,
280 schema: sch,
281 }
282 i++
283 }
284
285 *p = ptn
286 return nil
287 }
288
289
290 func (p PatternProperties) MarshalJSON() ([]byte, error) {
291 obj := map[string]interface{}{}
292 for _, prop := range p {
293 obj[prop.key] = prop.schema
294 }
295 return json.Marshal(obj)
296 }
297
298
299 type AdditionalProperties Schema
300
301
302 func NewAdditionalProperties() Keyword {
303 return &AdditionalProperties{}
304 }
305
306
307 func (ap *AdditionalProperties) Register(uri string, registry *SchemaRegistry) {
308 (*Schema)(ap).Register(uri, registry)
309 }
310
311
312 func (ap *AdditionalProperties) Resolve(pointer jptr.Pointer, uri string) *Schema {
313 return (*Schema)(ap).Resolve(pointer, uri)
314 }
315
316
317 func (ap *AdditionalProperties) ValidateKeyword(ctx context.Context, currentState *ValidationState, data interface{}) {
318 schemaDebug("[AdditionalProperties] Validating")
319 if obj, ok := data.(map[string]interface{}); ok {
320 subState := currentState.NewSubState()
321 subState.ClearState()
322 subState.DescendBase("additionalProperties")
323 subState.DescendRelative("additionalProperties")
324 for key := range obj {
325 if currentState.IsLocallyEvaluatedKey(key) {
326 continue
327 }
328 if ap.schemaType == schemaTypeFalse {
329 currentState.AddError(data, "additional properties are not allowed")
330 return
331 }
332 currentState.SetEvaluatedKey(key)
333 subState.ClearState()
334 subState.DescendInstanceFromState(currentState, key)
335
336 (*Schema)(ap).ValidateKeyword(ctx, subState, obj[key])
337 currentState.UpdateEvaluatedPropsAndItems(subState)
338 }
339 }
340 }
341
342
343 func (ap *AdditionalProperties) UnmarshalJSON(data []byte) error {
344 sch := &Schema{}
345 if err := json.Unmarshal(data, sch); err != nil {
346 return err
347 }
348 *ap = (AdditionalProperties)(*sch)
349 return nil
350 }
351
352
353 type PropertyNames Schema
354
355
356 func NewPropertyNames() Keyword {
357 return &PropertyNames{}
358 }
359
360
361 func (p *PropertyNames) Register(uri string, registry *SchemaRegistry) {
362 (*Schema)(p).Register(uri, registry)
363 }
364
365
366 func (p *PropertyNames) Resolve(pointer jptr.Pointer, uri string) *Schema {
367 return (*Schema)(p).Resolve(pointer, uri)
368 }
369
370
371 func (p *PropertyNames) ValidateKeyword(ctx context.Context, currentState *ValidationState, data interface{}) {
372 schemaDebug("[PropertyNames] Validating")
373 if obj, ok := data.(map[string]interface{}); ok {
374 for key := range obj {
375 subState := currentState.NewSubState()
376 subState.DescendBase("propertyNames")
377 subState.DescendRelative("propertyNames")
378 subState.DescendInstance(key)
379 (*Schema)(p).ValidateKeyword(ctx, subState, key)
380 }
381 }
382 }
383
384
385 func (p PropertyNames) JSONProp(name string) interface{} {
386 return Schema(p).JSONProp(name)
387 }
388
389
390 func (p PropertyNames) JSONChildren() (res map[string]JSONPather) {
391 return Schema(p).JSONChildren()
392 }
393
394
395 func (p *PropertyNames) UnmarshalJSON(data []byte) error {
396 var sch Schema
397 if err := json.Unmarshal(data, &sch); err != nil {
398 return err
399 }
400 *p = PropertyNames(sch)
401 return nil
402 }
403
404
405 func (p PropertyNames) MarshalJSON() ([]byte, error) {
406 return json.Marshal(Schema(p))
407 }
408
409
410 type DependentSchemas map[string]SchemaDependency
411
412
413 func NewDependentSchemas() Keyword {
414 return &DependentSchemas{}
415 }
416
417
418 func (d *DependentSchemas) Register(uri string, registry *SchemaRegistry) {
419 for _, v := range *d {
420 v.schema.Register(uri, registry)
421 }
422 }
423
424
425 func (d *DependentSchemas) Resolve(pointer jptr.Pointer, uri string) *Schema {
426 if pointer == nil {
427 return nil
428 }
429 current := pointer.Head()
430 if current == nil {
431 return nil
432 }
433
434 if schema, ok := (*d)[*current]; ok {
435 return schema.Resolve(pointer.Tail(), uri)
436 }
437
438 return nil
439 }
440
441
442 func (d *DependentSchemas) ValidateKeyword(ctx context.Context, currentState *ValidationState, data interface{}) {
443 schemaDebug("[DependentSchemas] Validating")
444 for _, v := range *d {
445 subState := currentState.NewSubState()
446 subState.DescendBase("dependentSchemas")
447 subState.DescendRelative("dependentSchemas")
448 subState.Misc["dependencyParent"] = "dependentSchemas"
449 v.ValidateKeyword(ctx, subState, data)
450 }
451 }
452
453 type _dependentSchemas map[string]Schema
454
455
456 func (d *DependentSchemas) UnmarshalJSON(data []byte) error {
457 _d := _dependentSchemas{}
458 if err := json.Unmarshal(data, &_d); err != nil {
459 return err
460 }
461 ds := DependentSchemas{}
462 for k, v := range _d {
463 sch := Schema(v)
464 ds[k] = SchemaDependency{
465 schema: &sch,
466 prop: k,
467 }
468 }
469 *d = ds
470 return nil
471 }
472
473
474 func (d DependentSchemas) JSONProp(name string) interface{} {
475 return d[name]
476 }
477
478
479 func (d DependentSchemas) JSONChildren() (r map[string]JSONPather) {
480 r = map[string]JSONPather{}
481 for key, val := range d {
482 r[key] = val
483 }
484 return
485 }
486
487
488 type SchemaDependency struct {
489 schema *Schema
490 prop string
491 }
492
493
494 func (d *SchemaDependency) Register(uri string, registry *SchemaRegistry) {
495 d.schema.Register(uri, registry)
496 }
497
498
499 func (d *SchemaDependency) Resolve(pointer jptr.Pointer, uri string) *Schema {
500 return d.schema.Resolve(pointer, uri)
501 }
502
503
504 func (d *SchemaDependency) ValidateKeyword(ctx context.Context, currentState *ValidationState, data interface{}) {
505 schemaDebug("[SchemaDependency] Validating")
506 depsData := map[string]interface{}{}
507 ok := false
508 if depsData, ok = data.(map[string]interface{}); !ok {
509 return
510 }
511 if _, okProp := depsData[d.prop]; !okProp {
512 return
513 }
514 subState := currentState.NewSubState()
515 subState.DescendBase(d.prop)
516 subState.DescendRelative(d.prop)
517 d.schema.ValidateKeyword(ctx, subState, data)
518 }
519
520
521 func (d SchemaDependency) MarshalJSON() ([]byte, error) {
522 return json.Marshal(d.schema)
523 }
524
525
526 func (d SchemaDependency) JSONProp(name string) interface{} {
527 return d.schema.JSONProp(name)
528 }
529
530
531 type DependentRequired map[string]PropertyDependency
532
533
534 func NewDependentRequired() Keyword {
535 return &DependentRequired{}
536 }
537
538
539 func (d *DependentRequired) Register(uri string, registry *SchemaRegistry) {}
540
541
542 func (d *DependentRequired) Resolve(pointer jptr.Pointer, uri string) *Schema {
543 return nil
544 }
545
546
547 func (d *DependentRequired) ValidateKeyword(ctx context.Context, currentState *ValidationState, data interface{}) {
548 schemaDebug("[DependentRequired] Validating")
549 for _, prop := range *d {
550 subState := currentState.NewSubState()
551 subState.DescendBase("dependentRequired")
552 subState.DescendRelative("dependentRequired")
553 subState.Misc["dependencyParent"] = "dependentRequired"
554 prop.ValidateKeyword(ctx, subState, data)
555 }
556 }
557
558 type _dependentRequired map[string][]string
559
560
561 func (d *DependentRequired) UnmarshalJSON(data []byte) error {
562 _d := _dependentRequired{}
563 if err := json.Unmarshal(data, &_d); err != nil {
564 return err
565 }
566 dr := DependentRequired{}
567 for k, v := range _d {
568 dr[k] = PropertyDependency{
569 dependencies: v,
570 prop: k,
571 }
572 }
573 *d = dr
574 return nil
575 }
576
577
578 func (d DependentRequired) MarshalJSON() ([]byte, error) {
579 obj := map[string]interface{}{}
580 for key, prop := range d {
581 obj[key] = prop.dependencies
582 }
583 return json.Marshal(obj)
584 }
585
586
587 func (d DependentRequired) JSONProp(name string) interface{} {
588 return d[name]
589 }
590
591
592 func (d DependentRequired) JSONChildren() (r map[string]JSONPather) {
593 r = map[string]JSONPather{}
594 for key, val := range d {
595 r[key] = val
596 }
597 return
598 }
599
600
601 type PropertyDependency struct {
602 dependencies []string
603 prop string
604 }
605
606
607 func (p *PropertyDependency) Register(uri string, registry *SchemaRegistry) {}
608
609
610 func (p *PropertyDependency) Resolve(pointer jptr.Pointer, uri string) *Schema {
611 return nil
612 }
613
614
615 func (p *PropertyDependency) ValidateKeyword(ctx context.Context, currentState *ValidationState, data interface{}) {
616 schemaDebug("[PropertyDependency] Validating")
617 if obj, ok := data.(map[string]interface{}); ok {
618 if obj[p.prop] == nil {
619 return
620 }
621 for _, dep := range p.dependencies {
622 if obj[dep] == nil {
623 currentState.AddError(data, fmt.Sprintf(`"%s" property is required`, dep))
624 }
625 }
626 }
627 }
628
629
630 func (p PropertyDependency) JSONProp(name string) interface{} {
631 idx, err := strconv.Atoi(name)
632 if err != nil {
633 return nil
634 }
635 if idx > len(p.dependencies) || idx < 0 {
636 return nil
637 }
638 return p.dependencies[idx]
639 }
640
641
642 type UnevaluatedProperties Schema
643
644
645 func NewUnevaluatedProperties() Keyword {
646 return &UnevaluatedProperties{}
647 }
648
649
650 func (up *UnevaluatedProperties) Register(uri string, registry *SchemaRegistry) {
651 (*Schema)(up).Register(uri, registry)
652 }
653
654
655 func (up *UnevaluatedProperties) Resolve(pointer jptr.Pointer, uri string) *Schema {
656 return (*Schema)(up).Resolve(pointer, uri)
657 }
658
659
660 func (up *UnevaluatedProperties) ValidateKeyword(ctx context.Context, currentState *ValidationState, data interface{}) {
661 schemaDebug("[UnevaluatedProperties] Validating")
662 if obj, ok := data.(map[string]interface{}); ok {
663 subState := currentState.NewSubState()
664 subState.ClearState()
665 subState.DescendBase("unevaluatedProperties")
666 subState.DescendRelative("unevaluatedProperties")
667 for key := range obj {
668 if currentState.IsEvaluatedKey(key) {
669 continue
670 }
671 if up.schemaType == schemaTypeFalse {
672 currentState.AddError(data, "unevaluated properties are not allowed")
673 return
674 }
675 subState.DescendInstanceFromState(currentState, key)
676
677 (*Schema)(up).ValidateKeyword(ctx, subState, obj[key])
678 }
679 }
680 }
681
682
683 func (up *UnevaluatedProperties) UnmarshalJSON(data []byte) error {
684 sch := &Schema{}
685 if err := json.Unmarshal(data, sch); err != nil {
686 return err
687 }
688 *up = (UnevaluatedProperties)(*sch)
689 return nil
690 }
691
View as plain text