json_schemer 0.2.18 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +7 -7
- data/CHANGELOG.md +102 -0
- data/Gemfile.lock +30 -10
- data/README.md +402 -6
- data/bin/hostname_character_classes +42 -0
- data/bin/rake +29 -0
- data/exe/json_schemer +62 -0
- data/json_schemer.gemspec +9 -12
- data/lib/json_schemer/cached_resolver.rb +16 -0
- data/lib/json_schemer/configuration.rb +31 -0
- data/lib/json_schemer/content.rb +18 -0
- data/lib/json_schemer/draft201909/meta.rb +320 -0
- data/lib/json_schemer/draft201909/vocab/applicator.rb +104 -0
- data/lib/json_schemer/draft201909/vocab/core.rb +45 -0
- data/lib/json_schemer/draft201909/vocab.rb +31 -0
- data/lib/json_schemer/draft202012/meta.rb +364 -0
- data/lib/json_schemer/draft202012/vocab/applicator.rb +382 -0
- data/lib/json_schemer/draft202012/vocab/content.rb +52 -0
- data/lib/json_schemer/draft202012/vocab/core.rb +160 -0
- data/lib/json_schemer/draft202012/vocab/format_annotation.rb +23 -0
- data/lib/json_schemer/draft202012/vocab/format_assertion.rb +23 -0
- data/lib/json_schemer/draft202012/vocab/meta_data.rb +30 -0
- data/lib/json_schemer/draft202012/vocab/unevaluated.rb +104 -0
- data/lib/json_schemer/draft202012/vocab/validation.rb +286 -0
- data/lib/json_schemer/draft202012/vocab.rb +105 -0
- data/lib/json_schemer/draft4/meta.rb +161 -0
- data/lib/json_schemer/draft4/vocab/validation.rb +39 -0
- data/lib/json_schemer/draft4/vocab.rb +18 -0
- data/lib/json_schemer/draft6/meta.rb +172 -0
- data/lib/json_schemer/draft6/vocab.rb +16 -0
- data/lib/json_schemer/draft7/meta.rb +183 -0
- data/lib/json_schemer/draft7/vocab/validation.rb +69 -0
- data/lib/json_schemer/draft7/vocab.rb +30 -0
- data/lib/json_schemer/ecma_regexp.rb +51 -0
- data/lib/json_schemer/errors.rb +1 -0
- data/lib/json_schemer/format/duration.rb +23 -0
- data/lib/json_schemer/format/email.rb +56 -0
- data/lib/json_schemer/format/hostname.rb +58 -0
- data/lib/json_schemer/format/json_pointer.rb +18 -0
- data/lib/json_schemer/format/uri_template.rb +34 -0
- data/lib/json_schemer/format.rb +128 -109
- data/lib/json_schemer/keyword.rb +56 -0
- data/lib/json_schemer/location.rb +25 -0
- data/lib/json_schemer/openapi.rb +38 -0
- data/lib/json_schemer/openapi30/document.rb +1672 -0
- data/lib/json_schemer/openapi30/meta.rb +32 -0
- data/lib/json_schemer/openapi30/vocab/base.rb +18 -0
- data/lib/json_schemer/openapi30/vocab.rb +12 -0
- data/lib/json_schemer/openapi31/document.rb +1557 -0
- data/lib/json_schemer/openapi31/meta.rb +136 -0
- data/lib/json_schemer/openapi31/vocab/base.rb +127 -0
- data/lib/json_schemer/openapi31/vocab.rb +18 -0
- data/lib/json_schemer/output.rb +56 -0
- data/lib/json_schemer/result.rb +242 -0
- data/lib/json_schemer/schema.rb +424 -0
- data/lib/json_schemer/version.rb +1 -1
- data/lib/json_schemer.rb +243 -30
- metadata +141 -25
- data/lib/json_schemer/cached_ref_resolver.rb +0 -14
- data/lib/json_schemer/schema/base.rb +0 -658
- data/lib/json_schemer/schema/draft4.rb +0 -44
- data/lib/json_schemer/schema/draft6.rb +0 -25
- data/lib/json_schemer/schema/draft7.rb +0 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e5ec5b8e547dc3a64b134054ec80b0f40cf9453cba4cce430995e6ae165a6e07
|
4
|
+
data.tar.gz: 943423f15ba47e7b8ecc5fbf3d1b566679c43e84e3d70bbc4e0f47f126e4275f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 43c26354e594ae3219e0edd22e18b6d89a64b6cbad6bc4ac3e85750ccc128c3827d7aa38b1dc0d48a232c733250467939eaa8d6049ab8d34c75f5a0ac6f0e715
|
7
|
+
data.tar.gz: eced2c5592c64adfffb89923d28a363494f09bdd87202233706baa24ad4067e8240a85120a29eb2145b70a5f9bbf85e3a7f2f71fbdd1a89a74bc2cc6e3a243ed
|
data/.github/workflows/ci.yml
CHANGED
@@ -6,21 +6,21 @@ 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, head, jruby, jruby-head, truffleruby, truffleruby-head]
|
10
10
|
exclude:
|
11
|
-
- os:
|
12
|
-
ruby:
|
13
|
-
- os:
|
14
|
-
ruby:
|
11
|
+
- os: ubuntu-latest
|
12
|
+
ruby: head
|
13
|
+
- os: macos-latest
|
14
|
+
ruby: head
|
15
15
|
- os: windows-latest
|
16
16
|
ruby: truffleruby
|
17
17
|
- os: windows-latest
|
18
18
|
ruby: truffleruby-head
|
19
19
|
runs-on: ${{ matrix.os }}
|
20
20
|
steps:
|
21
|
-
- uses: actions/checkout@
|
21
|
+
- uses: actions/checkout@v4
|
22
22
|
- uses: ruby/setup-ruby@v1
|
23
23
|
with:
|
24
24
|
ruby-version: ${{ matrix.ruby }}
|
25
25
|
bundler-cache: true
|
26
|
-
- run:
|
26
|
+
- run: bin/rake test
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## [2.3.0] - 2024-05-30
|
4
|
+
|
5
|
+
## Ruby Versions
|
6
|
+
|
7
|
+
- Ruby 2.5 and 2.6 are no longer supported.
|
8
|
+
|
9
|
+
## Bug Fixes
|
10
|
+
|
11
|
+
- Remove `base64` runtime dependency: https://github.com/davishmcclurg/json_schemer/pull/182
|
12
|
+
- Relax `uuid` format validation: https://github.com/davishmcclurg/json_schemer/pull/183
|
13
|
+
|
14
|
+
[2.3.0]: https://github.com/davishmcclurg/json_schemer/releases/tag/v2.3.0
|
15
|
+
|
16
|
+
## [2.2.0] - 2024-03-02
|
17
|
+
|
18
|
+
## Bug Fixes
|
19
|
+
|
20
|
+
- Support symbol keys when accessing original instance: https://github.com/davishmcclurg/json_schemer/commit/d52c130e9967919c6cf1c9dbc3f0babfb8b01cf8
|
21
|
+
- Support custom keywords in nested schemas: https://github.com/davishmcclurg/json_schemer/commit/93c85a5006981347c7e9a4c11b73c6bdb65d8ba2
|
22
|
+
- Stringify instance location for custom keywords: https://github.com/davishmcclurg/json_schemer/commit/513c99130b9e7986b09881e7efd3fb7143744754
|
23
|
+
- Reduce unhelpful error output in `unevaluated` keywords: https://github.com/davishmcclurg/json_schemer/pull/164
|
24
|
+
- Handle parse errors during schema validation: https://github.com/davishmcclurg/json_schemer/pull/171
|
25
|
+
- Follow refs when finding default property values: https://github.com/davishmcclurg/json_schemer/pull/175
|
26
|
+
|
27
|
+
## Features
|
28
|
+
|
29
|
+
- Global configuration with `Configuration` object: https://github.com/davishmcclurg/json_schemer/pull/170
|
30
|
+
- Symbol key property defaults with `insert_property_defaults: :symbol`: https://github.com/davishmcclurg/json_schemer/commit/a72473dc84199107ddedc8998950e5b82273232a
|
31
|
+
- Consistent schema type support for schema validation methods: https://github.com/davishmcclurg/json_schemer/commit/bbcd0cea20cbaa61cf2bdae5f53840861cae54b8
|
32
|
+
- Validation option support for schema validation methods: https://github.com/davishmcclurg/json_schemer/commit/2eeef77de522f127619b7d0faa51e0d7e40977ad
|
33
|
+
|
34
|
+
[2.2.0]: https://github.com/davishmcclurg/json_schemer/releases/tag/v2.2.0
|
35
|
+
|
36
|
+
## [2.1.1] - 2023-11-28
|
37
|
+
|
38
|
+
### Bug Fixes
|
39
|
+
|
40
|
+
- Fix refs to/through keyword objects: https://github.com/davishmcclurg/json_schemer/pull/160
|
41
|
+
- Temporary fix for incorrect `uri-reference` format in OpenAPI 3.x: https://github.com/davishmcclurg/json_schemer/pull/161
|
42
|
+
|
43
|
+
[2.1.1]: https://github.com/davishmcclurg/json_schemer/releases/tag/v2.1.1
|
44
|
+
|
45
|
+
## [2.1.0] - 2023-11-17
|
46
|
+
|
47
|
+
### Bug Fixes
|
48
|
+
|
49
|
+
- Limit anyOf/oneOf discriminator to listed refs: https://github.com/davishmcclurg/json_schemer/pull/145
|
50
|
+
- Require discriminator `propertyName` property: https://github.com/davishmcclurg/json_schemer/pull/145
|
51
|
+
- Support `Schema#ref` in subschemas: https://github.com/davishmcclurg/json_schemer/pull/145
|
52
|
+
- Resolve JSON pointer refs using correct base URI: https://github.com/davishmcclurg/json_schemer/pull/147
|
53
|
+
- `date` format in OpenAPI 3.0: https://github.com/davishmcclurg/json_schemer/commit/69fe7a815ecf0cfb1c40ac402bf46a789c05e972
|
54
|
+
|
55
|
+
### Features
|
56
|
+
|
57
|
+
- Custom error messages with `x-error` keyword and I18n: https://github.com/davishmcclurg/json_schemer/pull/149
|
58
|
+
- Custom content encodings and media types: https://github.com/davishmcclurg/json_schemer/pull/148
|
59
|
+
|
60
|
+
[2.1.0]: https://github.com/davishmcclurg/json_schemer/releases/tag/v2.1.0
|
61
|
+
|
62
|
+
## [2.0.0] - 2023-08-20
|
63
|
+
|
64
|
+
For 2.0.0, much of the codebase was rewritten to simplify support for the two new JSON Schema draft versions (2019-09 and 2020-12). The major change is moving each keyword into its own class and organizing them into vocabularies. [Output formats](https://json-schema.org/draft/2020-12/json-schema-core.html#section-12) and [annotations](https://json-schema.org/draft/2020-12/json-schema-core.html#section-7.7) from the new drafts are also supported. The known breaking changes are listed below, but there may be others that haven't been identified.
|
65
|
+
|
66
|
+
### Breaking Changes
|
67
|
+
|
68
|
+
- The default meta schema is now Draft 2020-12. Other meta schemas can be specified using `meta_schema`.
|
69
|
+
- Schemas use `json-schemer://schema` as the default base URI. Relative `$id` and `$ref` values are joined to the default base URI and are always absolute. For example, the schema `{ '$id' => 'foo', '$ref' => 'bar' }` uses `json-schemer://schema/foo` as the base URI and passes `json-schemer://schema/bar` to the ref resolver. For relative refs, `URI#path` can be used in the ref resolver to access the relative portion, ie: `URI('json-schemer://schema/bar').path => "/bar"`.
|
70
|
+
- Property validation hooks (`before_property_validation` and `after_property_validation`) run immediately before and after `properties` validation. Previously, `before_property_validation` ran before all "object" validations (`dependencies`, `patternProperties`, `additionalProperties`, etc) and `after_property_validation` was called after them.
|
71
|
+
- `insert_property_defaults` now inserts defaults in conditional subschemas when possible (if there's only one default or if there's only one unique default from a valid subtree).
|
72
|
+
- Error output
|
73
|
+
- Special characters in `schema_pointer` are no longer percent encoded (eg, `definitions/foo\"bar` instead of `/definitions/foo%22bar`)
|
74
|
+
- Keyword validation order changed so errors may be returned in a different order (eg, `items` errors before `contains`).
|
75
|
+
- Array `dependencies` return `"type": "dependencies"` errors instead of `"required"` and point to the schema that contains the `dependencies` keyword.
|
76
|
+
- `not` errors point to the schema that contains the `not` keyword (instead of the schema defined by the `not` keyword).
|
77
|
+
- Custom keyword errors are now always wrapped in regular error hashes. Returned strings are used to set `type`:
|
78
|
+
```
|
79
|
+
>> JSONSchemer.schema({ 'x' => 'y' }, :keywords => { 'x' => proc { false } }).validate({}).to_a
|
80
|
+
=> [{"data"=>{}, "data_pointer"=>"", "schema"=>{"x"=>"y"}, "schema_pointer"=>"", "root_schema"=>{"x"=>"y"}, "type"=>"x"}]
|
81
|
+
>> JSONSchemer.schema({ 'x' => 'y' }, :keywords => { 'x' => proc { 'wrong!' } }).validate({}).to_a
|
82
|
+
=> [{"data"=>{}, "data_pointer"=>"", "schema"=>{"x"=>"y"}, "schema_pointer"=>"", "root_schema"=>{"x"=>"y"}, "type"=>"wrong!"}]
|
83
|
+
```
|
84
|
+
|
85
|
+
[2.0.0]: https://github.com/davishmcclurg/json_schemer/releases/tag/v2.0.0
|
86
|
+
|
87
|
+
## [1.0.0] - 2023-05-26
|
88
|
+
|
89
|
+
### Breaking Changes
|
90
|
+
|
91
|
+
- Ruby 2.4 is no longer supported.
|
92
|
+
- The default `regexp_resolver` is now `ruby`, which passes patterns directly to `Regexp`. The previous default, `ecma`, rewrites patterns to behave more like Javascript (ECMA-262) regular expressions:
|
93
|
+
- Beginning of string: `^` -> `\A`
|
94
|
+
- End of string: `$` -> `\z`
|
95
|
+
- Space: `\s` -> `[\t\r\n\f\v\uFEFF\u2029\p{Zs}]`
|
96
|
+
- Non-space: `\S` -> `[^\t\r\n\f\v\uFEFF\u2029\p{Zs}]`
|
97
|
+
- Invalid ECMA-262 regular expressions raise `JSONSchemer::InvalidEcmaRegexp` when `regexp_resolver` is set to `ecma`.
|
98
|
+
- Embedded subschemas (ie, subschemas referenced by `$id`) can only be found under "known" keywords (eg, `definitions`). Previously, the entire schema object was scanned for `$id`.
|
99
|
+
- Empty fragments are now removed from `$ref` URIs before calling `ref_resolver`.
|
100
|
+
- Refs that are fragment-only JSON pointers with special characters must use the proper encoding (eg, `"$ref": "#/definitions/some-%7Bid%7D"`).
|
101
|
+
|
102
|
+
[1.0.0]: https://github.com/davishmcclurg/json_schemer/releases/tag/v1.0.0
|
data/Gemfile.lock
CHANGED
@@ -1,31 +1,51 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
json_schemer (
|
5
|
-
|
4
|
+
json_schemer (2.3.0)
|
5
|
+
bigdecimal
|
6
6
|
hana (~> 1.3)
|
7
7
|
regexp_parser (~> 2.0)
|
8
|
-
|
8
|
+
simpleidn (~> 0.2)
|
9
9
|
|
10
10
|
GEM
|
11
11
|
remote: https://rubygems.org/
|
12
12
|
specs:
|
13
|
-
|
14
|
-
|
13
|
+
base64 (0.2.0)
|
14
|
+
bigdecimal (3.1.8)
|
15
|
+
bigdecimal (3.1.8-java)
|
16
|
+
concurrent-ruby (1.2.2)
|
17
|
+
csv (3.2.8)
|
18
|
+
docile (1.4.0)
|
15
19
|
hana (1.3.7)
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
+
i18n (1.14.1)
|
21
|
+
concurrent-ruby (~> 1.0)
|
22
|
+
i18n-debug (1.2.0)
|
23
|
+
i18n (< 2)
|
24
|
+
minitest (5.15.0)
|
25
|
+
rake (13.1.0)
|
26
|
+
regexp_parser (2.9.2)
|
27
|
+
simplecov (0.22.0)
|
28
|
+
docile (~> 1.1)
|
29
|
+
simplecov-html (~> 0.11)
|
30
|
+
simplecov_json_formatter (~> 0.1)
|
31
|
+
simplecov-html (0.12.3)
|
32
|
+
simplecov_json_formatter (0.1.4)
|
33
|
+
simpleidn (0.2.3)
|
20
34
|
|
21
35
|
PLATFORMS
|
36
|
+
java
|
22
37
|
ruby
|
23
38
|
|
24
39
|
DEPENDENCIES
|
40
|
+
base64
|
25
41
|
bundler (~> 2.0)
|
42
|
+
csv
|
43
|
+
i18n
|
44
|
+
i18n-debug
|
26
45
|
json_schemer!
|
27
46
|
minitest (~> 5.0)
|
28
47
|
rake (~> 13.0)
|
48
|
+
simplecov (~> 0.22)
|
29
49
|
|
30
50
|
BUNDLED WITH
|
31
|
-
2.
|
51
|
+
2.3.27
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# JSONSchemer
|
2
2
|
|
3
|
-
JSON Schema validator. Supports drafts 4, 6, and
|
3
|
+
JSON Schema validator. Supports drafts 4, 6, 7, 2019-09, 2020-12, OpenAPI 3.0, and OpenAPI 3.1.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -45,7 +45,13 @@ schemer.valid?({ 'abc' => 10 })
|
|
45
45
|
# error validation (`validate` returns an enumerator)
|
46
46
|
|
47
47
|
schemer.validate({ 'abc' => 10 }).to_a
|
48
|
-
# => [{"data"=>10,
|
48
|
+
# => [{"data"=>10,
|
49
|
+
# "data_pointer"=>"/abc",
|
50
|
+
# "schema"=>{"type"=>"integer", "minimum"=>11},
|
51
|
+
# "schema_pointer"=>"/properties/abc",
|
52
|
+
# "root_schema"=>{"type"=>"object", "properties"=>{"abc"=>{"type"=>"integer", "minimum"=>11}}},
|
53
|
+
# "type"=>"minimum",
|
54
|
+
# "error"=>"number at `/abc` is less than: 11"}]
|
49
55
|
|
50
56
|
# default property values
|
51
57
|
|
@@ -74,6 +80,80 @@ schemer = JSONSchemer.schema(schema)
|
|
74
80
|
|
75
81
|
schema = '{ "type": "integer" }'
|
76
82
|
schemer = JSONSchemer.schema(schema)
|
83
|
+
|
84
|
+
# schema validation
|
85
|
+
|
86
|
+
JSONSchemer.valid_schema?({ '$id' => 'valid' })
|
87
|
+
# => true
|
88
|
+
|
89
|
+
JSONSchemer.validate_schema({ '$id' => '#invalid' }).to_a
|
90
|
+
# => [{"data"=>"#invalid",
|
91
|
+
# "data_pointer"=>"/$id",
|
92
|
+
# "schema"=>{"$ref"=>"#/$defs/uriReferenceString", "$comment"=>"Non-empty fragments not allowed.", "pattern"=>"^[^#]*#?$"},
|
93
|
+
# "schema_pointer"=>"/properties/$id",
|
94
|
+
# "root_schema"=>{...meta schema},
|
95
|
+
# "type"=>"pattern",
|
96
|
+
# "error"=>"string at `/$id` does not match pattern: ^[^#]*#?$"}]
|
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
|
+
# subschemas
|
111
|
+
|
112
|
+
schema = {
|
113
|
+
'type' => 'integer',
|
114
|
+
'$defs' => {
|
115
|
+
'foo' => {
|
116
|
+
'type' => 'string'
|
117
|
+
}
|
118
|
+
}
|
119
|
+
}
|
120
|
+
schemer = JSONSchemer.schema(schema)
|
121
|
+
|
122
|
+
schemer.ref('#/$defs/foo').validate(1).to_a
|
123
|
+
# => [{"data"=>1,
|
124
|
+
# "data_pointer"=>"",
|
125
|
+
# "schema"=>{"type"=>"string"},
|
126
|
+
# "schema_pointer"=>"/$defs/foo",
|
127
|
+
# "root_schema"=>{"type"=>"integer", "$defs"=>{"foo"=>{"type"=>"string"}}},
|
128
|
+
# "type"=>"string",
|
129
|
+
# "error"=>"value at root is not a string"}]
|
130
|
+
|
131
|
+
# schema bundling (https://json-schema.org/draft/2020-12/json-schema-core.html#section-9.3)
|
132
|
+
|
133
|
+
schema = {
|
134
|
+
'$id' => 'http://example.com/schema',
|
135
|
+
'allOf' => [
|
136
|
+
{ '$ref' => 'schema/one' },
|
137
|
+
{ '$ref' => 'schema/two' }
|
138
|
+
]
|
139
|
+
}
|
140
|
+
refs = {
|
141
|
+
URI('http://example.com/schema/one') => {
|
142
|
+
'type' => 'integer'
|
143
|
+
},
|
144
|
+
URI('http://example.com/schema/two') => {
|
145
|
+
'minimum' => 11
|
146
|
+
}
|
147
|
+
}
|
148
|
+
schemer = JSONSchemer.schema(schema, :ref_resolver => refs.to_proc)
|
149
|
+
|
150
|
+
schemer.bundle
|
151
|
+
# => {"$id"=>"http://example.com/schema",
|
152
|
+
# "allOf"=>[{"$ref"=>"schema/one"}, {"$ref"=>"schema/two"}],
|
153
|
+
# "$schema"=>"https://json-schema.org/draft/2020-12/schema",
|
154
|
+
# "$defs"=>
|
155
|
+
# {"http://example.com/schema/one"=>{"type"=>"integer", "$id"=>"http://example.com/schema/one", "$schema"=>"https://json-schema.org/draft/2020-12/schema"},
|
156
|
+
# "http://example.com/schema/two"=>{"minimum"=>11, "$id"=>"http://example.com/schema/two", "$schema"=>"https://json-schema.org/draft/2020-12/schema"}}}
|
77
157
|
```
|
78
158
|
|
79
159
|
## Options
|
@@ -82,13 +162,58 @@ schemer = JSONSchemer.schema(schema)
|
|
82
162
|
JSONSchemer.schema(
|
83
163
|
schema,
|
84
164
|
|
85
|
-
#
|
165
|
+
# meta schema to use for vocabularies (keyword behavior) and schema validation
|
166
|
+
# String/JSONSchemer::Schema
|
167
|
+
# 'https://json-schema.org/draft/2020-12/schema': JSONSchemer.draft202012
|
168
|
+
# 'https://json-schema.org/draft/2019-09/schema': JSONSchemer.draft201909
|
169
|
+
# 'http://json-schema.org/draft-07/schema#': JSONSchemer.draft7
|
170
|
+
# 'http://json-schema.org/draft-06/schema#': JSONSchemer.draft6
|
171
|
+
# 'http://json-schema.org/draft-04/schema#': JSONSchemer.draft4
|
172
|
+
# 'http://json-schema.org/schema#': JSONSchemer.draft4
|
173
|
+
# 'https://spec.openapis.org/oas/3.1/dialect/base': JSONSchemer.openapi31
|
174
|
+
# 'json-schemer://openapi30/schema': JSONSchemer.openapi30
|
175
|
+
# default: JSONSchemer.draft202012
|
176
|
+
meta_schema: 'https://json-schema.org/draft/2020-12/schema',
|
177
|
+
|
178
|
+
# validate `format` (https://json-schema.org/draft/2020-12/json-schema-validation.html#section-7)
|
86
179
|
# true/false
|
87
180
|
# default: true
|
88
181
|
format: true,
|
89
182
|
|
183
|
+
# custom formats
|
184
|
+
formats: {
|
185
|
+
'int32' => proc do |instance, _format|
|
186
|
+
instance.is_a?(Integer) && instance.bit_length <= 32
|
187
|
+
end,
|
188
|
+
# disable specific format
|
189
|
+
'email' => false
|
190
|
+
},
|
191
|
+
|
192
|
+
# custom content encodings
|
193
|
+
# only `base64` is available by default
|
194
|
+
content_encodings: {
|
195
|
+
# return [success, annotation] tuple
|
196
|
+
'urlsafe_base64' => proc do |instance|
|
197
|
+
[true, Base64.urlsafe_decode64(instance)]
|
198
|
+
rescue
|
199
|
+
[false, nil]
|
200
|
+
end
|
201
|
+
},
|
202
|
+
|
203
|
+
# custom content media types
|
204
|
+
# only `application/json` is available by default
|
205
|
+
content_media_types: {
|
206
|
+
# return [success, annotation] tuple
|
207
|
+
'text/csv' => proc do |instance|
|
208
|
+
[true, CSV.parse(instance)]
|
209
|
+
rescue
|
210
|
+
[false, nil]
|
211
|
+
end
|
212
|
+
},
|
213
|
+
|
90
214
|
# insert default property values during validation
|
91
|
-
#
|
215
|
+
# string keys by default (use `:symbol` to insert symbol keys)
|
216
|
+
# true/false/:symbol
|
92
217
|
# default: false
|
93
218
|
insert_property_defaults: true,
|
94
219
|
|
@@ -110,17 +235,288 @@ JSONSchemer.schema(
|
|
110
235
|
# 'net/http'/proc/lambda/respond_to?(:call)
|
111
236
|
# 'net/http': proc { |uri| JSON.parse(Net::HTTP.get(uri)) }
|
112
237
|
# default: proc { |uri| raise UnknownRef, uri.to_s }
|
113
|
-
ref_resolver: 'net/http'
|
238
|
+
ref_resolver: 'net/http',
|
239
|
+
|
240
|
+
# use different method to match regexes
|
241
|
+
# 'ruby'/'ecma'/proc/lambda/respond_to?(:call)
|
242
|
+
# 'ruby': proc { |pattern| Regexp.new(pattern) }
|
243
|
+
# default: 'ruby'
|
244
|
+
regexp_resolver: proc do |pattern|
|
245
|
+
RE2::Regexp.new(pattern)
|
246
|
+
end,
|
247
|
+
|
248
|
+
# output formatting (https://json-schema.org/draft/2020-12/json-schema-core.html#section-12)
|
249
|
+
# 'classic'/'flag'/'basic'/'detailed'/'verbose'
|
250
|
+
# default: 'classic'
|
251
|
+
output_format: 'basic',
|
252
|
+
|
253
|
+
# validate `readOnly`/`writeOnly` keywords (https://spec.openapis.org/oas/v3.0.3#fixed-fields-19)
|
254
|
+
# 'read'/'write'/nil
|
255
|
+
# default: nil
|
256
|
+
access_mode: 'read'
|
114
257
|
)
|
115
258
|
```
|
116
259
|
|
260
|
+
## Global Configuration
|
261
|
+
|
262
|
+
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).
|
263
|
+
|
264
|
+
```ruby
|
265
|
+
# configuration block
|
266
|
+
JSONSchemer.configure do |config|
|
267
|
+
config.regexp_resolver = 'ecma'
|
268
|
+
end
|
269
|
+
|
270
|
+
# configuration accessors
|
271
|
+
JSONSchemer.configuration.insert_property_defaults = true
|
272
|
+
```
|
273
|
+
|
274
|
+
## Custom Error Messages
|
275
|
+
|
276
|
+
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.
|
277
|
+
|
278
|
+
### `x-error` Keyword
|
279
|
+
|
280
|
+
```ruby
|
281
|
+
# override all errors for a schema
|
282
|
+
schemer = JSONSchemer.schema({
|
283
|
+
'type' => 'string',
|
284
|
+
'x-error' => 'custom error for schema and all keywords'
|
285
|
+
})
|
286
|
+
|
287
|
+
schemer.validate(1).first
|
288
|
+
# => {"data"=>1,
|
289
|
+
# "data_pointer"=>"",
|
290
|
+
# "schema"=>{"type"=>"string", "x-error"=>"custom error for schema and all keywords"},
|
291
|
+
# "schema_pointer"=>"",
|
292
|
+
# "root_schema"=>{"type"=>"string", "x-error"=>"custom error for schema and all keywords"},
|
293
|
+
# "type"=>"string",
|
294
|
+
# "error"=>"custom error for schema and all keywords",
|
295
|
+
# "x-error"=>true}
|
296
|
+
|
297
|
+
schemer.validate(1, :output_format => 'basic')
|
298
|
+
# => {"valid"=>false,
|
299
|
+
# "keywordLocation"=>"",
|
300
|
+
# "absoluteKeywordLocation"=>"json-schemer://schema#",
|
301
|
+
# "instanceLocation"=>"",
|
302
|
+
# "error"=>"custom error for schema and all keywords",
|
303
|
+
# "x-error"=>true,
|
304
|
+
# "errors"=>#<Enumerator: ...>}
|
305
|
+
|
306
|
+
# keyword-specific errors
|
307
|
+
schemer = JSONSchemer.schema({
|
308
|
+
'type' => 'string',
|
309
|
+
'minLength' => 10,
|
310
|
+
'x-error' => {
|
311
|
+
'type' => 'custom error for `type` keyword',
|
312
|
+
# special `^` keyword for schema-level error
|
313
|
+
'^' => 'custom error for schema',
|
314
|
+
# same behavior as when `x-error` is a string
|
315
|
+
'*' => 'fallback error for schema and all keywords'
|
316
|
+
}
|
317
|
+
})
|
318
|
+
|
319
|
+
schemer.validate(1).map { _1.fetch('error') }
|
320
|
+
# => ["custom error for `type` keyword"]
|
321
|
+
|
322
|
+
schemer.validate('1').map { _1.fetch('error') }
|
323
|
+
# => ["custom error for schema and all keywords"]
|
324
|
+
|
325
|
+
schemer.validate(1, :output_format => 'basic').fetch('error')
|
326
|
+
# => "custom error for schema"
|
327
|
+
|
328
|
+
# variable interpolation (instance/instanceLocation/keywordLocation/absoluteKeywordLocation)
|
329
|
+
schemer = JSONSchemer.schema({
|
330
|
+
'$id' => 'https://example.com/schema',
|
331
|
+
'properties' => {
|
332
|
+
'abc' => {
|
333
|
+
'type' => 'string',
|
334
|
+
'x-error' => <<~ERROR
|
335
|
+
instance: %{instance}
|
336
|
+
instance location: %{instanceLocation}
|
337
|
+
keyword location: %{keywordLocation}
|
338
|
+
absolute keyword location: %{absoluteKeywordLocation}
|
339
|
+
ERROR
|
340
|
+
}
|
341
|
+
}
|
342
|
+
})
|
343
|
+
|
344
|
+
puts schemer.validate({ 'abc' => 1 }).first.fetch('error')
|
345
|
+
# instance: 1
|
346
|
+
# instance location: /abc
|
347
|
+
# keyword location: /properties/abc/type
|
348
|
+
# absolute keyword location: https://example.com/schema#/properties/abc/type
|
349
|
+
```
|
350
|
+
|
351
|
+
### I18n
|
352
|
+
|
353
|
+
When the [I18n gem](https://github.com/ruby-i18n/i18n) is loaded, custom error messages are looked up under the `json_schemer` key. It may be necessary to restart your application after adding the root key because the existence check is cached for performance reasons.
|
354
|
+
|
355
|
+
Translation keys are looked up in this order:
|
356
|
+
|
357
|
+
1. `$LOCALE.json_schemer.errors.$ABSOLUTE_KEYWORD_LOCATION`
|
358
|
+
2. `$LOCALE.json_schemer.errors.$SCHEMA_ID.$KEYWORD_LOCATION`
|
359
|
+
3. `$LOCALE.json_schemer.errors.$KEYWORD_LOCATION`
|
360
|
+
4. `$LOCALE.json_schemer.errors.$SCHEMA_ID.$KEYWORD`
|
361
|
+
5. `$LOCALE.json_schemer.errors.$SCHEMA_ID.*`
|
362
|
+
6. `$LOCALE.json_schemer.errors.$META_SCHEMA_ID.$KEYWORD`
|
363
|
+
7. `$LOCALE.json_schemer.errors.$META_SCHEMA_ID.*`
|
364
|
+
8. `$LOCALE.json_schemer.errors.$KEYWORD`
|
365
|
+
9. `$LOCALE.json_schemer.errors.*`
|
366
|
+
|
367
|
+
Example translations file:
|
368
|
+
|
369
|
+
```yaml
|
370
|
+
en:
|
371
|
+
json_schemer:
|
372
|
+
errors:
|
373
|
+
'https://example.com/schema#/properties/abc/type': custom error for absolute keyword location
|
374
|
+
'https://example.com/schema':
|
375
|
+
'#/properties/abc/type': custom error for keyword location, nested under schema $id
|
376
|
+
'type': custom error for `type` keyword, nested under schema $id
|
377
|
+
'^': custom error for schema, nested under schema $id
|
378
|
+
'*': fallback error for schema and all keywords, nested under schema $id
|
379
|
+
'#/properties/abc/type': custom error for keyword location
|
380
|
+
'http://json-schema.org/draft-07/schema#':
|
381
|
+
'type': custom error for `type` keyword, nested under meta-schema $id ($schema)
|
382
|
+
'^': custom error for schema, nested under meta-schema $id
|
383
|
+
'*': fallback error for schema and all keywords, nested under meta-schema $id ($schema)
|
384
|
+
'type': custom error for `type` keyword
|
385
|
+
'^': custom error for schema
|
386
|
+
# variable interpolation (instance/instanceLocation/keywordLocation/absoluteKeywordLocation)
|
387
|
+
'*': |
|
388
|
+
fallback error for schema and all keywords
|
389
|
+
instance: %{instance}
|
390
|
+
instance location: %{instanceLocation}
|
391
|
+
keyword location: %{keywordLocation}
|
392
|
+
absolute keyword location: %{absoluteKeywordLocation}
|
393
|
+
```
|
394
|
+
|
395
|
+
And output:
|
396
|
+
|
397
|
+
```ruby
|
398
|
+
require 'i18n'
|
399
|
+
I18n.locale = :en # $LOCALE=en
|
400
|
+
|
401
|
+
schemer = JSONSchemer.schema({
|
402
|
+
'$id' => 'https://example.com/schema', # $SCHEMA_ID=https://example.com/schema
|
403
|
+
'$schema' => 'http://json-schema.org/draft-07/schema#', # $META_SCHEMA_ID=http://json-schema.org/draft-07/schema#
|
404
|
+
'properties' => {
|
405
|
+
'abc' => {
|
406
|
+
'type' => 'integer' # $KEYWORD=type
|
407
|
+
} # $KEYWORD_LOCATION=#/properties/abc/type
|
408
|
+
} # $ABSOLUTE_KEYWORD_LOCATION=https://example.com/schema#/properties/abc/type
|
409
|
+
})
|
410
|
+
|
411
|
+
schemer.validate({ 'abc' => 'not-an-integer' }).first
|
412
|
+
# => {"data"=>"not-an-integer",
|
413
|
+
# "data_pointer"=>"/abc",
|
414
|
+
# "schema"=>{"type"=>"integer"},
|
415
|
+
# "schema_pointer"=>"/properties/abc",
|
416
|
+
# "root_schema"=>{"$id"=>"https://example.com/schema", "$schema"=>"http://json-schema.org/draft-07/schema#", "properties"=>{"abc"=>{"type"=>"integer"}}},
|
417
|
+
# "type"=>"integer",
|
418
|
+
# "error"=>"custom error for absolute keyword location",
|
419
|
+
# "i18n"=>true
|
420
|
+
```
|
421
|
+
|
422
|
+
In the example above, custom error messsages are looked up using the following keys (in order until one is found):
|
423
|
+
|
424
|
+
1. `en.json_schemer.errors.'https://example.com/schema#/properties/abc/type'`
|
425
|
+
2. `en.json_schemer.errors.'https://example.com/schema'.'#/properties/abc/type'`
|
426
|
+
3. `en.json_schemer.errors.'#/properties/abc/type'`
|
427
|
+
4. `en.json_schemer.errors.'https://example.com/schema'.type`
|
428
|
+
5. `en.json_schemer.errors.'https://example.com/schema'.*`
|
429
|
+
6. `en.json_schemer.errors.'http://json-schema.org/draft-07/schema#'.type`
|
430
|
+
7. `en.json_schemer.errors.'http://json-schema.org/draft-07/schema#'.*`
|
431
|
+
8. `en.json_schemer.errors.type`
|
432
|
+
9. `en.json_schemer.errors.*`
|
433
|
+
|
434
|
+
## OpenAPI
|
435
|
+
|
436
|
+
```ruby
|
437
|
+
document = JSONSchemer.openapi({
|
438
|
+
'openapi' => '3.1.0',
|
439
|
+
'info' => {
|
440
|
+
'title' => 'example'
|
441
|
+
},
|
442
|
+
'components' => {
|
443
|
+
'schemas' => {
|
444
|
+
'example' => {
|
445
|
+
'type' => 'integer'
|
446
|
+
}
|
447
|
+
}
|
448
|
+
}
|
449
|
+
})
|
450
|
+
|
451
|
+
# document validation using meta schema
|
452
|
+
|
453
|
+
document.valid?
|
454
|
+
# => false
|
455
|
+
|
456
|
+
document.validate.to_a
|
457
|
+
# => [{"data"=>{"title"=>"example"},
|
458
|
+
# "data_pointer"=>"/info",
|
459
|
+
# "schema"=>{...info schema},
|
460
|
+
# "schema_pointer"=>"/$defs/info",
|
461
|
+
# "root_schema"=>{...meta schema},
|
462
|
+
# "type"=>"required",
|
463
|
+
# "details"=>{"missing_keys"=>["version"]}},
|
464
|
+
# ...]
|
465
|
+
|
466
|
+
# data validation using schema by name (in `components/schemas`)
|
467
|
+
|
468
|
+
document.schema('example').valid?(1)
|
469
|
+
# => true
|
470
|
+
|
471
|
+
document.schema('example').valid?('one')
|
472
|
+
# => false
|
473
|
+
|
474
|
+
# data validation using schema by ref
|
475
|
+
|
476
|
+
document.ref('#/components/schemas/example').valid?(1)
|
477
|
+
# => true
|
478
|
+
|
479
|
+
document.ref('#/components/schemas/example').valid?('one')
|
480
|
+
# => false
|
481
|
+
```
|
482
|
+
|
483
|
+
## CLI
|
484
|
+
|
485
|
+
The `json_schemer` executable takes a JSON schema file as the first argument followed by one or more JSON data files to validate. If there are any validation errors, it outputs them and returns an error code.
|
486
|
+
|
487
|
+
Validation errors are output as single-line JSON objects. The `--errors` option can be used to limit the number of errors returned or prevent output entirely (and fail fast).
|
488
|
+
|
489
|
+
The schema or data can also be read from stdin using `-`.
|
490
|
+
|
491
|
+
```
|
492
|
+
% json_schemer --help
|
493
|
+
Usage:
|
494
|
+
json_schemer [options] <schema> <data>...
|
495
|
+
json_schemer [options] <schema> -
|
496
|
+
json_schemer [options] - <data>...
|
497
|
+
json_schemer -h | --help
|
498
|
+
json_schemer --version
|
499
|
+
|
500
|
+
Options:
|
501
|
+
-e, --errors MAX Maximum number of errors to output
|
502
|
+
Use "0" to validate with no output
|
503
|
+
-h, --help Show help
|
504
|
+
-v, --version Show version
|
505
|
+
```
|
506
|
+
|
117
507
|
## Development
|
118
508
|
|
119
509
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
120
510
|
|
121
511
|
## Build Status
|
122
512
|
|
123
|
-
![
|
513
|
+
![CI](https://github.com/davishmcclurg/json_schemer/actions/workflows/ci.yml/badge.svg)
|
514
|
+
![JSON Schema Versions](https://img.shields.io/endpoint?url=https%3A%2F%2Fbowtie.report%2Fbadges%2Fruby-json_schemer%2Fsupported_versions.json)<br>
|
515
|
+
![Draft 2020-12](https://img.shields.io/endpoint?url=https%3A%2F%2Fbowtie.report%2Fbadges%2Fruby-json_schemer%2Fcompliance%2Fdraft2020-12.json)
|
516
|
+
![Draft 2019-09](https://img.shields.io/endpoint?url=https%3A%2F%2Fbowtie.report%2Fbadges%2Fruby-json_schemer%2Fcompliance%2Fdraft2019-09.json)
|
517
|
+
![Draft 7](https://img.shields.io/endpoint?url=https%3A%2F%2Fbowtie.report%2Fbadges%2Fruby-json_schemer%2Fcompliance%2Fdraft7.json)
|
518
|
+
![Draft 6](https://img.shields.io/endpoint?url=https%3A%2F%2Fbowtie.report%2Fbadges%2Fruby-json_schemer%2Fcompliance%2Fdraft6.json)
|
519
|
+
![Draft 4](https://img.shields.io/endpoint?url=https%3A%2F%2Fbowtie.report%2Fbadges%2Fruby-json_schemer%2Fcompliance%2Fdraft4.json)
|
124
520
|
|
125
521
|
## Contributing
|
126
522
|
|