| 618 | } |
| 619 | |
| 620 | func handleMapString(r Registry, s *Schema, path *PathBuffer, mode ValidateMode, m map[string]any, res *ValidateResult) { |
| 621 | if s.MinProperties != nil { |
| 622 | if len(m) < *s.MinProperties { |
| 623 | res.Add(path, m, s.msgMinProperties) |
| 624 | } |
| 625 | } |
| 626 | if s.MaxProperties != nil { |
| 627 | if len(m) > *s.MaxProperties { |
| 628 | res.Add(path, m, s.msgMaxProperties) |
| 629 | } |
| 630 | } |
| 631 | |
| 632 | for _, k := range s.propertyNames { |
| 633 | v := s.Properties[k] |
| 634 | |
| 635 | // Schemas are generated such that the read/write-only properties are set |
| 636 | // alongside the `$ref`, if it is present (i.e. for objects). If not, |
| 637 | // then the read/write-only properties are set directly on the schema and |
| 638 | // the `for` loop never runs. |
| 639 | readOnly := v.ReadOnly |
| 640 | writeOnly := v.WriteOnly |
| 641 | for v.Ref != "" { |
| 642 | v = r.SchemaFromRef(v.Ref) |
| 643 | } |
| 644 | |
| 645 | // We should be permissive by default to enable easy round-trips for the |
| 646 | // client without needing to remove read-only values. |
| 647 | // TODO: should we make this configurable? |
| 648 | |
| 649 | // Be stricter for responses, enabling validation of the server if desired. |
| 650 | if mode == ModeReadFromServer && writeOnly && m[k] != nil && !reflect.ValueOf(m[k]).IsZero() { |
| 651 | res.Add(path, m[k], "write only property is non-zero") |
| 652 | continue |
| 653 | } |
| 654 | |
| 655 | actualKey := k |
| 656 | _, ok := m[k] |
| 657 | if !ok && !ValidateStrictCasing { |
| 658 | for actual := range m { |
| 659 | if strings.EqualFold(actual, k) { |
| 660 | // Case-insensitive match found, so this is not an error. |
| 661 | actualKey = actual |
| 662 | ok = true |
| 663 | break |
| 664 | } |
| 665 | } |
| 666 | } |
| 667 | |
| 668 | if !ok { |
| 669 | if !s.requiredMap[k] { |
| 670 | continue |
| 671 | } |
| 672 | if (mode == ModeWriteToServer && readOnly) || |
| 673 | (mode == ModeReadFromServer && writeOnly) { |
| 674 | // These are not required for the current mode. |
| 675 | continue |
| 676 | } |
| 677 | res.Add(path, m, s.msgRequired[k]) |