Skip to content

Commit

Permalink
Specify formats in meta schemas
Browse files Browse the repository at this point in the history
And use the same code paths as custom formats.

This fixes an issue where older drafts have access to formats that
aren't in the specification.
  • Loading branch information
davishmcclurg committed Oct 14, 2023
1 parent 795a9b3 commit ccffac7
Show file tree
Hide file tree
Showing 13 changed files with 198 additions and 137 deletions.
28 changes: 9 additions & 19 deletions lib/json_schemer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ def draft202012
@draft202012 ||= Schema.new(
Draft202012::SCHEMA,
:base_uri => Draft202012::BASE_URI,
:formats => Draft202012::FORMATS,
:ref_resolver => Draft202012::Meta::SCHEMAS.to_proc,
:regexp_resolver => 'ecma'
)
Expand All @@ -154,6 +155,7 @@ def draft201909
@draft201909 ||= Schema.new(
Draft201909::SCHEMA,
:base_uri => Draft201909::BASE_URI,
:formats => Draft201909::FORMATS,
:ref_resolver => Draft201909::Meta::SCHEMAS.to_proc,
:regexp_resolver => 'ecma'
)
Expand All @@ -164,6 +166,7 @@ def draft7
Draft7::SCHEMA,
:vocabulary => { 'json-schemer://draft7' => true },
:base_uri => Draft7::BASE_URI,
:formats => Draft7::FORMATS,
:regexp_resolver => 'ecma'
)
end
Expand All @@ -173,6 +176,7 @@ def draft6
Draft6::SCHEMA,
:vocabulary => { 'json-schemer://draft6' => true },
:base_uri => Draft6::BASE_URI,
:formats => Draft6::FORMATS,
:regexp_resolver => 'ecma'
)
end
Expand All @@ -182,6 +186,7 @@ def draft4
Draft4::SCHEMA,
:vocabulary => { 'json-schemer://draft4' => true },
:base_uri => Draft4::BASE_URI,
:formats => Draft4::FORMATS,
:regexp_resolver => 'ecma'
)
end
Expand All @@ -190,16 +195,9 @@ def openapi31
@openapi31 ||= Schema.new(
OpenAPI31::SCHEMA,
:base_uri => OpenAPI31::BASE_URI,
:formats => OpenAPI31::FORMATS,
:ref_resolver => OpenAPI31::Meta::SCHEMAS.to_proc,
:regexp_resolver => 'ecma',
# https://spec.openapis.org/oas/latest.html#data-types
:formats => {
'int32' => proc { |instance, _value| instance.is_a?(Integer) && instance.bit_length <= 32 },
'int64' => proc { |instance, _value| instance.is_a?(Integer) && instance.bit_length <= 64 },
'float' => proc { |instance, _value| instance.is_a?(Float) },
'double' => proc { |instance, _value| instance.is_a?(Float) },
'password' => proc { |_instance, _value| true }
}
:regexp_resolver => 'ecma'
)
end

Expand All @@ -211,17 +209,9 @@ def openapi30
'json-schemer://openapi30' => true
},
:base_uri => OpenAPI30::BASE_URI,
:formats => OpenAPI30::FORMATS,
:ref_resolver => OpenAPI30::Meta::SCHEMAS.to_proc,
:regexp_resolver => 'ecma',
:formats => {
'int32' => proc { |instance, _value| instance.is_a?(Integer) && instance.bit_length <= 32 },
'int64' => proc { |instance, _value| instance.is_a?(Integer) && instance.bit_length <= 64 },
'float' => proc { |instance, _value| instance.is_a?(Float) },
'double' => proc { |instance, _value| instance.is_a?(Float) },
'byte' => proc { |instance, _value| Format.decode_content_encoding(instance, 'base64').first },
'binary' => proc { |instance, _value| instance.is_a?(String) && instance.encoding == Encoding::ASCII_8BIT },
'password' => proc { |_instance, _value| true }
}
:regexp_resolver => 'ecma'
)
end

Expand Down
1 change: 1 addition & 0 deletions lib/json_schemer/draft201909/meta.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
module JSONSchemer
module Draft201909
BASE_URI = URI('https://json-schema.org/draft/2019-09/schema')
FORMATS = Draft202012::FORMATS
SCHEMA = {
'$schema' => 'https://json-schema.org/draft/2019-09/schema',
'$id' => 'https://json-schema.org/draft/2019-09/schema',
Expand Down
21 changes: 21 additions & 0 deletions lib/json_schemer/draft202012/meta.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,27 @@
module JSONSchemer
module Draft202012
BASE_URI = URI('https://json-schema.org/draft/2020-12/schema')
FORMATS = {
'date-time' => Format::DATE_TIME,
'date' => Format::DATE,
'time' => Format::TIME,
'duration' => Format::DURATION,
'email' => Format::EMAIL,
'idn-email' => Format::IDN_EMAIL,
'hostname' => Format::HOSTNAME,
'idn-hostname' => Format::IDN_HOSTNAME,
'ipv4' => Format::IPV4,
'ipv6' => Format::IPV6,
'uri' => Format::URI,
'uri-reference' => Format::URI_REFERENCE,
'iri' => Format::IRI,
'iri-reference' => Format::IRI_REFERENCE,
'uuid' => Format::UUID,
'uri-template' => Format::URI_TEMPLATE,
'json-pointer' => Format::JSON_POINTER,
'relative-json-pointer' => Format::RELATIVE_JSON_POINTER,
'regex' => Format::REGEX
}
SCHEMA = {
'$schema' => 'https://json-schema.org/draft/2020-12/schema',
'$id' => 'https://json-schema.org/draft/2020-12/schema',
Expand Down
10 changes: 1 addition & 9 deletions lib/json_schemer/draft202012/vocab/format_annotation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,12 @@ module Draft202012
module Vocab
module FormatAnnotation
class Format < Keyword
extend JSONSchemer::Format

DEFAULT_FORMAT = proc do |instance, value|
!instance.is_a?(String) || valid_spec_format?(instance, value)
rescue UnknownFormat
true
end

def error(formatted_instance_location:, **)
"value at #{formatted_instance_location} does not match format: #{value}"
end

def parse
root.format && root.formats.fetch(value) { root.meta_schema.formats.fetch(value, DEFAULT_FORMAT) }
root.format && root.fetch_format(value, false)
end

def validate(instance, instance_location, keyword_location, _context)
Expand Down
8 changes: 1 addition & 7 deletions lib/json_schemer/draft202012/vocab/format_assertion.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,12 @@ module Draft202012
module Vocab
module FormatAssertion
class Format < Keyword
extend JSONSchemer::Format

DEFAULT_FORMAT = proc do |instance, value|
!instance.is_a?(String) || valid_spec_format?(instance, value)
end

def error(formatted_instance_location:, **)
"value at #{formatted_instance_location} does not match format: #{value}"
end

def parse
root.format && root.formats.fetch(value) { root.meta_schema.formats.fetch(value, DEFAULT_FORMAT) }
root.format && root.fetch_format(value) { raise UnknownFormat, value }
end

def validate(instance, instance_location, keyword_location, _context)
Expand Down
4 changes: 4 additions & 0 deletions lib/json_schemer/draft4/meta.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
module JSONSchemer
module Draft4
BASE_URI = URI('http://json-schema.org/draft-04/schema#')
FORMATS = Draft6::FORMATS.dup
FORMATS.delete('uri-reference')
FORMATS.delete('uri-template')
FORMATS.delete('json-pointer')
SCHEMA = {
'id' => 'http://json-schema.org/draft-04/schema#',
'$schema' => 'http://json-schema.org/draft-04/schema#',
Expand Down
9 changes: 9 additions & 0 deletions lib/json_schemer/draft6/meta.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
module JSONSchemer
module Draft6
BASE_URI = URI('http://json-schema.org/draft-06/schema#')
FORMATS = Draft7::FORMATS.dup
FORMATS.delete('date')
FORMATS.delete('time')
FORMATS.delete('idn-email')
FORMATS.delete('idn-hostname')
FORMATS.delete('iri')
FORMATS.delete('iri-reference')
FORMATS.delete('relative-json-pointer')
FORMATS.delete('regex')
SCHEMA = {
'$schema' => 'http://json-schema.org/draft-06/schema#',
'$id' => 'http://json-schema.org/draft-06/schema#',
Expand Down
3 changes: 3 additions & 0 deletions lib/json_schemer/draft7/meta.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
module JSONSchemer
module Draft7
BASE_URI = URI('http://json-schema.org/draft-07/schema#')
FORMATS = Draft201909::FORMATS.dup
FORMATS.delete('duration')
FORMATS.delete('uuid')
SCHEMA = {
'$schema' => 'http://json-schema.org/draft-07/schema#',
'$id' => 'http://json-schema.org/draft-07/schema#',
Expand Down
Loading

0 comments on commit ccffac7

Please sign in to comment.