json_schemer 2.1.1 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +5 -1
- data/CHANGELOG.md +45 -0
- data/Gemfile.lock +18 -16
- data/README.md +23 -14
- data/json_schemer.gemspec +5 -2
- data/lib/json_schemer/configuration.rb +31 -0
- data/lib/json_schemer/content.rb +1 -1
- data/lib/json_schemer/draft202012/vocab/applicator.rb +1 -1
- data/lib/json_schemer/draft202012/vocab/unevaluated.rb +18 -8
- data/lib/json_schemer/draft202012/vocab/validation.rb +5 -1
- data/lib/json_schemer/draft4/vocab/validation.rb +2 -3
- data/lib/json_schemer/format.rb +6 -7
- data/lib/json_schemer/keyword.rb +3 -0
- data/lib/json_schemer/openapi.rb +2 -4
- data/lib/json_schemer/openapi30/meta.rb +4 -2
- data/lib/json_schemer/openapi31/meta.rb +4 -4
- data/lib/json_schemer/openapi31/vocab/base.rb +1 -1
- data/lib/json_schemer/resources.rb +24 -0
- data/lib/json_schemer/result.rb +15 -2
- data/lib/json_schemer/schema.rb +84 -74
- data/lib/json_schemer/version.rb +1 -1
- data/lib/json_schemer.rb +53 -24
- metadata +50 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 04dbbf738ea9253437566fb50075ee7603fd0b536324181aebb0573c32c7f7bc
|
|
4
|
+
data.tar.gz: 29337d5892b33a2f74e435370986f059d7dd9762352bea610e7668dca3171e09
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 90f1510bcc40ad9500904453dee34434b91ef45b8ab7d8be84dc728aae4d4c1a4556037f1ae8b48595198529973fe2d1e5c20a64e5aeb9871cd6fddd23636268
|
|
7
|
+
data.tar.gz: c7e8a861d8f763d5248d79d486d8b253b9f81e40952e896d53b7dad84bdd5197a70224008e79de5cf5b6e7a3e9a8e646bd8af4188ae6a87c4a7ac9011998f36f
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -6,8 +6,12 @@ jobs:
|
|
|
6
6
|
fail-fast: false
|
|
7
7
|
matrix:
|
|
8
8
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
9
|
-
ruby: [2.
|
|
9
|
+
ruby: [2.7, 3.0, 3.1, 3.2, 3.3, 3.4, head, jruby, jruby-head, truffleruby, truffleruby-head]
|
|
10
10
|
exclude:
|
|
11
|
+
- os: ubuntu-latest
|
|
12
|
+
ruby: head
|
|
13
|
+
- os: macos-latest
|
|
14
|
+
ruby: head
|
|
11
15
|
- os: windows-latest
|
|
12
16
|
ruby: truffleruby
|
|
13
17
|
- os: windows-latest
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.4.0] - 2025-02-01
|
|
4
|
+
|
|
5
|
+
### Bug Fixes
|
|
6
|
+
|
|
7
|
+
- Store schema resource file URIs as strings to prevent conflicts: https://github.com/davishmcclurg/json_schemer/pull/189
|
|
8
|
+
- Require OpenAPI `discriminator` instances to be objects: https://github.com/davishmcclurg/json_schemer/pull/206
|
|
9
|
+
- Pass configuration options to subschemas: https://github.com/davishmcclurg/json_schemer/pull/208
|
|
10
|
+
- Check applicable instance types in OpenAPI `format` extensions: https://github.com/davishmcclurg/json_schemer/pull/209
|
|
11
|
+
- Use correct max values for OpenAPI `int32`/`int64` formats: https://github.com/davishmcclurg/json_schemer/commit/386c2a6fe089350c61775716643ef0600898060e
|
|
12
|
+
|
|
13
|
+
[2.4.0]: https://github.com/davishmcclurg/json_schemer/releases/tag/v2.4.0
|
|
14
|
+
|
|
15
|
+
## [2.3.0] - 2024-05-30
|
|
16
|
+
|
|
17
|
+
### Ruby Versions
|
|
18
|
+
|
|
19
|
+
- Ruby 2.5 and 2.6 are no longer supported.
|
|
20
|
+
|
|
21
|
+
### Bug Fixes
|
|
22
|
+
|
|
23
|
+
- Remove `base64` runtime dependency: https://github.com/davishmcclurg/json_schemer/pull/182
|
|
24
|
+
- Relax `uuid` format validation: https://github.com/davishmcclurg/json_schemer/pull/183
|
|
25
|
+
|
|
26
|
+
[2.3.0]: https://github.com/davishmcclurg/json_schemer/releases/tag/v2.3.0
|
|
27
|
+
|
|
28
|
+
## [2.2.0] - 2024-03-02
|
|
29
|
+
|
|
30
|
+
### Bug Fixes
|
|
31
|
+
|
|
32
|
+
- Support symbol keys when accessing original instance: https://github.com/davishmcclurg/json_schemer/commit/d52c130e9967919c6cf1c9dbc3f0babfb8b01cf8
|
|
33
|
+
- Support custom keywords in nested schemas: https://github.com/davishmcclurg/json_schemer/commit/93c85a5006981347c7e9a4c11b73c6bdb65d8ba2
|
|
34
|
+
- Stringify instance location for custom keywords: https://github.com/davishmcclurg/json_schemer/commit/513c99130b9e7986b09881e7efd3fb7143744754
|
|
35
|
+
- Reduce unhelpful error output in `unevaluated` keywords: https://github.com/davishmcclurg/json_schemer/pull/164
|
|
36
|
+
- Handle parse errors during schema validation: https://github.com/davishmcclurg/json_schemer/pull/171
|
|
37
|
+
- Follow refs when finding default property values: https://github.com/davishmcclurg/json_schemer/pull/175
|
|
38
|
+
|
|
39
|
+
### Features
|
|
40
|
+
|
|
41
|
+
- Global configuration with `Configuration` object: https://github.com/davishmcclurg/json_schemer/pull/170
|
|
42
|
+
- Symbol key property defaults with `insert_property_defaults: :symbol`: https://github.com/davishmcclurg/json_schemer/commit/a72473dc84199107ddedc8998950e5b82273232a
|
|
43
|
+
- Consistent schema type support for schema validation methods: https://github.com/davishmcclurg/json_schemer/commit/bbcd0cea20cbaa61cf2bdae5f53840861cae54b8
|
|
44
|
+
- Validation option support for schema validation methods: https://github.com/davishmcclurg/json_schemer/commit/2eeef77de522f127619b7d0faa51e0d7e40977ad
|
|
45
|
+
|
|
46
|
+
[2.2.0]: https://github.com/davishmcclurg/json_schemer/releases/tag/v2.2.0
|
|
47
|
+
|
|
3
48
|
## [2.1.1] - 2023-11-28
|
|
4
49
|
|
|
5
50
|
### Bug Fixes
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
json_schemer (2.
|
|
4
|
+
json_schemer (2.4.0)
|
|
5
|
+
bigdecimal
|
|
5
6
|
hana (~> 1.3)
|
|
6
7
|
regexp_parser (~> 2.0)
|
|
7
8
|
simpleidn (~> 0.2)
|
|
@@ -9,35 +10,36 @@ PATH
|
|
|
9
10
|
GEM
|
|
10
11
|
remote: https://rubygems.org/
|
|
11
12
|
specs:
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
base64 (0.2.0)
|
|
14
|
+
bigdecimal (3.1.9)
|
|
15
|
+
bigdecimal (3.1.9-java)
|
|
16
|
+
concurrent-ruby (1.3.5)
|
|
17
|
+
csv (3.3.2)
|
|
18
|
+
docile (1.4.1)
|
|
14
19
|
hana (1.3.7)
|
|
15
|
-
i18n (1.14.
|
|
20
|
+
i18n (1.14.6)
|
|
16
21
|
concurrent-ruby (~> 1.0)
|
|
17
22
|
i18n-debug (1.2.0)
|
|
18
23
|
i18n (< 2)
|
|
19
|
-
minitest (5.
|
|
20
|
-
rake (13.
|
|
21
|
-
regexp_parser (2.
|
|
24
|
+
minitest (5.25.4)
|
|
25
|
+
rake (13.2.1)
|
|
26
|
+
regexp_parser (2.10.0)
|
|
22
27
|
simplecov (0.22.0)
|
|
23
28
|
docile (~> 1.1)
|
|
24
29
|
simplecov-html (~> 0.11)
|
|
25
30
|
simplecov_json_formatter (~> 0.1)
|
|
26
|
-
simplecov-html (0.
|
|
31
|
+
simplecov-html (0.13.1)
|
|
27
32
|
simplecov_json_formatter (0.1.4)
|
|
28
|
-
simpleidn (0.2.
|
|
29
|
-
unf (~> 0.1.4)
|
|
30
|
-
unf (0.1.4)
|
|
31
|
-
unf_ext
|
|
32
|
-
unf (0.1.4-java)
|
|
33
|
-
unf_ext (0.0.9.1)
|
|
33
|
+
simpleidn (0.2.3)
|
|
34
34
|
|
|
35
35
|
PLATFORMS
|
|
36
36
|
java
|
|
37
37
|
ruby
|
|
38
38
|
|
|
39
39
|
DEPENDENCIES
|
|
40
|
-
|
|
40
|
+
base64
|
|
41
|
+
bundler (~> 2.4.0)
|
|
42
|
+
csv
|
|
41
43
|
i18n
|
|
42
44
|
i18n-debug
|
|
43
45
|
json_schemer!
|
|
@@ -46,4 +48,4 @@ DEPENDENCIES
|
|
|
46
48
|
simplecov (~> 0.22)
|
|
47
49
|
|
|
48
50
|
BUNDLED WITH
|
|
49
|
-
2.
|
|
51
|
+
2.4.22
|
data/README.md
CHANGED
|
@@ -95,18 +95,6 @@ JSONSchemer.validate_schema({ '$id' => '#invalid' }).to_a
|
|
|
95
95
|
# "type"=>"pattern",
|
|
96
96
|
# "error"=>"string at `/$id` does not match pattern: ^[^#]*#?$"}]
|
|
97
97
|
|
|
98
|
-
JSONSchemer.schema({ '$id' => 'valid' }).valid_schema?
|
|
99
|
-
# => true
|
|
100
|
-
|
|
101
|
-
JSONSchemer.schema({ '$id' => '#invalid' }).validate_schema.to_a
|
|
102
|
-
# => [{"data"=>"#invalid",
|
|
103
|
-
# "data_pointer"=>"/$id",
|
|
104
|
-
# "schema"=>{"$ref"=>"#/$defs/uriReferenceString", "$comment"=>"Non-empty fragments not allowed.", "pattern"=>"^[^#]*#?$"},
|
|
105
|
-
# "schema_pointer"=>"/properties/$id",
|
|
106
|
-
# "root_schema"=>{...meta schema},
|
|
107
|
-
# "type"=>"pattern",
|
|
108
|
-
# "error"=>"string at `/$id` does not match pattern: ^[^#]*#?$"}]
|
|
109
|
-
|
|
110
98
|
# subschemas
|
|
111
99
|
|
|
112
100
|
schema = {
|
|
@@ -212,7 +200,8 @@ JSONSchemer.schema(
|
|
|
212
200
|
},
|
|
213
201
|
|
|
214
202
|
# insert default property values during validation
|
|
215
|
-
#
|
|
203
|
+
# string keys by default (use `:symbol` to insert symbol keys)
|
|
204
|
+
# true/false/:symbol
|
|
216
205
|
# default: false
|
|
217
206
|
insert_property_defaults: true,
|
|
218
207
|
|
|
@@ -256,6 +245,20 @@ JSONSchemer.schema(
|
|
|
256
245
|
)
|
|
257
246
|
```
|
|
258
247
|
|
|
248
|
+
## Global Configuration
|
|
249
|
+
|
|
250
|
+
Configuration options can be set globally by modifying `JSONSchemer.configuration`. Global options are applied to any new schemas at creation time (global configuration changes are not reflected in existing schemas). They can be overridden with the regular keyword arguments described [above](#options).
|
|
251
|
+
|
|
252
|
+
```ruby
|
|
253
|
+
# configuration block
|
|
254
|
+
JSONSchemer.configure do |config|
|
|
255
|
+
config.regexp_resolver = 'ecma'
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# configuration accessors
|
|
259
|
+
JSONSchemer.configuration.insert_property_defaults = true
|
|
260
|
+
```
|
|
261
|
+
|
|
259
262
|
## Custom Error Messages
|
|
260
263
|
|
|
261
264
|
Error messages can be customized using the `x-error` keyword and/or [I18n](https://github.com/ruby-i18n/i18n) translations. `x-error` takes precedence if both are defined.
|
|
@@ -495,7 +498,13 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
|
495
498
|
|
|
496
499
|
## Build Status
|
|
497
500
|
|
|
498
|
-

|
|
502
|
+
<br>
|
|
503
|
+

|
|
504
|
+

|
|
505
|
+

|
|
506
|
+

|
|
507
|
+

|
|
499
508
|
|
|
500
509
|
## Contributing
|
|
501
510
|
|
data/json_schemer.gemspec
CHANGED
|
@@ -20,15 +20,18 @@ Gem::Specification.new do |spec|
|
|
|
20
20
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
21
21
|
spec.require_paths = ["lib"]
|
|
22
22
|
|
|
23
|
-
spec.required_ruby_version = '>= 2.
|
|
23
|
+
spec.required_ruby_version = '>= 2.7'
|
|
24
24
|
|
|
25
|
-
spec.add_development_dependency "
|
|
25
|
+
spec.add_development_dependency "base64"
|
|
26
|
+
spec.add_development_dependency "bundler", "~> 2.4.0"
|
|
26
27
|
spec.add_development_dependency "rake", "~> 13.0"
|
|
27
28
|
spec.add_development_dependency "minitest", "~> 5.0"
|
|
28
29
|
spec.add_development_dependency "simplecov", "~> 0.22"
|
|
30
|
+
spec.add_development_dependency "csv"
|
|
29
31
|
spec.add_development_dependency "i18n"
|
|
30
32
|
spec.add_development_dependency "i18n-debug"
|
|
31
33
|
|
|
34
|
+
spec.add_runtime_dependency "bigdecimal"
|
|
32
35
|
spec.add_runtime_dependency "hana", "~> 1.3"
|
|
33
36
|
spec.add_runtime_dependency "regexp_parser", "~> 2.0"
|
|
34
37
|
spec.add_runtime_dependency "simpleidn", "~> 0.2"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module JSONSchemer
|
|
3
|
+
Configuration = Struct.new(
|
|
4
|
+
:base_uri, :meta_schema, :vocabulary, :format, :formats, :content_encodings, :content_media_types, :keywords,
|
|
5
|
+
:before_property_validation, :after_property_validation, :insert_property_defaults, :property_default_resolver,
|
|
6
|
+
:ref_resolver, :regexp_resolver, :output_format, :resolve_enumerators, :access_mode,
|
|
7
|
+
keyword_init: true
|
|
8
|
+
) do
|
|
9
|
+
def initialize(
|
|
10
|
+
base_uri: URI('json-schemer://schema'),
|
|
11
|
+
meta_schema: Draft202012::BASE_URI.to_s,
|
|
12
|
+
vocabulary: nil,
|
|
13
|
+
format: true,
|
|
14
|
+
formats: {},
|
|
15
|
+
content_encodings: {},
|
|
16
|
+
content_media_types: {},
|
|
17
|
+
keywords: {},
|
|
18
|
+
before_property_validation: [],
|
|
19
|
+
after_property_validation: [],
|
|
20
|
+
insert_property_defaults: false,
|
|
21
|
+
property_default_resolver: nil,
|
|
22
|
+
ref_resolver: proc { |uri| raise UnknownRef, uri.to_s },
|
|
23
|
+
regexp_resolver: 'ruby',
|
|
24
|
+
output_format: 'classic',
|
|
25
|
+
resolve_enumerators: false,
|
|
26
|
+
access_mode: nil
|
|
27
|
+
)
|
|
28
|
+
super
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/json_schemer/content.rb
CHANGED
|
@@ -300,7 +300,7 @@ module JSONSchemer
|
|
|
300
300
|
end
|
|
301
301
|
|
|
302
302
|
def false_schema_error(formatted_instance_location:, **)
|
|
303
|
-
"object property at #{formatted_instance_location} is
|
|
303
|
+
"object property at #{formatted_instance_location} is a disallowed additional property"
|
|
304
304
|
end
|
|
305
305
|
|
|
306
306
|
def parse
|
|
@@ -8,6 +8,10 @@ module JSONSchemer
|
|
|
8
8
|
"array items at #{formatted_instance_location} do not match `unevaluatedItems` schema"
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
def false_schema_error(formatted_instance_location:, **)
|
|
12
|
+
"array item at #{formatted_instance_location} is a disallowed unevaluated item"
|
|
13
|
+
end
|
|
14
|
+
|
|
11
15
|
def parse
|
|
12
16
|
subschema(value)
|
|
13
17
|
end
|
|
@@ -18,7 +22,7 @@ module JSONSchemer
|
|
|
18
22
|
unevaluated_items = instance.size.times.to_set
|
|
19
23
|
|
|
20
24
|
context.adjacent_results.each_value do |adjacent_result|
|
|
21
|
-
collect_unevaluated_items(adjacent_result,
|
|
25
|
+
collect_unevaluated_items(adjacent_result, unevaluated_items)
|
|
22
26
|
end
|
|
23
27
|
|
|
24
28
|
nested = unevaluated_items.map do |index|
|
|
@@ -30,8 +34,7 @@ module JSONSchemer
|
|
|
30
34
|
|
|
31
35
|
private
|
|
32
36
|
|
|
33
|
-
def collect_unevaluated_items(result,
|
|
34
|
-
return unless result.valid && result.instance_location == instance_location
|
|
37
|
+
def collect_unevaluated_items(result, unevaluated_items)
|
|
35
38
|
case result.source
|
|
36
39
|
when Applicator::PrefixItems
|
|
37
40
|
unevaluated_items.subtract(0..result.annotation)
|
|
@@ -41,7 +44,9 @@ module JSONSchemer
|
|
|
41
44
|
unevaluated_items.subtract(result.annotation)
|
|
42
45
|
end
|
|
43
46
|
result.nested&.each do |subresult|
|
|
44
|
-
|
|
47
|
+
if subresult.valid && subresult.instance_location == result.instance_location
|
|
48
|
+
collect_unevaluated_items(subresult, unevaluated_items)
|
|
49
|
+
end
|
|
45
50
|
end
|
|
46
51
|
end
|
|
47
52
|
end
|
|
@@ -51,6 +56,10 @@ module JSONSchemer
|
|
|
51
56
|
"object properties at #{formatted_instance_location} do not match `unevaluatedProperties` schema"
|
|
52
57
|
end
|
|
53
58
|
|
|
59
|
+
def false_schema_error(formatted_instance_location:, **)
|
|
60
|
+
"object property at #{formatted_instance_location} is a disallowed unevaluated property"
|
|
61
|
+
end
|
|
62
|
+
|
|
54
63
|
def parse
|
|
55
64
|
subschema(value)
|
|
56
65
|
end
|
|
@@ -61,7 +70,7 @@ module JSONSchemer
|
|
|
61
70
|
evaluated_keys = Set[]
|
|
62
71
|
|
|
63
72
|
context.adjacent_results.each_value do |adjacent_result|
|
|
64
|
-
collect_evaluated_keys(adjacent_result,
|
|
73
|
+
collect_evaluated_keys(adjacent_result, evaluated_keys)
|
|
65
74
|
end
|
|
66
75
|
|
|
67
76
|
evaluated = instance.reject do |key, _value|
|
|
@@ -77,14 +86,15 @@ module JSONSchemer
|
|
|
77
86
|
|
|
78
87
|
private
|
|
79
88
|
|
|
80
|
-
def collect_evaluated_keys(result,
|
|
81
|
-
return unless result.valid && result.instance_location == instance_location
|
|
89
|
+
def collect_evaluated_keys(result, evaluated_keys)
|
|
82
90
|
case result.source
|
|
83
91
|
when Applicator::Properties, Applicator::PatternProperties, Applicator::AdditionalProperties, UnevaluatedProperties
|
|
84
92
|
evaluated_keys.merge(result.annotation)
|
|
85
93
|
end
|
|
86
94
|
result.nested&.each do |subresult|
|
|
87
|
-
|
|
95
|
+
if subresult.valid && subresult.instance_location == result.instance_location
|
|
96
|
+
collect_evaluated_keys(subresult, evaluated_keys)
|
|
97
|
+
end
|
|
88
98
|
end
|
|
89
99
|
end
|
|
90
100
|
end
|
|
@@ -4,6 +4,10 @@ module JSONSchemer
|
|
|
4
4
|
module Vocab
|
|
5
5
|
module Validation
|
|
6
6
|
class Type < Keyword
|
|
7
|
+
def self.valid_integer?(instance)
|
|
8
|
+
instance.is_a?(Numeric) && (instance.is_a?(Integer) || instance.floor == instance)
|
|
9
|
+
end
|
|
10
|
+
|
|
7
11
|
def error(formatted_instance_location:, **)
|
|
8
12
|
case value
|
|
9
13
|
when 'null'
|
|
@@ -45,7 +49,7 @@ module JSONSchemer
|
|
|
45
49
|
when 'number'
|
|
46
50
|
instance.is_a?(Numeric)
|
|
47
51
|
when 'integer'
|
|
48
|
-
|
|
52
|
+
self.class.valid_integer?(instance)
|
|
49
53
|
when 'string'
|
|
50
54
|
instance.is_a?(String)
|
|
51
55
|
when 'array'
|
|
@@ -4,9 +4,8 @@ module JSONSchemer
|
|
|
4
4
|
module Vocab
|
|
5
5
|
module Validation
|
|
6
6
|
class Type < Draft202012::Vocab::Validation::Type
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
type == 'integer' ? instance.is_a?(Integer) : super
|
|
7
|
+
def self.valid_integer?(instance)
|
|
8
|
+
instance.is_a?(Integer)
|
|
10
9
|
end
|
|
11
10
|
end
|
|
12
11
|
|
data/lib/json_schemer/format.rb
CHANGED
|
@@ -74,10 +74,10 @@ module JSONSchemer
|
|
|
74
74
|
IP_REGEX = /\A[\h:.]+\z/.freeze
|
|
75
75
|
INVALID_QUERY_REGEX = /\s/.freeze
|
|
76
76
|
IRI_ESCAPE_REGEX = /[^[:ascii:]]/
|
|
77
|
-
UUID_REGEX = /\A\h{8}-\h{4}-\h{4}
|
|
77
|
+
UUID_REGEX = /\A\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\z/i
|
|
78
78
|
NIL_UUID = '00000000-0000-0000-0000-000000000000'
|
|
79
|
-
|
|
80
|
-
out[-byte.chr] = -sprintf('%%%02X', byte)
|
|
79
|
+
BINARY_TO_PERCENT_ENCODED = 256.times.each_with_object({}) do |byte, out|
|
|
80
|
+
out[-byte.chr(Encoding::BINARY)] = -sprintf('%%%02X', byte)
|
|
81
81
|
end.freeze
|
|
82
82
|
|
|
83
83
|
class << self
|
|
@@ -88,10 +88,9 @@ module JSONSchemer
|
|
|
88
88
|
include URITemplate
|
|
89
89
|
|
|
90
90
|
def percent_encode(data, regexp)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
data.
|
|
94
|
-
data.force_encoding(Encoding::US_ASCII)
|
|
91
|
+
binary = data.b
|
|
92
|
+
binary.gsub!(regexp, BINARY_TO_PERCENT_ENCODED)
|
|
93
|
+
binary.force_encoding(data.encoding)
|
|
95
94
|
end
|
|
96
95
|
|
|
97
96
|
def valid_date_time?(data)
|
data/lib/json_schemer/keyword.rb
CHANGED
|
@@ -45,8 +45,11 @@ module JSONSchemer
|
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
def subschema(value, keyword = nil, **options)
|
|
48
|
+
options[:configuration] ||= schema.configuration
|
|
48
49
|
options[:base_uri] ||= schema.base_uri
|
|
49
50
|
options[:meta_schema] ||= schema.meta_schema
|
|
51
|
+
options[:ref_resolver] ||= schema.ref_resolver
|
|
52
|
+
options[:regexp_resolver] ||= schema.regexp_resolver
|
|
50
53
|
Schema.new(value, self, root, keyword, **options)
|
|
51
54
|
end
|
|
52
55
|
end
|
data/lib/json_schemer/openapi.rb
CHANGED
|
@@ -8,16 +8,14 @@ module JSONSchemer
|
|
|
8
8
|
case version
|
|
9
9
|
when /\A3\.1\.\d+\z/
|
|
10
10
|
@document_schema = JSONSchemer.openapi31_document
|
|
11
|
-
|
|
11
|
+
meta_schema = document.fetch('jsonSchemaDialect') { OpenAPI31::BASE_URI.to_s }
|
|
12
12
|
when /\A3\.0\.\d+\z/
|
|
13
13
|
@document_schema = JSONSchemer.openapi30_document
|
|
14
|
-
|
|
14
|
+
meta_schema = OpenAPI30::BASE_URI.to_s
|
|
15
15
|
else
|
|
16
16
|
raise UnsupportedOpenAPIVersion, version
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
meta_schema = META_SCHEMAS_BY_BASE_URI_STR[json_schema_dialect] || raise(UnsupportedMetaSchema, json_schema_dialect)
|
|
20
|
-
|
|
21
19
|
@schema = JSONSchemer.schema(@document, :meta_schema => meta_schema, **options)
|
|
22
20
|
end
|
|
23
21
|
|
|
@@ -4,8 +4,10 @@ module JSONSchemer
|
|
|
4
4
|
BASE_URI = URI('json-schemer://openapi30/schema')
|
|
5
5
|
# https://spec.openapis.org/oas/v3.0.3#data-types
|
|
6
6
|
FORMATS = OpenAPI31::FORMATS.merge(
|
|
7
|
-
'
|
|
8
|
-
'
|
|
7
|
+
'int32' => proc { |instance, _format| !Draft4::Vocab::Validation::Type.valid_integer?(instance) || instance.floor.bit_length < 32 },
|
|
8
|
+
'int64' => proc { |instance, _format| !Draft4::Vocab::Validation::Type.valid_integer?(instance) || instance.floor.bit_length < 64 },
|
|
9
|
+
'byte' => proc { |instance, _value| !instance.is_a?(String) || ContentEncoding::BASE64.call(instance).first },
|
|
10
|
+
'binary' => proc { |instance, _value| !instance.is_a?(String) || instance.encoding == Encoding::BINARY },
|
|
9
11
|
'date' => Format::DATE
|
|
10
12
|
)
|
|
11
13
|
SCHEMA = {
|
|
@@ -4,10 +4,10 @@ module JSONSchemer
|
|
|
4
4
|
BASE_URI = URI('https://spec.openapis.org/oas/3.1/dialect/base')
|
|
5
5
|
# https://spec.openapis.org/oas/v3.1.0#data-types
|
|
6
6
|
FORMATS = {
|
|
7
|
-
'int32' => proc { |instance, _format|
|
|
8
|
-
'int64' => proc { |instance, _format|
|
|
9
|
-
'float' => proc { |instance, _format| instance.is_a?(Float) },
|
|
10
|
-
'double' => proc { |instance, _format| instance.is_a?(Float) },
|
|
7
|
+
'int32' => proc { |instance, _format| !Draft202012::Vocab::Validation::Type.valid_integer?(instance) || instance.floor.bit_length < 32 },
|
|
8
|
+
'int64' => proc { |instance, _format| !Draft202012::Vocab::Validation::Type.valid_integer?(instance) || instance.floor.bit_length < 64 },
|
|
9
|
+
'float' => proc { |instance, _format| !instance.is_a?(Numeric) || instance.is_a?(Float) },
|
|
10
|
+
'double' => proc { |instance, _format| !instance.is_a?(Numeric) || instance.is_a?(Float) },
|
|
11
11
|
'password' => proc { |_instance, _format| true }
|
|
12
12
|
}
|
|
13
13
|
SCHEMA = {
|
|
@@ -100,7 +100,7 @@ module JSONSchemer
|
|
|
100
100
|
end
|
|
101
101
|
|
|
102
102
|
def validate(instance, instance_location, keyword_location, context)
|
|
103
|
-
return result(instance, instance_location, keyword_location,
|
|
103
|
+
return result(instance, instance_location, keyword_location, false) unless instance.is_a?(Hash)
|
|
104
104
|
|
|
105
105
|
property_name = value.fetch('propertyName')
|
|
106
106
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module JSONSchemer
|
|
3
|
+
class Resources
|
|
4
|
+
def initialize
|
|
5
|
+
@resources = {}
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def [](uri)
|
|
9
|
+
@resources[uri.to_s]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def []=(uri, resource)
|
|
13
|
+
@resources[uri.to_s] = resource
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def fetch(uri)
|
|
17
|
+
@resources.fetch(uri.to_s)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def key?(uri)
|
|
21
|
+
@resources.key?(uri.to_s)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
data/lib/json_schemer/result.rb
CHANGED
|
@@ -191,6 +191,7 @@ module JSONSchemer
|
|
|
191
191
|
|
|
192
192
|
def insert_property_defaults(context)
|
|
193
193
|
instance_locations = {}
|
|
194
|
+
instance_locations.compare_by_identity
|
|
194
195
|
|
|
195
196
|
results = [[self, true]]
|
|
196
197
|
while (result, valid = results.pop)
|
|
@@ -201,8 +202,8 @@ module JSONSchemer
|
|
|
201
202
|
|
|
202
203
|
if result.source.is_a?(Schema::PROPERTIES_KEYWORD_CLASS) && result.instance.is_a?(Hash)
|
|
203
204
|
result.source.parsed.each do |property, schema|
|
|
204
|
-
next if result.instance.key?(property)
|
|
205
|
-
default = schema
|
|
205
|
+
next if result.instance.key?(property)
|
|
206
|
+
next unless default = default_keyword_instance(schema)
|
|
206
207
|
instance_location = Location.join(result.instance_location, property)
|
|
207
208
|
keyword_location = Location.join(Location.join(result.keyword_location, property), default.keyword)
|
|
208
209
|
default_result = default.validate(nil, instance_location, keyword_location, nil)
|
|
@@ -225,5 +226,17 @@ module JSONSchemer
|
|
|
225
226
|
|
|
226
227
|
inserted
|
|
227
228
|
end
|
|
229
|
+
|
|
230
|
+
private
|
|
231
|
+
|
|
232
|
+
def default_keyword_instance(schema)
|
|
233
|
+
schema.parsed.fetch('default') do
|
|
234
|
+
schema.parsed.find do |_keyword, keyword_instance|
|
|
235
|
+
next unless keyword_instance.respond_to?(:ref_schema)
|
|
236
|
+
next unless default = default_keyword_instance(keyword_instance.ref_schema)
|
|
237
|
+
break default
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
228
241
|
end
|
|
229
242
|
end
|
data/lib/json_schemer/schema.rb
CHANGED
|
@@ -4,28 +4,27 @@ module JSONSchemer
|
|
|
4
4
|
Context = Struct.new(:instance, :dynamic_scope, :adjacent_results, :short_circuit, :access_mode) do
|
|
5
5
|
def original_instance(instance_location)
|
|
6
6
|
Hana::Pointer.parse(Location.resolve(instance_location)).reduce(instance) do |obj, token|
|
|
7
|
-
obj.
|
|
7
|
+
if obj.is_a?(Array)
|
|
8
|
+
obj.fetch(token.to_i)
|
|
9
|
+
elsif !obj.key?(token) && obj.key?(token.to_sym)
|
|
10
|
+
obj.fetch(token.to_sym)
|
|
11
|
+
else
|
|
12
|
+
obj.fetch(token)
|
|
13
|
+
end
|
|
8
14
|
end
|
|
9
15
|
end
|
|
10
16
|
end
|
|
11
17
|
|
|
18
|
+
extend Forwardable
|
|
12
19
|
include Output
|
|
13
20
|
|
|
14
|
-
DEFAULT_SCHEMA = Draft202012::BASE_URI.to_s.freeze
|
|
15
21
|
SCHEMA_KEYWORD_CLASS = Draft202012::Vocab::Core::Schema
|
|
16
22
|
VOCABULARY_KEYWORD_CLASS = Draft202012::Vocab::Core::Vocabulary
|
|
17
23
|
ID_KEYWORD_CLASS = Draft202012::Vocab::Core::Id
|
|
18
24
|
UNKNOWN_KEYWORD_CLASS = Draft202012::Vocab::Core::UnknownKeyword
|
|
19
25
|
NOT_KEYWORD_CLASS = Draft202012::Vocab::Applicator::Not
|
|
20
26
|
PROPERTIES_KEYWORD_CLASS = Draft202012::Vocab::Applicator::Properties
|
|
21
|
-
|
|
22
|
-
DEFAULT_FORMATS = {}.freeze
|
|
23
|
-
DEFAULT_CONTENT_ENCODINGS = {}.freeze
|
|
24
|
-
DEFAULT_CONTENT_MEDIA_TYPES = {}.freeze
|
|
25
|
-
DEFAULT_KEYWORDS = {}.freeze
|
|
26
|
-
DEFAULT_BEFORE_PROPERTY_VALIDATION = [].freeze
|
|
27
|
-
DEFAULT_AFTER_PROPERTY_VALIDATION = [].freeze
|
|
28
|
-
DEFAULT_REF_RESOLVER = proc { |uri| raise UnknownRef, uri.to_s }
|
|
27
|
+
|
|
29
28
|
NET_HTTP_REF_RESOLVER = proc { |uri| JSON.parse(Net::HTTP.get(uri)) }
|
|
30
29
|
RUBY_REGEXP_RESOLVER = proc { |pattern| Regexp.new(pattern) }
|
|
31
30
|
ECMA_REGEXP_RESOLVER = proc { |pattern| Regexp.new(EcmaRegexp.ruby_equivalent(pattern)) }
|
|
@@ -40,33 +39,38 @@ module JSONSchemer
|
|
|
40
39
|
false
|
|
41
40
|
end
|
|
42
41
|
end
|
|
42
|
+
SYMBOL_PROPERTY_DEFAULT_RESOLVER = proc do |instance, property, results_with_tree_validity|
|
|
43
|
+
DEFAULT_PROPERTY_DEFAULT_RESOLVER.call(instance, property.to_sym, results_with_tree_validity)
|
|
44
|
+
end
|
|
43
45
|
|
|
44
46
|
attr_accessor :base_uri, :meta_schema, :keywords, :keyword_order
|
|
45
|
-
attr_reader :value, :parent, :root, :parsed
|
|
46
|
-
|
|
47
|
+
attr_reader :value, :parent, :root, :configuration, :parsed
|
|
48
|
+
def_delegators :@configuration, :vocabulary, :format, :formats, :content_encodings, :content_media_types, :before_property_validation, :after_property_validation, :insert_property_defaults
|
|
49
|
+
def_delegator :@configuration, :keywords, :custom_keywords
|
|
47
50
|
|
|
48
51
|
def initialize(
|
|
49
52
|
value,
|
|
50
53
|
parent = nil,
|
|
51
54
|
root = self,
|
|
52
55
|
keyword = nil,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
56
|
+
configuration: JSONSchemer.configuration,
|
|
57
|
+
base_uri: configuration.base_uri,
|
|
58
|
+
meta_schema: configuration.meta_schema,
|
|
59
|
+
vocabulary: configuration.vocabulary,
|
|
60
|
+
format: configuration.format,
|
|
61
|
+
formats: configuration.formats,
|
|
62
|
+
content_encodings: configuration.content_encodings,
|
|
63
|
+
content_media_types: configuration.content_media_types,
|
|
64
|
+
keywords: configuration.keywords,
|
|
65
|
+
before_property_validation: configuration.before_property_validation,
|
|
66
|
+
after_property_validation: configuration.after_property_validation,
|
|
67
|
+
insert_property_defaults: configuration.insert_property_defaults,
|
|
68
|
+
property_default_resolver: configuration.property_default_resolver,
|
|
69
|
+
ref_resolver: configuration.ref_resolver,
|
|
70
|
+
regexp_resolver: configuration.regexp_resolver,
|
|
71
|
+
output_format: configuration.output_format,
|
|
72
|
+
resolve_enumerators: configuration.resolve_enumerators,
|
|
73
|
+
access_mode: configuration.access_mode
|
|
70
74
|
)
|
|
71
75
|
@value = deep_stringify_keys(value)
|
|
72
76
|
@parent = parent
|
|
@@ -75,21 +79,25 @@ module JSONSchemer
|
|
|
75
79
|
@schema = self
|
|
76
80
|
@base_uri = base_uri
|
|
77
81
|
@meta_schema = meta_schema
|
|
78
|
-
@
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
82
|
+
@configuration = Configuration.new(
|
|
83
|
+
:base_uri => base_uri,
|
|
84
|
+
:meta_schema => meta_schema,
|
|
85
|
+
:vocabulary => vocabulary,
|
|
86
|
+
:format => format,
|
|
87
|
+
:formats => formats,
|
|
88
|
+
:content_encodings => content_encodings,
|
|
89
|
+
:content_media_types => content_media_types,
|
|
90
|
+
:keywords => keywords,
|
|
91
|
+
:before_property_validation => Array(before_property_validation),
|
|
92
|
+
:after_property_validation => Array(after_property_validation),
|
|
93
|
+
:insert_property_defaults => insert_property_defaults,
|
|
94
|
+
:property_default_resolver => property_default_resolver,
|
|
95
|
+
:ref_resolver => ref_resolver,
|
|
96
|
+
:regexp_resolver => regexp_resolver,
|
|
97
|
+
:output_format => output_format,
|
|
98
|
+
:resolve_enumerators => resolve_enumerators,
|
|
99
|
+
:access_mode => access_mode
|
|
100
|
+
)
|
|
93
101
|
@parsed = parse
|
|
94
102
|
end
|
|
95
103
|
|
|
@@ -97,7 +105,7 @@ module JSONSchemer
|
|
|
97
105
|
validate(instance, :output_format => 'flag', **options).fetch('valid')
|
|
98
106
|
end
|
|
99
107
|
|
|
100
|
-
def validate(instance, output_format: @output_format, resolve_enumerators: @resolve_enumerators, access_mode: @access_mode)
|
|
108
|
+
def validate(instance, output_format: @configuration.output_format, resolve_enumerators: @configuration.resolve_enumerators, access_mode: @configuration.access_mode)
|
|
101
109
|
instance_location = Location.root
|
|
102
110
|
context = Context.new(instance, [], nil, (!insert_property_defaults && output_format == 'flag'), access_mode)
|
|
103
111
|
result = validate_instance(deep_stringify_keys(instance), instance_location, root_keyword_location, context)
|
|
@@ -109,12 +117,12 @@ module JSONSchemer
|
|
|
109
117
|
output
|
|
110
118
|
end
|
|
111
119
|
|
|
112
|
-
def valid_schema?
|
|
113
|
-
meta_schema.valid?(value)
|
|
120
|
+
def valid_schema?(**options)
|
|
121
|
+
meta_schema.valid?(value, **options)
|
|
114
122
|
end
|
|
115
123
|
|
|
116
|
-
def validate_schema
|
|
117
|
-
meta_schema.validate(value)
|
|
124
|
+
def validate_schema(**options)
|
|
125
|
+
meta_schema.validate(value, **options)
|
|
118
126
|
end
|
|
119
127
|
|
|
120
128
|
def ref(value)
|
|
@@ -142,10 +150,11 @@ module JSONSchemer
|
|
|
142
150
|
adjacent_results[keyword_instance.class] = keyword_result
|
|
143
151
|
end
|
|
144
152
|
|
|
145
|
-
if custom_keywords.any?
|
|
146
|
-
|
|
153
|
+
if root.custom_keywords.any?
|
|
154
|
+
resolved_instance_location = Location.resolve(instance_location)
|
|
155
|
+
root.custom_keywords.each do |custom_keyword, callable|
|
|
147
156
|
if value.key?(custom_keyword)
|
|
148
|
-
[*callable.call(instance, value,
|
|
157
|
+
[*callable.call(instance, value, resolved_instance_location)].each do |custom_keyword_result|
|
|
149
158
|
custom_keyword_valid = custom_keyword_result == true
|
|
150
159
|
valid &&= custom_keyword_valid
|
|
151
160
|
type = custom_keyword_result.is_a?(String) ? custom_keyword_result : custom_keyword
|
|
@@ -184,16 +193,9 @@ module JSONSchemer
|
|
|
184
193
|
uri.fragment = nil
|
|
185
194
|
remote_schema = JSONSchemer.schema(
|
|
186
195
|
ref_resolver.call(uri) || raise(InvalidRefResolution, uri.to_s),
|
|
196
|
+
:configuration => configuration,
|
|
187
197
|
:base_uri => uri,
|
|
188
198
|
:meta_schema => meta_schema,
|
|
189
|
-
:format => format,
|
|
190
|
-
:formats => formats,
|
|
191
|
-
:content_encodings => content_encodings,
|
|
192
|
-
:content_media_types => content_media_types,
|
|
193
|
-
:keywords => custom_keywords,
|
|
194
|
-
:before_property_validation => before_property_validation,
|
|
195
|
-
:after_property_validation => after_property_validation,
|
|
196
|
-
:property_default_resolver => property_default_resolver,
|
|
197
199
|
:ref_resolver => ref_resolver,
|
|
198
200
|
:regexp_resolver => regexp_resolver
|
|
199
201
|
)
|
|
@@ -331,7 +333,7 @@ module JSONSchemer
|
|
|
331
333
|
end
|
|
332
334
|
|
|
333
335
|
def resources
|
|
334
|
-
@resources ||= { :lexical =>
|
|
336
|
+
@resources ||= { :lexical => Resources.new, :dynamic => Resources.new }
|
|
335
337
|
end
|
|
336
338
|
|
|
337
339
|
def error(formatted_instance_location:, **options)
|
|
@@ -342,6 +344,21 @@ module JSONSchemer
|
|
|
342
344
|
end
|
|
343
345
|
end
|
|
344
346
|
|
|
347
|
+
def ref_resolver
|
|
348
|
+
@ref_resolver ||= @configuration.ref_resolver == 'net/http' ? CachedResolver.new(&NET_HTTP_REF_RESOLVER) : @configuration.ref_resolver
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
def regexp_resolver
|
|
352
|
+
@regexp_resolver ||= case @configuration.regexp_resolver
|
|
353
|
+
when 'ecma'
|
|
354
|
+
CachedResolver.new(&ECMA_REGEXP_RESOLVER)
|
|
355
|
+
when 'ruby'
|
|
356
|
+
CachedResolver.new(&RUBY_REGEXP_RESOLVER)
|
|
357
|
+
else
|
|
358
|
+
@configuration.regexp_resolver
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
|
|
345
362
|
def inspect
|
|
346
363
|
"#<#{self.class.name} @value=#{@value.inspect} @parent=#{@parent.inspect} @keyword=#{@keyword.inspect}>"
|
|
347
364
|
end
|
|
@@ -353,8 +370,8 @@ module JSONSchemer
|
|
|
353
370
|
|
|
354
371
|
if value.is_a?(Hash) && value.key?('$schema')
|
|
355
372
|
@parsed['$schema'] = SCHEMA_KEYWORD_CLASS.new(value.fetch('$schema'), self, '$schema')
|
|
356
|
-
elsif
|
|
357
|
-
SCHEMA_KEYWORD_CLASS.new(
|
|
373
|
+
elsif meta_schema.is_a?(String)
|
|
374
|
+
SCHEMA_KEYWORD_CLASS.new(meta_schema, self, '$schema')
|
|
358
375
|
end
|
|
359
376
|
|
|
360
377
|
if value.is_a?(Hash) && value.key?('$vocabulary')
|
|
@@ -394,18 +411,11 @@ module JSONSchemer
|
|
|
394
411
|
@root_keyword_location ||= Location.root
|
|
395
412
|
end
|
|
396
413
|
|
|
397
|
-
def
|
|
398
|
-
@
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
def regexp_resolver
|
|
402
|
-
@regexp_resolver ||= case @original_regexp_resolver
|
|
403
|
-
when 'ecma'
|
|
404
|
-
CachedResolver.new(&ECMA_REGEXP_RESOLVER)
|
|
405
|
-
when 'ruby'
|
|
406
|
-
CachedResolver.new(&RUBY_REGEXP_RESOLVER)
|
|
414
|
+
def property_default_resolver
|
|
415
|
+
@property_default_resolver ||= if @configuration.property_default_resolver
|
|
416
|
+
@configuration.property_default_resolver
|
|
407
417
|
else
|
|
408
|
-
|
|
418
|
+
insert_property_defaults == :symbol ? SYMBOL_PROPERTY_DEFAULT_RESOLVER : DEFAULT_PROPERTY_DEFAULT_RESOLVER
|
|
409
419
|
end
|
|
410
420
|
end
|
|
411
421
|
|
data/lib/json_schemer/version.rb
CHANGED
data/lib/json_schemer.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
require 'base64'
|
|
3
2
|
require 'bigdecimal'
|
|
3
|
+
require 'forwardable'
|
|
4
4
|
require 'ipaddr'
|
|
5
5
|
require 'json'
|
|
6
6
|
require 'net/http'
|
|
@@ -59,10 +59,11 @@ require 'json_schemer/openapi30/meta'
|
|
|
59
59
|
require 'json_schemer/openapi30/vocab/base'
|
|
60
60
|
require 'json_schemer/openapi30/vocab'
|
|
61
61
|
require 'json_schemer/openapi'
|
|
62
|
+
require 'json_schemer/configuration'
|
|
63
|
+
require 'json_schemer/resources'
|
|
62
64
|
require 'json_schemer/schema'
|
|
63
65
|
|
|
64
66
|
module JSONSchemer
|
|
65
|
-
class UnsupportedMetaSchema < StandardError; end
|
|
66
67
|
class UnsupportedOpenAPIVersion < StandardError; end
|
|
67
68
|
class UnknownRef < StandardError; end
|
|
68
69
|
class UnknownFormat < StandardError; end
|
|
@@ -104,42 +105,32 @@ module JSONSchemer
|
|
|
104
105
|
|
|
105
106
|
WINDOWS_URI_PATH_REGEX = /\A\/[a-z]:/i
|
|
106
107
|
|
|
108
|
+
# :nocov:
|
|
109
|
+
URI_PARSER = URI.const_defined?(:RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::DEFAULT_PARSER
|
|
110
|
+
# :nocov:
|
|
111
|
+
|
|
107
112
|
FILE_URI_REF_RESOLVER = proc do |uri|
|
|
108
113
|
raise InvalidFileURI, 'must use `file` scheme' unless uri.scheme == 'file'
|
|
109
114
|
raise InvalidFileURI, 'cannot have a host (use `file:///`)' if uri.host && !uri.host.empty?
|
|
110
115
|
path = uri.path
|
|
111
116
|
path = path[1..-1] if path.match?(WINDOWS_URI_PATH_REGEX)
|
|
112
|
-
JSON.parse(File.read(
|
|
117
|
+
JSON.parse(File.read(URI_PARSER.unescape(path)))
|
|
113
118
|
end
|
|
114
119
|
|
|
115
120
|
class << self
|
|
116
|
-
def schema(schema,
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
schema = JSON.parse(schema)
|
|
120
|
-
when Pathname
|
|
121
|
-
base_uri = URI.parse(File.join('file:', URI::DEFAULT_PARSER.escape(schema.realpath.to_s)))
|
|
122
|
-
options[:base_uri] = base_uri
|
|
123
|
-
schema = if options.key?(:ref_resolver)
|
|
124
|
-
FILE_URI_REF_RESOLVER.call(base_uri)
|
|
125
|
-
else
|
|
126
|
-
ref_resolver = CachedResolver.new(&FILE_URI_REF_RESOLVER)
|
|
127
|
-
options[:ref_resolver] = ref_resolver
|
|
128
|
-
ref_resolver.call(base_uri)
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
unless meta_schema.is_a?(Schema)
|
|
132
|
-
meta_schema = META_SCHEMAS_BY_BASE_URI_STR[meta_schema] || raise(UnsupportedMetaSchema, meta_schema)
|
|
133
|
-
end
|
|
134
|
-
Schema.new(schema, :meta_schema => meta_schema, **options)
|
|
121
|
+
def schema(schema, **options)
|
|
122
|
+
schema = resolve(schema, options)
|
|
123
|
+
Schema.new(schema, **options)
|
|
135
124
|
end
|
|
136
125
|
|
|
137
126
|
def valid_schema?(schema, **options)
|
|
138
|
-
schema(schema,
|
|
127
|
+
schema = resolve(schema, options)
|
|
128
|
+
meta_schema(schema, options).valid?(schema, **options.slice(:output_format, :resolve_enumerators, :access_mode))
|
|
139
129
|
end
|
|
140
130
|
|
|
141
131
|
def validate_schema(schema, **options)
|
|
142
|
-
schema(schema,
|
|
132
|
+
schema = resolve(schema, options)
|
|
133
|
+
meta_schema(schema, options).validate(schema, **options.slice(:output_format, :resolve_enumerators, :access_mode))
|
|
143
134
|
end
|
|
144
135
|
|
|
145
136
|
def draft202012
|
|
@@ -245,6 +236,44 @@ module JSONSchemer
|
|
|
245
236
|
def openapi(document, **options)
|
|
246
237
|
OpenAPI.new(document, **options)
|
|
247
238
|
end
|
|
239
|
+
|
|
240
|
+
def configuration
|
|
241
|
+
@configuration ||= Configuration.new
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def configure
|
|
245
|
+
yield configuration
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
private
|
|
249
|
+
|
|
250
|
+
def resolve(schema, options)
|
|
251
|
+
case schema
|
|
252
|
+
when String
|
|
253
|
+
JSON.parse(schema)
|
|
254
|
+
when Pathname
|
|
255
|
+
base_uri = URI.parse(File.join('file:', URI_PARSER.escape(schema.realpath.to_s)))
|
|
256
|
+
options[:base_uri] = base_uri
|
|
257
|
+
if options.key?(:ref_resolver)
|
|
258
|
+
FILE_URI_REF_RESOLVER.call(base_uri)
|
|
259
|
+
else
|
|
260
|
+
ref_resolver = CachedResolver.new(&FILE_URI_REF_RESOLVER)
|
|
261
|
+
options[:ref_resolver] = ref_resolver
|
|
262
|
+
ref_resolver.call(base_uri)
|
|
263
|
+
end
|
|
264
|
+
else
|
|
265
|
+
schema
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def meta_schema(schema, options)
|
|
270
|
+
parseable_schema = {}
|
|
271
|
+
if schema.is_a?(Hash)
|
|
272
|
+
meta_schema = schema['$schema'] || schema[:'$schema']
|
|
273
|
+
parseable_schema['$schema'] = meta_schema if meta_schema.is_a?(String)
|
|
274
|
+
end
|
|
275
|
+
schema(parseable_schema, **options).meta_schema
|
|
276
|
+
end
|
|
248
277
|
end
|
|
249
278
|
|
|
250
279
|
META_SCHEMA_CALLABLES_BY_BASE_URI_STR = {
|
metadata
CHANGED
|
@@ -1,29 +1,42 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: json_schemer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Harsha
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 2025-02-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: base64
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
13
26
|
- !ruby/object:Gem::Dependency
|
|
14
27
|
name: bundler
|
|
15
28
|
requirement: !ruby/object:Gem::Requirement
|
|
16
29
|
requirements:
|
|
17
30
|
- - "~>"
|
|
18
31
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
32
|
+
version: 2.4.0
|
|
20
33
|
type: :development
|
|
21
34
|
prerelease: false
|
|
22
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
36
|
requirements:
|
|
24
37
|
- - "~>"
|
|
25
38
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
39
|
+
version: 2.4.0
|
|
27
40
|
- !ruby/object:Gem::Dependency
|
|
28
41
|
name: rake
|
|
29
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -66,6 +79,20 @@ dependencies:
|
|
|
66
79
|
- - "~>"
|
|
67
80
|
- !ruby/object:Gem::Version
|
|
68
81
|
version: '0.22'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: csv
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '0'
|
|
89
|
+
type: :development
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0'
|
|
69
96
|
- !ruby/object:Gem::Dependency
|
|
70
97
|
name: i18n
|
|
71
98
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -94,6 +121,20 @@ dependencies:
|
|
|
94
121
|
- - ">="
|
|
95
122
|
- !ruby/object:Gem::Version
|
|
96
123
|
version: '0'
|
|
124
|
+
- !ruby/object:Gem::Dependency
|
|
125
|
+
name: bigdecimal
|
|
126
|
+
requirement: !ruby/object:Gem::Requirement
|
|
127
|
+
requirements:
|
|
128
|
+
- - ">="
|
|
129
|
+
- !ruby/object:Gem::Version
|
|
130
|
+
version: '0'
|
|
131
|
+
type: :runtime
|
|
132
|
+
prerelease: false
|
|
133
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - ">="
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: '0'
|
|
97
138
|
- !ruby/object:Gem::Dependency
|
|
98
139
|
name: hana
|
|
99
140
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -136,7 +177,6 @@ dependencies:
|
|
|
136
177
|
- - "~>"
|
|
137
178
|
- !ruby/object:Gem::Version
|
|
138
179
|
version: '0.2'
|
|
139
|
-
description:
|
|
140
180
|
email:
|
|
141
181
|
- davishmcclurg@gmail.com
|
|
142
182
|
executables:
|
|
@@ -160,6 +200,7 @@ files:
|
|
|
160
200
|
- json_schemer.gemspec
|
|
161
201
|
- lib/json_schemer.rb
|
|
162
202
|
- lib/json_schemer/cached_resolver.rb
|
|
203
|
+
- lib/json_schemer/configuration.rb
|
|
163
204
|
- lib/json_schemer/content.rb
|
|
164
205
|
- lib/json_schemer/draft201909/meta.rb
|
|
165
206
|
- lib/json_schemer/draft201909/vocab.rb
|
|
@@ -203,6 +244,7 @@ files:
|
|
|
203
244
|
- lib/json_schemer/openapi31/vocab.rb
|
|
204
245
|
- lib/json_schemer/openapi31/vocab/base.rb
|
|
205
246
|
- lib/json_schemer/output.rb
|
|
247
|
+
- lib/json_schemer/resources.rb
|
|
206
248
|
- lib/json_schemer/result.rb
|
|
207
249
|
- lib/json_schemer/schema.rb
|
|
208
250
|
- lib/json_schemer/version.rb
|
|
@@ -210,7 +252,6 @@ homepage: https://github.com/davishmcclurg/json_schemer
|
|
|
210
252
|
licenses:
|
|
211
253
|
- MIT
|
|
212
254
|
metadata: {}
|
|
213
|
-
post_install_message:
|
|
214
255
|
rdoc_options: []
|
|
215
256
|
require_paths:
|
|
216
257
|
- lib
|
|
@@ -218,15 +259,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
218
259
|
requirements:
|
|
219
260
|
- - ">="
|
|
220
261
|
- !ruby/object:Gem::Version
|
|
221
|
-
version: '2.
|
|
262
|
+
version: '2.7'
|
|
222
263
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
223
264
|
requirements:
|
|
224
265
|
- - ">="
|
|
225
266
|
- !ruby/object:Gem::Version
|
|
226
267
|
version: '0'
|
|
227
268
|
requirements: []
|
|
228
|
-
rubygems_version: 3.
|
|
229
|
-
signing_key:
|
|
269
|
+
rubygems_version: 3.6.2
|
|
230
270
|
specification_version: 4
|
|
231
271
|
summary: JSON Schema validator. Supports drafts 4, 6, 7, 2019-09, 2020-12, OpenAPI
|
|
232
272
|
3.0, and OpenAPI 3.1.
|