json-schema-openc-fork 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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