dato_json_schema 0.20.8
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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +75 -0
- data/bin/validate-schema +40 -0
- data/lib/commands/validate_schema.rb +130 -0
- data/lib/json_pointer.rb +7 -0
- data/lib/json_pointer/evaluator.rb +80 -0
- data/lib/json_reference.rb +58 -0
- data/lib/json_schema.rb +31 -0
- data/lib/json_schema/attributes.rb +117 -0
- data/lib/json_schema/configuration.rb +28 -0
- data/lib/json_schema/document_store.rb +30 -0
- data/lib/json_schema/error.rb +85 -0
- data/lib/json_schema/parser.rb +390 -0
- data/lib/json_schema/reference_expander.rb +364 -0
- data/lib/json_schema/schema.rb +295 -0
- data/lib/json_schema/validator.rb +606 -0
- data/schemas/hyper-schema.json +168 -0
- data/schemas/schema.json +150 -0
- data/test/bin_test.rb +19 -0
- data/test/commands/validate_schema_test.rb +121 -0
- data/test/data_scaffold.rb +241 -0
- data/test/json_pointer/evaluator_test.rb +69 -0
- data/test/json_reference/reference_test.rb +45 -0
- data/test/json_schema/attribute_test.rb +121 -0
- data/test/json_schema/document_store_test.rb +42 -0
- data/test/json_schema/error_test.rb +18 -0
- data/test/json_schema/parser_test.rb +362 -0
- data/test/json_schema/reference_expander_test.rb +618 -0
- data/test/json_schema/schema_test.rb +46 -0
- data/test/json_schema/validator_test.rb +1078 -0
- data/test/json_schema_test.rb +46 -0
- data/test/test_helper.rb +17 -0
- metadata +77 -0
@@ -0,0 +1,364 @@
|
|
1
|
+
require "set"
|
2
|
+
|
3
|
+
module JsonSchema
|
4
|
+
class ReferenceExpander
|
5
|
+
attr_accessor :errors
|
6
|
+
attr_accessor :store
|
7
|
+
|
8
|
+
def expand(schema, options = {})
|
9
|
+
@errors = []
|
10
|
+
@local_store = DocumentStore.new
|
11
|
+
@schema = schema
|
12
|
+
@schema_paths = {}
|
13
|
+
@store = options[:store] || DocumentStore.new
|
14
|
+
|
15
|
+
# If the given JSON schema is _just_ a JSON reference and nothing else,
|
16
|
+
# short circuit the whole expansion process and return the result.
|
17
|
+
if schema.reference && !schema.expanded?
|
18
|
+
return dereference(schema, [])
|
19
|
+
end
|
20
|
+
|
21
|
+
@uri = URI.parse(schema.uri)
|
22
|
+
|
23
|
+
@store.each do |uri, store_schema|
|
24
|
+
build_schema_paths(uri, store_schema)
|
25
|
+
end
|
26
|
+
|
27
|
+
# we run #to_s on lookup for URIs; the #to_s of nil is ""
|
28
|
+
build_schema_paths("", schema)
|
29
|
+
|
30
|
+
traverse_schema(schema)
|
31
|
+
|
32
|
+
refs = unresolved_refs(schema).sort
|
33
|
+
if refs.count > 0
|
34
|
+
message = %{Couldn't resolve references: #{refs.to_a.join(", ")}.}
|
35
|
+
@errors << SchemaError.new(schema, message, :unresolved_references)
|
36
|
+
end
|
37
|
+
|
38
|
+
@errors.count == 0
|
39
|
+
end
|
40
|
+
|
41
|
+
def expand!(schema, options = {})
|
42
|
+
if !expand(schema, options)
|
43
|
+
raise AggregateError.new(@errors)
|
44
|
+
end
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def add_reference(schema)
|
51
|
+
uri = URI.parse(schema.uri)
|
52
|
+
|
53
|
+
# In case we've already added a schema for the same reference, don't
|
54
|
+
# re-add it unless the new schema's pointer path is shorter than the one
|
55
|
+
# we've already stored.
|
56
|
+
stored_schema = lookup_reference(uri)
|
57
|
+
if stored_schema && stored_schema.pointer.length < schema.pointer.length
|
58
|
+
return
|
59
|
+
end
|
60
|
+
|
61
|
+
if uri.absolute?
|
62
|
+
@store.add_schema(schema)
|
63
|
+
else
|
64
|
+
@local_store.add_schema(schema)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def build_schema_paths(uri, schema)
|
69
|
+
return if schema.reference
|
70
|
+
|
71
|
+
paths = @schema_paths[uri] ||= {}
|
72
|
+
paths[schema.pointer] = schema
|
73
|
+
|
74
|
+
schema_children(schema) do |subschema|
|
75
|
+
build_schema_paths(uri, subschema)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Also insert alternate tree for schema's custom URI. O(crazy).
|
79
|
+
if schema.uri != uri
|
80
|
+
fragment, parent = schema.fragment, schema.parent
|
81
|
+
schema.fragment, schema.parent = "#", nil
|
82
|
+
build_schema_paths(schema.uri, schema)
|
83
|
+
schema.fragment, schema.parent = fragment, parent
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def dereference(ref_schema, ref_stack, parent_ref: nil)
|
88
|
+
ref = ref_schema.reference
|
89
|
+
|
90
|
+
# Some schemas don't have a reference, but do
|
91
|
+
# have children. If that's the case, we need to
|
92
|
+
# dereference the subschemas.
|
93
|
+
if !ref
|
94
|
+
schema_children(ref_schema) do |subschema|
|
95
|
+
next unless subschema.reference
|
96
|
+
next if ref_schema.uri == parent_ref.uri.to_s
|
97
|
+
|
98
|
+
if !subschema.reference.uri && parent_ref
|
99
|
+
subschema.reference = JsonReference::Reference.new("#{parent_ref.uri}#{subschema.reference.pointer}")
|
100
|
+
end
|
101
|
+
|
102
|
+
dereference(subschema, ref_stack)
|
103
|
+
end
|
104
|
+
return true
|
105
|
+
end
|
106
|
+
|
107
|
+
# detects a reference cycle
|
108
|
+
if ref_stack.include?(ref)
|
109
|
+
message = %{Reference loop detected: #{ref_stack.sort.join(", ")}.}
|
110
|
+
@errors << SchemaError.new(ref_schema, message, :loop_detected)
|
111
|
+
return false
|
112
|
+
end
|
113
|
+
|
114
|
+
new_schema = resolve_reference(ref_schema)
|
115
|
+
return false unless new_schema
|
116
|
+
|
117
|
+
# if the reference resolved to a new reference we need to continue
|
118
|
+
# dereferencing until we either hit a non-reference schema, or a
|
119
|
+
# reference which is already resolved
|
120
|
+
if new_schema.reference && !new_schema.expanded?
|
121
|
+
success = dereference(new_schema, ref_stack + [ref])
|
122
|
+
return false unless success
|
123
|
+
end
|
124
|
+
|
125
|
+
# If the reference schema is a global reference
|
126
|
+
# then we'll need to manually expand any nested
|
127
|
+
# references.
|
128
|
+
if ref.uri
|
129
|
+
schema_children(new_schema) do |subschema|
|
130
|
+
# Don't bother if the subschema points to the same
|
131
|
+
# schema as the reference schema.
|
132
|
+
next if ref_schema == subschema
|
133
|
+
|
134
|
+
if subschema.reference
|
135
|
+
# If the subschema has a reference, then
|
136
|
+
# we don't need to recurse if the schema is
|
137
|
+
# already expanded.
|
138
|
+
next if subschema.expanded?
|
139
|
+
|
140
|
+
if !subschema.reference.uri
|
141
|
+
# the subschema's ref is local to the file that the
|
142
|
+
# subschema is in; however since there's no URI
|
143
|
+
# the 'resolve_pointer' method would try to look it up
|
144
|
+
# within @schema. So: manually reconstruct the reference to
|
145
|
+
# use the URI of the parent ref.
|
146
|
+
subschema.reference = JsonReference::Reference.new("#{ref.uri}#{subschema.reference.pointer}")
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
if subschema.items && subschema.items.reference
|
151
|
+
next if subschema.expanded?
|
152
|
+
|
153
|
+
if !subschema.items.reference.uri
|
154
|
+
# The subschema's ref is local to the file that the
|
155
|
+
# subschema is in. Manually reconstruct the reference
|
156
|
+
# so we can resolve it.
|
157
|
+
subschema.items.reference = JsonReference::Reference.new("#{ref.uri}#{subschema.items.reference.pointer}")
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# If we're recursing into a schema via a global reference, then if
|
162
|
+
# the current subschema doesn't have a reference, we have no way of
|
163
|
+
# figuring out what schema we're in. The resolve_pointer method will
|
164
|
+
# default to looking it up in the initial schema. Instead, we're
|
165
|
+
# passing the parent ref here, so we can grab the URI
|
166
|
+
# later if needed.
|
167
|
+
dereference(subschema, ref_stack, parent_ref: ref)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# copy new schema into existing one while preserving parent, fragment,
|
172
|
+
# and reference
|
173
|
+
parent = ref_schema.parent
|
174
|
+
ref_schema.copy_from(new_schema)
|
175
|
+
ref_schema.parent = parent
|
176
|
+
|
177
|
+
# correct all parent references to point back to ref_schema instead of
|
178
|
+
# new_schema
|
179
|
+
if ref_schema.original?
|
180
|
+
schema_children(ref_schema) do |schema|
|
181
|
+
schema.parent = ref_schema
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
true
|
186
|
+
end
|
187
|
+
|
188
|
+
def lookup_pointer(uri, pointer)
|
189
|
+
paths = @schema_paths[uri.to_s] ||= {}
|
190
|
+
paths[pointer]
|
191
|
+
end
|
192
|
+
|
193
|
+
def lookup_reference(uri)
|
194
|
+
if uri.absolute?
|
195
|
+
@store.lookup_schema(uri.to_s)
|
196
|
+
else
|
197
|
+
@local_store.lookup_schema(uri.to_s)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def resolve_pointer(ref_schema, resolved_schema)
|
202
|
+
ref = ref_schema.reference
|
203
|
+
|
204
|
+
if !(new_schema = lookup_pointer(ref.uri, ref.pointer))
|
205
|
+
new_schema = JsonPointer.evaluate(resolved_schema, ref.pointer)
|
206
|
+
|
207
|
+
# couldn't resolve pointer within known schema; that's an error
|
208
|
+
if new_schema.nil?
|
209
|
+
message = %{Couldn't resolve pointer "#{ref.pointer}".}
|
210
|
+
@errors << SchemaError.new(resolved_schema, message, :unresolved_pointer)
|
211
|
+
return
|
212
|
+
end
|
213
|
+
|
214
|
+
# Try to aggressively detect a circular dependency in case of another
|
215
|
+
# reference. See:
|
216
|
+
#
|
217
|
+
# https://github.com/brandur/json_schema/issues/50
|
218
|
+
#
|
219
|
+
if new_schema.reference &&
|
220
|
+
new_new_schema = lookup_pointer(ref.uri, new_schema.reference.pointer)
|
221
|
+
new_new_schema.clones << ref_schema
|
222
|
+
else
|
223
|
+
# Parse a new schema and use the same parent node. Basically this is
|
224
|
+
# exclusively for the case of a reference that needs to be
|
225
|
+
# de-referenced again to be resolved.
|
226
|
+
build_schema_paths(ref.uri, resolved_schema)
|
227
|
+
end
|
228
|
+
else
|
229
|
+
# insert a clone record so that the expander knows to expand it when
|
230
|
+
# the schema traversal is finished
|
231
|
+
new_schema.clones << ref_schema
|
232
|
+
end
|
233
|
+
new_schema
|
234
|
+
end
|
235
|
+
|
236
|
+
def resolve_reference(ref_schema)
|
237
|
+
ref = ref_schema.reference
|
238
|
+
uri = ref.uri
|
239
|
+
|
240
|
+
if uri && uri.host
|
241
|
+
scheme = uri.scheme || "http"
|
242
|
+
# allow resolution if something we've already parsed has claimed the
|
243
|
+
# full URL
|
244
|
+
if @store.lookup_schema(uri.to_s)
|
245
|
+
resolve_uri(ref_schema, uri)
|
246
|
+
else
|
247
|
+
message =
|
248
|
+
%{Reference resolution over #{scheme} is not currently supported (URI: #{uri}).}
|
249
|
+
@errors << SchemaError.new(ref_schema, message, :scheme_not_supported)
|
250
|
+
nil
|
251
|
+
end
|
252
|
+
# absolute
|
253
|
+
elsif uri && uri.path[0] == "/"
|
254
|
+
resolve_uri(ref_schema, uri)
|
255
|
+
# relative
|
256
|
+
elsif uri
|
257
|
+
# Build an absolute path using the URI of the current schema.
|
258
|
+
#
|
259
|
+
# Note that this code path will never currently be hit because the
|
260
|
+
# incoming reference schema will never have a URI.
|
261
|
+
if ref_schema.uri
|
262
|
+
schema_uri = ref_schema.uri.chomp("/")
|
263
|
+
resolve_uri(ref_schema, URI.parse(schema_uri + "/" + uri.path))
|
264
|
+
else
|
265
|
+
nil
|
266
|
+
end
|
267
|
+
|
268
|
+
# just a JSON Pointer -- resolve against schema root
|
269
|
+
else
|
270
|
+
resolve_pointer(ref_schema, @schema)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
def resolve_uri(ref_schema, uri)
|
275
|
+
if schema = lookup_reference(uri)
|
276
|
+
resolve_pointer(ref_schema, schema)
|
277
|
+
else
|
278
|
+
message = %{Couldn't resolve URI: #{uri.to_s}.}
|
279
|
+
@errors << SchemaError.new(ref_schema, message, :unresolved_pointer)
|
280
|
+
nil
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def schema_children(schema)
|
285
|
+
schema.all_of.each { |s| yield s }
|
286
|
+
schema.any_of.each { |s| yield s }
|
287
|
+
schema.one_of.each { |s| yield s }
|
288
|
+
schema.definitions.each { |_, s| yield s }
|
289
|
+
schema.pattern_properties.each { |_, s| yield s }
|
290
|
+
schema.properties.each { |_, s| yield s }
|
291
|
+
|
292
|
+
if additional = schema.additional_properties
|
293
|
+
if additional.is_a?(Schema)
|
294
|
+
yield additional
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
if schema.not
|
299
|
+
yield schema.not
|
300
|
+
end
|
301
|
+
|
302
|
+
# can either be a single schema (list validation) or multiple (tuple
|
303
|
+
# validation)
|
304
|
+
if items = schema.items
|
305
|
+
if items.is_a?(Array)
|
306
|
+
items.each { |s| yield s }
|
307
|
+
else
|
308
|
+
yield items
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
# dependencies can either be simple or "schema"; only replace the
|
313
|
+
# latter
|
314
|
+
schema.dependencies.
|
315
|
+
each_value { |s| yield s if s.is_a?(Schema) }
|
316
|
+
|
317
|
+
# schemas contained inside hyper-schema links objects
|
318
|
+
if schema.links
|
319
|
+
schema.links.each { |l|
|
320
|
+
yield l.schema if l.schema
|
321
|
+
yield l.target_schema if l.target_schema
|
322
|
+
}
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
def unresolved_refs(schema)
|
327
|
+
# prevent endless recursion
|
328
|
+
return [] unless schema.original?
|
329
|
+
|
330
|
+
arr = []
|
331
|
+
schema_children(schema) do |subschema|
|
332
|
+
if !subschema.expanded?
|
333
|
+
arr += [subschema.reference]
|
334
|
+
else
|
335
|
+
arr += unresolved_refs(subschema)
|
336
|
+
end
|
337
|
+
end
|
338
|
+
arr
|
339
|
+
end
|
340
|
+
|
341
|
+
def traverse_schema(schema)
|
342
|
+
add_reference(schema)
|
343
|
+
|
344
|
+
schema_children(schema) do |subschema|
|
345
|
+
if subschema.reference && !subschema.expanded?
|
346
|
+
dereference(subschema, [])
|
347
|
+
end
|
348
|
+
|
349
|
+
if !subschema.reference
|
350
|
+
traverse_schema(subschema)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
# after finishing a schema traversal, find all clones and re-hydrate them
|
355
|
+
if schema.original?
|
356
|
+
schema.clones.each do |clone_schema|
|
357
|
+
parent = clone_schema.parent
|
358
|
+
clone_schema.copy_from(schema)
|
359
|
+
clone_schema.parent = parent
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
@@ -0,0 +1,295 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
module JsonSchema
|
4
|
+
class Schema
|
5
|
+
TYPE_MAP = {
|
6
|
+
"array" => Array,
|
7
|
+
"boolean" => [FalseClass, TrueClass],
|
8
|
+
"integer" => Integer,
|
9
|
+
"number" => [Integer, Float],
|
10
|
+
"null" => NilClass,
|
11
|
+
"object" => Hash,
|
12
|
+
"string" => String,
|
13
|
+
}
|
14
|
+
|
15
|
+
include Attributes
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
# nil out all our fields so that it's possible to instantiate a schema
|
19
|
+
# instance without going through the parser and validate against it
|
20
|
+
# without Ruby throwing warnings about uninitialized instance variables.
|
21
|
+
initialize_attrs
|
22
|
+
|
23
|
+
# Don't put this in as an attribute default. We require that this precise
|
24
|
+
# pointer gets copied between all clones of any given schema so that they
|
25
|
+
# all share exactly the same set.
|
26
|
+
@clones = Set.new
|
27
|
+
end
|
28
|
+
|
29
|
+
# Fragment of a JSON Pointer that can help us build a pointer back to this
|
30
|
+
# schema for debugging.
|
31
|
+
attr_accessor :fragment
|
32
|
+
|
33
|
+
# Rather than a normal schema, the node may be a JSON Reference. In this
|
34
|
+
# case, no other attributes will be filled in except for #parent.
|
35
|
+
attr_accessor :reference
|
36
|
+
|
37
|
+
attr_copyable :expanded
|
38
|
+
|
39
|
+
# A reference to the data which the Schema was initialized from. Used for
|
40
|
+
# resolving JSON Pointer references.
|
41
|
+
#
|
42
|
+
# Type: Hash
|
43
|
+
attr_copyable :data
|
44
|
+
|
45
|
+
#
|
46
|
+
# Relations
|
47
|
+
#
|
48
|
+
|
49
|
+
# Parent Schema object. Child may come from any of `definitions`,
|
50
|
+
# `properties`, `anyOf`, etc.
|
51
|
+
#
|
52
|
+
# Type: Schema
|
53
|
+
attr_copyable :parent
|
54
|
+
|
55
|
+
# Collection of clones of this schema object, meaning all Schemas that were
|
56
|
+
# initialized after the original. Used for JSON Reference expansion. The
|
57
|
+
# only copy not present in this set is the original Schema object.
|
58
|
+
#
|
59
|
+
# Note that this doesn't have a default option because we rely on the fact
|
60
|
+
# that the set is the *same object* between all clones of any given schema.
|
61
|
+
#
|
62
|
+
# Type: Set[Schema]
|
63
|
+
attr_copyable :clones
|
64
|
+
|
65
|
+
# The normalized URI of this schema. Note that child schemas inherit a URI
|
66
|
+
# from their parent unless they have one explicitly defined, so this is
|
67
|
+
# likely not a unique value in any given schema hierarchy.
|
68
|
+
#
|
69
|
+
# Type: String
|
70
|
+
attr_copyable :uri
|
71
|
+
|
72
|
+
#
|
73
|
+
# Metadata
|
74
|
+
#
|
75
|
+
|
76
|
+
# Alters resolution scope. This value is used along with the parent scope's
|
77
|
+
# URI to build a new address for this schema. Relative ID's will append to
|
78
|
+
# the parent, and absolute URI's will replace it.
|
79
|
+
#
|
80
|
+
# Type: String
|
81
|
+
attr_schema :id
|
82
|
+
|
83
|
+
# Short title of the schema (or the hyper-schema link if this is one).
|
84
|
+
#
|
85
|
+
# Type: String
|
86
|
+
attr_schema :title
|
87
|
+
|
88
|
+
# More detailed description of the schema (or the hyper-schema link if this
|
89
|
+
# is one).
|
90
|
+
#
|
91
|
+
# Type: String
|
92
|
+
attr_schema :description
|
93
|
+
|
94
|
+
# Default JSON value for this particular schema
|
95
|
+
#
|
96
|
+
# Type: [any]
|
97
|
+
attr_schema :default
|
98
|
+
|
99
|
+
#
|
100
|
+
# Validation: Any
|
101
|
+
#
|
102
|
+
|
103
|
+
# A collection of subschemas of which data must validate against the full
|
104
|
+
# set of to be valid.
|
105
|
+
#
|
106
|
+
# Type: Array[Schema]
|
107
|
+
attr_schema :all_of, :default => [], :schema_name => :allOf
|
108
|
+
|
109
|
+
# A collection of subschemas of which data must validate against any schema
|
110
|
+
# in the set to be be valid.
|
111
|
+
#
|
112
|
+
# Type: Array[Schema]
|
113
|
+
attr_schema :any_of, :default => [], :schema_name => :anyOf
|
114
|
+
|
115
|
+
# A collection of inlined subschemas. Standard convention is to subschemas
|
116
|
+
# here and reference them from elsewhere.
|
117
|
+
#
|
118
|
+
# Type: Hash[String => Schema]
|
119
|
+
attr_schema :definitions, :default => {}
|
120
|
+
|
121
|
+
# A collection of objects that must include the data for it to be valid.
|
122
|
+
#
|
123
|
+
# Type: Array
|
124
|
+
attr_schema :enum
|
125
|
+
|
126
|
+
# A collection of subschemas of which data must validate against exactly
|
127
|
+
# one of to be valid.
|
128
|
+
#
|
129
|
+
# Type: Array[Schema]
|
130
|
+
attr_schema :one_of, :default => [], :schema_name => :oneOf
|
131
|
+
|
132
|
+
# A subschema which data must not validate against to be valid.
|
133
|
+
#
|
134
|
+
# Type: Schema
|
135
|
+
attr_schema :not
|
136
|
+
|
137
|
+
# An array of types that data is allowed to be. The spec allows this to be
|
138
|
+
# a string as well, but the parser will always normalize this to an array
|
139
|
+
# of strings.
|
140
|
+
#
|
141
|
+
# Type: Array[String]
|
142
|
+
attr_schema :type, :default => [], :clear_cache => :@type_parsed
|
143
|
+
|
144
|
+
# validation: array
|
145
|
+
attr_schema :additional_items, :default => true, :schema_name => :additionalItems
|
146
|
+
attr_schema :items
|
147
|
+
attr_schema :max_items, :schema_name => :maxItems
|
148
|
+
attr_schema :min_items, :schema_name => :minItems
|
149
|
+
attr_schema :unique_items, :schema_name => :uniqueItems
|
150
|
+
|
151
|
+
# validation: number/integer
|
152
|
+
attr_schema :max, :schema_name => :maximum
|
153
|
+
attr_schema :max_exclusive, :default => false, :schema_name => :exclusiveMaximum
|
154
|
+
attr_schema :min, :schema_name => :minimum
|
155
|
+
attr_schema :min_exclusive, :default => false, :schema_name => :exclusiveMinimum
|
156
|
+
attr_schema :multiple_of, :schema_name => :multipleOf
|
157
|
+
|
158
|
+
# validation: object
|
159
|
+
attr_schema :additional_properties, :default => true, :schema_name => :additionalProperties
|
160
|
+
attr_schema :dependencies, :default => {}
|
161
|
+
attr_schema :max_properties, :schema_name => :maxProperties
|
162
|
+
attr_schema :min_properties, :schema_name => :minProperties
|
163
|
+
attr_schema :pattern_properties, :default => {}, :schema_name => :patternProperties
|
164
|
+
attr_schema :properties, :default => {}
|
165
|
+
attr_schema :required
|
166
|
+
# warning: strictProperties is technically V5 spec (but I needed it now)
|
167
|
+
attr_schema :strict_properties, :default => false, :schema_name => :strictProperties
|
168
|
+
|
169
|
+
# validation: string
|
170
|
+
attr_schema :format
|
171
|
+
attr_schema :max_length, :schema_name => :maxLength
|
172
|
+
attr_schema :min_length, :schema_name => :minLength
|
173
|
+
attr_schema :pattern
|
174
|
+
|
175
|
+
# hyperschema
|
176
|
+
attr_schema :links, :default => []
|
177
|
+
attr_schema :media
|
178
|
+
attr_schema :path_start, :schema_name => :pathStart
|
179
|
+
attr_schema :read_only, :schema_name => :readOnly
|
180
|
+
|
181
|
+
# hyperschema link attributes
|
182
|
+
attr_schema :enc_type, :schema_name => :encType, :default => "application/json"
|
183
|
+
attr_schema :href
|
184
|
+
attr_schema :media_type, :schema_name => :mediaType, :default => "application/json"
|
185
|
+
attr_schema :method
|
186
|
+
attr_schema :rel
|
187
|
+
attr_schema :schema
|
188
|
+
attr_schema :target_schema, :schema_name => :targetSchema
|
189
|
+
attr_schema :job_schema, :schema_name => :jobSchema
|
190
|
+
|
191
|
+
# allow booleans to be access with question mark
|
192
|
+
alias :additional_items? :additional_items
|
193
|
+
alias :expanded? :expanded
|
194
|
+
alias :max_exclusive? :max_exclusive
|
195
|
+
alias :min_exclusive? :min_exclusive
|
196
|
+
alias :read_only? :read_only
|
197
|
+
alias :unique_items? :unique_items
|
198
|
+
|
199
|
+
def expand_references(options = {})
|
200
|
+
expander = ReferenceExpander.new
|
201
|
+
if expander.expand(self, options)
|
202
|
+
[true, nil]
|
203
|
+
else
|
204
|
+
[false, expander.errors]
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def expand_references!(options = {})
|
209
|
+
ReferenceExpander.new.expand!(self, options)
|
210
|
+
true
|
211
|
+
end
|
212
|
+
|
213
|
+
# An array of Ruby classes that are equivalent to the types defined in the
|
214
|
+
# schema.
|
215
|
+
#
|
216
|
+
# Type: Array[Class]
|
217
|
+
def type_parsed
|
218
|
+
@type_parsed ||= type.flat_map { |t| TYPE_MAP[t] }.compact
|
219
|
+
end
|
220
|
+
|
221
|
+
def inspect
|
222
|
+
"\#<JsonSchema::Schema pointer=#{pointer}>"
|
223
|
+
end
|
224
|
+
|
225
|
+
def inspect_schema
|
226
|
+
if reference
|
227
|
+
str = reference.to_s
|
228
|
+
str += expanded? ? " [EXPANDED]" : " [COLLAPSED]"
|
229
|
+
str += original? ? " [ORIGINAL]" : " [CLONE]"
|
230
|
+
str
|
231
|
+
else
|
232
|
+
hash = {}
|
233
|
+
self.class.copyable_attrs.each do |copyable, _|
|
234
|
+
next if [:@clones, :@data, :@parent, :@uri].include?(copyable)
|
235
|
+
if value = instance_variable_get(copyable)
|
236
|
+
if value.is_a?(Array)
|
237
|
+
if !value.empty?
|
238
|
+
hash[copyable] = value.map { |v| inspect_value(v) }
|
239
|
+
end
|
240
|
+
elsif value.is_a?(Hash)
|
241
|
+
if !value.empty?
|
242
|
+
hash[copyable] =
|
243
|
+
Hash[*value.map { |k, v| [k, inspect_value(v)] }.flatten]
|
244
|
+
end
|
245
|
+
else
|
246
|
+
hash[copyable] = inspect_value(value)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
hash
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def inspect_value(value)
|
255
|
+
if value.is_a?(Schema)
|
256
|
+
value.inspect_schema
|
257
|
+
else
|
258
|
+
value.inspect
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def original?
|
263
|
+
!clones.include?(self)
|
264
|
+
end
|
265
|
+
|
266
|
+
def pointer
|
267
|
+
if parent
|
268
|
+
(parent.pointer + "/".freeze + fragment).freeze
|
269
|
+
else
|
270
|
+
fragment
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
def validate(data, fail_fast: false)
|
275
|
+
validator = Validator.new(self)
|
276
|
+
valid = validator.validate(data, fail_fast: fail_fast)
|
277
|
+
[valid, validator.errors]
|
278
|
+
end
|
279
|
+
|
280
|
+
def validate!(data, fail_fast: false)
|
281
|
+
Validator.new(self).validate!(data, fail_fast: fail_fast)
|
282
|
+
end
|
283
|
+
|
284
|
+
# Link subobject for a hyperschema.
|
285
|
+
class Link < Schema
|
286
|
+
inherit_attrs
|
287
|
+
end
|
288
|
+
|
289
|
+
# Media type subobject for a hyperschema.
|
290
|
+
class Media
|
291
|
+
attr_accessor :binary_encoding
|
292
|
+
attr_accessor :type
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|