json-schema-openc-fork 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. checksums.yaml +15 -0
  2. data/LICENSE.md +19 -0
  3. data/README.textile +452 -0
  4. data/lib/json-schema.rb +19 -0
  5. data/lib/json-schema/attribute.rb +43 -0
  6. data/lib/json-schema/attributes/additionalitems.rb +28 -0
  7. data/lib/json-schema/attributes/additionalproperties.rb +58 -0
  8. data/lib/json-schema/attributes/allof.rb +39 -0
  9. data/lib/json-schema/attributes/anyof.rb +47 -0
  10. data/lib/json-schema/attributes/dependencies.rb +44 -0
  11. data/lib/json-schema/attributes/disallow.rb +12 -0
  12. data/lib/json-schema/attributes/divisibleby.rb +22 -0
  13. data/lib/json-schema/attributes/enum.rb +24 -0
  14. data/lib/json-schema/attributes/extends.rb +50 -0
  15. data/lib/json-schema/attributes/format.rb +14 -0
  16. data/lib/json-schema/attributes/formats/custom.rb +21 -0
  17. data/lib/json-schema/attributes/formats/date.rb +24 -0
  18. data/lib/json-schema/attributes/formats/date_time.rb +36 -0
  19. data/lib/json-schema/attributes/formats/date_time_v4.rb +15 -0
  20. data/lib/json-schema/attributes/formats/ip.rb +41 -0
  21. data/lib/json-schema/attributes/formats/time.rb +22 -0
  22. data/lib/json-schema/attributes/formats/uri.rb +20 -0
  23. data/lib/json-schema/attributes/items.rb +26 -0
  24. data/lib/json-schema/attributes/limit.rb +179 -0
  25. data/lib/json-schema/attributes/maxdecimal.rb +18 -0
  26. data/lib/json-schema/attributes/multipleof.rb +11 -0
  27. data/lib/json-schema/attributes/not.rb +30 -0
  28. data/lib/json-schema/attributes/oneof.rb +56 -0
  29. data/lib/json-schema/attributes/pattern.rb +18 -0
  30. data/lib/json-schema/attributes/patternproperties.rb +22 -0
  31. data/lib/json-schema/attributes/properties.rb +74 -0
  32. data/lib/json-schema/attributes/properties_optional.rb +26 -0
  33. data/lib/json-schema/attributes/ref.rb +74 -0
  34. data/lib/json-schema/attributes/required.rb +28 -0
  35. data/lib/json-schema/attributes/type.rb +83 -0
  36. data/lib/json-schema/attributes/type_v4.rb +29 -0
  37. data/lib/json-schema/attributes/uniqueitems.rb +16 -0
  38. data/lib/json-schema/errors/custom_format_error.rb +6 -0
  39. data/lib/json-schema/errors/json_parse_error.rb +6 -0
  40. data/lib/json-schema/errors/schema_error.rb +6 -0
  41. data/lib/json-schema/errors/validation_error.rb +46 -0
  42. data/lib/json-schema/schema.rb +63 -0
  43. data/lib/json-schema/schema/reader.rb +113 -0
  44. data/lib/json-schema/schema/validator.rb +36 -0
  45. data/lib/json-schema/util/array_set.rb +14 -0
  46. data/lib/json-schema/util/uri.rb +16 -0
  47. data/lib/json-schema/util/uuid.rb +285 -0
  48. data/lib/json-schema/validator.rb +592 -0
  49. data/lib/json-schema/validators/draft1.rb +45 -0
  50. data/lib/json-schema/validators/draft2.rb +46 -0
  51. data/lib/json-schema/validators/draft3.rb +50 -0
  52. data/lib/json-schema/validators/draft4.rb +56 -0
  53. data/lib/json-schema/validators/hyper-draft4.rb +14 -0
  54. data/resources/draft-01.json +155 -0
  55. data/resources/draft-02.json +166 -0
  56. data/resources/draft-03.json +174 -0
  57. data/resources/draft-04.json +150 -0
  58. data/test/data/all_of_ref_data.json +3 -0
  59. data/test/data/any_of_ref_data.json +7 -0
  60. data/test/data/bad_data_1.json +3 -0
  61. data/test/data/good_data_1.json +3 -0
  62. data/test/data/one_of_ref_links_data.json +5 -0
  63. data/test/schemas/address_microformat.json +18 -0
  64. data/test/schemas/all_of_ref_base_schema.json +6 -0
  65. data/test/schemas/all_of_ref_schema.json +7 -0
  66. data/test/schemas/any_of_ref_jane_schema.json +4 -0
  67. data/test/schemas/any_of_ref_jimmy_schema.json +4 -0
  68. data/test/schemas/any_of_ref_john_schema.json +4 -0
  69. data/test/schemas/any_of_ref_schema.json +15 -0
  70. data/test/schemas/definition_schema.json +15 -0
  71. data/test/schemas/extends_and_additionalProperties-1-filename.schema.json +34 -0
  72. data/test/schemas/extends_and_additionalProperties-1-ref.schema.json +34 -0
  73. data/test/schemas/extends_and_additionalProperties-2-filename.schema.json +33 -0
  74. data/test/schemas/extends_and_additionalProperties-2-ref.schema.json +33 -0
  75. data/test/schemas/good_schema_1.json +10 -0
  76. data/test/schemas/good_schema_2.json +10 -0
  77. data/test/schemas/good_schema_extends1.json +10 -0
  78. data/test/schemas/good_schema_extends2.json +13 -0
  79. data/test/schemas/inner.schema.json +21 -0
  80. data/test/schemas/one_of_ref_links_schema.json +16 -0
  81. data/test/schemas/ref john with spaces schema.json +11 -0
  82. data/test/schemas/relative_definition_schema.json +8 -0
  83. data/test/schemas/self_link_schema.json +17 -0
  84. data/test/schemas/up_link_schema.json +17 -0
  85. data/test/test_all_of_ref_schema.rb +35 -0
  86. data/test/test_any_of_ref_schema.rb +35 -0
  87. data/test/test_bad_schema_ref.rb +39 -0
  88. data/test/test_common_test_suite.rb +66 -0
  89. data/test/test_custom_format.rb +116 -0
  90. data/test/test_definition.rb +15 -0
  91. data/test/test_extended_schema.rb +62 -0
  92. data/test/test_extends_and_additionalProperties.rb +52 -0
  93. data/test/test_files_v3.rb +43 -0
  94. data/test/test_fragment_resolution.rb +30 -0
  95. data/test/test_fragment_validation_with_ref.rb +34 -0
  96. data/test/test_full_validation.rb +208 -0
  97. data/test/test_helper.rb +47 -0
  98. data/test/test_initialize_data.rb +118 -0
  99. data/test/test_jsonschema_draft1.rb +171 -0
  100. data/test/test_jsonschema_draft2.rb +142 -0
  101. data/test/test_jsonschema_draft3.rb +502 -0
  102. data/test/test_jsonschema_draft4.rb +704 -0
  103. data/test/test_list_option.rb +21 -0
  104. data/test/test_merge_missing_values.rb +45 -0
  105. data/test/test_minitems.rb +16 -0
  106. data/test/test_one_of.rb +85 -0
  107. data/test/test_ruby_schema.rb +59 -0
  108. data/test/test_schema_loader.rb +74 -0
  109. data/test/test_schema_type_attribute.rb +20 -0
  110. data/test/test_schema_validation.rb +185 -0
  111. data/test/test_stringify.rb +48 -0
  112. data/test/test_uri_related.rb +67 -0
  113. data/test/test_validator.rb +53 -0
  114. metadata +284 -0
@@ -0,0 +1,18 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class PatternAttribute < Attribute
6
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
+ return unless data.is_a?(String)
8
+
9
+ pattern = current_schema.schema['pattern']
10
+ regexp = Regexp.new(pattern)
11
+ unless regexp.match(data)
12
+ message = "The property '#{build_fragment(fragments)}' value #{data.inspect} did not match the regex '#{pattern}'"
13
+ validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,22 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class PatternPropertiesAttribute < Attribute
6
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
+ return unless data.is_a?(Hash)
8
+
9
+ current_schema.schema['patternProperties'].each do |property, property_schema|
10
+ regexp = Regexp.new(property)
11
+
12
+ # Check each key in the data hash to see if it matches the regex
13
+ data.each do |key, value|
14
+ next unless regexp.match(key)
15
+ schema = JSON::Schema.new(property_schema, current_schema.uri, validator)
16
+ schema.validate(data[key], fragments + [key], processor, options)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,74 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class PropertiesAttribute < Attribute
6
+ def self.required?(schema, options)
7
+ schema.fetch('required') { options[:strict] }
8
+ end
9
+
10
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
11
+ return unless data.is_a?(Hash)
12
+
13
+ schema = current_schema.schema
14
+ schema['properties'].each do |property, property_schema|
15
+ property = property.to_s
16
+
17
+ if !data.key?(property) &&
18
+ options[:insert_defaults] &&
19
+ property_schema['default'] &&
20
+ !property_schema['readonly']
21
+ default = property_schema['default']
22
+ data[property] = default.is_a?(Hash) ? default.clone : default
23
+ end
24
+
25
+ if required?(property_schema, options) && !data.has_key?(property)
26
+ message = "The property '#{build_fragment(fragments)}' did not contain a required property of '#{property}'"
27
+ validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
28
+ end
29
+
30
+ if data.has_key?(property)
31
+ expected_schema = JSON::Schema.new(property_schema, current_schema.uri, validator)
32
+ expected_schema.validate(data[property], fragments + [property], processor, options)
33
+ end
34
+ end
35
+
36
+ # When strict is true, ensure no undefined properties exist in the data
37
+ return unless options[:strict] == true && !schema.key?('additionalProperties')
38
+
39
+ diff = data.select do |k, v|
40
+ k = k.to_s
41
+
42
+ if schema.has_key?('patternProperties')
43
+ match = false
44
+ schema['patternProperties'].each do |property, property_schema|
45
+ regexp = Regexp.new(property)
46
+ if regexp.match(k)
47
+ match = true
48
+ break
49
+ end
50
+ end
51
+
52
+ !schema['properties'].has_key?(k) && !match
53
+ else
54
+ !schema['properties'].has_key?(k)
55
+ end
56
+ end
57
+
58
+ if diff.size > 0
59
+ properties = data.to_a.map { |(key, _)| key }.join(', ')
60
+ message = "The property '#{build_fragment(fragments)}' contained undefined properties: '#{properties}'"
61
+ validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
62
+ end
63
+ end
64
+ end
65
+
66
+ class PropertiesV4Attribute < PropertiesAttribute
67
+ # draft4 relies on its own RequiredAttribute validation at a higher level, rather than
68
+ # as an attribute of individual properties.
69
+ def self.required?(schema, options)
70
+ options[:strict] == true
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,26 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class PropertiesOptionalAttribute < Attribute
6
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
+ return unless data.is_a?(Hash)
8
+
9
+ schema = current_schema.schema
10
+ schema['properties'].each do |property, property_schema|
11
+ property = property.to_s
12
+
13
+ if !property_schema['optional'] && !data.key?(property)
14
+ message = "The property '#{build_fragment(fragments)}' did not contain a required property of '#{property}'"
15
+ validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
16
+ end
17
+
18
+ if data.has_key?(property)
19
+ expected_schema = JSON::Schema.new(property_schema, current_schema.uri, validator)
20
+ expected_schema.validate(data[property], fragments + [property], processor, options)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,74 @@
1
+ require 'json-schema/attribute'
2
+ require 'json-schema/errors/schema_error'
3
+
4
+ module JSON
5
+ class Schema
6
+ class RefAttribute < Attribute
7
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
8
+ uri,schema = get_referenced_uri_and_schema(current_schema.schema, current_schema, validator)
9
+
10
+ if schema
11
+ schema.validate(data, fragments, processor, options)
12
+ elsif uri
13
+ message = "The referenced schema '#{uri.to_s}' cannot be found"
14
+ validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
15
+ else
16
+ message = "The property '#{build_fragment(fragments)}' was not a valid schema"
17
+ validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
18
+ end
19
+ end
20
+
21
+ def self.get_referenced_uri_and_schema(s, current_schema, validator)
22
+ uri,schema = nil,nil
23
+
24
+ temp_uri = Addressable::URI.parse(s['$ref'])
25
+ if temp_uri.relative?
26
+ temp_uri = current_schema.uri.clone
27
+ # Check for absolute path
28
+ path = s['$ref'].split("#")[0]
29
+ if path.nil? || path == ''
30
+ temp_uri.path = current_schema.uri.path
31
+ elsif path[0,1] == "/"
32
+ temp_uri.path = Pathname.new(path).cleanpath.to_s
33
+ else
34
+ temp_uri = current_schema.uri.join(path)
35
+ end
36
+ temp_uri.fragment = s['$ref'].split("#")[1]
37
+ end
38
+ temp_uri.fragment = "" if temp_uri.fragment.nil?
39
+
40
+ # Grab the parent schema from the schema list
41
+ schema_key = temp_uri.to_s.split("#")[0] + "#"
42
+
43
+ ref_schema = JSON::Validator.schema_for_uri(schema_key)
44
+
45
+ if ref_schema
46
+ # Perform fragment resolution to retrieve the appropriate level for the schema
47
+ target_schema = ref_schema.schema
48
+ fragments = temp_uri.fragment.split("/")
49
+ fragment_path = ''
50
+ fragments.each do |fragment|
51
+ if fragment && fragment != ''
52
+ fragment = Addressable::URI.unescape(fragment.gsub('~0', '~').gsub('~1', '/'))
53
+ if target_schema.is_a?(Array)
54
+ target_schema = target_schema[fragment.to_i]
55
+ else
56
+ target_schema = target_schema[fragment]
57
+ end
58
+ fragment_path = fragment_path + "/#{fragment}"
59
+ if target_schema.nil?
60
+ raise SchemaError.new("The fragment '#{fragment_path}' does not exist on schema #{ref_schema.uri.to_s}")
61
+ end
62
+ end
63
+ end
64
+
65
+ # We have the schema finally, build it and validate!
66
+ uri = temp_uri
67
+ schema = JSON::Schema.new(target_schema,temp_uri,validator)
68
+ end
69
+
70
+ [uri,schema]
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,28 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class RequiredAttribute < Attribute
6
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
+ return unless data.is_a?(Hash)
8
+
9
+ schema = current_schema.schema
10
+ defined_properties = schema['properties']
11
+
12
+ schema['required'].each do |property, property_schema|
13
+ next if data.has_key?(property.to_s)
14
+ prop_defaults = options[:insert_defaults] &&
15
+ defined_properties &&
16
+ defined_properties[property] &&
17
+ !defined_properties[property]["default"].nil? &&
18
+ !defined_properties[property]["readonly"]
19
+
20
+ if !prop_defaults
21
+ message = "The property '#{build_fragment(fragments)}' did not contain a required property of '#{property}'"
22
+ validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,83 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class TypeAttribute < Attribute
6
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
+ union = true
8
+ if options[:disallow]
9
+ types = current_schema.schema['disallow']
10
+ else
11
+ types = current_schema.schema['type']
12
+ end
13
+
14
+ if !types.is_a?(Array)
15
+ types = [types]
16
+ union = false
17
+ end
18
+ valid = false
19
+
20
+ # Create a hash to hold errors that are generated during union validation
21
+ union_errors = Hash.new { |hsh, k| hsh[k] = [] }
22
+
23
+ types.each_with_index do |type, type_index|
24
+ if type.is_a?(String)
25
+ valid = data_valid_for_type?(data, type)
26
+ elsif type.is_a?(Hash) && union
27
+ # Validate as a schema
28
+ schema = JSON::Schema.new(type,current_schema.uri,validator)
29
+
30
+ # We're going to add a little cruft here to try and maintain any validation errors that occur in this union type
31
+ # We'll handle this by keeping an error count before and after validation, extracting those errors and pushing them onto a union error
32
+ pre_validation_error_count = validation_errors(processor).count
33
+
34
+ begin
35
+ schema.validate(data,fragments,processor,options.merge(:disallow => false))
36
+ valid = true
37
+ rescue ValidationError
38
+ # We don't care that these schemas don't validate - we only care that one validated
39
+ end
40
+
41
+ diff = validation_errors(processor).count - pre_validation_error_count
42
+ valid = false if diff > 0
43
+ while diff > 0
44
+ diff = diff - 1
45
+ union_errors["type ##{type_index}"].push(validation_errors(processor).pop)
46
+ end
47
+ end
48
+
49
+ break if valid
50
+ end
51
+
52
+ if options[:disallow]
53
+ return if !valid
54
+ message = "The property '#{build_fragment(fragments)}' matched one or more of the following types: #{list_types(types)}"
55
+ validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
56
+ elsif !valid
57
+ if union
58
+ message = "The property '#{build_fragment(fragments)}' of type #{type_of_data(data)} did not match one or more of the following types: #{list_types(types)}"
59
+ validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
60
+ validation_errors(processor).last.sub_errors = union_errors
61
+ else
62
+ message = "The property '#{build_fragment(fragments)}' of type #{type_of_data(data)} did not match the following type: #{list_types(types)}"
63
+ validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
64
+ end
65
+ end
66
+ end
67
+
68
+ def self.list_types(types)
69
+ types.map { |type| type.is_a?(String) ? type : '(schema)' }.join(', ')
70
+ end
71
+
72
+ # Lookup Schema type of given class instance
73
+ def self.type_of_data(data)
74
+ type, _ = TYPE_CLASS_MAPPINGS.map { |k,v| [k,v] }.sort_by { |(_, v)|
75
+ -Array(v).map { |klass| klass.ancestors.size }.max
76
+ }.find { |(_, v)|
77
+ Array(v).any? { |klass| data.kind_of?(klass) }
78
+ }
79
+ type
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,29 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class TypeV4Attribute < Attribute
6
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
+ union = true
8
+ types = current_schema.schema['type']
9
+ if !types.is_a?(Array)
10
+ types = [types]
11
+ union = false
12
+ end
13
+
14
+ return if types.any? { |type| data_valid_for_type?(data, type) }
15
+
16
+ types = types.map { |type| type.is_a?(String) ? type : '(schema)' }.join(', ')
17
+ message = format(
18
+ "The property '%s' of type %s did not match %s: %s",
19
+ build_fragment(fragments),
20
+ data.class,
21
+ union ? 'one or more of the following types' : 'the following type',
22
+ types
23
+ )
24
+
25
+ validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,16 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class UniqueItemsAttribute < Attribute
6
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
+ return unless data.is_a?(Array)
8
+
9
+ if data.clone.uniq!
10
+ message = "The property '#{build_fragment(fragments)}' contained duplicated array values"
11
+ validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,6 @@
1
+ module JSON
2
+ class Schema
3
+ class CustomFormatError < StandardError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module JSON
2
+ class Schema
3
+ class JsonParseError < StandardError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module JSON
2
+ class Schema
3
+ class SchemaError < StandardError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,46 @@
1
+ module JSON
2
+ class Schema
3
+ class ValidationError < StandardError
4
+ INDENT = " "
5
+ attr_accessor :fragments, :schema, :failed_attribute, :sub_errors, :message
6
+
7
+ def initialize(message, fragments, failed_attribute, schema)
8
+ @fragments = fragments.clone
9
+ @schema = schema
10
+ @sub_errors = {}
11
+ @failed_attribute = failed_attribute
12
+ @message = message
13
+ super(message_with_schema)
14
+ end
15
+
16
+ def to_string(subschema_level = 0)
17
+ if @sub_errors.empty?
18
+ subschema_level == 0 ? message_with_schema : message
19
+ else
20
+ messages = ["#{message}. The schema specific errors were:\n"]
21
+ @sub_errors.each do |subschema, errors|
22
+ messages.push "- #{subschema}:"
23
+ messages.concat Array(errors).map { |e| "#{INDENT}- #{e.to_string(subschema_level + 1)}" }
24
+ end
25
+ messages.map { |m| (INDENT * subschema_level) + m }.join("\n")
26
+ end
27
+ end
28
+
29
+ def to_hash
30
+ base = {:schema => @schema.uri, :fragment => ::JSON::Schema::Attribute.build_fragment(fragments), :message => message_with_schema, :failed_attribute => @failed_attribute.to_s.split(":").last.split("Attribute").first}
31
+ if !@sub_errors.empty?
32
+ base[:errors] = @sub_errors.inject({}) do |hsh, (subschema, errors)|
33
+ subschema_sym = subschema.downcase.gsub(/\W+/, '_').to_sym
34
+ hsh[subschema_sym] = Array(errors).map{|e| e.to_hash}
35
+ hsh
36
+ end
37
+ end
38
+ base
39
+ end
40
+
41
+ def message_with_schema
42
+ "#{message} in schema #{schema.uri}"
43
+ end
44
+ end
45
+ end
46
+ end