jsi 0.4.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +1 -1
- data/CHANGELOG.md +33 -0
- data/LICENSE.md +1 -1
- data/README.md +114 -42
- data/jsi.gemspec +14 -12
- data/lib/jsi/base/node.rb +183 -0
- data/lib/jsi/base.rb +388 -220
- data/lib/jsi/jsi_coder.rb +8 -7
- data/lib/jsi/metaschema.rb +0 -1
- data/lib/jsi/metaschema_node/bootstrap_schema.rb +101 -0
- data/lib/jsi/metaschema_node.rb +159 -135
- data/lib/jsi/ptr.rb +303 -0
- data/lib/jsi/schema/application/child_application/contains.rb +25 -0
- data/lib/jsi/schema/application/child_application/draft04.rb +22 -0
- data/lib/jsi/schema/application/child_application/draft06.rb +29 -0
- data/lib/jsi/schema/application/child_application/draft07.rb +29 -0
- data/lib/jsi/schema/application/child_application/items.rb +18 -0
- data/lib/jsi/schema/application/child_application/properties.rb +25 -0
- data/lib/jsi/schema/application/child_application.rb +38 -0
- data/lib/jsi/schema/application/draft04.rb +8 -0
- data/lib/jsi/schema/application/draft06.rb +8 -0
- data/lib/jsi/schema/application/draft07.rb +8 -0
- data/lib/jsi/schema/application/inplace_application/dependencies.rb +28 -0
- data/lib/jsi/schema/application/inplace_application/draft04.rb +26 -0
- data/lib/jsi/schema/application/inplace_application/draft06.rb +27 -0
- data/lib/jsi/schema/application/inplace_application/draft07.rb +33 -0
- data/lib/jsi/schema/application/inplace_application/ifthenelse.rb +20 -0
- data/lib/jsi/schema/application/inplace_application/ref.rb +18 -0
- data/lib/jsi/schema/application/inplace_application/someof.rb +44 -0
- data/lib/jsi/schema/application/inplace_application.rb +41 -0
- data/lib/jsi/schema/application.rb +12 -0
- data/lib/jsi/schema/draft04.rb +14 -0
- data/lib/jsi/schema/draft06.rb +14 -0
- data/lib/jsi/schema/draft07.rb +14 -0
- data/lib/jsi/schema/issue.rb +36 -0
- data/lib/jsi/schema/ref.rb +160 -0
- data/lib/jsi/schema/schema_ancestor_node.rb +113 -0
- data/lib/jsi/schema/validation/array.rb +69 -0
- data/lib/jsi/schema/validation/const.rb +20 -0
- data/lib/jsi/schema/validation/contains.rb +25 -0
- data/lib/jsi/schema/validation/core.rb +39 -0
- data/lib/jsi/schema/validation/dependencies.rb +49 -0
- data/lib/jsi/schema/validation/draft04/minmax.rb +91 -0
- data/lib/jsi/schema/validation/draft04.rb +112 -0
- data/lib/jsi/schema/validation/draft06.rb +122 -0
- data/lib/jsi/schema/validation/draft07.rb +159 -0
- data/lib/jsi/schema/validation/enum.rb +25 -0
- data/lib/jsi/schema/validation/ifthenelse.rb +46 -0
- data/lib/jsi/schema/validation/items.rb +54 -0
- data/lib/jsi/schema/validation/not.rb +20 -0
- data/lib/jsi/schema/validation/numeric.rb +121 -0
- data/lib/jsi/schema/validation/object.rb +45 -0
- data/lib/jsi/schema/validation/pattern.rb +34 -0
- data/lib/jsi/schema/validation/properties.rb +101 -0
- data/lib/jsi/schema/validation/property_names.rb +32 -0
- data/lib/jsi/schema/validation/ref.rb +40 -0
- data/lib/jsi/schema/validation/required.rb +27 -0
- data/lib/jsi/schema/validation/someof.rb +90 -0
- data/lib/jsi/schema/validation/string.rb +47 -0
- data/lib/jsi/schema/validation/type.rb +49 -0
- data/lib/jsi/schema/validation.rb +51 -0
- data/lib/jsi/schema.rb +508 -149
- data/lib/jsi/schema_classes.rb +199 -59
- data/lib/jsi/schema_registry.rb +151 -0
- data/lib/jsi/schema_set.rb +181 -0
- data/lib/jsi/simple_wrap.rb +23 -4
- data/lib/jsi/util/private/attr_struct.rb +127 -0
- data/lib/jsi/util/private.rb +204 -0
- data/lib/jsi/util/typelike.rb +229 -0
- data/lib/jsi/util.rb +89 -53
- data/lib/jsi/validation/error.rb +34 -0
- data/lib/jsi/validation/result.rb +210 -0
- data/lib/jsi/validation.rb +15 -0
- data/lib/jsi/version.rb +3 -1
- data/lib/jsi.rb +44 -14
- data/lib/schemas/json-schema.org/draft-04/schema.rb +10 -3
- data/lib/schemas/json-schema.org/draft-06/schema.rb +10 -3
- data/lib/schemas/json-schema.org/draft-07/schema.rb +14 -0
- data/readme.rb +138 -0
- data/{resources}/schemas/json-schema.org/draft-04/schema.json +149 -0
- data/{resources}/schemas/json-schema.org/draft-06/schema.json +154 -0
- data/{resources}/schemas/json-schema.org/draft-07/schema.json +168 -0
- metadata +75 -122
- data/.simplecov +0 -3
- data/Rakefile.rb +0 -9
- data/lib/jsi/base/to_rb.rb +0 -128
- data/lib/jsi/json/node.rb +0 -203
- data/lib/jsi/json/pointer.rb +0 -419
- data/lib/jsi/json-schema-fragments.rb +0 -61
- data/lib/jsi/json.rb +0 -10
- data/lib/jsi/pathed_node.rb +0 -118
- data/lib/jsi/typelike_modules.rb +0 -240
- data/resources/icons/AGPL-3.0.png +0 -0
- data/test/base_array_test.rb +0 -323
- data/test/base_hash_test.rb +0 -337
- data/test/base_test.rb +0 -486
- data/test/jsi_coder_test.rb +0 -85
- data/test/jsi_json_arraynode_test.rb +0 -150
- data/test/jsi_json_hashnode_test.rb +0 -132
- data/test/jsi_json_node_test.rb +0 -257
- data/test/jsi_json_pointer_test.rb +0 -102
- data/test/jsi_test.rb +0 -11
- data/test/jsi_typelike_as_json_test.rb +0 -53
- data/test/metaschema_node_test.rb +0 -19
- data/test/schema_module_test.rb +0 -21
- data/test/schema_test.rb +0 -208
- data/test/spreedly_openapi_test.rb +0 -8
- data/test/test_helper.rb +0 -97
- data/test/util_test.rb +0 -62
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JSI
|
4
|
+
module Schema::Validation::MultipleOf
|
5
|
+
# @private
|
6
|
+
def internal_validate_multipleOf(result_builder)
|
7
|
+
if keyword?('multipleOf')
|
8
|
+
value = schema_content['multipleOf']
|
9
|
+
# The value of "multipleOf" MUST be a number, strictly greater than 0.
|
10
|
+
if value.is_a?(Numeric) && value > 0
|
11
|
+
# A numeric instance is valid only if division by this keyword's value results in an integer.
|
12
|
+
if result_builder.instance.is_a?(Numeric)
|
13
|
+
if result_builder.instance.is_a?(Integer) && value.is_a?(Integer)
|
14
|
+
valid = result_builder.instance % value == 0
|
15
|
+
else
|
16
|
+
quotient = result_builder.instance / value
|
17
|
+
if quotient.finite?
|
18
|
+
valid = quotient % 1.0 == 0.0
|
19
|
+
else
|
20
|
+
valid = BigDecimal(result_builder.instance, Float::DIG) % BigDecimal(value, Float::DIG) == 0
|
21
|
+
end
|
22
|
+
end
|
23
|
+
result_builder.validate(
|
24
|
+
valid,
|
25
|
+
'instance is not a multiple of `multipleOf` value',
|
26
|
+
keyword: 'multipleOf',
|
27
|
+
)
|
28
|
+
end
|
29
|
+
else
|
30
|
+
result_builder.schema_error('`multipleOf` is not a number greater than 0', 'multipleOf')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module Schema::Validation::MinMax
|
37
|
+
# @private
|
38
|
+
def internal_validate_maximum(result_builder)
|
39
|
+
if keyword?('maximum')
|
40
|
+
value = schema_content['maximum']
|
41
|
+
# The value of "maximum" MUST be a number, representing an inclusive upper limit for a numeric instance.
|
42
|
+
if value.is_a?(Numeric)
|
43
|
+
# If the instance is a number, then this keyword validates only if the instance is less than or
|
44
|
+
# exactly equal to "maximum".
|
45
|
+
if result_builder.instance.is_a?(Numeric)
|
46
|
+
result_builder.validate(
|
47
|
+
result_builder.instance <= value,
|
48
|
+
'instance is not less than or equal to `maximum` value',
|
49
|
+
keyword: 'maximum',
|
50
|
+
)
|
51
|
+
end
|
52
|
+
else
|
53
|
+
result_builder.schema_error('`maximum` is not a number', 'maximum')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# @private
|
59
|
+
def internal_validate_exclusiveMaximum(result_builder)
|
60
|
+
if keyword?('exclusiveMaximum')
|
61
|
+
value = schema_content['exclusiveMaximum']
|
62
|
+
# The value of "exclusiveMaximum" MUST be number, representing an exclusive upper limit for a numeric instance.
|
63
|
+
if value.is_a?(Numeric)
|
64
|
+
# If the instance is a number, then the instance is valid only if it has a value strictly less than
|
65
|
+
# (not equal to) "exclusiveMaximum".
|
66
|
+
if result_builder.instance.is_a?(Numeric)
|
67
|
+
result_builder.validate(
|
68
|
+
result_builder.instance < value,
|
69
|
+
'instance is not less than `exclusiveMaximum` value',
|
70
|
+
keyword: 'exclusiveMaximum',
|
71
|
+
)
|
72
|
+
end
|
73
|
+
else
|
74
|
+
result_builder.schema_error('`exclusiveMaximum` is not a number', 'exclusiveMaximum')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# @private
|
80
|
+
def internal_validate_minimum(result_builder)
|
81
|
+
if keyword?('minimum')
|
82
|
+
value = schema_content['minimum']
|
83
|
+
# The value of "minimum" MUST be a number, representing an inclusive lower limit for a numeric instance.
|
84
|
+
if value.is_a?(Numeric)
|
85
|
+
# If the instance is a number, then this keyword validates only if the instance is greater than or
|
86
|
+
# exactly equal to "minimum".
|
87
|
+
if result_builder.instance.is_a?(Numeric)
|
88
|
+
result_builder.validate(
|
89
|
+
result_builder.instance >= value,
|
90
|
+
'instance is not greater than or equal to `minimum` value',
|
91
|
+
keyword: 'minimum',
|
92
|
+
)
|
93
|
+
end
|
94
|
+
else
|
95
|
+
result_builder.schema_error('`minimum` is not a number', 'minimum')
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# @private
|
101
|
+
def internal_validate_exclusiveMinimum(result_builder)
|
102
|
+
if keyword?('exclusiveMinimum')
|
103
|
+
value = schema_content['exclusiveMinimum']
|
104
|
+
# The value of "exclusiveMinimum" MUST be number, representing an exclusive lower limit for a numeric instance.
|
105
|
+
if value.is_a?(Numeric)
|
106
|
+
# If the instance is a number, then the instance is valid only if it has a value strictly greater
|
107
|
+
# than (not equal to) "exclusiveMinimum".
|
108
|
+
if result_builder.instance.is_a?(Numeric)
|
109
|
+
result_builder.validate(
|
110
|
+
result_builder.instance > value,
|
111
|
+
'instance is not greater than `exclusiveMinimum` value',
|
112
|
+
keyword: 'exclusiveMinimum',
|
113
|
+
)
|
114
|
+
end
|
115
|
+
else
|
116
|
+
result_builder.schema_error('`exclusiveMinimum` is not a number', 'exclusiveMinimum')
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JSI
|
4
|
+
module Schema::Validation::MinMaxProperties
|
5
|
+
# @private
|
6
|
+
def internal_validate_maxProperties(result_builder)
|
7
|
+
if keyword?('maxProperties')
|
8
|
+
value = schema_content['maxProperties']
|
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_hash)
|
12
|
+
# An object instance is valid against "maxProperties" if its number of properties is less than, or equal to, the value of this keyword.
|
13
|
+
result_builder.validate(
|
14
|
+
result_builder.instance.size <= value,
|
15
|
+
'instance object contains more properties than `maxProperties` value',
|
16
|
+
keyword: 'maxProperties',
|
17
|
+
)
|
18
|
+
end
|
19
|
+
else
|
20
|
+
result_builder.schema_error('`maxProperties` is not a non-negative integer', 'maxProperties')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# @private
|
26
|
+
def internal_validate_minProperties(result_builder)
|
27
|
+
if keyword?('minProperties')
|
28
|
+
value = schema_content['minProperties']
|
29
|
+
# The value of this keyword MUST be a non-negative integer.
|
30
|
+
if internal_integer?(value) && value >= 0
|
31
|
+
if result_builder.instance.respond_to?(:to_hash)
|
32
|
+
# An object instance is valid against "minProperties" if its number of properties is greater than, or equal to, the value of this keyword.
|
33
|
+
result_builder.validate(
|
34
|
+
result_builder.instance.size >= value,
|
35
|
+
'instance object contains fewer properties than `minProperties` value',
|
36
|
+
keyword: 'minProperties',
|
37
|
+
)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
result_builder.schema_error('`minProperties` is not a non-negative integer', 'minProperties')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JSI
|
4
|
+
module Schema::Validation::Pattern
|
5
|
+
# @private
|
6
|
+
def internal_validate_pattern(result_builder)
|
7
|
+
if keyword?('pattern')
|
8
|
+
value = schema_content['pattern']
|
9
|
+
# The value of this keyword MUST be a string.
|
10
|
+
if value.respond_to?(:to_str)
|
11
|
+
if result_builder.instance.respond_to?(:to_str)
|
12
|
+
begin
|
13
|
+
# This string SHOULD be a valid regular expression, according to the ECMA 262 regular expression
|
14
|
+
# dialect.
|
15
|
+
# TODO
|
16
|
+
regexp = Regexp.new(value)
|
17
|
+
# A string instance is considered valid if the regular expression matches the instance
|
18
|
+
# succssfully. Recall: regular expressions are not implicitly anchored.
|
19
|
+
result_builder.validate(
|
20
|
+
regexp.match(result_builder.instance),
|
21
|
+
'instance string does not match `pattern` regular expression value',
|
22
|
+
keyword: 'pattern',
|
23
|
+
)
|
24
|
+
rescue RegexpError => e
|
25
|
+
result_builder.schema_error("`pattern` is not a valid regular expression: #{e.message}", 'pattern')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
else
|
29
|
+
result_builder.schema_error('`pattern` is not a string', 'pattern')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,101 @@
|
|
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
|
+
['properties', property_name],
|
23
|
+
[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
|
+
['patternProperties', value_property_pattern],
|
58
|
+
[property_name],
|
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
|
+
['additionalProperties'],
|
87
|
+
[property_name],
|
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
|
@@ -0,0 +1,32 @@
|
|
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
|
@@ -0,0 +1,40 @@
|
|
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 = jsi_memoize(:ref) { Schema::Ref.new(value, self) }
|
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
|
@@ -0,0 +1,27 @@
|
|
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
|
@@ -0,0 +1,90 @@
|
|
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
|
@@ -0,0 +1,47 @@
|
|
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
|
@@ -0,0 +1,49 @@
|
|
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
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JSI
|
4
|
+
module Schema::Validation
|
5
|
+
autoload :Core, 'jsi/schema/validation/core'
|
6
|
+
|
7
|
+
autoload :Draft04, 'jsi/schema/validation/draft04'
|
8
|
+
autoload :Draft06, 'jsi/schema/validation/draft06'
|
9
|
+
autoload :Draft07, 'jsi/schema/validation/draft07'
|
10
|
+
|
11
|
+
# ref application
|
12
|
+
autoload :Ref, 'jsi/schema/validation/ref'
|
13
|
+
|
14
|
+
# inplace subschema application
|
15
|
+
autoload :AllOf, 'jsi/schema/validation/someof'
|
16
|
+
autoload :AnyOf, 'jsi/schema/validation/someof'
|
17
|
+
autoload :OneOf, 'jsi/schema/validation/someof'
|
18
|
+
autoload :IfThenElse, 'jsi/schema/validation/ifthenelse'
|
19
|
+
|
20
|
+
# child subschema application
|
21
|
+
autoload :Items, 'jsi/schema/validation/items'
|
22
|
+
autoload :Contains, 'jsi/schema/validation/contains'
|
23
|
+
autoload :Properties, 'jsi/schema/validation/properties'
|
24
|
+
|
25
|
+
# property names subschema application
|
26
|
+
autoload :PropertyNames, 'jsi/schema/validation/property_names'
|
27
|
+
|
28
|
+
# any type validation
|
29
|
+
autoload :Type, 'jsi/schema/validation/type'
|
30
|
+
autoload :Enum, 'jsi/schema/validation/enum'
|
31
|
+
autoload :Const, 'jsi/schema/validation/const'
|
32
|
+
autoload :Not, 'jsi/schema/validation/not'
|
33
|
+
|
34
|
+
# object validation
|
35
|
+
autoload :Required, 'jsi/schema/validation/required'
|
36
|
+
autoload :Dependencies, 'jsi/schema/validation/dependencies'
|
37
|
+
autoload :MinMaxProperties, 'jsi/schema/validation/object'
|
38
|
+
|
39
|
+
# array validation
|
40
|
+
autoload :ArrayLength, 'jsi/schema/validation/array'
|
41
|
+
autoload :UniqueItems, 'jsi/schema/validation/array'
|
42
|
+
|
43
|
+
# string validation
|
44
|
+
autoload :StringLength, 'jsi/schema/validation/string'
|
45
|
+
autoload :Pattern, 'jsi/schema/validation/pattern'
|
46
|
+
|
47
|
+
# numeric validation
|
48
|
+
autoload :MultipleOf, 'jsi/schema/validation/numeric'
|
49
|
+
autoload :MinMax, 'jsi/schema/validation/numeric'
|
50
|
+
end
|
51
|
+
end
|