Skip to content

Commit f448d97

Browse files
committed
Update official test suite and fix ref handling edge cases
1 parent d9b72d4 commit f448d97

File tree

5 files changed

+26
-19
lines changed

5 files changed

+26
-19
lines changed

lib/ex_json_schema/schema.ex

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ defmodule ExJsonSchema.Schema do
2626
alias ExJsonSchema.Validator
2727
alias ExJsonSchema.Schema.Ref
2828

29-
@type ref_path :: [:root | String.t()]
30-
@type resolved :: ExJsonSchema.data() | %{String.t() => (Root.t() -> {Root.t(), resolved}) | ref_path} | true | false
29+
@type resolved :: ExJsonSchema.data()
3130
@type invalid_reference_error :: {:error, :invalid_reference}
3231

3332
@current_draft_schema_url "http://json-schema.org/schema"
@@ -60,7 +59,7 @@ defmodule ExJsonSchema.Schema do
6059
resolve(%Root{schema: schema}, options)
6160
end
6261

63-
@spec get_fragment(Root.t(), ref_path | ExJsonSchema.json_path()) ::
62+
@spec get_fragment(Root.t(), Ref.t() | ExJsonSchema.json_path()) ::
6463
{:ok, resolved} | invalid_reference_error | no_return
6564
def get_fragment(root = %Root{}, ref) when is_binary(ref) do
6665
get_fragment(root, Ref.from_string(ref, root))
@@ -77,15 +76,15 @@ defmodule ExJsonSchema.Schema do
7776
end
7877
end
7978

80-
@spec get_fragment!(Root.t(), ref_path | ExJsonSchema.json_path()) :: resolved | no_return
79+
@spec get_fragment!(Root.t(), Ref.t() | ExJsonSchema.json_path()) :: resolved | no_return
8180
def get_fragment!(root, ref) do
8281
case get_fragment(root, ref) do
8382
{:ok, schema} -> schema
8483
{:error, :invalid_reference} -> raise_invalid_reference_error(ref)
8584
end
8685
end
8786

88-
@spec get_ref_schema(Root.t(), [:root | String.t()]) :: ExJsonSchema.data() | no_return
87+
@spec get_ref_schema(Root.t(), Ref.t()) :: ExJsonSchema.data() | no_return
8988
def get_ref_schema(%Root{schema: schema}, %Ref{location: :root, fragment: fragment} = ref) do
9089
case get_ref_schema_with_schema(schema, fragment, ref) do
9190
{:error, error} ->
@@ -107,7 +106,7 @@ defmodule ExJsonSchema.Schema do
107106
end
108107

109108
@spec resolve_root(boolean | Root.t()) :: Root.t() | no_return
110-
defp resolve_root(%Root{schema: root_schema} = root) do
109+
defp resolve_root(%Root{schema: root_schema} = root, scope \\ "") do
111110
schema_version =
112111
root_schema
113112
|> Map.get("$schema", @current_draft_schema_url <> "#")
@@ -123,7 +122,7 @@ defmodule ExJsonSchema.Schema do
123122
end
124123

125124
root = %Root{root | version: schema_version}
126-
{root, schema} = resolve_with_root(root, root_schema)
125+
{root, schema} = resolve_with_root(root, root_schema, scope)
127126

128127
%Root{root | schema: schema}
129128
|> resolve_refs(schema)
@@ -186,8 +185,6 @@ defmodule ExJsonSchema.Schema do
186185
end
187186
end
188187

189-
defp resolve_with_root(root, schema, scope \\ "")
190-
191188
defp resolve_with_root(root, %{"$ref" => ref}, scope) when is_binary(ref) do
192189
do_resolve(root, %{"$ref" => ref}, scope)
193190
end
@@ -206,7 +203,7 @@ defmodule ExJsonSchema.Schema do
206203
defp resolve_with_id(root, schema, scope, id) do
207204
scope =
208205
case URI.parse(scope) do
209-
%URI{host: nil} -> id
206+
%URI{host: nil} = uri -> to_string(%URI{uri | fragment: nil}) <> id
210207
uri -> uri |> URI.merge(id) |> to_string()
211208
end
212209

@@ -268,7 +265,7 @@ defmodule ExJsonSchema.Schema do
268265

269266
defp resolve_remote_schema(root, url, remote_schema) do
270267
root = root_with_ref(root, url, remote_schema)
271-
%Root{schema: schema, refs: refs} = resolve_root(%Root{root | schema: remote_schema, location: url})
268+
%Root{schema: schema, refs: refs} = resolve_root(%Root{root | schema: remote_schema, location: url}, url)
272269

273270
%Root{root | refs: refs}
274271
|> root_with_ref(url, schema)

lib/ex_json_schema/schema/ref.ex

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
defmodule ExJsonSchema.Schema.Ref do
2-
defstruct location: nil, fragment: nil, fragment_pointer?: false
2+
defstruct location: nil, fragment: [], fragment_pointer?: false
3+
4+
@type t :: %ExJsonSchema.Schema.Ref{
5+
location: String.t() | nil,
6+
fragment: [String.t()],
7+
fragment_pointer?: boolean()
8+
}
39

410
alias ExJsonSchema.Schema.Root
511

lib/ex_json_schema/validator/format.ex

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ defmodule ExJsonSchema.Validator.Format do
1818
"hostname" => ~r/^[\pL0-9](?:[\pL0-9-]{0,61}[\pL0-9])?(?:\.[\pL0-9](?:[-0-9\pL]{0,61}[0-9\pL])?)*$/iu,
1919
"idn-email" => ~r<^[\w!#$%&'*+/=?`{|}~^-]+(?:\.[\w!#$%&'*+/=?`{|}~^-]+)*@(?:[\pL0-9-]+\.)+[\pL]{2,6}$>iu,
2020
"idn-hostname" => ~r/^[\pL0-9](?:[\pL0-9-]{0,61}[\pL0-9])?(?:\.[\pL0-9](?:[-0-9\pL]{0,61}[0-9\pL])?)*$/iu,
21-
"ipv4" => ~r/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,
21+
"ipv4" => ~r/^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/,
2222
"ipv6" =>
2323
~r/^(?:(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}|(?=(?:[A-F0-9]{0,4}:){0,7}[A-F0-9]{0,4}$)(([0-9A-F]{1,4}:){1,7}|:)((:[0-9A-F]{1,4}){1,7}|:)|(?:[A-F0-9]{1,4}:){7}:|:(:[A-F0-9]{1,4}){7})$/i,
2424
"iri-reference" =>
@@ -112,10 +112,12 @@ defmodule ExJsonSchema.Validator.Format do
112112
end
113113

114114
defp validate_with_custom_validator(validator, format, data) do
115-
result = case validator do
116-
{mod, fun} -> apply(mod, fun, [format, data])
117-
fun when is_function(fun, 2) -> fun.(format, data)
118-
end
115+
result =
116+
case validator do
117+
{mod, fun} -> apply(mod, fun, [format, data])
118+
fun when is_function(fun, 2) -> fun.(format, data)
119+
end
120+
119121
case result do
120122
true -> []
121123
false -> [%Error{error: %Error.Format{expected: format}}]

test/JSON-Schema-Test-Suite

test/json_schema_draft7_test_suite_test.exs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ defmodule ExJsonSchema.JsonSchemaDraft7TestSuiteTest do
1111
"optional/format/idn-hostname",
1212
# TODO: check this one
1313
"optional/format/ipv6",
14-
"optional/float-overflow"
14+
"optional/float-overflow",
15+
# TODO: remove this once we have 2019-09 support
16+
"optional/cross-draft"
1517
],
1618
ignored_tests: [
1719
"validation of IP addresses: leading zeroes should be rejected, as they are treated as octals",

0 commit comments

Comments
 (0)