jsi-dev 0.0.8 → 0.0.10
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 +4 -4
- data/.yardopts +3 -4
- data/CHANGELOG.md +19 -0
- data/LICENSE.md +2 -3
- data/README.md +87 -43
- data/docs/Glossary.md +313 -0
- data/jsi.gemspec +1 -1
- data/lib/jsi/base/mutability.rb +48 -0
- data/lib/jsi/base/node.rb +66 -52
- data/lib/jsi/base.rb +593 -176
- data/lib/jsi/jsi_coder.rb +4 -2
- data/lib/jsi/metaschema_node/bootstrap_schema.rb +118 -59
- data/lib/jsi/metaschema_node.rb +245 -154
- data/lib/jsi/ptr.rb +45 -17
- data/lib/jsi/ref.rb +197 -0
- data/lib/jsi/registry.rb +311 -0
- data/lib/jsi/schema/cxt/child_application.rb +35 -0
- data/lib/jsi/schema/cxt/inplace_application.rb +37 -0
- data/lib/jsi/schema/cxt.rb +80 -0
- data/lib/jsi/schema/dialect.rb +137 -0
- data/lib/jsi/schema/draft04.rb +113 -5
- data/lib/jsi/schema/draft06.rb +123 -5
- data/lib/jsi/schema/draft07.rb +157 -5
- data/lib/jsi/schema/draft202012.rb +303 -0
- data/lib/jsi/schema/dynamic_anchor_map.rb +63 -0
- data/lib/jsi/schema/element.rb +69 -0
- data/lib/jsi/schema/elements/anchor.rb +13 -0
- data/lib/jsi/schema/elements/array_validation.rb +82 -0
- data/lib/jsi/schema/elements/comment.rb +10 -0
- data/lib/jsi/schema/{validation → elements}/const.rb +11 -7
- data/lib/jsi/schema/elements/contains.rb +59 -0
- data/lib/jsi/schema/elements/contains_minmax.rb +91 -0
- data/lib/jsi/schema/elements/content_encoding.rb +10 -0
- data/lib/jsi/schema/elements/content_media_type.rb +10 -0
- data/lib/jsi/schema/elements/content_schema.rb +16 -0
- data/lib/jsi/schema/elements/default.rb +11 -0
- data/lib/jsi/schema/elements/definitions.rb +19 -0
- data/lib/jsi/schema/elements/dependencies.rb +99 -0
- data/lib/jsi/schema/elements/dependent_required.rb +49 -0
- data/lib/jsi/schema/elements/dependent_schemas.rb +69 -0
- data/lib/jsi/schema/elements/dynamic_ref.rb +69 -0
- data/lib/jsi/schema/elements/enum.rb +26 -0
- data/lib/jsi/schema/elements/examples.rb +10 -0
- data/lib/jsi/schema/elements/format.rb +10 -0
- data/lib/jsi/schema/elements/id.rb +30 -0
- data/lib/jsi/schema/elements/if_then_else.rb +82 -0
- data/lib/jsi/schema/elements/info_bool.rb +10 -0
- data/lib/jsi/schema/elements/info_string.rb +10 -0
- data/lib/jsi/schema/elements/items.rb +93 -0
- data/lib/jsi/schema/elements/items_prefixed.rb +96 -0
- data/lib/jsi/schema/elements/not.rb +31 -0
- data/lib/jsi/schema/elements/numeric.rb +137 -0
- data/lib/jsi/schema/elements/numeric_draft04.rb +77 -0
- data/lib/jsi/schema/elements/object_validation.rb +55 -0
- data/lib/jsi/schema/elements/pattern.rb +35 -0
- data/lib/jsi/schema/elements/properties.rb +145 -0
- data/lib/jsi/schema/elements/property_names.rb +48 -0
- data/lib/jsi/schema/elements/ref.rb +62 -0
- data/lib/jsi/schema/elements/required.rb +34 -0
- data/lib/jsi/schema/elements/self.rb +24 -0
- data/lib/jsi/schema/elements/some_of.rb +180 -0
- data/lib/jsi/schema/elements/string_validation.rb +57 -0
- data/lib/jsi/schema/elements/type.rb +43 -0
- data/lib/jsi/schema/elements/unevaluated_items.rb +54 -0
- data/lib/jsi/schema/elements/unevaluated_properties.rb +54 -0
- data/lib/jsi/schema/elements/xschema.rb +10 -0
- data/lib/jsi/schema/elements/xvocabulary.rb +10 -0
- data/lib/jsi/schema/elements.rb +101 -0
- data/lib/jsi/schema/issue.rb +3 -4
- data/lib/jsi/schema/schema_ancestor_node.rb +105 -52
- data/lib/jsi/schema/vocabulary.rb +36 -0
- data/lib/jsi/schema.rb +598 -383
- data/lib/jsi/schema_classes.rb +195 -141
- data/lib/jsi/schema_set.rb +85 -128
- data/lib/jsi/set.rb +23 -0
- data/lib/jsi/simple_wrap.rb +14 -17
- data/lib/jsi/struct.rb +57 -0
- data/lib/jsi/uri.rb +40 -0
- data/lib/jsi/util/private/memo_map.rb +9 -13
- data/lib/jsi/util/private.rb +59 -31
- data/lib/jsi/util/typelike.rb +19 -60
- data/lib/jsi/util.rb +53 -34
- data/lib/jsi/validation/error.rb +45 -2
- data/lib/jsi/validation/result.rb +121 -90
- data/lib/jsi/validation.rb +1 -6
- data/lib/jsi/version.rb +1 -1
- data/lib/jsi.rb +170 -36
- data/lib/schemas/json-schema.org/draft/2020-12/schema.rb +62 -0
- data/lib/schemas/json-schema.org/draft-04/schema.rb +60 -109
- data/lib/schemas/json-schema.org/draft-06/schema.rb +53 -108
- data/lib/schemas/json-schema.org/draft-07/schema.rb +63 -127
- data/readme.rb +4 -4
- data/{resources}/schemas/2020-12_strict.json +19 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/applicator.json +48 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/content.json +17 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/core.json +51 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/format-annotation.json +14 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/format-assertion.json +14 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/meta-data.json +37 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/unevaluated.json +15 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/validation.json +98 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/schema.json +58 -0
- metadata +73 -52
- data/docs/glossary.md +0 -281
- data/lib/jsi/metaschema.rb +0 -6
- data/lib/jsi/schema/application/child_application/contains.rb +0 -25
- data/lib/jsi/schema/application/child_application/draft04.rb +0 -21
- data/lib/jsi/schema/application/child_application/draft06.rb +0 -28
- data/lib/jsi/schema/application/child_application/draft07.rb +0 -28
- data/lib/jsi/schema/application/child_application/items.rb +0 -18
- data/lib/jsi/schema/application/child_application/properties.rb +0 -25
- data/lib/jsi/schema/application/child_application.rb +0 -13
- data/lib/jsi/schema/application/draft04.rb +0 -8
- data/lib/jsi/schema/application/draft06.rb +0 -8
- data/lib/jsi/schema/application/draft07.rb +0 -8
- data/lib/jsi/schema/application/inplace_application/dependencies.rb +0 -28
- data/lib/jsi/schema/application/inplace_application/draft04.rb +0 -25
- data/lib/jsi/schema/application/inplace_application/draft06.rb +0 -26
- data/lib/jsi/schema/application/inplace_application/draft07.rb +0 -32
- data/lib/jsi/schema/application/inplace_application/ifthenelse.rb +0 -20
- data/lib/jsi/schema/application/inplace_application/ref.rb +0 -18
- data/lib/jsi/schema/application/inplace_application/someof.rb +0 -44
- data/lib/jsi/schema/application/inplace_application.rb +0 -14
- data/lib/jsi/schema/application.rb +0 -12
- data/lib/jsi/schema/ref.rb +0 -183
- data/lib/jsi/schema/validation/array.rb +0 -69
- data/lib/jsi/schema/validation/contains.rb +0 -25
- data/lib/jsi/schema/validation/dependencies.rb +0 -49
- data/lib/jsi/schema/validation/draft04/minmax.rb +0 -91
- data/lib/jsi/schema/validation/draft04.rb +0 -110
- data/lib/jsi/schema/validation/draft06.rb +0 -120
- data/lib/jsi/schema/validation/draft07.rb +0 -157
- data/lib/jsi/schema/validation/enum.rb +0 -25
- data/lib/jsi/schema/validation/ifthenelse.rb +0 -46
- data/lib/jsi/schema/validation/items.rb +0 -54
- data/lib/jsi/schema/validation/not.rb +0 -20
- data/lib/jsi/schema/validation/numeric.rb +0 -121
- data/lib/jsi/schema/validation/object.rb +0 -45
- data/lib/jsi/schema/validation/pattern.rb +0 -34
- data/lib/jsi/schema/validation/properties.rb +0 -101
- data/lib/jsi/schema/validation/property_names.rb +0 -32
- data/lib/jsi/schema/validation/ref.rb +0 -40
- data/lib/jsi/schema/validation/required.rb +0 -27
- data/lib/jsi/schema/validation/someof.rb +0 -90
- data/lib/jsi/schema/validation/string.rb +0 -47
- data/lib/jsi/schema/validation/type.rb +0 -49
- data/lib/jsi/schema/validation.rb +0 -49
- data/lib/jsi/schema_registry.rb +0 -190
- data/lib/jsi/util/private/attr_struct.rb +0 -130
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module JSI
|
|
4
|
-
module Schema::Validation::Properties
|
|
5
|
-
# @private
|
|
6
|
-
def internal_validate_properties(result_builder)
|
|
7
|
-
evaluated_property_names = Set[]
|
|
8
|
-
|
|
9
|
-
if keyword?('properties')
|
|
10
|
-
value = schema_content['properties']
|
|
11
|
-
# The value of "properties" MUST be an object. Each value of this object MUST be a valid JSON Schema.
|
|
12
|
-
if value.respond_to?(:to_hash)
|
|
13
|
-
# Validation succeeds if, for each name that appears in both the instance and as a name within this
|
|
14
|
-
# keyword's value, the child instance for that name successfully validates against the corresponding
|
|
15
|
-
# schema.
|
|
16
|
-
if result_builder.instance.respond_to?(:to_hash)
|
|
17
|
-
results = {}
|
|
18
|
-
result_builder.instance.keys.each do |property_name|
|
|
19
|
-
if value.key?(property_name)
|
|
20
|
-
evaluated_property_names << property_name
|
|
21
|
-
results[property_name] = result_builder.child_subschema_validate(
|
|
22
|
-
property_name,
|
|
23
|
-
['properties', property_name],
|
|
24
|
-
)
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
result_builder.validate(
|
|
28
|
-
results.values.all?(&:valid?),
|
|
29
|
-
'instance object properties are not all valid against corresponding `properties` schema values',
|
|
30
|
-
keyword: 'properties',
|
|
31
|
-
results: results.values,
|
|
32
|
-
)
|
|
33
|
-
end
|
|
34
|
-
else
|
|
35
|
-
result_builder.schema_error('`properties` is not an object', 'properties')
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
if keyword?('patternProperties')
|
|
40
|
-
value = schema_content['patternProperties']
|
|
41
|
-
# The value of "patternProperties" MUST be an object. Each property name of this object SHOULD be a
|
|
42
|
-
# valid regular expression, according to the ECMA 262 regular expression dialect. Each property value
|
|
43
|
-
# of this object MUST be a valid JSON Schema.
|
|
44
|
-
if value.respond_to?(:to_hash)
|
|
45
|
-
# Validation succeeds if, for each instance name that matches any regular expressions that appear as
|
|
46
|
-
# a property name in this keyword's value, the child instance for that name successfully validates
|
|
47
|
-
# against each schema that corresponds to a matching regular expression.
|
|
48
|
-
if result_builder.instance.respond_to?(:to_hash)
|
|
49
|
-
results = {}
|
|
50
|
-
result_builder.instance.keys.each do |property_name|
|
|
51
|
-
value.keys.each do |value_property_pattern|
|
|
52
|
-
begin
|
|
53
|
-
# TODO ECMA 262
|
|
54
|
-
if value_property_pattern.respond_to?(:to_str) && Regexp.new(value_property_pattern).match(property_name.to_s)
|
|
55
|
-
evaluated_property_names << property_name
|
|
56
|
-
results[property_name] = result_builder.child_subschema_validate(
|
|
57
|
-
property_name,
|
|
58
|
-
['patternProperties', value_property_pattern],
|
|
59
|
-
)
|
|
60
|
-
end
|
|
61
|
-
rescue ::RegexpError
|
|
62
|
-
result_builder.schema_error("`patternProperties` key #{property_name.inspect} is not a valid regular expression: #{e.message}", 'patternProperties')
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
result_builder.validate(
|
|
67
|
-
results.values.all?(&:valid?),
|
|
68
|
-
'instance object properties are not all valid against corresponding `patternProperties` schema values',
|
|
69
|
-
keyword: 'patternProperties',
|
|
70
|
-
results: results.values,
|
|
71
|
-
)
|
|
72
|
-
end
|
|
73
|
-
else
|
|
74
|
-
result_builder.schema_error('`patternProperties` is not an object', 'patternProperties')
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
if keyword?('additionalProperties')
|
|
79
|
-
value = schema_content['additionalProperties']
|
|
80
|
-
# The value of "additionalProperties" MUST be a valid JSON Schema.
|
|
81
|
-
if result_builder.instance.respond_to?(:to_hash)
|
|
82
|
-
results = {}
|
|
83
|
-
result_builder.instance.keys.each do |property_name|
|
|
84
|
-
if !evaluated_property_names.include?(property_name)
|
|
85
|
-
results[property_name] = result_builder.child_subschema_validate(
|
|
86
|
-
property_name,
|
|
87
|
-
['additionalProperties'],
|
|
88
|
-
)
|
|
89
|
-
end
|
|
90
|
-
end.compact
|
|
91
|
-
result_builder.validate(
|
|
92
|
-
results.values.all?(&:valid?),
|
|
93
|
-
'instance object additional properties are not all valid against `additionalProperties` schema value',
|
|
94
|
-
keyword: 'additionalProperties',
|
|
95
|
-
results: results.values,
|
|
96
|
-
)
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
end
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module JSI
|
|
4
|
-
module Schema::Validation::PropertyNames
|
|
5
|
-
# @private
|
|
6
|
-
def internal_validate_propertyNames(result_builder)
|
|
7
|
-
if keyword?('propertyNames')
|
|
8
|
-
# The value of "propertyNames" MUST be a valid JSON Schema.
|
|
9
|
-
#
|
|
10
|
-
# If the instance is an object, this keyword validates if every property name in the instance
|
|
11
|
-
# validates against the provided schema. Note the property name that the schema is testing will
|
|
12
|
-
# always be a string.
|
|
13
|
-
if result_builder.instance.respond_to?(:to_hash)
|
|
14
|
-
results = {}
|
|
15
|
-
result_builder.instance.keys.each do |property_name|
|
|
16
|
-
results[property_name] = subschema(['propertyNames']).internal_validate_instance(
|
|
17
|
-
Ptr[],
|
|
18
|
-
property_name,
|
|
19
|
-
validate_only: result_builder.validate_only,
|
|
20
|
-
)
|
|
21
|
-
end
|
|
22
|
-
result_builder.validate(
|
|
23
|
-
results.values.all?(&:valid?),
|
|
24
|
-
'instance object property names are not all valid against `propertyNames` schema value',
|
|
25
|
-
keyword: 'propertyNames',
|
|
26
|
-
results: results.values,
|
|
27
|
-
)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module JSI
|
|
4
|
-
module Schema::Validation::Ref
|
|
5
|
-
# @private
|
|
6
|
-
# @param throw_result [Boolean] if a $ref is present, whether to throw the result being built after
|
|
7
|
-
# validating the $ref, bypassing subsequent keyword validation
|
|
8
|
-
def internal_validate_ref(result_builder, throw_result: false)
|
|
9
|
-
if keyword?('$ref')
|
|
10
|
-
value = schema_content['$ref']
|
|
11
|
-
|
|
12
|
-
if value.respond_to?(:to_str)
|
|
13
|
-
schema_ref = self.schema_ref
|
|
14
|
-
|
|
15
|
-
if result_builder.visited_refs.include?(schema_ref)
|
|
16
|
-
result_builder.schema_error('self-referential schema structure', '$ref')
|
|
17
|
-
else
|
|
18
|
-
ref_result = schema_ref.deref_schema.internal_validate_instance(
|
|
19
|
-
result_builder.instance_ptr,
|
|
20
|
-
result_builder.instance_document,
|
|
21
|
-
validate_only: result_builder.validate_only,
|
|
22
|
-
visited_refs: result_builder.visited_refs + [schema_ref],
|
|
23
|
-
)
|
|
24
|
-
result_builder.validate(
|
|
25
|
-
ref_result.valid?,
|
|
26
|
-
'instance is not valid against the schema referenced by `$ref` value',
|
|
27
|
-
keyword: '$ref',
|
|
28
|
-
results: [ref_result],
|
|
29
|
-
)
|
|
30
|
-
if throw_result
|
|
31
|
-
throw(:jsi_validation_result, result_builder.result)
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
else
|
|
35
|
-
result_builder.schema_error("`$ref` is not a string", '$ref')
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module JSI
|
|
4
|
-
module Schema::Validation::Required
|
|
5
|
-
# @private
|
|
6
|
-
def internal_validate_required(result_builder)
|
|
7
|
-
if keyword?('required')
|
|
8
|
-
value = schema_content['required']
|
|
9
|
-
# The value of this keyword MUST be an array. Elements of this array, if any, MUST be strings, and MUST be unique.
|
|
10
|
-
if value.respond_to?(:to_ary)
|
|
11
|
-
if result_builder.instance.respond_to?(:to_hash)
|
|
12
|
-
# An object instance is valid against this keyword if every item in the array is the name of a property in the instance.
|
|
13
|
-
missing_required = value.reject { |property_name| result_builder.instance.key?(property_name) }
|
|
14
|
-
# TODO include missing required property names in the validation error
|
|
15
|
-
result_builder.validate(
|
|
16
|
-
missing_required.empty?,
|
|
17
|
-
'instance object does not contain all property names specified by `required` value',
|
|
18
|
-
keyword: 'required',
|
|
19
|
-
)
|
|
20
|
-
end
|
|
21
|
-
else
|
|
22
|
-
result_builder.schema_error('`required` is not an array', 'required')
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module JSI
|
|
4
|
-
module Schema::Validation::AllOf
|
|
5
|
-
# @private
|
|
6
|
-
def internal_validate_allOf(result_builder)
|
|
7
|
-
if keyword?('allOf')
|
|
8
|
-
value = schema_content['allOf']
|
|
9
|
-
# This keyword's value MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema.
|
|
10
|
-
if value.respond_to?(:to_ary)
|
|
11
|
-
# An instance validates successfully against this keyword if it validates successfully against all
|
|
12
|
-
# schemas defined by this keyword's value.
|
|
13
|
-
allOf_results = value.each_index.map do |i|
|
|
14
|
-
result_builder.inplace_subschema_validate(['allOf', i])
|
|
15
|
-
end
|
|
16
|
-
result_builder.validate(
|
|
17
|
-
allOf_results.all?(&:valid?),
|
|
18
|
-
'instance is not valid against all schemas specified by `allOf` value',
|
|
19
|
-
keyword: 'allOf',
|
|
20
|
-
results: allOf_results,
|
|
21
|
-
)
|
|
22
|
-
else
|
|
23
|
-
result_builder.schema_error('`allOf` is not an array', 'allOf')
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
module Schema::Validation::AnyOf
|
|
30
|
-
# @private
|
|
31
|
-
def internal_validate_anyOf(result_builder)
|
|
32
|
-
if keyword?('anyOf')
|
|
33
|
-
value = schema_content['anyOf']
|
|
34
|
-
# This keyword's value MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema.
|
|
35
|
-
if value.respond_to?(:to_ary)
|
|
36
|
-
# An instance validates successfully against this keyword if it validates successfully against at
|
|
37
|
-
# least one schema defined by this keyword's value.
|
|
38
|
-
# Note that when annotations are being collected, all subschemas MUST be examined so that
|
|
39
|
-
# annotations are collected from each subschema that validates successfully.
|
|
40
|
-
anyOf_results = value.each_index.map do |i|
|
|
41
|
-
result_builder.inplace_subschema_validate(['anyOf', i])
|
|
42
|
-
end
|
|
43
|
-
result_builder.validate(
|
|
44
|
-
anyOf_results.any?(&:valid?),
|
|
45
|
-
'instance is not valid against any schemas specified by `anyOf` value',
|
|
46
|
-
keyword: 'anyOf',
|
|
47
|
-
results: anyOf_results,
|
|
48
|
-
)
|
|
49
|
-
else
|
|
50
|
-
result_builder.schema_error('`anyOf` is not an array', 'anyOf')
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
module Schema::Validation::OneOf
|
|
57
|
-
# @private
|
|
58
|
-
def internal_validate_oneOf(result_builder)
|
|
59
|
-
if keyword?('oneOf')
|
|
60
|
-
value = schema_content['oneOf']
|
|
61
|
-
# This keyword's value MUST be a non-empty array. Each item of the array MUST be a valid JSON Schema.
|
|
62
|
-
if value.respond_to?(:to_ary)
|
|
63
|
-
# An instance validates successfully against this keyword if it validates successfully against
|
|
64
|
-
# exactly one schema defined by this keyword's value.
|
|
65
|
-
oneOf_results = value.each_index.map do |i|
|
|
66
|
-
result_builder.inplace_subschema_validate(['oneOf', i])
|
|
67
|
-
end
|
|
68
|
-
if oneOf_results.none?(&:valid?)
|
|
69
|
-
result_builder.validate(
|
|
70
|
-
false,
|
|
71
|
-
'instance is not valid against any schemas specified by `oneOf` value',
|
|
72
|
-
keyword: 'oneOf',
|
|
73
|
-
results: oneOf_results,
|
|
74
|
-
)
|
|
75
|
-
else
|
|
76
|
-
# TODO better info on what schemas passed/failed validation
|
|
77
|
-
result_builder.validate(
|
|
78
|
-
oneOf_results.select(&:valid?).size == 1,
|
|
79
|
-
'instance is valid against more than one schema specified by `oneOf` value',
|
|
80
|
-
keyword: 'oneOf',
|
|
81
|
-
results: oneOf_results,
|
|
82
|
-
)
|
|
83
|
-
end
|
|
84
|
-
else
|
|
85
|
-
result_builder.schema_error('`oneOf` is not an array', 'oneOf')
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
end
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module JSI
|
|
4
|
-
module Schema::Validation::StringLength
|
|
5
|
-
# @private
|
|
6
|
-
def internal_validate_maxLength(result_builder)
|
|
7
|
-
if keyword?('maxLength')
|
|
8
|
-
value = schema_content['maxLength']
|
|
9
|
-
# The value of this keyword MUST be a non-negative integer.
|
|
10
|
-
if internal_integer?(value) && value >= 0
|
|
11
|
-
if result_builder.instance.respond_to?(:to_str)
|
|
12
|
-
# A string instance is valid against this keyword if its length is less than, or equal to, the
|
|
13
|
-
# value of this keyword.
|
|
14
|
-
result_builder.validate(
|
|
15
|
-
result_builder.instance.to_str.length <= value,
|
|
16
|
-
'instance string length is not less than or equal to `maxLength` value',
|
|
17
|
-
keyword: 'maxLength',
|
|
18
|
-
)
|
|
19
|
-
end
|
|
20
|
-
else
|
|
21
|
-
result_builder.schema_error('`maxLength` is not a non-negative integer', 'maxLength')
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# @private
|
|
27
|
-
def internal_validate_minLength(result_builder)
|
|
28
|
-
if keyword?('minLength')
|
|
29
|
-
value = schema_content['minLength']
|
|
30
|
-
# The value of this keyword MUST be a non-negative integer.
|
|
31
|
-
if internal_integer?(value) && value >= 0
|
|
32
|
-
if result_builder.instance.respond_to?(:to_str)
|
|
33
|
-
# A string instance is valid against this keyword if its length is greater than, or equal to, the
|
|
34
|
-
# value of this keyword.
|
|
35
|
-
result_builder.validate(
|
|
36
|
-
result_builder.instance.to_str.length >= value,
|
|
37
|
-
'instance string length is not greater than or equal to `minLength` value',
|
|
38
|
-
keyword: 'minLength',
|
|
39
|
-
)
|
|
40
|
-
end
|
|
41
|
-
else
|
|
42
|
-
result_builder.schema_error('`minLength` is not a non-negative integer', 'minLength')
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module JSI
|
|
4
|
-
module Schema::Validation::Type
|
|
5
|
-
# @private
|
|
6
|
-
def internal_validate_type(result_builder)
|
|
7
|
-
if keyword?('type')
|
|
8
|
-
value = schema_content['type']
|
|
9
|
-
instance = result_builder.instance
|
|
10
|
-
# The value of this keyword MUST be either a string or an array. If it is an array, elements of
|
|
11
|
-
# the array MUST be strings and MUST be unique.
|
|
12
|
-
if value.respond_to?(:to_str) || value.respond_to?(:to_ary)
|
|
13
|
-
types = value.respond_to?(:to_str) ? [value] : value
|
|
14
|
-
matched_type = types.each_with_index.any? do |type, i|
|
|
15
|
-
if type.respond_to?(:to_str)
|
|
16
|
-
case type.to_str
|
|
17
|
-
when 'null'
|
|
18
|
-
instance == nil
|
|
19
|
-
when 'boolean'
|
|
20
|
-
instance == true || instance == false
|
|
21
|
-
when 'object'
|
|
22
|
-
instance.respond_to?(:to_hash)
|
|
23
|
-
when 'array'
|
|
24
|
-
instance.respond_to?(:to_ary)
|
|
25
|
-
when 'string'
|
|
26
|
-
instance.respond_to?(:to_str)
|
|
27
|
-
when 'number'
|
|
28
|
-
instance.is_a?(Numeric)
|
|
29
|
-
when 'integer'
|
|
30
|
-
internal_integer?(instance)
|
|
31
|
-
else
|
|
32
|
-
result_builder.schema_error("`type` is not one of: null, boolean, object, array, string, number, or integer", 'type')
|
|
33
|
-
end
|
|
34
|
-
else
|
|
35
|
-
result_builder.schema_error("`type` is not a string at index #{i}", 'type')
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
result_builder.validate(
|
|
39
|
-
matched_type,
|
|
40
|
-
'instance type does not match `type` value',
|
|
41
|
-
keyword: 'type',
|
|
42
|
-
)
|
|
43
|
-
else
|
|
44
|
-
result_builder.schema_error('`type` is not a string or array', 'type')
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
end
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module JSI
|
|
4
|
-
module Schema::Validation
|
|
5
|
-
autoload :Draft04, 'jsi/schema/validation/draft04'
|
|
6
|
-
autoload :Draft06, 'jsi/schema/validation/draft06'
|
|
7
|
-
autoload :Draft07, 'jsi/schema/validation/draft07'
|
|
8
|
-
|
|
9
|
-
# ref application
|
|
10
|
-
autoload :Ref, 'jsi/schema/validation/ref'
|
|
11
|
-
|
|
12
|
-
# inplace subschema application
|
|
13
|
-
autoload :AllOf, 'jsi/schema/validation/someof'
|
|
14
|
-
autoload :AnyOf, 'jsi/schema/validation/someof'
|
|
15
|
-
autoload :OneOf, 'jsi/schema/validation/someof'
|
|
16
|
-
autoload :IfThenElse, 'jsi/schema/validation/ifthenelse'
|
|
17
|
-
|
|
18
|
-
# child subschema application
|
|
19
|
-
autoload :Items, 'jsi/schema/validation/items'
|
|
20
|
-
autoload :Contains, 'jsi/schema/validation/contains'
|
|
21
|
-
autoload :Properties, 'jsi/schema/validation/properties'
|
|
22
|
-
|
|
23
|
-
# property names subschema application
|
|
24
|
-
autoload :PropertyNames, 'jsi/schema/validation/property_names'
|
|
25
|
-
|
|
26
|
-
# any type validation
|
|
27
|
-
autoload :Type, 'jsi/schema/validation/type'
|
|
28
|
-
autoload :Enum, 'jsi/schema/validation/enum'
|
|
29
|
-
autoload :Const, 'jsi/schema/validation/const'
|
|
30
|
-
autoload :Not, 'jsi/schema/validation/not'
|
|
31
|
-
|
|
32
|
-
# object validation
|
|
33
|
-
autoload :Required, 'jsi/schema/validation/required'
|
|
34
|
-
autoload :Dependencies, 'jsi/schema/validation/dependencies'
|
|
35
|
-
autoload :MinMaxProperties, 'jsi/schema/validation/object'
|
|
36
|
-
|
|
37
|
-
# array validation
|
|
38
|
-
autoload :ArrayLength, 'jsi/schema/validation/array'
|
|
39
|
-
autoload :UniqueItems, 'jsi/schema/validation/array'
|
|
40
|
-
|
|
41
|
-
# string validation
|
|
42
|
-
autoload :StringLength, 'jsi/schema/validation/string'
|
|
43
|
-
autoload :Pattern, 'jsi/schema/validation/pattern'
|
|
44
|
-
|
|
45
|
-
# numeric validation
|
|
46
|
-
autoload :MultipleOf, 'jsi/schema/validation/numeric'
|
|
47
|
-
autoload :MinMax, 'jsi/schema/validation/numeric'
|
|
48
|
-
end
|
|
49
|
-
end
|
data/lib/jsi/schema_registry.rb
DELETED
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module JSI
|
|
4
|
-
class SchemaRegistry
|
|
5
|
-
# an exception raised when an attempt is made to register a resource using a URI which is already
|
|
6
|
-
# registered with another resource
|
|
7
|
-
class Collision < StandardError
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
# an exception raised when an attempt is made to access (register or find) a resource of the
|
|
11
|
-
# registry using a URI which is not absolute (it is a relative URI or it contains a fragment)
|
|
12
|
-
class NonAbsoluteURI < StandardError
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
# an exception raised when a URI we are looking for has not been registered
|
|
16
|
-
class ResourceNotFound < StandardError
|
|
17
|
-
attr_accessor :uri
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def initialize
|
|
21
|
-
@resources = {}
|
|
22
|
-
@autoload_uris = {}
|
|
23
|
-
@resources_mutex = Mutex.new
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# registers the given resource and/or schema resources it contains in the registry.
|
|
27
|
-
#
|
|
28
|
-
# each descendent node of the resource (including the resource itself) is registered if it is a schema
|
|
29
|
-
# that has an absolute URI (generally defined by the '$id' keyword).
|
|
30
|
-
#
|
|
31
|
-
# the given resource itself will be registered, whether or not it is a schema, if it is the root
|
|
32
|
-
# of its document and was instantiated with the option `uri` specified.
|
|
33
|
-
#
|
|
34
|
-
# @param resource [JSI::Base] a JSI containing resources to register
|
|
35
|
-
# @return [void]
|
|
36
|
-
def register(resource)
|
|
37
|
-
unless resource.is_a?(JSI::Base)
|
|
38
|
-
raise(ArgumentError, "resource must be a JSI::Base. got: #{resource.pretty_inspect.chomp}")
|
|
39
|
-
end
|
|
40
|
-
unless resource.is_a?(JSI::Schema) || resource.jsi_ptr.root?
|
|
41
|
-
# unsure, should this be allowed? the given JSI is not a "resource" as we define it, but
|
|
42
|
-
# if this check is removed it will just register any resources (schemas) below the given JSI.
|
|
43
|
-
raise(ArgumentError, "undefined behavior: registration of a JSI which is not a schema and is not at the root of a document")
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# allow for registration of resources at the root of a document whether or not they are schemas.
|
|
47
|
-
# jsi_schema_base_uri at the root comes from the `uri` parameter to new_jsi / new_schema.
|
|
48
|
-
if resource.jsi_schema_base_uri && resource.jsi_ptr.root?
|
|
49
|
-
register_single(resource.jsi_schema_base_uri, resource)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
resource.jsi_each_descendent_node do |node|
|
|
53
|
-
if node.is_a?(JSI::Schema) && node.schema_absolute_uri
|
|
54
|
-
register_single(node.schema_absolute_uri, node)
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
nil
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# takes a URI identifying a resource to be loaded by the given block
|
|
62
|
-
# when a reference to the URI is followed.
|
|
63
|
-
#
|
|
64
|
-
# for example:
|
|
65
|
-
#
|
|
66
|
-
# JSI.schema_registry.autoload_uri('http://example.com/schema.json') do
|
|
67
|
-
# JSI.new_schema({
|
|
68
|
-
# '$schema' => 'http://json-schema.org/draft-07/schema#',
|
|
69
|
-
# '$id' => 'http://example.com/schema.json',
|
|
70
|
-
# 'title' => 'my schema',
|
|
71
|
-
# })
|
|
72
|
-
# end
|
|
73
|
-
#
|
|
74
|
-
# the block would normally load JSON from the filesystem or similar.
|
|
75
|
-
#
|
|
76
|
-
# @param uri [Addressable::URI]
|
|
77
|
-
# @yieldreturn [JSI::Base] a JSI instance containing the resource identified by the given uri
|
|
78
|
-
# @return [void]
|
|
79
|
-
def autoload_uri(uri, &block)
|
|
80
|
-
uri = ensure_uri_absolute(uri)
|
|
81
|
-
mutating
|
|
82
|
-
unless block
|
|
83
|
-
raise(ArgumentError, ["#{SchemaRegistry}#autoload_uri must be invoked with a block", "URI: #{uri}"].join("\n"))
|
|
84
|
-
end
|
|
85
|
-
if @autoload_uris.key?(uri)
|
|
86
|
-
raise(Collision, ["already registered URI for autoload", "URI: #{uri}", "loader: #{@autoload_uris[uri]}"].join("\n"))
|
|
87
|
-
end
|
|
88
|
-
@autoload_uris[uri] = block
|
|
89
|
-
nil
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# @param uri [Addressable::URI, #to_str]
|
|
93
|
-
# @return [JSI::Base]
|
|
94
|
-
# @raise [JSI::SchemaRegistry::ResourceNotFound]
|
|
95
|
-
def find(uri)
|
|
96
|
-
uri = ensure_uri_absolute(uri)
|
|
97
|
-
if @autoload_uris.key?(uri)
|
|
98
|
-
autoloaded = @autoload_uris[uri].call
|
|
99
|
-
register(autoloaded)
|
|
100
|
-
@autoload_uris.delete(uri)
|
|
101
|
-
end
|
|
102
|
-
if !@resources.key?(uri)
|
|
103
|
-
if autoloaded
|
|
104
|
-
msg = [
|
|
105
|
-
"URI #{uri} was registered with autoload_uri but the result did not contain a resource with that URI.",
|
|
106
|
-
"the resource resulting from autoload_uri was:",
|
|
107
|
-
autoloaded.pretty_inspect.chomp,
|
|
108
|
-
]
|
|
109
|
-
else
|
|
110
|
-
msg = ["URI #{uri} is not registered. registered URIs:", *(@resources.keys | @autoload_uris.keys)]
|
|
111
|
-
end
|
|
112
|
-
raise(ResourceNotFound.new(msg.join("\n")).tap { |e| e.uri = uri })
|
|
113
|
-
end
|
|
114
|
-
@resources[uri]
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
def inspect
|
|
118
|
-
[
|
|
119
|
-
'#<JSI::SchemaRegistry',
|
|
120
|
-
*[['autoload', @autoload_uris.keys], ['resources', @resources.keys]].map do |label, uris|
|
|
121
|
-
[
|
|
122
|
-
" #{label} (#{uris.size})#{uris.empty? ? "" : ":"}",
|
|
123
|
-
*uris.map do |uri|
|
|
124
|
-
" #{uri}"
|
|
125
|
-
end,
|
|
126
|
-
]
|
|
127
|
-
end.inject([], &:+),
|
|
128
|
-
'>',
|
|
129
|
-
].join("\n").freeze
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
alias_method :to_s, :inspect
|
|
133
|
-
|
|
134
|
-
def dup
|
|
135
|
-
self.class.new.tap do |reg|
|
|
136
|
-
@resources.each do |uri, resource|
|
|
137
|
-
reg.register_single(uri, resource)
|
|
138
|
-
end
|
|
139
|
-
@autoload_uris.each do |uri, autoload|
|
|
140
|
-
reg.autoload_uri(uri, &autoload)
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
def freeze
|
|
146
|
-
@resources.freeze
|
|
147
|
-
@autoload_uris.freeze
|
|
148
|
-
@resources_mutex = nil
|
|
149
|
-
super
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
protected
|
|
153
|
-
# @param uri [Addressable::URI]
|
|
154
|
-
# @param resource [JSI::Base]
|
|
155
|
-
# @return [void]
|
|
156
|
-
def register_single(uri, resource)
|
|
157
|
-
mutating
|
|
158
|
-
@resources_mutex.synchronize do
|
|
159
|
-
ensure_uri_absolute(uri)
|
|
160
|
-
if @resources.key?(uri)
|
|
161
|
-
if @resources[uri] != resource
|
|
162
|
-
raise(Collision, "URI collision on #{uri}.\nexisting:\n#{@resources[uri].pretty_inspect.chomp}\nnew:\n#{resource.pretty_inspect.chomp}")
|
|
163
|
-
end
|
|
164
|
-
else
|
|
165
|
-
@resources[uri] = resource
|
|
166
|
-
end
|
|
167
|
-
end
|
|
168
|
-
nil
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
private
|
|
172
|
-
|
|
173
|
-
def ensure_uri_absolute(uri)
|
|
174
|
-
uri = Util.uri(uri)
|
|
175
|
-
if uri.fragment
|
|
176
|
-
raise(NonAbsoluteURI, "#{self.class} only registers absolute URIs. cannot access URI with fragment: #{uri}")
|
|
177
|
-
end
|
|
178
|
-
if uri.relative?
|
|
179
|
-
raise(NonAbsoluteURI, "#{self.class} only registers absolute URIs. cannot access relative URI: #{uri}")
|
|
180
|
-
end
|
|
181
|
-
uri
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
def mutating
|
|
185
|
-
if frozen?
|
|
186
|
-
raise(FrozenError, "cannot modify frozen #{self.class}")
|
|
187
|
-
end
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
end
|