json_schemer 2.0.0 → 2.1.1
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 +1 -1
- data/CHANGELOG.md +27 -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 +9 -3
- 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 +12 -0
- data/lib/json_schemer/openapi30/document.rb +1 -2
- data/lib/json_schemer/openapi30/meta.rb +6 -0
- data/lib/json_schemer/openapi31/document.rb +2 -4
- 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 +64 -31
- 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: 1981782f2342c7123beb0374680037bc11f3be72a8e4be0a213cfb7691643415
|
4
|
+
data.tar.gz: e8b6c605ba1ce2a2751ac90167f53c27fad975140ab9a0e8328c2b04ce8fe732
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d4c6e6c3660560a602b5610559efe37b5839f1142af6486e350bd207cad49e37a00f397d555878957b14951da95745092240bd14417e2ffd02826f053099952
|
7
|
+
data.tar.gz: 8dc561414463ceeacf7bbd0be95be9c441a50919cdb839ff8366fa0268d96d91e4947985bd7bab406f3c163e913bf7ca923a38e22d777d2549af3329f91c14f2
|
data/.github/workflows/ci.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,32 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
## [2.
|
3
|
+
## [2.1.1] - 2023-11-28
|
4
|
+
|
5
|
+
### Bug Fixes
|
6
|
+
|
7
|
+
- Fix refs to/through keyword objects: https://github.com/davishmcclurg/json_schemer/pull/160
|
8
|
+
- Temporary fix for incorrect `uri-reference` format in OpenAPI 3.x: https://github.com/davishmcclurg/json_schemer/pull/161
|
9
|
+
|
10
|
+
[2.1.1]: https://github.com/davishmcclurg/json_schemer/releases/tag/v2.1.1
|
11
|
+
|
12
|
+
## [2.1.0] - 2023-11-17
|
13
|
+
|
14
|
+
### Bug Fixes
|
15
|
+
|
16
|
+
- Limit anyOf/oneOf discriminator to listed refs: https://github.com/davishmcclurg/json_schemer/pull/145
|
17
|
+
- Require discriminator `propertyName` property: https://github.com/davishmcclurg/json_schemer/pull/145
|
18
|
+
- Support `Schema#ref` in subschemas: https://github.com/davishmcclurg/json_schemer/pull/145
|
19
|
+
- Resolve JSON pointer refs using correct base URI: https://github.com/davishmcclurg/json_schemer/pull/147
|
20
|
+
- `date` format in OpenAPI 3.0: https://github.com/davishmcclurg/json_schemer/commit/69fe7a815ecf0cfb1c40ac402bf46a789c05e972
|
21
|
+
|
22
|
+
### Features
|
23
|
+
|
24
|
+
- Custom error messages with `x-error` keyword and I18n: https://github.com/davishmcclurg/json_schemer/pull/149
|
25
|
+
- Custom content encodings and media types: https://github.com/davishmcclurg/json_schemer/pull/148
|
26
|
+
|
27
|
+
[2.1.0]: https://github.com/davishmcclurg/json_schemer/releases/tag/v2.1.0
|
28
|
+
|
29
|
+
## [2.0.0] - 2023-08-20
|
4
30
|
|
5
31
|
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
32
|
|
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.1)
|
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.1)
|
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)
|
@@ -130,7 +136,7 @@ module JSONSchemer
|
|
130
136
|
end
|
131
137
|
end
|
132
138
|
|
133
|
-
def
|
139
|
+
def fetch(token)
|
134
140
|
if value.is_a?(Hash)
|
135
141
|
parsed[token] ||= JSONSchemer::Schema::UNKNOWN_KEYWORD_CLASS.new(value.fetch(token), self, token, schema)
|
136
142
|
elsif value.is_a?(Array)
|
@@ -140,8 +146,8 @@ module JSONSchemer
|
|
140
146
|
end
|
141
147
|
end
|
142
148
|
|
143
|
-
def
|
144
|
-
@
|
149
|
+
def parsed_schema
|
150
|
+
@parsed_schema ||= subschema(value)
|
145
151
|
end
|
146
152
|
|
147
153
|
def validate(instance, instance_location, keyword_location, _context)
|
@@ -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#',
|