json_schemer 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +1 -1
- data/CHANGELOG.md +18 -1
- data/Gemfile.lock +10 -3
- data/README.md +192 -1
- data/json_schemer.gemspec +2 -0
- data/lib/json_schemer/content.rb +18 -0
- data/lib/json_schemer/draft201909/meta.rb +3 -18
- data/lib/json_schemer/draft202012/meta.rb +27 -24
- data/lib/json_schemer/draft202012/vocab/content.rb +10 -2
- data/lib/json_schemer/draft202012/vocab/core.rb +6 -0
- data/lib/json_schemer/draft202012/vocab/format_annotation.rb +1 -9
- data/lib/json_schemer/draft202012/vocab/format_assertion.rb +1 -7
- data/lib/json_schemer/draft202012/vocab.rb +3 -1
- data/lib/json_schemer/draft4/meta.rb +6 -0
- data/lib/json_schemer/draft6/meta.rb +11 -0
- data/lib/json_schemer/draft7/meta.rb +5 -0
- data/lib/json_schemer/draft7/vocab/validation.rb +4 -4
- data/lib/json_schemer/format.rb +113 -117
- data/lib/json_schemer/keyword.rb +4 -0
- data/lib/json_schemer/openapi30/meta.rb +6 -0
- data/lib/json_schemer/openapi31/meta.rb +8 -0
- data/lib/json_schemer/openapi31/vocab/base.rb +65 -27
- data/lib/json_schemer/output.rb +6 -5
- data/lib/json_schemer/result.rb +71 -10
- data/lib/json_schemer/schema.rb +57 -23
- data/lib/json_schemer/version.rb +1 -1
- data/lib/json_schemer.rb +20 -19
- metadata +31 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bd2081bcd9677b1615f3076f9c83171bba0f4a9c7030b94f175ea0b350b75b7c
|
4
|
+
data.tar.gz: 920692383076ade224cb1b1f02f1579a824792c07333ebdee467596a0c3e3b6a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 84f674bd5560c44cbc752a18365cfbff341bb4e94a7cae43674d5591bb2229e0bd5d096346bca9e8ef54313c500496195ebabcb3d3100a39346e23be4d984f20
|
7
|
+
data.tar.gz: 77a8391db7a554ec8ff93b155c77f0945984a194a3f64178d3f184fdbfc21806736f18788295579812ddc138c6b5a1dde977a16e8aa3b8c7edf7545c15eae3fa
|
data/.github/workflows/ci.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,23 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
## [2.
|
3
|
+
## [2.1.0] - XXXX-XX-XX
|
4
|
+
|
5
|
+
### Bug Fixes
|
6
|
+
|
7
|
+
- Limit anyOf/oneOf discriminator to listed refs: https://github.com/davishmcclurg/json_schemer/pull/145
|
8
|
+
- Require discriminator `propertyName` property: https://github.com/davishmcclurg/json_schemer/pull/145
|
9
|
+
- Support `Schema#ref` in subschemas: https://github.com/davishmcclurg/json_schemer/pull/145
|
10
|
+
- Resolve JSON pointer refs using correct base URI: https://github.com/davishmcclurg/json_schemer/pull/147
|
11
|
+
- `date` format in OpenAPI 3.0: https://github.com/davishmcclurg/json_schemer/commit/69fe7a815ecf0cfb1c40ac402bf46a789c05e972
|
12
|
+
|
13
|
+
### Features
|
14
|
+
|
15
|
+
- Custom error messages with `x-error` keyword and I18n: https://github.com/davishmcclurg/json_schemer/pull/149
|
16
|
+
- Custom content encodings and media types: https://github.com/davishmcclurg/json_schemer/pull/148
|
17
|
+
|
18
|
+
[2.1.0]: https://github.com/davishmcclurg/json_schemer/releases/tag/v2.1.0
|
19
|
+
|
20
|
+
## [2.0.0] - 2023-08-20
|
4
21
|
|
5
22
|
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.
|
6
23
|
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
json_schemer (2.
|
4
|
+
json_schemer (2.1.0)
|
5
5
|
hana (~> 1.3)
|
6
6
|
regexp_parser (~> 2.0)
|
7
7
|
simpleidn (~> 0.2)
|
@@ -9,11 +9,16 @@ PATH
|
|
9
9
|
GEM
|
10
10
|
remote: https://rubygems.org/
|
11
11
|
specs:
|
12
|
+
concurrent-ruby (1.2.2)
|
12
13
|
docile (1.4.0)
|
13
14
|
hana (1.3.7)
|
15
|
+
i18n (1.14.1)
|
16
|
+
concurrent-ruby (~> 1.0)
|
17
|
+
i18n-debug (1.2.0)
|
18
|
+
i18n (< 2)
|
14
19
|
minitest (5.15.0)
|
15
20
|
rake (13.0.6)
|
16
|
-
regexp_parser (2.8.
|
21
|
+
regexp_parser (2.8.2)
|
17
22
|
simplecov (0.22.0)
|
18
23
|
docile (~> 1.1)
|
19
24
|
simplecov-html (~> 0.11)
|
@@ -25,7 +30,7 @@ GEM
|
|
25
30
|
unf (0.1.4)
|
26
31
|
unf_ext
|
27
32
|
unf (0.1.4-java)
|
28
|
-
unf_ext (0.0.
|
33
|
+
unf_ext (0.0.9)
|
29
34
|
|
30
35
|
PLATFORMS
|
31
36
|
java
|
@@ -33,6 +38,8 @@ PLATFORMS
|
|
33
38
|
|
34
39
|
DEPENDENCIES
|
35
40
|
bundler (~> 2.0)
|
41
|
+
i18n
|
42
|
+
i18n-debug
|
36
43
|
json_schemer!
|
37
44
|
minitest (~> 5.0)
|
38
45
|
rake (~> 13.0)
|
data/README.md
CHANGED
@@ -126,7 +126,7 @@ schemer.ref('#/$defs/foo').validate(1).to_a
|
|
126
126
|
# "schema_pointer"=>"/$defs/foo",
|
127
127
|
# "root_schema"=>{"type"=>"integer", "$defs"=>{"foo"=>{"type"=>"string"}}},
|
128
128
|
# "type"=>"string",
|
129
|
-
# "error"=>"
|
129
|
+
# "error"=>"value at root is not a string"}]
|
130
130
|
|
131
131
|
# schema bundling (https://json-schema.org/draft/2020-12/json-schema-core.html#section-9.3)
|
132
132
|
|
@@ -180,6 +180,37 @@ JSONSchemer.schema(
|
|
180
180
|
# default: true
|
181
181
|
format: true,
|
182
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
|
+
|
183
214
|
# insert default property values during validation
|
184
215
|
# true/false
|
185
216
|
# default: false
|
@@ -225,6 +256,166 @@ JSONSchemer.schema(
|
|
225
256
|
)
|
226
257
|
```
|
227
258
|
|
259
|
+
## Custom Error Messages
|
260
|
+
|
261
|
+
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.
|
262
|
+
|
263
|
+
### `x-error` Keyword
|
264
|
+
|
265
|
+
```ruby
|
266
|
+
# override all errors for a schema
|
267
|
+
schemer = JSONSchemer.schema({
|
268
|
+
'type' => 'string',
|
269
|
+
'x-error' => 'custom error for schema and all keywords'
|
270
|
+
})
|
271
|
+
|
272
|
+
schemer.validate(1).first
|
273
|
+
# => {"data"=>1,
|
274
|
+
# "data_pointer"=>"",
|
275
|
+
# "schema"=>{"type"=>"string", "x-error"=>"custom error for schema and all keywords"},
|
276
|
+
# "schema_pointer"=>"",
|
277
|
+
# "root_schema"=>{"type"=>"string", "x-error"=>"custom error for schema and all keywords"},
|
278
|
+
# "type"=>"string",
|
279
|
+
# "error"=>"custom error for schema and all keywords",
|
280
|
+
# "x-error"=>true}
|
281
|
+
|
282
|
+
schemer.validate(1, :output_format => 'basic')
|
283
|
+
# => {"valid"=>false,
|
284
|
+
# "keywordLocation"=>"",
|
285
|
+
# "absoluteKeywordLocation"=>"json-schemer://schema#",
|
286
|
+
# "instanceLocation"=>"",
|
287
|
+
# "error"=>"custom error for schema and all keywords",
|
288
|
+
# "x-error"=>true,
|
289
|
+
# "errors"=>#<Enumerator: ...>}
|
290
|
+
|
291
|
+
# keyword-specific errors
|
292
|
+
schemer = JSONSchemer.schema({
|
293
|
+
'type' => 'string',
|
294
|
+
'minLength' => 10,
|
295
|
+
'x-error' => {
|
296
|
+
'type' => 'custom error for `type` keyword',
|
297
|
+
# special `^` keyword for schema-level error
|
298
|
+
'^' => 'custom error for schema',
|
299
|
+
# same behavior as when `x-error` is a string
|
300
|
+
'*' => 'fallback error for schema and all keywords'
|
301
|
+
}
|
302
|
+
})
|
303
|
+
|
304
|
+
schemer.validate(1).map { _1.fetch('error') }
|
305
|
+
# => ["custom error for `type` keyword"]
|
306
|
+
|
307
|
+
schemer.validate('1').map { _1.fetch('error') }
|
308
|
+
# => ["custom error for schema and all keywords"]
|
309
|
+
|
310
|
+
schemer.validate(1, :output_format => 'basic').fetch('error')
|
311
|
+
# => "custom error for schema"
|
312
|
+
|
313
|
+
# variable interpolation (instance/instanceLocation/keywordLocation/absoluteKeywordLocation)
|
314
|
+
schemer = JSONSchemer.schema({
|
315
|
+
'$id' => 'https://example.com/schema',
|
316
|
+
'properties' => {
|
317
|
+
'abc' => {
|
318
|
+
'type' => 'string',
|
319
|
+
'x-error' => <<~ERROR
|
320
|
+
instance: %{instance}
|
321
|
+
instance location: %{instanceLocation}
|
322
|
+
keyword location: %{keywordLocation}
|
323
|
+
absolute keyword location: %{absoluteKeywordLocation}
|
324
|
+
ERROR
|
325
|
+
}
|
326
|
+
}
|
327
|
+
})
|
328
|
+
|
329
|
+
puts schemer.validate({ 'abc' => 1 }).first.fetch('error')
|
330
|
+
# instance: 1
|
331
|
+
# instance location: /abc
|
332
|
+
# keyword location: /properties/abc/type
|
333
|
+
# absolute keyword location: https://example.com/schema#/properties/abc/type
|
334
|
+
```
|
335
|
+
|
336
|
+
### I18n
|
337
|
+
|
338
|
+
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.
|
339
|
+
|
340
|
+
Translation keys are looked up in this order:
|
341
|
+
|
342
|
+
1. `$LOCALE.json_schemer.errors.$ABSOLUTE_KEYWORD_LOCATION`
|
343
|
+
2. `$LOCALE.json_schemer.errors.$SCHEMA_ID.$KEYWORD_LOCATION`
|
344
|
+
3. `$LOCALE.json_schemer.errors.$KEYWORD_LOCATION`
|
345
|
+
4. `$LOCALE.json_schemer.errors.$SCHEMA_ID.$KEYWORD`
|
346
|
+
5. `$LOCALE.json_schemer.errors.$SCHEMA_ID.*`
|
347
|
+
6. `$LOCALE.json_schemer.errors.$META_SCHEMA_ID.$KEYWORD`
|
348
|
+
7. `$LOCALE.json_schemer.errors.$META_SCHEMA_ID.*`
|
349
|
+
8. `$LOCALE.json_schemer.errors.$KEYWORD`
|
350
|
+
9. `$LOCALE.json_schemer.errors.*`
|
351
|
+
|
352
|
+
Example translations file:
|
353
|
+
|
354
|
+
```yaml
|
355
|
+
en:
|
356
|
+
json_schemer:
|
357
|
+
errors:
|
358
|
+
'https://example.com/schema#/properties/abc/type': custom error for absolute keyword location
|
359
|
+
'https://example.com/schema':
|
360
|
+
'#/properties/abc/type': custom error for keyword location, nested under schema $id
|
361
|
+
'type': custom error for `type` keyword, nested under schema $id
|
362
|
+
'^': custom error for schema, nested under schema $id
|
363
|
+
'*': fallback error for schema and all keywords, nested under schema $id
|
364
|
+
'#/properties/abc/type': custom error for keyword location
|
365
|
+
'http://json-schema.org/draft-07/schema#':
|
366
|
+
'type': custom error for `type` keyword, nested under meta-schema $id ($schema)
|
367
|
+
'^': custom error for schema, nested under meta-schema $id
|
368
|
+
'*': fallback error for schema and all keywords, nested under meta-schema $id ($schema)
|
369
|
+
'type': custom error for `type` keyword
|
370
|
+
'^': custom error for schema
|
371
|
+
# variable interpolation (instance/instanceLocation/keywordLocation/absoluteKeywordLocation)
|
372
|
+
'*': |
|
373
|
+
fallback error for schema and all keywords
|
374
|
+
instance: %{instance}
|
375
|
+
instance location: %{instanceLocation}
|
376
|
+
keyword location: %{keywordLocation}
|
377
|
+
absolute keyword location: %{absoluteKeywordLocation}
|
378
|
+
```
|
379
|
+
|
380
|
+
And output:
|
381
|
+
|
382
|
+
```ruby
|
383
|
+
require 'i18n'
|
384
|
+
I18n.locale = :en # $LOCALE=en
|
385
|
+
|
386
|
+
schemer = JSONSchemer.schema({
|
387
|
+
'$id' => 'https://example.com/schema', # $SCHEMA_ID=https://example.com/schema
|
388
|
+
'$schema' => 'http://json-schema.org/draft-07/schema#', # $META_SCHEMA_ID=http://json-schema.org/draft-07/schema#
|
389
|
+
'properties' => {
|
390
|
+
'abc' => {
|
391
|
+
'type' => 'integer' # $KEYWORD=type
|
392
|
+
} # $KEYWORD_LOCATION=#/properties/abc/type
|
393
|
+
} # $ABSOLUTE_KEYWORD_LOCATION=https://example.com/schema#/properties/abc/type
|
394
|
+
})
|
395
|
+
|
396
|
+
schemer.validate({ 'abc' => 'not-an-integer' }).first
|
397
|
+
# => {"data"=>"not-an-integer",
|
398
|
+
# "data_pointer"=>"/abc",
|
399
|
+
# "schema"=>{"type"=>"integer"},
|
400
|
+
# "schema_pointer"=>"/properties/abc",
|
401
|
+
# "root_schema"=>{"$id"=>"https://example.com/schema", "$schema"=>"http://json-schema.org/draft-07/schema#", "properties"=>{"abc"=>{"type"=>"integer"}}},
|
402
|
+
# "type"=>"integer",
|
403
|
+
# "error"=>"custom error for absolute keyword location",
|
404
|
+
# "i18n"=>true
|
405
|
+
```
|
406
|
+
|
407
|
+
In the example above, custom error messsages are looked up using the following keys (in order until one is found):
|
408
|
+
|
409
|
+
1. `en.json_schemer.errors.'https://example.com/schema#/properties/abc/type'`
|
410
|
+
2. `en.json_schemer.errors.'https://example.com/schema'.'#/properties/abc/type'`
|
411
|
+
3. `en.json_schemer.errors.'#/properties/abc/type'`
|
412
|
+
4. `en.json_schemer.errors.'https://example.com/schema'.type`
|
413
|
+
5. `en.json_schemer.errors.'https://example.com/schema'.*`
|
414
|
+
6. `en.json_schemer.errors.'http://json-schema.org/draft-07/schema#'.type`
|
415
|
+
7. `en.json_schemer.errors.'http://json-schema.org/draft-07/schema#'.*`
|
416
|
+
8. `en.json_schemer.errors.type`
|
417
|
+
9. `en.json_schemer.errors.*`
|
418
|
+
|
228
419
|
## OpenAPI
|
229
420
|
|
230
421
|
```ruby
|
data/json_schemer.gemspec
CHANGED
@@ -26,6 +26,8 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_development_dependency "rake", "~> 13.0"
|
27
27
|
spec.add_development_dependency "minitest", "~> 5.0"
|
28
28
|
spec.add_development_dependency "simplecov", "~> 0.22"
|
29
|
+
spec.add_development_dependency "i18n"
|
30
|
+
spec.add_development_dependency "i18n-debug"
|
29
31
|
|
30
32
|
spec.add_runtime_dependency "hana", "~> 1.3"
|
31
33
|
spec.add_runtime_dependency "regexp_parser", "~> 2.0"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module JSONSchemer
|
3
|
+
module ContentEncoding
|
4
|
+
BASE64 = proc do |instance|
|
5
|
+
[true, Base64.strict_decode64(instance)]
|
6
|
+
rescue
|
7
|
+
[false, nil]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module ContentMediaType
|
12
|
+
JSON = proc do |instance|
|
13
|
+
[true, ::JSON.parse(instance)]
|
14
|
+
rescue
|
15
|
+
[false, nil]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -2,6 +2,9 @@
|
|
2
2
|
module JSONSchemer
|
3
3
|
module Draft201909
|
4
4
|
BASE_URI = URI('https://json-schema.org/draft/2019-09/schema')
|
5
|
+
FORMATS = Draft202012::FORMATS
|
6
|
+
CONTENT_ENCODINGS = Draft202012::CONTENT_ENCODINGS
|
7
|
+
CONTENT_MEDIA_TYPES = Draft202012::CONTENT_MEDIA_TYPES
|
5
8
|
SCHEMA = {
|
6
9
|
'$schema' => 'https://json-schema.org/draft/2019-09/schema',
|
7
10
|
'$id' => 'https://json-schema.org/draft/2019-09/schema',
|
@@ -48,9 +51,6 @@ module JSONSchemer
|
|
48
51
|
CORE = {
|
49
52
|
'$schema' => 'https://json-schema.org/draft/2019-09/schema',
|
50
53
|
'$id' => 'https://json-schema.org/draft/2019-09/meta/core',
|
51
|
-
'$vocabulary' => {
|
52
|
-
'https://json-schema.org/draft/2019-09/vocab/core' => true
|
53
|
-
},
|
54
54
|
'$recursiveAnchor' => true,
|
55
55
|
'title' => 'Core vocabulary meta-schema',
|
56
56
|
'type' => ['object', 'boolean'],
|
@@ -105,9 +105,6 @@ module JSONSchemer
|
|
105
105
|
APPLICATOR = {
|
106
106
|
'$schema' => 'https://json-schema.org/draft/2019-09/schema',
|
107
107
|
'$id' => 'https://json-schema.org/draft/2019-09/meta/applicator',
|
108
|
-
'$vocabulary' => {
|
109
|
-
'https://json-schema.org/draft/2019-09/vocab/applicator' => true
|
110
|
-
},
|
111
108
|
'$recursiveAnchor' => true,
|
112
109
|
'title' => 'Applicator vocabulary meta-schema',
|
113
110
|
'type' => ['object', 'boolean'],
|
@@ -161,9 +158,6 @@ module JSONSchemer
|
|
161
158
|
VALIDATION = {
|
162
159
|
'$schema' => 'https://json-schema.org/draft/2019-09/schema',
|
163
160
|
'$id' => 'https://json-schema.org/draft/2019-09/meta/validation',
|
164
|
-
'$vocabulary' => {
|
165
|
-
'https://json-schema.org/draft/2019-09/vocab/validation' => true
|
166
|
-
},
|
167
161
|
'$recursiveAnchor' => true,
|
168
162
|
'title' => 'Validation vocabulary meta-schema',
|
169
163
|
'type' => ['object', 'boolean'],
|
@@ -259,9 +253,6 @@ module JSONSchemer
|
|
259
253
|
META_DATA = {
|
260
254
|
'$schema' => 'https://json-schema.org/draft/2019-09/schema',
|
261
255
|
'$id' => 'https://json-schema.org/draft/2019-09/meta/meta-data',
|
262
|
-
'$vocabulary' => {
|
263
|
-
'https://json-schema.org/draft/2019-09/vocab/meta-data' => true
|
264
|
-
},
|
265
256
|
'$recursiveAnchor' => true,
|
266
257
|
'title' => 'Meta-data vocabulary meta-schema',
|
267
258
|
'type' => ['object', 'boolean'],
|
@@ -295,9 +286,6 @@ module JSONSchemer
|
|
295
286
|
FORMAT = {
|
296
287
|
'$schema' => 'https://json-schema.org/draft/2019-09/schema',
|
297
288
|
'$id' => 'https://json-schema.org/draft/2019-09/meta/format',
|
298
|
-
'$vocabulary' => {
|
299
|
-
'https://json-schema.org/draft/2019-09/vocab/format' => true
|
300
|
-
},
|
301
289
|
'$recursiveAnchor' => true,
|
302
290
|
'title' => 'Format vocabulary meta-schema',
|
303
291
|
'type' => ['object', 'boolean'],
|
@@ -309,9 +297,6 @@ module JSONSchemer
|
|
309
297
|
CONTENT = {
|
310
298
|
'$schema' => 'https://json-schema.org/draft/2019-09/schema',
|
311
299
|
'$id' => 'https://json-schema.org/draft/2019-09/meta/content',
|
312
|
-
'$vocabulary' => {
|
313
|
-
'https://json-schema.org/draft/2019-09/vocab/content' => true
|
314
|
-
},
|
315
300
|
'$recursiveAnchor' => true,
|
316
301
|
'title' => 'Content vocabulary meta-schema',
|
317
302
|
'type' => ['object', 'boolean'],
|
@@ -2,6 +2,33 @@
|
|
2
2
|
module JSONSchemer
|
3
3
|
module Draft202012
|
4
4
|
BASE_URI = URI('https://json-schema.org/draft/2020-12/schema')
|
5
|
+
FORMATS = {
|
6
|
+
'date-time' => Format::DATE_TIME,
|
7
|
+
'date' => Format::DATE,
|
8
|
+
'time' => Format::TIME,
|
9
|
+
'duration' => Format::DURATION,
|
10
|
+
'email' => Format::EMAIL,
|
11
|
+
'idn-email' => Format::IDN_EMAIL,
|
12
|
+
'hostname' => Format::HOSTNAME,
|
13
|
+
'idn-hostname' => Format::IDN_HOSTNAME,
|
14
|
+
'ipv4' => Format::IPV4,
|
15
|
+
'ipv6' => Format::IPV6,
|
16
|
+
'uri' => Format::URI,
|
17
|
+
'uri-reference' => Format::URI_REFERENCE,
|
18
|
+
'iri' => Format::IRI,
|
19
|
+
'iri-reference' => Format::IRI_REFERENCE,
|
20
|
+
'uuid' => Format::UUID,
|
21
|
+
'uri-template' => Format::URI_TEMPLATE,
|
22
|
+
'json-pointer' => Format::JSON_POINTER,
|
23
|
+
'relative-json-pointer' => Format::RELATIVE_JSON_POINTER,
|
24
|
+
'regex' => Format::REGEX
|
25
|
+
}
|
26
|
+
CONTENT_ENCODINGS = {
|
27
|
+
'base64' => ContentEncoding::BASE64
|
28
|
+
}
|
29
|
+
CONTENT_MEDIA_TYPES = {
|
30
|
+
'application/json' => ContentMediaType::JSON
|
31
|
+
}
|
5
32
|
SCHEMA = {
|
6
33
|
'$schema' => 'https://json-schema.org/draft/2020-12/schema',
|
7
34
|
'$id' => 'https://json-schema.org/draft/2020-12/schema',
|
@@ -64,9 +91,6 @@ module JSONSchemer
|
|
64
91
|
CORE = {
|
65
92
|
'$schema' => 'https://json-schema.org/draft/2020-12/schema',
|
66
93
|
'$id' => 'https://json-schema.org/draft/2020-12/meta/core',
|
67
|
-
'$vocabulary' => {
|
68
|
-
'https://json-schema.org/draft/2020-12/vocab/core' => true
|
69
|
-
},
|
70
94
|
'$dynamicAnchor' => 'meta',
|
71
95
|
'title' => 'Core vocabulary meta-schema',
|
72
96
|
'type' => ['object', 'boolean'],
|
@@ -114,9 +138,6 @@ module JSONSchemer
|
|
114
138
|
APPLICATOR = {
|
115
139
|
'$schema' => 'https://json-schema.org/draft/2020-12/schema',
|
116
140
|
'$id' => 'https://json-schema.org/draft/2020-12/meta/applicator',
|
117
|
-
'$vocabulary' => {
|
118
|
-
'https://json-schema.org/draft/2020-12/vocab/applicator' => true
|
119
|
-
},
|
120
141
|
'$dynamicAnchor' => 'meta',
|
121
142
|
'title' => 'Applicator vocabulary meta-schema',
|
122
143
|
'type' => ['object', 'boolean'],
|
@@ -161,9 +182,6 @@ module JSONSchemer
|
|
161
182
|
UNEVALUATED = {
|
162
183
|
'$schema' => 'https://json-schema.org/draft/2020-12/schema',
|
163
184
|
'$id' => 'https://json-schema.org/draft/2020-12/meta/unevaluated',
|
164
|
-
'$vocabulary' => {
|
165
|
-
'https://json-schema.org/draft/2020-12/vocab/unevaluated' => true
|
166
|
-
},
|
167
185
|
'$dynamicAnchor' => 'meta',
|
168
186
|
'title' => 'Unevaluated applicator vocabulary meta-schema',
|
169
187
|
'type' => ['object', 'boolean'],
|
@@ -175,9 +193,6 @@ module JSONSchemer
|
|
175
193
|
VALIDATION = {
|
176
194
|
'$schema' => 'https://json-schema.org/draft/2020-12/schema',
|
177
195
|
'$id' => 'https://json-schema.org/draft/2020-12/meta/validation',
|
178
|
-
'$vocabulary' => {
|
179
|
-
'https://json-schema.org/draft/2020-12/vocab/validation' => true
|
180
|
-
},
|
181
196
|
'$dynamicAnchor' => 'meta',
|
182
197
|
'title' => 'Validation vocabulary meta-schema',
|
183
198
|
'type' => ['object', 'boolean'],
|
@@ -272,9 +287,6 @@ module JSONSchemer
|
|
272
287
|
META_DATA = {
|
273
288
|
'$schema' => 'https://json-schema.org/draft/2020-12/schema',
|
274
289
|
'$id' => 'https://json-schema.org/draft/2020-12/meta/meta-data',
|
275
|
-
'$vocabulary' => {
|
276
|
-
'https://json-schema.org/draft/2020-12/vocab/meta-data' => true
|
277
|
-
},
|
278
290
|
'$dynamicAnchor' => 'meta',
|
279
291
|
'title' => 'Meta-data vocabulary meta-schema',
|
280
292
|
'type' => ['object', 'boolean'],
|
@@ -307,9 +319,6 @@ module JSONSchemer
|
|
307
319
|
FORMAT_ANNOTATION = {
|
308
320
|
'$schema' => 'https://json-schema.org/draft/2020-12/schema',
|
309
321
|
'$id' => 'https://json-schema.org/draft/2020-12/meta/format-annotation',
|
310
|
-
'$vocabulary' => {
|
311
|
-
'https://json-schema.org/draft/2020-12/vocab/format-annotation' => true
|
312
|
-
},
|
313
322
|
'$dynamicAnchor' => 'meta',
|
314
323
|
'title' => 'Format vocabulary meta-schema for annotation results',
|
315
324
|
'type' => ['object', 'boolean'],
|
@@ -320,9 +329,6 @@ module JSONSchemer
|
|
320
329
|
FORMAT_ASSERTION = {
|
321
330
|
'$schema' => 'https://json-schema.org/draft/2020-12/schema',
|
322
331
|
'$id' => 'https://json-schema.org/draft/2020-12/meta/format-assertion',
|
323
|
-
'$vocabulary' => {
|
324
|
-
'https://json-schema.org/draft/2020-12/vocab/format-assertion' => true
|
325
|
-
},
|
326
332
|
'$dynamicAnchor' => 'meta',
|
327
333
|
'title' => 'Format vocabulary meta-schema for assertion results',
|
328
334
|
'type' => ['object', 'boolean'],
|
@@ -333,9 +339,6 @@ module JSONSchemer
|
|
333
339
|
CONTENT = {
|
334
340
|
'$schema' => 'https://json-schema.org/draft/2020-12/schema',
|
335
341
|
'$id' => 'https://json-schema.org/draft/2020-12/meta/content',
|
336
|
-
'$vocabulary' => {
|
337
|
-
'https://json-schema.org/draft/2020-12/vocab/content' => true
|
338
|
-
},
|
339
342
|
'$dynamicAnchor' => 'meta',
|
340
343
|
'title' => 'Content vocabulary meta-schema',
|
341
344
|
'type' => ['object', 'boolean'],
|
@@ -4,21 +4,29 @@ module JSONSchemer
|
|
4
4
|
module Vocab
|
5
5
|
module Content
|
6
6
|
class ContentEncoding < Keyword
|
7
|
+
def parse
|
8
|
+
root.fetch_content_encoding(value) { raise UnknownContentEncoding, value }
|
9
|
+
end
|
10
|
+
|
7
11
|
def validate(instance, instance_location, keyword_location, _context)
|
8
12
|
return result(instance, instance_location, keyword_location, true) unless instance.is_a?(String)
|
9
13
|
|
10
|
-
_valid, annotation =
|
14
|
+
_valid, annotation = parsed.call(instance)
|
11
15
|
|
12
16
|
result(instance, instance_location, keyword_location, true, :annotation => annotation)
|
13
17
|
end
|
14
18
|
end
|
15
19
|
|
16
20
|
class ContentMediaType < Keyword
|
21
|
+
def parse
|
22
|
+
root.fetch_content_media_type(value) { raise UnknownContentMediaType, value }
|
23
|
+
end
|
24
|
+
|
17
25
|
def validate(instance, instance_location, keyword_location, context)
|
18
26
|
return result(instance, instance_location, keyword_location, true) unless instance.is_a?(String)
|
19
27
|
|
20
28
|
decoded_instance = context.adjacent_results[ContentEncoding]&.annotation || instance
|
21
|
-
_valid, annotation =
|
29
|
+
_valid, annotation = parsed.call(decoded_instance)
|
22
30
|
|
23
31
|
result(instance, instance_location, keyword_location, true, :annotation => annotation)
|
24
32
|
end
|
@@ -119,6 +119,12 @@ module JSONSchemer
|
|
119
119
|
|
120
120
|
class Comment < Keyword; end
|
121
121
|
|
122
|
+
class XError < Keyword
|
123
|
+
def message(error_key)
|
124
|
+
value.is_a?(Hash) ? (value[error_key] || value[CATCHALL]) : value
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
122
128
|
class UnknownKeyword < Keyword
|
123
129
|
def parse
|
124
130
|
if value.is_a?(Hash)
|
@@ -4,20 +4,12 @@ module JSONSchemer
|
|
4
4
|
module Vocab
|
5
5
|
module FormatAnnotation
|
6
6
|
class Format < Keyword
|
7
|
-
extend JSONSchemer::Format
|
8
|
-
|
9
|
-
DEFAULT_FORMAT = proc do |instance, value|
|
10
|
-
!instance.is_a?(String) || valid_spec_format?(instance, value)
|
11
|
-
rescue UnknownFormat
|
12
|
-
true
|
13
|
-
end
|
14
|
-
|
15
7
|
def error(formatted_instance_location:, **)
|
16
8
|
"value at #{formatted_instance_location} does not match format: #{value}"
|
17
9
|
end
|
18
10
|
|
19
11
|
def parse
|
20
|
-
root.format && root.
|
12
|
+
root.format && root.fetch_format(value, false)
|
21
13
|
end
|
22
14
|
|
23
15
|
def validate(instance, instance_location, keyword_location, _context)
|
@@ -4,18 +4,12 @@ module JSONSchemer
|
|
4
4
|
module Vocab
|
5
5
|
module FormatAssertion
|
6
6
|
class Format < Keyword
|
7
|
-
extend JSONSchemer::Format
|
8
|
-
|
9
|
-
DEFAULT_FORMAT = proc do |instance, value|
|
10
|
-
!instance.is_a?(String) || valid_spec_format?(instance, value)
|
11
|
-
end
|
12
|
-
|
13
7
|
def error(formatted_instance_location:, **)
|
14
8
|
"value at #{formatted_instance_location} does not match format: #{value}"
|
15
9
|
end
|
16
10
|
|
17
11
|
def parse
|
18
|
-
root.format && root.
|
12
|
+
root.format && root.fetch_format(value) { raise UnknownFormat, value }
|
19
13
|
end
|
20
14
|
|
21
15
|
def validate(instance, instance_location, keyword_location, _context)
|
@@ -16,7 +16,9 @@ module JSONSchemer
|
|
16
16
|
'$defs' => Core::Defs,
|
17
17
|
'definitions' => Core::Defs,
|
18
18
|
# https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-01#section-8.3
|
19
|
-
'$comment' => Core::Comment
|
19
|
+
'$comment' => Core::Comment,
|
20
|
+
# https://github.com/orgs/json-schema-org/discussions/329
|
21
|
+
'x-error' => Core::XError
|
20
22
|
}
|
21
23
|
# https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-01#section-10
|
22
24
|
APPLICATOR = {
|
@@ -2,6 +2,12 @@
|
|
2
2
|
module JSONSchemer
|
3
3
|
module Draft4
|
4
4
|
BASE_URI = URI('http://json-schema.org/draft-04/schema#')
|
5
|
+
FORMATS = Draft6::FORMATS.dup
|
6
|
+
FORMATS.delete('uri-reference')
|
7
|
+
FORMATS.delete('uri-template')
|
8
|
+
FORMATS.delete('json-pointer')
|
9
|
+
CONTENT_ENCODINGS = Draft6::CONTENT_ENCODINGS
|
10
|
+
CONTENT_MEDIA_TYPES = Draft6::CONTENT_MEDIA_TYPES
|
5
11
|
SCHEMA = {
|
6
12
|
'id' => 'http://json-schema.org/draft-04/schema#',
|
7
13
|
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
@@ -2,6 +2,17 @@
|
|
2
2
|
module JSONSchemer
|
3
3
|
module Draft6
|
4
4
|
BASE_URI = URI('http://json-schema.org/draft-06/schema#')
|
5
|
+
FORMATS = Draft7::FORMATS.dup
|
6
|
+
FORMATS.delete('date')
|
7
|
+
FORMATS.delete('time')
|
8
|
+
FORMATS.delete('idn-email')
|
9
|
+
FORMATS.delete('idn-hostname')
|
10
|
+
FORMATS.delete('iri')
|
11
|
+
FORMATS.delete('iri-reference')
|
12
|
+
FORMATS.delete('relative-json-pointer')
|
13
|
+
FORMATS.delete('regex')
|
14
|
+
CONTENT_ENCODINGS = Draft7::CONTENT_ENCODINGS
|
15
|
+
CONTENT_MEDIA_TYPES = Draft7::CONTENT_MEDIA_TYPES
|
5
16
|
SCHEMA = {
|
6
17
|
'$schema' => 'http://json-schema.org/draft-06/schema#',
|
7
18
|
'$id' => 'http://json-schema.org/draft-06/schema#',
|
@@ -2,6 +2,11 @@
|
|
2
2
|
module JSONSchemer
|
3
3
|
module Draft7
|
4
4
|
BASE_URI = URI('http://json-schema.org/draft-07/schema#')
|
5
|
+
FORMATS = Draft201909::FORMATS.dup
|
6
|
+
FORMATS.delete('duration')
|
7
|
+
FORMATS.delete('uuid')
|
8
|
+
CONTENT_ENCODINGS = Draft201909::CONTENT_ENCODINGS
|
9
|
+
CONTENT_MEDIA_TYPES = Draft201909::CONTENT_MEDIA_TYPES
|
5
10
|
SCHEMA = {
|
6
11
|
'$schema' => 'http://json-schema.org/draft-07/schema#',
|
7
12
|
'$id' => 'http://json-schema.org/draft-07/schema#',
|