Skip to content

Commit

Permalink
version-up, bugfix
Browse files Browse the repository at this point in the history
  • Loading branch information
ityonemo committed Sep 16, 2023
1 parent 03cc3bc commit c1590d0
Show file tree
Hide file tree
Showing 10 changed files with 95 additions and 5 deletions.
7 changes: 6 additions & 1 deletion VERSIONS
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,9 @@
- deprecates `:content_type` option and renames it `:encoding`

# 1.1.1
- updates elixir versioning requirements
- updates elixir versioning requirements

# 1.1.2
- adds extra message when the reference pointer is not found
- fixes bug where references are not recursively cached when the entrypoint
isn't the root entrypoint
1 change: 1 addition & 0 deletions lib/exonerate.ex
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,7 @@ defmodule Exonerate do
if id = id_from(schema) do
resource = id
Cache.put_schema(caller.module, resource, schema)

resource
else
resource_uri
Expand Down
18 changes: 16 additions & 2 deletions lib/exonerate/cache.ex
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,11 @@ defmodule Exonerate.Cache do
put_schema(module, resource_uri, new_schema)

:ok
rescue
KeyError ->
raise CompileError,
description:
"reference points to: #{JsonPtr.to_uri(pointer)}, this location not found in the schema"
end

@spec has_schema?(module, resource_uri :: String.t()) :: boolean
Expand All @@ -178,6 +183,10 @@ defmodule Exonerate.Cache do
{tgt_resource, tgt_pointer}
end

defmatchspecp get_ref_pointers_ms(module, tgt_resource) do
{{:ref, {^module, ^tgt_resource, _}}, {^module, ^tgt_resource, tgt_pointer}} -> tgt_pointer
end

def register_ref(module, ref_resource, ref_pointer, tgt_resource, tgt_pointer) do
:ets.insert(
get_table(),
Expand All @@ -189,11 +198,17 @@ defmodule Exonerate.Cache do

def traverse_ref!(module, ref_resource, ref_pointer) do
case :ets.select(get_table(), get_ref_ms(module, ref_resource, ref_pointer)) do
[] -> raise "ref not found"
[] -> raise "ref at #{JsonPtr.to_uri(ref_pointer)} not found"
[ref] -> ref
end
end

def all_ref_pointers(module, ref_resource) do
get_table()
|> :ets.select(get_ref_pointers_ms(module, ref_resource))
|> MapSet.new()
end

# CONTEXTS

def register_context(module, call) when is_atom(call) do
Expand All @@ -212,7 +227,6 @@ defmodule Exonerate.Cache do
end
end

require MatchSpec
@all MatchSpec.fun2ms(fn any -> any end)
def dump do
:ets.select(get_table(), @all)
Expand Down
18 changes: 18 additions & 0 deletions lib/exonerate/schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,26 @@ defmodule Exonerate.Schema do
json_decoded
|> Degeneracy.canonicalize(opts)
|> tap(&Cache.put_schema(caller.module, resource, &1))
|> cache_assignments(caller, resource, opts)
end

defp cache_assignments(schema, caller, resource, opts, seen \\ MapSet.new()) do
schema
|> Id.prescan(caller.module, resource, opts)
|> ref_prescan(caller, resource, opts)

# recursively jump into references and go ahead run cache assignments on them.
caller.module
|> Cache.all_ref_pointers(resource)
|> Enum.reject(&(&1 in seen))
|> Enum.reduce(seen, fn pointer, seen_so_far ->
new_seen = MapSet.put(seen_so_far, pointer)
new_opts = Keyword.replace(opts, :entrypoint, JsonPtr.to_path(pointer))
cache_assignments(schema, caller, resource, new_opts, new_seen)
Cache.all_ref_pointers(caller.module, resource)
end)

schema
end

@doc """
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule Exonerate.MixProject do
def project do
[
app: :exonerate,
version: "1.1.1",
version: "1.1.2",
elixir: "~> 1.14",
start_permanent: Mix.env() == :prod,
deps: deps(),
Expand Down
15 changes: 15 additions & 0 deletions test/errors/ref_not_found_root.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
defmodule ExonerateTest.RefNotFoundRoot do
require Exonerate

Exonerate.function_from_string(
:defp,
:yaml,
"""
type: object
parameters:
foo:
$ref: "#/definitions/foo"
""",
encoding: "application/yaml"
)
end
13 changes: 13 additions & 0 deletions test/errors/ref_not_found_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
defmodule ExonerateTest.RefNotFoundTest do
use ExUnit.Case, async: true

test "if there's a missing ref, we get a CompileError" do

Check failure on line 4 in test/errors/ref_not_found_test.exs

View workflow job for this annotation

GitHub Actions / Build and test

test if there's a missing ref, we get a CompileError (ExonerateTest.RefNotFoundTest)
assert_raise CompileError,
"reference points to: #/definitions/foo, this location not found in the schema",
fn ->
__DIR__
|> Path.join("ref_not_found_root.exs")
|> Code.compile_file()
end
end
end
1 change: 0 additions & 1 deletion test/format_test.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
defmodule ExonerateTest.FormatTest do
use ExUnit.Case, async: true

require Exonerate

describe "builtin formats:" do
Expand Down
File renamed without changes.
25 changes: 25 additions & 0 deletions test/regression/nested_ref_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
defmodule ExonerateTest.Regression.NestedRefTest do
require Exonerate

# combining an entrypoint with a $ref fails.
Exonerate.function_from_string(
:def,
:ref_trace_entrypoint,
~S"""
schema:
type: object
properties:
bar:
$ref: '#/one'
one:
type: object
properties:
foo:
$ref: '#/two'
two:
type: string
""",
encoding: "application/yaml",
entrypoint: "/schema"
)
end

0 comments on commit c1590d0

Please sign in to comment.