json_schemer 1.0.3 → 2.4.0

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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +7 -8
  3. data/CHANGELOG.md +96 -0
  4. data/Gemfile.lock +23 -14
  5. data/README.md +343 -20
  6. data/json_schemer.gemspec +8 -3
  7. data/lib/json_schemer/configuration.rb +31 -0
  8. data/lib/json_schemer/content.rb +18 -0
  9. data/lib/json_schemer/draft201909/meta.rb +320 -0
  10. data/lib/json_schemer/draft201909/vocab/applicator.rb +104 -0
  11. data/lib/json_schemer/draft201909/vocab/core.rb +45 -0
  12. data/lib/json_schemer/draft201909/vocab.rb +31 -0
  13. data/lib/json_schemer/draft202012/meta.rb +364 -0
  14. data/lib/json_schemer/draft202012/vocab/applicator.rb +382 -0
  15. data/lib/json_schemer/draft202012/vocab/content.rb +52 -0
  16. data/lib/json_schemer/draft202012/vocab/core.rb +160 -0
  17. data/lib/json_schemer/draft202012/vocab/format_annotation.rb +23 -0
  18. data/lib/json_schemer/draft202012/vocab/format_assertion.rb +23 -0
  19. data/lib/json_schemer/draft202012/vocab/meta_data.rb +30 -0
  20. data/lib/json_schemer/draft202012/vocab/unevaluated.rb +104 -0
  21. data/lib/json_schemer/draft202012/vocab/validation.rb +290 -0
  22. data/lib/json_schemer/draft202012/vocab.rb +105 -0
  23. data/lib/json_schemer/draft4/meta.rb +161 -0
  24. data/lib/json_schemer/draft4/vocab/validation.rb +38 -0
  25. data/lib/json_schemer/draft4/vocab.rb +18 -0
  26. data/lib/json_schemer/draft6/meta.rb +172 -0
  27. data/lib/json_schemer/draft6/vocab.rb +16 -0
  28. data/lib/json_schemer/draft7/meta.rb +183 -0
  29. data/lib/json_schemer/draft7/vocab/validation.rb +69 -0
  30. data/lib/json_schemer/draft7/vocab.rb +30 -0
  31. data/lib/json_schemer/errors.rb +1 -0
  32. data/lib/json_schemer/format/duration.rb +23 -0
  33. data/lib/json_schemer/format/json_pointer.rb +18 -0
  34. data/lib/json_schemer/format.rb +127 -106
  35. data/lib/json_schemer/keyword.rb +56 -0
  36. data/lib/json_schemer/location.rb +25 -0
  37. data/lib/json_schemer/openapi.rb +38 -0
  38. data/lib/json_schemer/openapi30/document.rb +1672 -0
  39. data/lib/json_schemer/openapi30/meta.rb +34 -0
  40. data/lib/json_schemer/openapi30/vocab/base.rb +18 -0
  41. data/lib/json_schemer/openapi30/vocab.rb +12 -0
  42. data/lib/json_schemer/openapi31/document.rb +1557 -0
  43. data/lib/json_schemer/openapi31/meta.rb +136 -0
  44. data/lib/json_schemer/openapi31/vocab/base.rb +127 -0
  45. data/lib/json_schemer/openapi31/vocab.rb +18 -0
  46. data/lib/json_schemer/output.rb +56 -0
  47. data/lib/json_schemer/resources.rb +24 -0
  48. data/lib/json_schemer/result.rb +242 -0
  49. data/lib/json_schemer/schema.rb +433 -0
  50. data/lib/json_schemer/version.rb +1 -1
  51. data/lib/json_schemer.rb +235 -32
  52. metadata +119 -18
  53. data/lib/json_schemer/schema/base.rb +0 -677
  54. data/lib/json_schemer/schema/draft4.json +0 -149
  55. data/lib/json_schemer/schema/draft4.rb +0 -44
  56. data/lib/json_schemer/schema/draft6.json +0 -155
  57. data/lib/json_schemer/schema/draft6.rb +0 -25
  58. data/lib/json_schemer/schema/draft7.json +0 -172
  59. data/lib/json_schemer/schema/draft7.rb +0 -32
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+ module JSONSchemer
3
+ module Draft202012
4
+ module Vocab
5
+ module Unevaluated
6
+ class UnevaluatedItems < Keyword
7
+ def error(formatted_instance_location:, **)
8
+ "array items at #{formatted_instance_location} do not match `unevaluatedItems` schema"
9
+ end
10
+
11
+ def false_schema_error(formatted_instance_location:, **)
12
+ "array item at #{formatted_instance_location} is a disallowed unevaluated item"
13
+ end
14
+
15
+ def parse
16
+ subschema(value)
17
+ end
18
+
19
+ def validate(instance, instance_location, keyword_location, context)
20
+ return result(instance, instance_location, keyword_location, true) unless instance.is_a?(Array)
21
+
22
+ unevaluated_items = instance.size.times.to_set
23
+
24
+ context.adjacent_results.each_value do |adjacent_result|
25
+ collect_unevaluated_items(adjacent_result, unevaluated_items)
26
+ end
27
+
28
+ nested = unevaluated_items.map do |index|
29
+ parsed.validate_instance(instance.fetch(index), join_location(instance_location, index.to_s), keyword_location, context)
30
+ end
31
+
32
+ result(instance, instance_location, keyword_location, nested.all?(&:valid), nested, :annotation => nested.any?)
33
+ end
34
+
35
+ private
36
+
37
+ def collect_unevaluated_items(result, unevaluated_items)
38
+ case result.source
39
+ when Applicator::PrefixItems
40
+ unevaluated_items.subtract(0..result.annotation)
41
+ when Applicator::Items, UnevaluatedItems
42
+ unevaluated_items.clear if result.annotation
43
+ when Applicator::Contains
44
+ unevaluated_items.subtract(result.annotation)
45
+ end
46
+ result.nested&.each do |subresult|
47
+ if subresult.valid && subresult.instance_location == result.instance_location
48
+ collect_unevaluated_items(subresult, unevaluated_items)
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ class UnevaluatedProperties < Keyword
55
+ def error(formatted_instance_location:, **)
56
+ "object properties at #{formatted_instance_location} do not match `unevaluatedProperties` schema"
57
+ end
58
+
59
+ def false_schema_error(formatted_instance_location:, **)
60
+ "object property at #{formatted_instance_location} is a disallowed unevaluated property"
61
+ end
62
+
63
+ def parse
64
+ subschema(value)
65
+ end
66
+
67
+ def validate(instance, instance_location, keyword_location, context)
68
+ return result(instance, instance_location, keyword_location, true) unless instance.is_a?(Hash)
69
+
70
+ evaluated_keys = Set[]
71
+
72
+ context.adjacent_results.each_value do |adjacent_result|
73
+ collect_evaluated_keys(adjacent_result, evaluated_keys)
74
+ end
75
+
76
+ evaluated = instance.reject do |key, _value|
77
+ evaluated_keys.include?(key)
78
+ end
79
+
80
+ nested = evaluated.map do |key, value|
81
+ parsed.validate_instance(value, join_location(instance_location, key), keyword_location, context)
82
+ end
83
+
84
+ result(instance, instance_location, keyword_location, nested.all?(&:valid), nested, :annotation => evaluated.keys)
85
+ end
86
+
87
+ private
88
+
89
+ def collect_evaluated_keys(result, evaluated_keys)
90
+ case result.source
91
+ when Applicator::Properties, Applicator::PatternProperties, Applicator::AdditionalProperties, UnevaluatedProperties
92
+ evaluated_keys.merge(result.annotation)
93
+ end
94
+ result.nested&.each do |subresult|
95
+ if subresult.valid && subresult.instance_location == result.instance_location
96
+ collect_evaluated_keys(subresult, evaluated_keys)
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,290 @@
1
+ # frozen_string_literal: true
2
+ module JSONSchemer
3
+ module Draft202012
4
+ module Vocab
5
+ module Validation
6
+ class Type < Keyword
7
+ def self.valid_integer?(instance)
8
+ instance.is_a?(Numeric) && (instance.is_a?(Integer) || instance.floor == instance)
9
+ end
10
+
11
+ def error(formatted_instance_location:, **)
12
+ case value
13
+ when 'null'
14
+ "value at #{formatted_instance_location} is not null"
15
+ when 'boolean'
16
+ "value at #{formatted_instance_location} is not a boolean"
17
+ when 'number'
18
+ "value at #{formatted_instance_location} is not a number"
19
+ when 'integer'
20
+ "value at #{formatted_instance_location} is not an integer"
21
+ when 'string'
22
+ "value at #{formatted_instance_location} is not a string"
23
+ when 'array'
24
+ "value at #{formatted_instance_location} is not an array"
25
+ when 'object'
26
+ "value at #{formatted_instance_location} is not an object"
27
+ else
28
+ "value at #{formatted_instance_location} is not one of the types: #{value}"
29
+ end
30
+ end
31
+
32
+ def validate(instance, instance_location, keyword_location, _context)
33
+ case parsed
34
+ when String
35
+ result(instance, instance_location, keyword_location, valid_type(parsed, instance), :type => parsed)
36
+ when Array
37
+ result(instance, instance_location, keyword_location, parsed.any? { |type| valid_type(type, instance) })
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def valid_type(type, instance)
44
+ case type
45
+ when 'null'
46
+ instance.nil?
47
+ when 'boolean'
48
+ instance == true || instance == false
49
+ when 'number'
50
+ instance.is_a?(Numeric)
51
+ when 'integer'
52
+ self.class.valid_integer?(instance)
53
+ when 'string'
54
+ instance.is_a?(String)
55
+ when 'array'
56
+ instance.is_a?(Array)
57
+ when 'object'
58
+ instance.is_a?(Hash)
59
+ else
60
+ true
61
+ end
62
+ end
63
+ end
64
+
65
+ class Enum < Keyword
66
+ def error(formatted_instance_location:, **)
67
+ "value at #{formatted_instance_location} is not one of: #{value}"
68
+ end
69
+
70
+ def validate(instance, instance_location, keyword_location, _context)
71
+ result(instance, instance_location, keyword_location, !value || value.include?(instance))
72
+ end
73
+ end
74
+
75
+ class Const < Keyword
76
+ def error(formatted_instance_location:, **)
77
+ "value at #{formatted_instance_location} is not: #{value}"
78
+ end
79
+
80
+ def validate(instance, instance_location, keyword_location, _context)
81
+ result(instance, instance_location, keyword_location, value == instance)
82
+ end
83
+ end
84
+
85
+ class MultipleOf < Keyword
86
+ def error(formatted_instance_location:, **)
87
+ "number at #{formatted_instance_location} is not a multiple of: #{value}"
88
+ end
89
+
90
+ def validate(instance, instance_location, keyword_location, _context)
91
+ result(instance, instance_location, keyword_location, !instance.is_a?(Numeric) || BigDecimal(instance.to_s).modulo(value).zero?)
92
+ end
93
+ end
94
+
95
+ class Maximum < Keyword
96
+ def error(formatted_instance_location:, **)
97
+ "number at #{formatted_instance_location} is greater than: #{value}"
98
+ end
99
+
100
+ def validate(instance, instance_location, keyword_location, _context)
101
+ result(instance, instance_location, keyword_location, !instance.is_a?(Numeric) || instance <= value)
102
+ end
103
+ end
104
+
105
+ class ExclusiveMaximum < Keyword
106
+ def error(formatted_instance_location:, **)
107
+ "number at #{formatted_instance_location} is greater than or equal to: #{value}"
108
+ end
109
+
110
+ def validate(instance, instance_location, keyword_location, _context)
111
+ result(instance, instance_location, keyword_location, !instance.is_a?(Numeric) || instance < value)
112
+ end
113
+ end
114
+
115
+ class Minimum < Keyword
116
+ def error(formatted_instance_location:, **)
117
+ "number at #{formatted_instance_location} is less than: #{value}"
118
+ end
119
+
120
+ def validate(instance, instance_location, keyword_location, _context)
121
+ result(instance, instance_location, keyword_location, !instance.is_a?(Numeric) || instance >= value)
122
+ end
123
+ end
124
+
125
+ class ExclusiveMinimum < Keyword
126
+ def error(formatted_instance_location:, **)
127
+ "number at #{formatted_instance_location} is less than or equal to: #{value}"
128
+ end
129
+
130
+ def validate(instance, instance_location, keyword_location, _context)
131
+ result(instance, instance_location, keyword_location, !instance.is_a?(Numeric) || instance > value)
132
+ end
133
+ end
134
+
135
+ class MaxLength < Keyword
136
+ def error(formatted_instance_location:, **)
137
+ "string length at #{formatted_instance_location} is greater than: #{value}"
138
+ end
139
+
140
+ def validate(instance, instance_location, keyword_location, _context)
141
+ result(instance, instance_location, keyword_location, !instance.is_a?(String) || instance.size <= value)
142
+ end
143
+ end
144
+
145
+ class MinLength < Keyword
146
+ def error(formatted_instance_location:, **)
147
+ "string length at #{formatted_instance_location} is less than: #{value}"
148
+ end
149
+
150
+ def validate(instance, instance_location, keyword_location, _context)
151
+ result(instance, instance_location, keyword_location, !instance.is_a?(String) || instance.size >= value)
152
+ end
153
+ end
154
+
155
+ class Pattern < Keyword
156
+ def error(formatted_instance_location:, **)
157
+ "string at #{formatted_instance_location} does not match pattern: #{value}"
158
+ end
159
+
160
+ def parse
161
+ root.resolve_regexp(value)
162
+ end
163
+
164
+ def validate(instance, instance_location, keyword_location, _context)
165
+ result(instance, instance_location, keyword_location, !instance.is_a?(String) || parsed.match?(instance))
166
+ end
167
+ end
168
+
169
+ class MaxItems < Keyword
170
+ def error(formatted_instance_location:, **)
171
+ "array size at #{formatted_instance_location} is greater than: #{value}"
172
+ end
173
+
174
+ def validate(instance, instance_location, keyword_location, _context)
175
+ result(instance, instance_location, keyword_location, !instance.is_a?(Array) || instance.size <= value)
176
+ end
177
+ end
178
+
179
+ class MinItems < Keyword
180
+ def error(formatted_instance_location:, **)
181
+ "array size at #{formatted_instance_location} is less than: #{value}"
182
+ end
183
+
184
+ def validate(instance, instance_location, keyword_location, _context)
185
+ result(instance, instance_location, keyword_location, !instance.is_a?(Array) || instance.size >= value)
186
+ end
187
+ end
188
+
189
+ class UniqueItems < Keyword
190
+ def error(formatted_instance_location:, **)
191
+ "array items at #{formatted_instance_location} are not unique"
192
+ end
193
+
194
+ def validate(instance, instance_location, keyword_location, _context)
195
+ result(instance, instance_location, keyword_location, !instance.is_a?(Array) || value == false || instance.size == instance.uniq.size)
196
+ end
197
+ end
198
+
199
+ class MaxContains < Keyword
200
+ def error(formatted_instance_location:, **)
201
+ "number of array items at #{formatted_instance_location} matching `contains` schema is greater than: #{value}"
202
+ end
203
+
204
+ def validate(instance, instance_location, keyword_location, context)
205
+ return result(instance, instance_location, keyword_location, true) unless instance.is_a?(Array) && context.adjacent_results.key?(Applicator::Contains)
206
+ evaluated_items = context.adjacent_results.fetch(Applicator::Contains).annotation
207
+ result(instance, instance_location, keyword_location, evaluated_items.size <= value)
208
+ end
209
+ end
210
+
211
+ class MinContains < Keyword
212
+ def error(formatted_instance_location:, **)
213
+ "number of array items at #{formatted_instance_location} matching `contains` schema is less than: #{value}"
214
+ end
215
+
216
+ def validate(instance, instance_location, keyword_location, context)
217
+ return result(instance, instance_location, keyword_location, true) unless instance.is_a?(Array) && context.adjacent_results.key?(Applicator::Contains)
218
+ evaluated_items = context.adjacent_results.fetch(Applicator::Contains).annotation
219
+ result(instance, instance_location, keyword_location, evaluated_items.size >= value)
220
+ end
221
+ end
222
+
223
+ class MaxProperties < Keyword
224
+ def error(formatted_instance_location:, **)
225
+ "object size at #{formatted_instance_location} is greater than: #{value}"
226
+ end
227
+
228
+ def validate(instance, instance_location, keyword_location, _context)
229
+ result(instance, instance_location, keyword_location, !instance.is_a?(Hash) || instance.size <= value)
230
+ end
231
+ end
232
+
233
+ class MinProperties < Keyword
234
+ def error(formatted_instance_location:, **)
235
+ "object size at #{formatted_instance_location} is less than: #{value}"
236
+ end
237
+
238
+ def validate(instance, instance_location, keyword_location, _context)
239
+ result(instance, instance_location, keyword_location, !instance.is_a?(Hash) || instance.size >= value)
240
+ end
241
+ end
242
+
243
+ class Required < Keyword
244
+ def error(formatted_instance_location:, details:, **)
245
+ "object at #{formatted_instance_location} is missing required properties: #{details.fetch('missing_keys').join(', ')}"
246
+ end
247
+
248
+ def validate(instance, instance_location, keyword_location, context)
249
+ return result(instance, instance_location, keyword_location, true) unless instance.is_a?(Hash)
250
+
251
+ required_keys = value
252
+
253
+ if context.access_mode && schema.parsed.key?('properties')
254
+ inapplicable_access_mode_keys = []
255
+ schema.parsed.fetch('properties').parsed.each do |property, subschema|
256
+ read_only, write_only = subschema.parsed.values_at('readOnly', 'writeOnly')
257
+ inapplicable_access_mode_keys << property if context.access_mode == 'write' && read_only&.parsed == true
258
+ inapplicable_access_mode_keys << property if context.access_mode == 'read' && write_only&.parsed == true
259
+ end
260
+ required_keys -= inapplicable_access_mode_keys
261
+ end
262
+
263
+ missing_keys = required_keys - instance.keys
264
+ result(instance, instance_location, keyword_location, missing_keys.none?, :details => { 'missing_keys' => missing_keys })
265
+ end
266
+ end
267
+
268
+ class DependentRequired < Keyword
269
+ def error(formatted_instance_location:, **)
270
+ "object at #{formatted_instance_location} is missing required `dependentRequired` properties"
271
+ end
272
+
273
+ def validate(instance, instance_location, keyword_location, _context)
274
+ return result(instance, instance_location, keyword_location, true) unless instance.is_a?(Hash)
275
+
276
+ existing_keys = instance.keys
277
+
278
+ nested = value.select do |key, _required_keys|
279
+ instance.key?(key)
280
+ end.map do |key, required_keys|
281
+ result(instance, join_location(instance_location, key), join_location(keyword_location, key), (required_keys - existing_keys).none?)
282
+ end
283
+
284
+ result(instance, instance_location, keyword_location, nested.all?(&:valid), nested)
285
+ end
286
+ end
287
+ end
288
+ end
289
+ end
290
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+ module JSONSchemer
3
+ module Draft202012
4
+ module Vocab
5
+ # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-01#section-8
6
+ CORE = {
7
+ # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-01#section-8.1
8
+ '$schema' => Core::Schema,
9
+ '$vocabulary' => Core::Vocabulary,
10
+ # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-01#section-8.2
11
+ '$id' => Core::Id,
12
+ '$anchor' => Core::Anchor,
13
+ '$ref' => Core::Ref,
14
+ '$dynamicAnchor' => Core::DynamicAnchor,
15
+ '$dynamicRef' => Core::DynamicRef,
16
+ '$defs' => Core::Defs,
17
+ 'definitions' => Core::Defs,
18
+ # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-01#section-8.3
19
+ '$comment' => Core::Comment,
20
+ # https://github.com/orgs/json-schema-org/discussions/329
21
+ 'x-error' => Core::XError
22
+ }
23
+ # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-01#section-10
24
+ APPLICATOR = {
25
+ # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-01#section-10.2
26
+ 'allOf' => Applicator::AllOf,
27
+ 'anyOf' => Applicator::AnyOf,
28
+ 'oneOf' => Applicator::OneOf,
29
+ 'not' => Applicator::Not,
30
+ # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-01#section-10.2.2
31
+ 'if' => Applicator::If,
32
+ 'then' => Applicator::Then,
33
+ 'else' => Applicator::Else,
34
+ 'dependentSchemas' => Applicator::DependentSchemas,
35
+ # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-01#section-10.3
36
+ 'prefixItems' => Applicator::PrefixItems,
37
+ 'items' => Applicator::Items,
38
+ 'contains' => Applicator::Contains,
39
+ # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-01#section-10.3.2
40
+ 'properties' => Applicator::Properties,
41
+ 'patternProperties' => Applicator::PatternProperties,
42
+ 'additionalProperties' => Applicator::AdditionalProperties,
43
+ 'propertyNames' => Applicator::PropertyNames,
44
+ 'dependencies' => Applicator::Dependencies
45
+ }
46
+ # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-01#section-11
47
+ UNEVALUATED = {
48
+ 'unevaluatedItems' => Unevaluated::UnevaluatedItems,
49
+ 'unevaluatedProperties' => Unevaluated::UnevaluatedProperties
50
+ }
51
+ # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-01#section-6
52
+ VALIDATION = {
53
+ # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-01#section-6.1
54
+ 'type' => Validation::Type,
55
+ 'enum' => Validation::Enum,
56
+ 'const' => Validation::Const,
57
+ # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-01#section-6.2
58
+ 'multipleOf' => Validation::MultipleOf,
59
+ 'maximum' => Validation::Maximum,
60
+ 'exclusiveMaximum' => Validation::ExclusiveMaximum,
61
+ 'minimum' => Validation::Minimum,
62
+ 'exclusiveMinimum' => Validation::ExclusiveMinimum,
63
+ # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-01#section-6.3
64
+ 'maxLength' => Validation::MaxLength,
65
+ 'minLength' => Validation::MinLength,
66
+ 'pattern' => Validation::Pattern,
67
+ # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-01#section-6.4
68
+ 'maxItems' => Validation::MaxItems,
69
+ 'minItems' => Validation::MinItems,
70
+ 'uniqueItems' => Validation::UniqueItems,
71
+ 'maxContains' => Validation::MaxContains,
72
+ 'minContains' => Validation::MinContains,
73
+ # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-01#section-6.5
74
+ 'maxProperties' => Validation::MaxProperties,
75
+ 'minProperties' => Validation::MinProperties,
76
+ 'required' => Validation::Required,
77
+ 'dependentRequired' => Validation::DependentRequired
78
+ }
79
+ # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-01#section-7.2.1
80
+ FORMAT_ANNOTATION = {
81
+ 'format' => FormatAnnotation::Format
82
+ }
83
+ # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-01#section-7.2.2
84
+ FORMAT_ASSERTION = {
85
+ 'format' => FormatAssertion::Format
86
+ }
87
+ # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-01#section-8
88
+ CONTENT = {
89
+ 'contentEncoding' => Content::ContentEncoding,
90
+ 'contentMediaType' => Content::ContentMediaType,
91
+ 'contentSchema' => Content::ContentSchema
92
+ }
93
+ # https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-01#section-9
94
+ META_DATA = {
95
+ # 'title' => MetaData::Title,
96
+ # 'description' => MetaData::Description,
97
+ # 'default' => MetaData::Default,
98
+ # 'deprecated' => MetaData::Deprecated,
99
+ 'readOnly' => MetaData::ReadOnly,
100
+ 'writeOnly' => MetaData::WriteOnly,
101
+ # 'examples' => MetaData::Examples
102
+ }
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+ module JSONSchemer
3
+ module Draft4
4
+ BASE_URI = URI('http://json-schema.org/draft-04/schema#')
5
+ FORMATS = Draft6::FORMATS.dup
6
+ FORMATS.delete('uri-reference')
7
+ FORMATS.delete('uri-template')
8
+ FORMATS.delete('json-pointer')
9
+ CONTENT_ENCODINGS = Draft6::CONTENT_ENCODINGS
10
+ CONTENT_MEDIA_TYPES = Draft6::CONTENT_MEDIA_TYPES
11
+ SCHEMA = {
12
+ 'id' => 'http://json-schema.org/draft-04/schema#',
13
+ '$schema' => 'http://json-schema.org/draft-04/schema#',
14
+ 'description' => 'Core schema meta-schema',
15
+ 'definitions' => {
16
+ 'schemaArray' => {
17
+ 'type' => 'array',
18
+ 'minItems' => 1,
19
+ 'items' => { '$ref' => '#' }
20
+ },
21
+ 'positiveInteger' => {
22
+ 'type' => 'integer',
23
+ 'minimum' => 0
24
+ },
25
+ 'positiveIntegerDefault0' => {
26
+ 'allOf' => [ { '$ref' => '#/definitions/positiveInteger' }, { 'default' => 0 } ]
27
+ },
28
+ 'simpleTypes' => {
29
+ 'enum' => [ 'array', 'boolean', 'integer', 'null', 'number', 'object', 'string' ]
30
+ },
31
+ 'stringArray' => {
32
+ 'type' => 'array',
33
+ 'items' => { 'type' => 'string' },
34
+ 'minItems' => 1,
35
+ 'uniqueItems' => true
36
+ }
37
+ },
38
+ 'type' => 'object',
39
+ 'properties' => {
40
+ 'id' => {
41
+ 'type' => 'string'
42
+ },
43
+ '$schema' => {
44
+ 'type' => 'string'
45
+ },
46
+ 'title' => {
47
+ 'type' => 'string'
48
+ },
49
+ 'description' => {
50
+ 'type' => 'string'
51
+ },
52
+ 'default' => {},
53
+ 'multipleOf' => {
54
+ 'type' => 'number',
55
+ 'minimum' => 0,
56
+ 'exclusiveMinimum' => true
57
+ },
58
+ 'maximum' => {
59
+ 'type' => 'number'
60
+ },
61
+ 'exclusiveMaximum' => {
62
+ 'type' => 'boolean',
63
+ 'default' => false
64
+ },
65
+ 'minimum' => {
66
+ 'type' => 'number'
67
+ },
68
+ 'exclusiveMinimum' => {
69
+ 'type' => 'boolean',
70
+ 'default' => false
71
+ },
72
+ 'maxLength' => { '$ref' => '#/definitions/positiveInteger' },
73
+ 'minLength' => { '$ref' => '#/definitions/positiveIntegerDefault0' },
74
+ 'pattern' => {
75
+ 'type' => 'string',
76
+ 'format' => 'regex'
77
+ },
78
+ 'additionalItems' => {
79
+ 'anyOf' => [
80
+ { 'type' => 'boolean' },
81
+ { '$ref' => '#' }
82
+ ],
83
+ 'default' => {}
84
+ },
85
+ 'items' => {
86
+ 'anyOf' => [
87
+ { '$ref' => '#' },
88
+ { '$ref' => '#/definitions/schemaArray' }
89
+ ],
90
+ 'default' => {}
91
+ },
92
+ 'maxItems' => { '$ref' => '#/definitions/positiveInteger' },
93
+ 'minItems' => { '$ref' => '#/definitions/positiveIntegerDefault0' },
94
+ 'uniqueItems' => {
95
+ 'type' => 'boolean',
96
+ 'default' => false
97
+ },
98
+ 'maxProperties' => { '$ref' => '#/definitions/positiveInteger' },
99
+ 'minProperties' => { '$ref' => '#/definitions/positiveIntegerDefault0' },
100
+ 'required' => { '$ref' => '#/definitions/stringArray' },
101
+ 'additionalProperties' => {
102
+ 'anyOf' => [
103
+ { 'type' => 'boolean' },
104
+ { '$ref' => '#' }
105
+ ],
106
+ 'default' => {}
107
+ },
108
+ 'definitions' => {
109
+ 'type' => 'object',
110
+ 'additionalProperties' => { '$ref' => '#' },
111
+ 'default' => {}
112
+ },
113
+ 'properties' => {
114
+ 'type' => 'object',
115
+ 'additionalProperties' => { '$ref' => '#' },
116
+ 'default' => {}
117
+ },
118
+ 'patternProperties' => {
119
+ 'type' => 'object',
120
+ 'additionalProperties' => { '$ref' => '#' },
121
+ 'default' => {}
122
+ },
123
+ 'dependencies' => {
124
+ 'type' => 'object',
125
+ 'additionalProperties' => {
126
+ 'anyOf' => [
127
+ { '$ref' => '#' },
128
+ { '$ref' => '#/definitions/stringArray' }
129
+ ]
130
+ }
131
+ },
132
+ 'enum' => {
133
+ 'type' => 'array',
134
+ 'minItems' => 1,
135
+ 'uniqueItems' => true
136
+ },
137
+ 'type' => {
138
+ 'anyOf' => [
139
+ { '$ref' => '#/definitions/simpleTypes' },
140
+ {
141
+ 'type' => 'array',
142
+ 'items' => { '$ref' => '#/definitions/simpleTypes' },
143
+ 'minItems' => 1,
144
+ 'uniqueItems' => true
145
+ }
146
+ ]
147
+ },
148
+ 'format' => { 'type' => 'string' },
149
+ 'allOf' => { '$ref' => '#/definitions/schemaArray' },
150
+ 'anyOf' => { '$ref' => '#/definitions/schemaArray' },
151
+ 'oneOf' => { '$ref' => '#/definitions/schemaArray' },
152
+ 'not' => { '$ref' => '#' }
153
+ },
154
+ 'dependencies' => {
155
+ 'exclusiveMaximum' => [ 'maximum' ],
156
+ 'exclusiveMinimum' => [ 'minimum' ]
157
+ },
158
+ 'default' => {}
159
+ }
160
+ end
161
+ end