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,36 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class DateTimeFormat < FormatAttribute
6
+ REGEXP = /\A\d{4}-\d{2}-\d{2}T(\d{2}):(\d{2}):(\d{2})([\.,]\d+)?(Z|[+-](\d{2})(:?\d{2})?)?\z/
7
+
8
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
9
+ # Timestamp in restricted ISO-8601 YYYY-MM-DDThh:mm:ssZ with optional decimal fraction of the second
10
+ if data.is_a?(String)
11
+ error_message = "The property '#{build_fragment(fragments)}' must be a date/time in the ISO-8601 format of YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DDThh:mm:ss.ssZ"
12
+ if (m = REGEXP.match(data))
13
+ parts = data.split("T")
14
+
15
+ begin
16
+ Date.parse(parts[0])
17
+ rescue Exception
18
+ validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
19
+ return
20
+ end
21
+
22
+ begin
23
+ validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[1].to_i > 23
24
+ validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[2].to_i > 59
25
+ validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[3].to_i > 59
26
+ rescue Exception
27
+ validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
28
+ end
29
+ else
30
+ validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,15 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class DateTimeV4Format < FormatAttribute
6
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
+ return unless data.is_a?(String)
8
+ DateTime.rfc3339(data)
9
+ rescue ArgumentError
10
+ error_message = "The property '#{build_fragment(fragments)}' must be a valid RFC3339 date/time string"
11
+ validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,41 @@
1
+ require 'json-schema/attributes/format'
2
+ require 'ipaddr'
3
+ require 'socket'
4
+
5
+ module JSON
6
+ class Schema
7
+ class IPFormat < FormatAttribute
8
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
9
+ return unless data.is_a?(String)
10
+
11
+ begin
12
+ ip = IPAddr.new(data)
13
+ rescue ArgumentError => e
14
+ raise e unless e.message == 'invalid address'
15
+ end
16
+
17
+ family = ip_version == 6 ? Socket::AF_INET6 : Socket::AF_INET
18
+ unless ip && ip.family == family
19
+ error_message = "The property '#{build_fragment(fragments)}' must be a valid IPv#{ip_version} address"
20
+ validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
21
+ end
22
+ end
23
+
24
+ def self.ip_version
25
+ raise NotImplementedError
26
+ end
27
+ end
28
+
29
+ class IP4Format < IPFormat
30
+ def self.ip_version
31
+ 4
32
+ end
33
+ end
34
+
35
+ class IP6Format < IPFormat
36
+ def self.ip_version
37
+ 6
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,22 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class TimeFormat < FormatAttribute
6
+ REGEXP = /\A(\d{2}):(\d{2}):(\d{2})\z/
7
+
8
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
9
+ if data.is_a?(String)
10
+ error_message = "The property '#{build_fragment(fragments)}' must be a time in the format of hh:mm:ss"
11
+ if (m = REGEXP.match(data))
12
+ validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[1].to_i > 23
13
+ validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[2].to_i > 59
14
+ validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors]) and return if m[3].to_i > 59
15
+ else
16
+ validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ require 'json-schema/attribute'
2
+ require 'addressable/uri'
3
+ module JSON
4
+ class Schema
5
+ class UriFormat < FormatAttribute
6
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
+ return unless data.is_a?(String)
8
+ error_message = "The property '#{build_fragment(fragments)}' must be a valid URI"
9
+ begin
10
+ # TODO
11
+ # Addressable only throws an exception on to_s for invalid URI strings, although it
12
+ # probably should throughout parse already - https://github.com/sporkmonger/addressable/issues/177
13
+ Addressable::URI.parse(data).to_s
14
+ rescue Addressable::URI::InvalidURIError
15
+ validation_error(processor, error_message, fragments, current_schema, self, options[:record_errors])
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,26 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class ItemsAttribute < Attribute
6
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
+ return unless data.is_a?(Array)
8
+
9
+ items = current_schema.schema['items']
10
+ case items
11
+ when Hash
12
+ schema = JSON::Schema.new(items, current_schema.uri, validator)
13
+ data.each_with_index do |item, i|
14
+ schema.validate(item, fragments + [i.to_s], processor, options)
15
+ end
16
+
17
+ when Array
18
+ items.each_with_index do |item_schema, i|
19
+ schema = JSON::Schema.new(item_schema, current_schema.uri, validator)
20
+ schema.validate(data[i], fragments + [i.to_s], processor, options)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,179 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class LimitAttribute < Attribute
6
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
+ schema = current_schema.schema
8
+ return unless data.is_a?(acceptable_type) && invalid?(schema, value(data))
9
+
10
+ property = build_fragment(fragments)
11
+ description = error_message(schema)
12
+ message = format("The property '%s' %s", property, description)
13
+ validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
14
+ end
15
+
16
+ def self.invalid?(schema, data)
17
+ exclusive = exclusive?(schema)
18
+ limit = limit(schema)
19
+
20
+ if limit_name.start_with?('max')
21
+ exclusive ? data >= limit : data > limit
22
+ else
23
+ exclusive ? data <= limit : data < limit
24
+ end
25
+ end
26
+
27
+ def self.limit(schema)
28
+ schema[limit_name]
29
+ end
30
+
31
+ def self.exclusive?(schema)
32
+ false
33
+ end
34
+
35
+ def self.value(data)
36
+ data
37
+ end
38
+
39
+ def self.acceptable_type
40
+ raise NotImplementedError
41
+ end
42
+
43
+ def self.error_message(schema)
44
+ raise NotImplementedError
45
+ end
46
+
47
+ def self.limit_name
48
+ raise NotImplementedError
49
+ end
50
+ end
51
+
52
+ class MinLengthAttribute < LimitAttribute
53
+ def self.acceptable_type
54
+ String
55
+ end
56
+
57
+ def self.limit_name
58
+ 'minLength'
59
+ end
60
+
61
+ def self.error_message(schema)
62
+ "was not of a minimum string length of #{limit(schema)}"
63
+ end
64
+
65
+ def self.value(data)
66
+ data.length
67
+ end
68
+ end
69
+
70
+ class MaxLengthAttribute < MinLengthAttribute
71
+ def self.limit_name
72
+ 'maxLength'
73
+ end
74
+
75
+ def self.error_message(schema)
76
+ "was not of a maximum string length of #{limit(schema)}"
77
+ end
78
+ end
79
+
80
+ class MinItemsAttribute < LimitAttribute
81
+ def self.acceptable_type
82
+ Array
83
+ end
84
+
85
+ def self.value(data)
86
+ data.length
87
+ end
88
+
89
+ def self.limit_name
90
+ 'minItems'
91
+ end
92
+
93
+ def self.error_message(schema)
94
+ "did not contain a minimum number of items #{limit(schema)}"
95
+ end
96
+ end
97
+
98
+ class MaxItemsAttribute < MinItemsAttribute
99
+ def self.limit_name
100
+ 'maxItems'
101
+ end
102
+
103
+ def self.error_message(schema)
104
+ "had more items than the allowed #{limit(schema)}"
105
+ end
106
+ end
107
+
108
+ class MinPropertiesAttribute < LimitAttribute
109
+ def self.acceptable_type
110
+ Hash
111
+ end
112
+
113
+ def self.value(data)
114
+ data.size
115
+ end
116
+
117
+ def self.limit_name
118
+ 'minProperties'
119
+ end
120
+
121
+ def self.error_message(schema)
122
+ "did not contain a minimum number of properties #{limit(schema)}"
123
+ end
124
+ end
125
+
126
+ class MaxPropertiesAttribute < MinPropertiesAttribute
127
+ def self.limit_name
128
+ 'maxProperties'
129
+ end
130
+
131
+ def self.error_message(schema)
132
+ "had more properties than the allowed #{limit(schema)}"
133
+ end
134
+ end
135
+
136
+ class NumericLimitAttribute < LimitAttribute
137
+ def self.acceptable_type
138
+ Numeric
139
+ end
140
+
141
+ def self.error_message(schema)
142
+ exclusivity = exclusive?(schema) ? 'exclusively' : 'inclusively'
143
+ format("did not have a %s value of %s, %s", limit_name, limit(schema), exclusivity)
144
+ end
145
+ end
146
+
147
+ class MaximumAttribute < NumericLimitAttribute
148
+ def self.limit_name
149
+ 'maximum'
150
+ end
151
+
152
+ def self.exclusive?(schema)
153
+ schema['exclusiveMaximum']
154
+ end
155
+ end
156
+
157
+ class MaximumInclusiveAttribute < MaximumAttribute
158
+ def self.exclusive?(schema)
159
+ schema['maximumCanEqual'] == false
160
+ end
161
+ end
162
+
163
+ class MinimumAttribute < NumericLimitAttribute
164
+ def self.limit_name
165
+ 'minimum'
166
+ end
167
+
168
+ def self.exclusive?(schema)
169
+ schema['exclusiveMinimum']
170
+ end
171
+ end
172
+
173
+ class MinimumInclusiveAttribute < MinimumAttribute
174
+ def self.exclusive?(schema)
175
+ schema['minimumCanEqual'] == false
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,18 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class MaxDecimalAttribute < Attribute
6
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
+ return unless data.is_a?(Numeric)
8
+
9
+ max_decimal_places = current_schema.schema['maxDecimal']
10
+ s = data.to_s.split(".")[1]
11
+ if s && s.length > max_decimal_places
12
+ message = "The property '#{build_fragment(fragments)}' had more decimal places than the allowed #{max_decimal_places}"
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,11 @@
1
+ require 'json-schema/attributes/divisibleby'
2
+
3
+ module JSON
4
+ class Schema
5
+ class MultipleOfAttribute < DivisibleByAttribute
6
+ def self.keyword
7
+ 'multipleOf'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class NotAttribute < Attribute
6
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
+ schema = JSON::Schema.new(current_schema.schema['not'],current_schema.uri,validator)
8
+ failed = true
9
+ errors_copy = processor.validation_errors.clone
10
+
11
+ begin
12
+ schema.validate(data,fragments,processor,options)
13
+ # If we're recording errors, we don't throw an exception. Instead, check the errors array length
14
+ if options[:record_errors] && errors_copy.length != processor.validation_errors.length
15
+ processor.validation_errors.replace(errors_copy)
16
+ else
17
+ message = "The property '#{build_fragment(fragments)}' of type #{data.class} matched the disallowed schema"
18
+ failed = false
19
+ end
20
+ rescue
21
+ # Yay, we failed validation.
22
+ end
23
+
24
+ unless failed
25
+ validation_error(processor, message, fragments, current_schema, self, options[:record_errors])
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,56 @@
1
+ require 'json-schema/attribute'
2
+
3
+ module JSON
4
+ class Schema
5
+ class OneOfAttribute < Attribute
6
+ def self.validate(current_schema, data, fragments, processor, validator, options = {})
7
+ errors = Hash.new { |hsh, k| hsh[k] = [] }
8
+
9
+ validation_errors = 0
10
+ one_of = current_schema.schema['oneOf']
11
+
12
+ original_data = data.is_a?(Hash) ? data.clone : data
13
+ success_data = nil
14
+
15
+ valid = false
16
+
17
+ one_of.each_with_index do |element, schema_index|
18
+ schema = JSON::Schema.new(element,current_schema.uri,validator)
19
+ pre_validation_error_count = validation_errors(processor).count
20
+ begin
21
+ schema.validate(data,fragments,processor,options)
22
+ success_data = data.is_a?(Hash) ? data.clone : data
23
+ valid = true
24
+ rescue ValidationError
25
+ valid = false
26
+ end
27
+
28
+ diff = validation_errors(processor).count - pre_validation_error_count
29
+ valid = false if diff > 0
30
+ validation_errors += 1 if !valid
31
+ while diff > 0
32
+ diff = diff - 1
33
+ errors["oneOf ##{schema_index}"].push(validation_errors(processor).pop)
34
+ end
35
+ data = original_data
36
+ end
37
+
38
+
39
+
40
+ if validation_errors == one_of.length - 1
41
+ data = success_data
42
+ return
43
+ end
44
+
45
+ if validation_errors == one_of.length
46
+ message = "The property '#{build_fragment(fragments)}' of type #{data.class} did not match any of the required schemas"
47
+ else
48
+ message = "The property '#{build_fragment(fragments)}' of type #{data.class} matched more than one of the required schemas"
49
+ end
50
+
51
+ validation_error(processor, message, fragments, current_schema, self, options[:record_errors]) if message
52
+ validation_errors(processor).last.sub_errors = errors if message
53
+ end
54
+ end
55
+ end
56
+ end