json_skooma 0.1.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +22 -0
- data/LICENSE.txt +21 -0
- data/README.md +107 -0
- data/data/draft-2019-09/README.md +60 -0
- data/data/draft-2019-09/hyper-schema.json +26 -0
- data/data/draft-2019-09/links.json +91 -0
- data/data/draft-2019-09/meta/applicator.json +56 -0
- data/data/draft-2019-09/meta/content.json +17 -0
- data/data/draft-2019-09/meta/core.json +57 -0
- data/data/draft-2019-09/meta/format.json +14 -0
- data/data/draft-2019-09/meta/hyper-schema.json +29 -0
- data/data/draft-2019-09/meta/meta-data.json +37 -0
- data/data/draft-2019-09/meta/validation.json +98 -0
- data/data/draft-2019-09/output/hyper-schema.json +62 -0
- data/data/draft-2019-09/output/schema.json +86 -0
- data/data/draft-2019-09/output/verbose-example.json +130 -0
- data/data/draft-2019-09/schema.json +42 -0
- data/data/draft-2020-12/README.md +89 -0
- data/data/draft-2020-12/adr/README.md +15 -0
- data/data/draft-2020-12/archive/hyper-schema.json +28 -0
- data/data/draft-2020-12/archive/links.json +93 -0
- data/data/draft-2020-12/archive/meta/hyper-schema.json +30 -0
- data/data/draft-2020-12/hyper-schema.json +27 -0
- data/data/draft-2020-12/links.json +85 -0
- data/data/draft-2020-12/meta/applicator.json +48 -0
- data/data/draft-2020-12/meta/content.json +17 -0
- data/data/draft-2020-12/meta/core.json +51 -0
- data/data/draft-2020-12/meta/format-annotation.json +14 -0
- data/data/draft-2020-12/meta/format-assertion.json +14 -0
- data/data/draft-2020-12/meta/hyper-schema.json +29 -0
- data/data/draft-2020-12/meta/meta-data.json +37 -0
- data/data/draft-2020-12/meta/unevaluated.json +15 -0
- data/data/draft-2020-12/meta/validation.json +98 -0
- data/data/draft-2020-12/output/hyper-schema.json +62 -0
- data/data/draft-2020-12/output/schema.json +96 -0
- data/data/draft-2020-12/output/verbose-example.json +130 -0
- data/data/draft-2020-12/schema.json +58 -0
- data/lib/json_skooma/dialects/draft201909.rb +137 -0
- data/lib/json_skooma/dialects/draft202012.rb +146 -0
- data/lib/json_skooma/formatters.rb +135 -0
- data/lib/json_skooma/inflector.rb +13 -0
- data/lib/json_skooma/json_node.rb +100 -0
- data/lib/json_skooma/json_pointer.rb +79 -0
- data/lib/json_skooma/json_schema.rb +176 -0
- data/lib/json_skooma/keywords/applicator/additional_properties.rb +37 -0
- data/lib/json_skooma/keywords/applicator/all_of.rb +25 -0
- data/lib/json_skooma/keywords/applicator/any_of.rb +26 -0
- data/lib/json_skooma/keywords/applicator/contains.rb +31 -0
- data/lib/json_skooma/keywords/applicator/dependent_schemas.rb +35 -0
- data/lib/json_skooma/keywords/applicator/else.rb +22 -0
- data/lib/json_skooma/keywords/applicator/if.rb +17 -0
- data/lib/json_skooma/keywords/applicator/items.rb +36 -0
- data/lib/json_skooma/keywords/applicator/not.rb +19 -0
- data/lib/json_skooma/keywords/applicator/one_of.rb +35 -0
- data/lib/json_skooma/keywords/applicator/pattern_properties.rb +46 -0
- data/lib/json_skooma/keywords/applicator/prefix_items.rb +31 -0
- data/lib/json_skooma/keywords/applicator/properties.rb +34 -0
- data/lib/json_skooma/keywords/applicator/property_names.rb +25 -0
- data/lib/json_skooma/keywords/applicator/then.rb +22 -0
- data/lib/json_skooma/keywords/base.rb +74 -0
- data/lib/json_skooma/keywords/base_annotation.rb +12 -0
- data/lib/json_skooma/keywords/content/content_encoding.rb +12 -0
- data/lib/json_skooma/keywords/content/content_media_type.rb +12 -0
- data/lib/json_skooma/keywords/content/content_schema.rb +19 -0
- data/lib/json_skooma/keywords/core/anchor.rb +22 -0
- data/lib/json_skooma/keywords/core/comment.rb +12 -0
- data/lib/json_skooma/keywords/core/defs.rb +13 -0
- data/lib/json_skooma/keywords/core/dynamic_anchor.rb +22 -0
- data/lib/json_skooma/keywords/core/dynamic_ref.rb +67 -0
- data/lib/json_skooma/keywords/core/id.rb +28 -0
- data/lib/json_skooma/keywords/core/ref.rb +35 -0
- data/lib/json_skooma/keywords/core/schema.rb +26 -0
- data/lib/json_skooma/keywords/core/vocabulary.rb +34 -0
- data/lib/json_skooma/keywords/draft_2019_09/additional_items.rb +40 -0
- data/lib/json_skooma/keywords/draft_2019_09/items.rb +41 -0
- data/lib/json_skooma/keywords/draft_2019_09/recursive_anchor.rb +12 -0
- data/lib/json_skooma/keywords/draft_2019_09/recursive_ref.rb +46 -0
- data/lib/json_skooma/keywords/draft_2019_09/unevaluated_items.rb +56 -0
- data/lib/json_skooma/keywords/format_annotation/format.rb +27 -0
- data/lib/json_skooma/keywords/meta_data/default.rb +11 -0
- data/lib/json_skooma/keywords/meta_data/deprecated.rb +11 -0
- data/lib/json_skooma/keywords/meta_data/description.rb +11 -0
- data/lib/json_skooma/keywords/meta_data/examples.rb +11 -0
- data/lib/json_skooma/keywords/meta_data/read_only.rb +11 -0
- data/lib/json_skooma/keywords/meta_data/title.rb +11 -0
- data/lib/json_skooma/keywords/meta_data/write_only.rb +11 -0
- data/lib/json_skooma/keywords/unevaluated/unevaluated_items.rb +59 -0
- data/lib/json_skooma/keywords/unevaluated/unevaluated_properties.rb +43 -0
- data/lib/json_skooma/keywords/unknown.rb +21 -0
- data/lib/json_skooma/keywords/validation/const.rb +17 -0
- data/lib/json_skooma/keywords/validation/dependent_required.rb +24 -0
- data/lib/json_skooma/keywords/validation/enum.rb +19 -0
- data/lib/json_skooma/keywords/validation/exclusive_maximum.rb +18 -0
- data/lib/json_skooma/keywords/validation/exclusive_minimum.rb +18 -0
- data/lib/json_skooma/keywords/validation/max_contains.rb +24 -0
- data/lib/json_skooma/keywords/validation/max_items.rb +18 -0
- data/lib/json_skooma/keywords/validation/max_length.rb +18 -0
- data/lib/json_skooma/keywords/validation/max_properties.rb +18 -0
- data/lib/json_skooma/keywords/validation/maximum.rb +18 -0
- data/lib/json_skooma/keywords/validation/min_contains.rb +31 -0
- data/lib/json_skooma/keywords/validation/min_items.rb +18 -0
- data/lib/json_skooma/keywords/validation/min_length.rb +18 -0
- data/lib/json_skooma/keywords/validation/min_properties.rb +18 -0
- data/lib/json_skooma/keywords/validation/minimum.rb +18 -0
- data/lib/json_skooma/keywords/validation/multiple_of.rb +20 -0
- data/lib/json_skooma/keywords/validation/pattern.rb +23 -0
- data/lib/json_skooma/keywords/validation/required.rb +19 -0
- data/lib/json_skooma/keywords/validation/type.rb +26 -0
- data/lib/json_skooma/keywords/validation/unique_items.rb +20 -0
- data/lib/json_skooma/keywords/value_schemas.rb +87 -0
- data/lib/json_skooma/memoizable.rb +21 -0
- data/lib/json_skooma/metaschema.rb +32 -0
- data/lib/json_skooma/registry.rb +130 -0
- data/lib/json_skooma/result.rb +125 -0
- data/lib/json_skooma/sources.rb +55 -0
- data/lib/json_skooma/validators/base.rb +31 -0
- data/lib/json_skooma/validators/date.rb +18 -0
- data/lib/json_skooma/validators/date_time.rb +24 -0
- data/lib/json_skooma/validators/duration.rb +25 -0
- data/lib/json_skooma/validators/email.rb +36 -0
- data/lib/json_skooma/validators/hostname.rb +17 -0
- data/lib/json_skooma/validators/idn_email.rb +30 -0
- data/lib/json_skooma/validators/idn_hostname.rb +15 -0
- data/lib/json_skooma/validators/ipv4.rb +20 -0
- data/lib/json_skooma/validators/ipv6.rb +16 -0
- data/lib/json_skooma/validators/iri.rb +47 -0
- data/lib/json_skooma/validators/iri_reference.rb +15 -0
- data/lib/json_skooma/validators/json_pointer.rb +19 -0
- data/lib/json_skooma/validators/regex.rb +15 -0
- data/lib/json_skooma/validators/relative_json_pointer.rb +18 -0
- data/lib/json_skooma/validators/time.rb +32 -0
- data/lib/json_skooma/validators/uri.rb +60 -0
- data/lib/json_skooma/validators/uri_reference.rb +15 -0
- data/lib/json_skooma/validators/uri_template.rb +26 -0
- data/lib/json_skooma/validators/uuid.rb +15 -0
- data/lib/json_skooma/validators.rb +17 -0
- data/lib/json_skooma/version.rb +5 -0
- data/lib/json_skooma/vocabulary.rb +12 -0
- data/lib/json_skooma.rb +39 -0
- metadata +244 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSONSkooma
|
|
4
|
+
module Keywords
|
|
5
|
+
module Applicator
|
|
6
|
+
class PrefixItems < Base
|
|
7
|
+
self.key = "prefixItems"
|
|
8
|
+
self.instance_types = "array"
|
|
9
|
+
self.value_schema = :array_of_schemas
|
|
10
|
+
|
|
11
|
+
def evaluate(instance, result)
|
|
12
|
+
annotation = nil
|
|
13
|
+
error = []
|
|
14
|
+
instance.take(json.length).each_with_index do |item, index|
|
|
15
|
+
annotation = index
|
|
16
|
+
result.call(item, index.to_s) do |subresult|
|
|
17
|
+
unless json[index].evaluate(item, subresult).passed?
|
|
18
|
+
error << index
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
return result.failure(error) unless error.empty?
|
|
23
|
+
return if annotation.nil?
|
|
24
|
+
|
|
25
|
+
annotation = true if annotation == instance.length - 1
|
|
26
|
+
result.annotate(annotation)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSONSkooma
|
|
4
|
+
module Keywords
|
|
5
|
+
module Applicator
|
|
6
|
+
class Properties < Base
|
|
7
|
+
self.key = "properties"
|
|
8
|
+
self.instance_types = "object"
|
|
9
|
+
self.value_schema = :object_of_schemas
|
|
10
|
+
|
|
11
|
+
def evaluate(instance, result)
|
|
12
|
+
annotation = []
|
|
13
|
+
err_names = []
|
|
14
|
+
instance.each do |name, item|
|
|
15
|
+
next unless json.value.key?(name)
|
|
16
|
+
|
|
17
|
+
result.call(item, name) do |subresult|
|
|
18
|
+
json[name].evaluate(item, subresult)
|
|
19
|
+
if subresult.passed?
|
|
20
|
+
annotation << name
|
|
21
|
+
else
|
|
22
|
+
err_names << name
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
return result.annotate(annotation) if err_names.empty?
|
|
28
|
+
|
|
29
|
+
result.failure("Properties #{err_names} are invalid")
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSONSkooma
|
|
4
|
+
module Keywords
|
|
5
|
+
module Applicator
|
|
6
|
+
class PropertyNames < Base
|
|
7
|
+
self.key = "propertyNames"
|
|
8
|
+
self.instance_types = "object"
|
|
9
|
+
self.value_schema = :schema
|
|
10
|
+
|
|
11
|
+
def evaluate(instance, result)
|
|
12
|
+
error = []
|
|
13
|
+
instance.each do |name,|
|
|
14
|
+
next if json.evaluate(JSONNode.new(name, key: name, parent: instance), result).passed?
|
|
15
|
+
|
|
16
|
+
error << name
|
|
17
|
+
result.success
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
result.failure(error) if error.any?
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSONSkooma
|
|
4
|
+
module Keywords
|
|
5
|
+
module Applicator
|
|
6
|
+
class Then < Base
|
|
7
|
+
self.key = "then"
|
|
8
|
+
self.value_schema = :schema
|
|
9
|
+
self.depends_on = %w[if]
|
|
10
|
+
|
|
11
|
+
def evaluate(instance, result)
|
|
12
|
+
condition = result.sibling(instance, "if")
|
|
13
|
+
if condition&.valid?
|
|
14
|
+
json.evaluate(instance, result)
|
|
15
|
+
else
|
|
16
|
+
result.discard
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSONSkooma
|
|
4
|
+
module Keywords
|
|
5
|
+
class Base
|
|
6
|
+
attr_reader :json, :parent_schema
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
attr_accessor :static, :depends_on, :schema_value_class
|
|
10
|
+
attr_writer :key
|
|
11
|
+
attr_reader :instance_types
|
|
12
|
+
|
|
13
|
+
def key
|
|
14
|
+
@key or raise "Key must be defined"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def instance_types=(value)
|
|
18
|
+
@instance_types = Array(value)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def value_schema=(schema_keys)
|
|
22
|
+
Array(schema_keys).each do |k|
|
|
23
|
+
prepend ValueSchemas[k]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def set_defaults
|
|
28
|
+
@instance_types = %w[boolean object array string number null]
|
|
29
|
+
@static = false
|
|
30
|
+
@depends_on = []
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def inherited(subclass)
|
|
34
|
+
subclass.set_defaults
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def initialize(parent_schema, value)
|
|
39
|
+
@parent_schema = parent_schema
|
|
40
|
+
@json = wrap_value(value)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def key
|
|
44
|
+
self.class.key
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def static
|
|
48
|
+
self.class.static
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def instance_types
|
|
52
|
+
self.class.instance_types
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def evaluate(instance, result)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def each_schema(&block)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def resolve
|
|
62
|
+
each_schema do |schema|
|
|
63
|
+
schema.resolve_references if schema.respond_to?(:resolve_references)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def wrap_value(value)
|
|
70
|
+
JSONNode.new(value, key: key, parent: parent_schema)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSONSkooma
|
|
4
|
+
module Keywords
|
|
5
|
+
module Content
|
|
6
|
+
class ContentSchema < BaseAnnotation
|
|
7
|
+
self.key = "contentSchema"
|
|
8
|
+
self.instance_types = %w[string]
|
|
9
|
+
self.depends_on = %w[contentMediaType]
|
|
10
|
+
|
|
11
|
+
def evaluate(instance, result)
|
|
12
|
+
return super if result.sibling(instance, "contentMediaType")
|
|
13
|
+
|
|
14
|
+
result.discard
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSONSkooma
|
|
4
|
+
module Keywords
|
|
5
|
+
module Core
|
|
6
|
+
class Anchor < Base
|
|
7
|
+
self.key = "$anchor"
|
|
8
|
+
self.static = true
|
|
9
|
+
|
|
10
|
+
def initialize(parent_schema, value)
|
|
11
|
+
super
|
|
12
|
+
|
|
13
|
+
base_uri = parent_schema.base_uri
|
|
14
|
+
raise Error, "No base URI for `$anchor` value `#{value}`" if base_uri.nil?
|
|
15
|
+
|
|
16
|
+
uri = base_uri.dup.tap { |u| u.fragment = value }
|
|
17
|
+
parent_schema.registry.add_schema(uri, parent_schema, cache_id: parent_schema.cache_id)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSONSkooma
|
|
4
|
+
module Keywords
|
|
5
|
+
module Core
|
|
6
|
+
class DynamicAnchor < Base
|
|
7
|
+
self.key = "$dynamicAnchor"
|
|
8
|
+
self.static = true
|
|
9
|
+
|
|
10
|
+
def initialize(parent_schema, value)
|
|
11
|
+
super
|
|
12
|
+
|
|
13
|
+
base_uri = parent_schema.base_uri
|
|
14
|
+
raise Error, "No base URI for `$dynamicAnchor` value `#{value}`" if base_uri.nil?
|
|
15
|
+
|
|
16
|
+
uri = base_uri.dup.tap { |u| u.fragment = value }
|
|
17
|
+
parent_schema.registry.add_schema(uri, parent_schema, cache_id: parent_schema.cache_id)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSONSkooma
|
|
4
|
+
module Keywords
|
|
5
|
+
module Core
|
|
6
|
+
class DynamicRef < Base
|
|
7
|
+
self.key = "$dynamicRef"
|
|
8
|
+
|
|
9
|
+
def initialize(parent_schema, value)
|
|
10
|
+
super
|
|
11
|
+
@fragment = URI.parse(value).fragment
|
|
12
|
+
@dynamic = false
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def resolve
|
|
16
|
+
uri = URI.parse(json)
|
|
17
|
+
if uri.relative?
|
|
18
|
+
base_uri = parent_schema.base_uri
|
|
19
|
+
if base_uri.nil?
|
|
20
|
+
raise "No base URI against which to resolve the `$dynamicRef` value `#{uri}`"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
uri = base_uri + uri
|
|
24
|
+
end
|
|
25
|
+
@ref_schema = parent_schema.registry.schema(
|
|
26
|
+
uri,
|
|
27
|
+
metaschema_uri: parent_schema.metaschema_uri,
|
|
28
|
+
cache_id: parent_schema.cache_id
|
|
29
|
+
)
|
|
30
|
+
dynamic_anchor = @ref_schema["$dynamicAnchor"]
|
|
31
|
+
@dynamic = dynamic_anchor == @fragment
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def evaluate(instance, result)
|
|
35
|
+
ref_schema = @ref_schema
|
|
36
|
+
if @dynamic
|
|
37
|
+
target = result
|
|
38
|
+
checked_uris = Set.new
|
|
39
|
+
while target
|
|
40
|
+
base_uri = target.schema.base_uri
|
|
41
|
+
if base_uri && !checked_uris.include?(base_uri)
|
|
42
|
+
checked_uris << base_uri
|
|
43
|
+
target_uri = base_uri.dup.tap { |u| u.fragment = @fragment }
|
|
44
|
+
|
|
45
|
+
begin
|
|
46
|
+
found_schema = parent_schema.registry.schema(
|
|
47
|
+
target_uri,
|
|
48
|
+
cache_id: parent_schema.cache_id
|
|
49
|
+
)
|
|
50
|
+
dynamic_anchor = found_schema["$dynamicAnchor"]
|
|
51
|
+
ref_schema = found_schema if dynamic_anchor == @fragment
|
|
52
|
+
rescue RegistryError, Hana::Pointer::FormatError
|
|
53
|
+
# do nothing
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
target = target.parent
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
ref_schema.evaluate(instance, result)
|
|
62
|
+
result.ref_schema = ref_schema
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSONSkooma
|
|
4
|
+
module Keywords
|
|
5
|
+
module Core
|
|
6
|
+
class Id < Base
|
|
7
|
+
self.key = "$id"
|
|
8
|
+
self.static = true
|
|
9
|
+
|
|
10
|
+
def initialize(parent_schema, value)
|
|
11
|
+
super
|
|
12
|
+
|
|
13
|
+
uri = URI.parse(value)
|
|
14
|
+
if uri.relative?
|
|
15
|
+
base_uri = parent_schema.base_uri
|
|
16
|
+
if base_uri
|
|
17
|
+
uri = base_uri + uri
|
|
18
|
+
else
|
|
19
|
+
raise "No base URI against which to resolve the `$id` value `#{value}`"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
parent_schema.uri = uri
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSONSkooma
|
|
4
|
+
module Keywords
|
|
5
|
+
module Core
|
|
6
|
+
class Ref < Base
|
|
7
|
+
self.key = "$ref"
|
|
8
|
+
|
|
9
|
+
def resolve
|
|
10
|
+
uri = resolve_uri
|
|
11
|
+
@ref_schema = parent_schema.registry.schema(
|
|
12
|
+
uri,
|
|
13
|
+
metaschema_uri: parent_schema.metaschema_uri,
|
|
14
|
+
cache_id: parent_schema.cache_id
|
|
15
|
+
)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def evaluate(instance, result)
|
|
19
|
+
@ref_schema.evaluate(instance, result)
|
|
20
|
+
result.ref_schema = @ref_schema
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def resolve_uri
|
|
26
|
+
uri = URI.parse(json)
|
|
27
|
+
return uri if uri.absolute?
|
|
28
|
+
return parent_schema.base_uri + uri if parent_schema.base_uri
|
|
29
|
+
|
|
30
|
+
raise Error, "No base URI against which to resolve the `$ref` value `#{uri}`"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSONSkooma
|
|
4
|
+
module Keywords
|
|
5
|
+
module Core
|
|
6
|
+
class Schema < Base
|
|
7
|
+
self.key = "$schema"
|
|
8
|
+
self.static = true
|
|
9
|
+
|
|
10
|
+
def initialize(parent_schema, value)
|
|
11
|
+
super
|
|
12
|
+
|
|
13
|
+
# todo: validate URI.validate(require_scheme=True, require_normalized=True)
|
|
14
|
+
uri = URI.parse(value)
|
|
15
|
+
parent_schema.metaschema_uri = uri
|
|
16
|
+
|
|
17
|
+
if parent_schema.is_a?(Metaschema)
|
|
18
|
+
metaschema = parent_schema.registry.metaschema(uri)
|
|
19
|
+
parent_schema.core_vocabulary ||= metaschema.core_vocabulary
|
|
20
|
+
parent_schema.default_vocabularies ||= metaschema.default_vocabularies
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSONSkooma
|
|
4
|
+
module Keywords
|
|
5
|
+
module Core
|
|
6
|
+
class Vocabulary < Base
|
|
7
|
+
self.key = "$vocabulary"
|
|
8
|
+
self.static = true
|
|
9
|
+
|
|
10
|
+
def initialize(parent_schema, value)
|
|
11
|
+
super
|
|
12
|
+
|
|
13
|
+
return unless parent_schema.is_a?(Metaschema)
|
|
14
|
+
|
|
15
|
+
core_vocab_uri = parent_schema.core_vocabulary&.uri.to_s
|
|
16
|
+
if core_vocab_uri.empty? || !value[core_vocab_uri]
|
|
17
|
+
raise Error, "The `$vocabulary` keyword must list the core vocabulary with a value of true"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
value.each do |vocabulary_uri, vocabulary_required|
|
|
21
|
+
uri = URI.parse(vocabulary_uri)
|
|
22
|
+
|
|
23
|
+
vocabulary = parent_schema.registry.vocabulary(uri)
|
|
24
|
+
parent_schema.kw_classes.merge!(vocabulary.kw_classes)
|
|
25
|
+
rescue RegistryError
|
|
26
|
+
if vocabulary_required
|
|
27
|
+
raise Error, "The metaschema requires an unrecognized vocabulary #{vocabulary_uri}"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSONSkooma
|
|
4
|
+
module Keywords
|
|
5
|
+
module Draft201909
|
|
6
|
+
class AdditionalItems < Base
|
|
7
|
+
self.key = "additionalItems"
|
|
8
|
+
self.instance_types = "array"
|
|
9
|
+
self.value_schema = :schema
|
|
10
|
+
self.depends_on = %w[items]
|
|
11
|
+
|
|
12
|
+
def evaluate(instance, result)
|
|
13
|
+
items = result.sibling(instance, "items")
|
|
14
|
+
return result.discard unless items&.annotation.is_a?(Integer)
|
|
15
|
+
|
|
16
|
+
annotation = nil
|
|
17
|
+
error = []
|
|
18
|
+
|
|
19
|
+
instance.each_with_index do |item, index|
|
|
20
|
+
next if items.annotation >= index
|
|
21
|
+
|
|
22
|
+
if json.evaluate(item, result).passed?
|
|
23
|
+
annotation = true
|
|
24
|
+
else
|
|
25
|
+
error << index
|
|
26
|
+
# reset to passed for the next iteration
|
|
27
|
+
result.success
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
if error.any?
|
|
32
|
+
result.failure(error)
|
|
33
|
+
else
|
|
34
|
+
result.annotate(annotation)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSONSkooma
|
|
4
|
+
module Keywords
|
|
5
|
+
module Draft201909
|
|
6
|
+
class Items < Base
|
|
7
|
+
self.key = "items"
|
|
8
|
+
self.instance_types = %w[array]
|
|
9
|
+
self.value_schema = [:schema, :array_of_schemas]
|
|
10
|
+
|
|
11
|
+
def evaluate(instance, result)
|
|
12
|
+
return if instance.empty?
|
|
13
|
+
|
|
14
|
+
if json.type == "boolean"
|
|
15
|
+
json.evaluate(instance, result)
|
|
16
|
+
elsif json.is_a?(JSONSchema)
|
|
17
|
+
instance.each do |item|
|
|
18
|
+
json.evaluate(item, result)
|
|
19
|
+
end
|
|
20
|
+
result.annotate(true) if result.passed?
|
|
21
|
+
elsif json.type == "array"
|
|
22
|
+
annotation = nil
|
|
23
|
+
error = []
|
|
24
|
+
|
|
25
|
+
instance.take(json.length).each_with_index do |item, index|
|
|
26
|
+
annotation = index
|
|
27
|
+
result.call(item, index.to_s) do |subresult|
|
|
28
|
+
unless json[index].evaluate(item, subresult).passed?
|
|
29
|
+
error << index
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
return result.failure(error) unless error.empty?
|
|
34
|
+
|
|
35
|
+
result.annotate(annotation)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSONSkooma
|
|
4
|
+
module Keywords
|
|
5
|
+
module Draft201909
|
|
6
|
+
class RecursiveRef < Base
|
|
7
|
+
self.key = "$recursiveRef"
|
|
8
|
+
|
|
9
|
+
def initialize(parent_schema, value)
|
|
10
|
+
super
|
|
11
|
+
raise "`$recursiveRef` may only take the value `#`" if value != "#"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def resolve
|
|
15
|
+
base_uri = parent_schema.base_uri
|
|
16
|
+
if base_uri.nil?
|
|
17
|
+
raise "No base URI against which to resolve the `$recursiveRef`"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
@ref_schema = parent_schema.registry.schema(
|
|
21
|
+
base_uri,
|
|
22
|
+
metaschema_uri: parent_schema.metaschema_uri,
|
|
23
|
+
cache_id: parent_schema.cache_id
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def evaluate(instance, result)
|
|
28
|
+
ref_schema = @ref_schema
|
|
29
|
+
recursive_anchor = ref_schema["$recursiveAnchor"]
|
|
30
|
+
if recursive_anchor&.value
|
|
31
|
+
target = result
|
|
32
|
+
while target
|
|
33
|
+
base_anchor = target.schema["$recursiveAnchor"]
|
|
34
|
+
if base_anchor&.value
|
|
35
|
+
ref_schema = target.schema
|
|
36
|
+
end
|
|
37
|
+
target = target.parent
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
ref_schema.evaluate(instance, result)
|
|
41
|
+
result.ref_schema = ref_schema
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|