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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +7 -7
  3. data/CHANGELOG.md +102 -0
  4. data/Gemfile.lock +30 -10
  5. data/README.md +402 -6
  6. data/bin/hostname_character_classes +42 -0
  7. data/bin/rake +29 -0
  8. data/exe/json_schemer +62 -0
  9. data/json_schemer.gemspec +9 -12
  10. data/lib/json_schemer/cached_resolver.rb +16 -0
  11. data/lib/json_schemer/configuration.rb +31 -0
  12. data/lib/json_schemer/content.rb +18 -0
  13. data/lib/json_schemer/draft201909/meta.rb +320 -0
  14. data/lib/json_schemer/draft201909/vocab/applicator.rb +104 -0
  15. data/lib/json_schemer/draft201909/vocab/core.rb +45 -0
  16. data/lib/json_schemer/draft201909/vocab.rb +31 -0
  17. data/lib/json_schemer/draft202012/meta.rb +364 -0
  18. data/lib/json_schemer/draft202012/vocab/applicator.rb +382 -0
  19. data/lib/json_schemer/draft202012/vocab/content.rb +52 -0
  20. data/lib/json_schemer/draft202012/vocab/core.rb +160 -0
  21. data/lib/json_schemer/draft202012/vocab/format_annotation.rb +23 -0
  22. data/lib/json_schemer/draft202012/vocab/format_assertion.rb +23 -0
  23. data/lib/json_schemer/draft202012/vocab/meta_data.rb +30 -0
  24. data/lib/json_schemer/draft202012/vocab/unevaluated.rb +104 -0
  25. data/lib/json_schemer/draft202012/vocab/validation.rb +286 -0
  26. data/lib/json_schemer/draft202012/vocab.rb +105 -0
  27. data/lib/json_schemer/draft4/meta.rb +161 -0
  28. data/lib/json_schemer/draft4/vocab/validation.rb +39 -0
  29. data/lib/json_schemer/draft4/vocab.rb +18 -0
  30. data/lib/json_schemer/draft6/meta.rb +172 -0
  31. data/lib/json_schemer/draft6/vocab.rb +16 -0
  32. data/lib/json_schemer/draft7/meta.rb +183 -0
  33. data/lib/json_schemer/draft7/vocab/validation.rb +69 -0
  34. data/lib/json_schemer/draft7/vocab.rb +30 -0
  35. data/lib/json_schemer/ecma_regexp.rb +51 -0
  36. data/lib/json_schemer/errors.rb +1 -0
  37. data/lib/json_schemer/format/duration.rb +23 -0
  38. data/lib/json_schemer/format/email.rb +56 -0
  39. data/lib/json_schemer/format/hostname.rb +58 -0
  40. data/lib/json_schemer/format/json_pointer.rb +18 -0
  41. data/lib/json_schemer/format/uri_template.rb +34 -0
  42. data/lib/json_schemer/format.rb +128 -109
  43. data/lib/json_schemer/keyword.rb +56 -0
  44. data/lib/json_schemer/location.rb +25 -0
  45. data/lib/json_schemer/openapi.rb +38 -0
  46. data/lib/json_schemer/openapi30/document.rb +1672 -0
  47. data/lib/json_schemer/openapi30/meta.rb +32 -0
  48. data/lib/json_schemer/openapi30/vocab/base.rb +18 -0
  49. data/lib/json_schemer/openapi30/vocab.rb +12 -0
  50. data/lib/json_schemer/openapi31/document.rb +1557 -0
  51. data/lib/json_schemer/openapi31/meta.rb +136 -0
  52. data/lib/json_schemer/openapi31/vocab/base.rb +127 -0
  53. data/lib/json_schemer/openapi31/vocab.rb +18 -0
  54. data/lib/json_schemer/output.rb +56 -0
  55. data/lib/json_schemer/result.rb +242 -0
  56. data/lib/json_schemer/schema.rb +424 -0
  57. data/lib/json_schemer/version.rb +1 -1
  58. data/lib/json_schemer.rb +243 -30
  59. metadata +141 -25
  60. data/lib/json_schemer/cached_ref_resolver.rb +0 -14
  61. data/lib/json_schemer/schema/base.rb +0 -658
  62. data/lib/json_schemer/schema/draft4.rb +0 -44
  63. data/lib/json_schemer/schema/draft6.rb +0 -25
  64. data/lib/json_schemer/schema/draft7.rb +0 -32
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 426a95173deee91594b5ad1df05dac15ea3c90bac6e17b3136a7170744555c50
4
- data.tar.gz: 25268e7f5cb108245aad3d112624eee96774fe8820eda5e24a4f91d97d91e676
3
+ metadata.gz: e5ec5b8e547dc3a64b134054ec80b0f40cf9453cba4cce430995e6ae165a6e07
4
+ data.tar.gz: 943423f15ba47e7b8ecc5fbf3d1b566679c43e84e3d70bbc4e0f47f126e4275f
5
5
  SHA512:
6
- metadata.gz: 5534a623dfece170bd27bbb2cbc34978e9e81c075ddad60213d8ce4538d50a6ee119b7efcb2b750068cee0cf565ee582a35eac990c14c2ca647d9d8cc314d4f5
7
- data.tar.gz: b813c1b1acd0b1cf0680bf0ecae7eef33b6eb576ff5735d76f072e82fc5cfb36e80ec5b0a3fde6829ec3de856e903b42164a917b833019c33a11d79d6db6080b
6
+ metadata.gz: 43c26354e594ae3219e0edd22e18b6d89a64b6cbad6bc4ac3e85750ccc128c3827d7aa38b1dc0d48a232c733250467939eaa8d6049ab8d34c75f5a0ac6f0e715
7
+ data.tar.gz: eced2c5592c64adfffb89923d28a363494f09bdd87202233706baa24ad4067e8240a85120a29eb2145b70a5f9bbf85e3a7f2f71fbdd1a89a74bc2cc6e3a243ed
@@ -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.4, 2.5, 2.6, 2.7, 3.0, head, jruby, jruby-head, truffleruby, truffleruby-head]
9
+ ruby: [2.7, 3.0, 3.1, 3.2, 3.3, head, jruby, jruby-head, truffleruby, truffleruby-head]
10
10
  exclude:
11
- - os: windows-latest
12
- ruby: jruby
13
- - os: windows-latest
14
- ruby: jruby-head
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@v2
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: bundle exec rake test
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 (0.2.18)
5
- ecma-re-validator (~> 0.3)
4
+ json_schemer (2.3.0)
5
+ bigdecimal
6
6
  hana (~> 1.3)
7
7
  regexp_parser (~> 2.0)
8
- uri_template (~> 0.7)
8
+ simpleidn (~> 0.2)
9
9
 
10
10
  GEM
11
11
  remote: https://rubygems.org/
12
12
  specs:
13
- ecma-re-validator (0.3.0)
14
- regexp_parser (~> 2.0)
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
- minitest (5.14.3)
17
- rake (13.0.1)
18
- regexp_parser (2.1.1)
19
- uri_template (0.7.0)
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.2.11
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 7.
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, "schema"=>{"type"=>"integer", "minimum"=>11}, "pointer"=>"#/abc", "type"=>"minimum"}]
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
- # validate `format` (https://tools.ietf.org/html/draft-handrews-json-schema-validation-00#section-7)
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
- # true/false
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
- ![Build Status](https://github.com/davishmcclurg/json_schemer/workflows/ci/badge.svg)
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