Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add value and details to i18n available interpolations #211

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,42 @@ GEM
bigdecimal (3.1.9-java)
concurrent-ruby (1.3.5)
csv (3.3.2)
date (3.4.1)
debug (1.10.0)
irb (~> 1.10)
reline (>= 0.3.8)
docile (1.4.1)
hana (1.3.7)
i18n (1.14.6)
concurrent-ruby (~> 1.0)
i18n-debug (1.2.0)
i18n (< 2)
io-console (0.8.0)
irb (1.15.1)
pp (>= 0.6.0)
rdoc (>= 4.0.0)
reline (>= 0.4.2)
minitest (5.25.4)
pp (0.6.2)
prettyprint
prettyprint (0.2.0)
psych (5.2.3)
date
stringio
rake (13.2.1)
rdoc (6.11.0)
psych (>= 4.0.0)
regexp_parser (2.10.0)
reline (0.6.0)
io-console (~> 0.5)
simplecov (0.22.0)
docile (~> 1.1)
simplecov-html (~> 0.11)
simplecov_json_formatter (~> 0.1)
simplecov-html (0.13.1)
simplecov_json_formatter (0.1.4)
simpleidn (0.2.3)
stringio (3.1.2)

PLATFORMS
java
Expand All @@ -40,6 +60,7 @@ DEPENDENCIES
base64
bundler (~> 2.4.0)
csv
debug
i18n
i18n-debug
json_schemer!
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ JSONSchemer.schema(
# 'net/http': proc { |uri| JSON.parse(Net::HTTP.get(uri)) }
# default: proc { |uri| raise UnknownRef, uri.to_s }
ref_resolver: 'net/http',

# use different method to match regexes
# 'ruby'/'ecma'/proc/lambda/respond_to?(:call)
# 'ruby': proc { |pattern| Regexp.new(pattern) }
Expand Down Expand Up @@ -371,13 +371,15 @@ en:
'*': fallback error for schema and all keywords, nested under meta-schema $id ($schema)
'type': custom error for `type` keyword
'^': custom error for schema
# variable interpolation (instance/instanceLocation/keywordLocation/absoluteKeywordLocation)
# variable interpolation (instance/instanceLocation/keywordLocation/absoluteKeywordLocation/value/details)
'*': |
fallback error for schema and all keywords
instance: %{instance}
instance location: %{instanceLocation}
keyword location: %{keywordLocation}
absolute keyword location: %{absoluteKeywordLocation}
value: %{value} - the value of the keyword - for instance `integer` for `type`
details: %{details} - some errors report details
```

And output:
Expand Down
1 change: 1 addition & 0 deletions json_schemer.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Gem::Specification.new do |spec|
spec.add_development_dependency "csv"
spec.add_development_dependency "i18n"
spec.add_development_dependency "i18n-debug"
spec.add_development_dependency "debug"

spec.add_runtime_dependency "bigdecimal"
spec.add_runtime_dependency "hana", "~> 1.3"
Expand Down
23 changes: 20 additions & 3 deletions lib/json_schemer/result.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module JSONSchemer
I18N_SEPARATOR = "\x1F" # unit separator
I18N_SCOPE = 'json_schemer'
I18N_ERRORS_SCOPE = "#{I18N_SCOPE}#{I18N_SEPARATOR}errors"
X_ERROR_REGEX = /%\{(instance|instanceLocation|keywordLocation|absoluteKeywordLocation)\}/
X_ERROR_REGEX = /%\{(instance|instanceLocation|keywordLocation|absoluteKeywordLocation|value|details)\}/
CLASSIC_ERROR_TYPES = Hash.new do |hash, klass|
hash[klass] = klass.name.rpartition('::').last.sub(/\A[[:alpha:]]/, &:downcase)
end
Expand Down Expand Up @@ -36,7 +36,9 @@ def error
'%{instance}' => instance,
'%{instanceLocation}' => Location.resolve(instance_location),
'%{keywordLocation}' => Location.resolve(keyword_location),
'%{absoluteKeywordLocation}' => source.absolute_keyword_location
'%{absoluteKeywordLocation}' => source.absolute_keyword_location,
'%{value}' => source.value,
'%{details}' => details,
)
@x_error = true
else
Expand Down Expand Up @@ -81,7 +83,9 @@ def i18n!
:instance => instance,
:instanceLocation => Location.resolve(instance_location),
:keywordLocation => resolved_keyword_location,
:absoluteKeywordLocation => source.absolute_keyword_location
:absoluteKeywordLocation => source.absolute_keyword_location,
:value => source.value,
:details => details_string
)
end

Expand Down Expand Up @@ -229,6 +233,19 @@ def insert_property_defaults(context)

private

def details_string
return nil unless details

details.map do |key, value|
if value.respond_to?(:join)
"#{key}: #{value.join(', ')}"
else
"#{key}: #{value}"
end
end.join(', ')
end


def default_keyword_instance(schema)
schema.parsed.fetch('default') do
schema.parsed.find do |_keyword, keyword_instance|
Expand Down
50 changes: 44 additions & 6 deletions test/errors_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,14 @@ def test_x_error
},
{
'x-error' => {
'not' => '%{instance} `%{instanceLocation}` %{keywordLocation} %{absoluteKeywordLocation}'
'not' => <<~ERR
instance: %{instance}
instanceLocation: %{instanceLocation}
keywordLocation: %{keywordLocation}
absoluteKeywordLocation: %{absoluteKeywordLocation}
value: %{value}
details: %{details}
ERR
},
'required' => ['b'],
'not' => { 'required' => ['a'] }
Expand All @@ -26,9 +33,17 @@ def test_x_error
'a' => 'foo',
'b' => 'bar'
}
error1, error2 = JSONSchemer.schema(schema).validate(data).map { |error| error.fetch('error') }.sort
error1, error2 = JSONSchemer.schema(schema).validate(data).map { |error| error.fetch('error') }

assert_equal('properties a and b were provided, however only one or the other may be specified', error1)
assert_match(optional_space_regexp('{"a"', '=>', '"foo", "b"', '=>', '"bar"} `` /oneOf/1/not json-schemer://schema#/oneOf/1/not'), error2)
assert_equal(<<~ERR.gsub(/\s/,''), error2.gsub(/\s/,''))
instance: {"a" => "foo", "b" => "bar"}
instanceLocation:
keywordLocation: /oneOf/1/not
absoluteKeywordLocation: json-schemer://schema#/oneOf/1/not
value: {"required" => ["a"]}
details:
ERR

assert_equal('schema error', JSONSchemer.schema(schema).validate(data, :output_format => 'basic').fetch('error'))
assert_equal('oneOf error', JSONSchemer.schema(schema).validate(data, :output_format => 'detailed').fetch('error'))
Expand Down Expand Up @@ -125,6 +140,28 @@ def test_x_error_precedence
end
end

def test_i18n_details_as_hash
schema = {
'properties' => {
'yah' => {
'type' => 'string'
}
},
'required' => ['yah']
}

schemer = JSONSchemer.schema(schema)
data = {}

errors = {
'#/required' => 'missing %{details}',
}

i18n(errors) do
assert_equal('missing missing_keys: yah', schemer.validate(data, :output_format => 'detailed').fetch('error'))
end
end

def test_i18n_error
schema = {
'$id' => 'https://example.com/schema',
Expand All @@ -133,7 +170,8 @@ def test_i18n_error
'yah' => {
'type' => 'string'
}
}
},
'required' => ['yah']
}
schemer = JSONSchemer.schema(schema)
data = { 'yah' => 1 }
Expand All @@ -152,7 +190,7 @@ def test_i18n_error
'#' => 'C',
'https://json-schema.org/draft/2019-09/schema' => {
'^' => 'F',
'type' => '6',
'type' => '6 %{value}',
'*' => 'G/7'
},
'^' => 'H',
Expand Down Expand Up @@ -200,7 +238,7 @@ def test_i18n_error
errors.fetch('https://example.com/schema').delete('*')
i18n(errors) do
assert_equal('F', schemer.validate(data, :output_format => 'basic').fetch('error'))
assert_equal('6', schemer.validate(data).first.fetch('error'))
assert_equal('6 string', schemer.validate(data).first.fetch('error'))
end

errors.fetch('https://json-schema.org/draft/2019-09/schema').delete('^')
Expand Down