mirror of
https://github.com/ash-project/ash.git
synced 2024-09-20 13:33:20 +12:00
improvement: much more readable errors when building loads
This commit is contained in:
parent
d16b7057a3
commit
a479970e23
5 changed files with 45 additions and 40 deletions
|
@ -169,11 +169,11 @@ defmodule Ash.Error do
|
||||||
case stacktrace do
|
case stacktrace do
|
||||||
%Stacktrace{stacktrace: nil} ->
|
%Stacktrace{stacktrace: nil} ->
|
||||||
{:current_stacktrace, stacktrace} = Process.info(self(), :current_stacktrace)
|
{:current_stacktrace, stacktrace} = Process.info(self(), :current_stacktrace)
|
||||||
%Stacktrace{stacktrace: stacktrace}
|
%Stacktrace{stacktrace: Enum.drop(stacktrace, 2)}
|
||||||
|
|
||||||
nil ->
|
nil ->
|
||||||
{:current_stacktrace, stacktrace} = Process.info(self(), :current_stacktrace)
|
{:current_stacktrace, stacktrace} = Process.info(self(), :current_stacktrace)
|
||||||
%Stacktrace{stacktrace: stacktrace}
|
%Stacktrace{stacktrace: Enum.drop(stacktrace, 2)}
|
||||||
|
|
||||||
stacktrace ->
|
stacktrace ->
|
||||||
%Stacktrace{stacktrace: stacktrace}
|
%Stacktrace{stacktrace: stacktrace}
|
||||||
|
|
|
@ -37,7 +37,9 @@ defmodule Ash.Error.Exception do
|
||||||
case Process.info(self(), :current_stacktrace) do
|
case Process.info(self(), :current_stacktrace) do
|
||||||
{:current_stacktrace, stacktrace} ->
|
{:current_stacktrace, stacktrace} ->
|
||||||
super(
|
super(
|
||||||
Keyword.put_new(opts, :stacktrace, %Ash.Error.Stacktrace{stacktrace: stacktrace})
|
Keyword.put_new(opts, :stacktrace, %Ash.Error.Stacktrace{
|
||||||
|
stacktrace: Enum.drop(stacktrace, 2)
|
||||||
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
|
|
|
@ -12,7 +12,18 @@ defmodule Ash.Error.Load.InvalidQuery do
|
||||||
def class(_), do: :invalid
|
def class(_), do: :invalid
|
||||||
|
|
||||||
def message(%{query: query, load_path: load_path}) do
|
def message(%{query: query, load_path: load_path}) do
|
||||||
"Invalid query: #{inspect(query)} at #{Enum.join(load_path, ".")}"
|
errors_by_path =
|
||||||
|
query.errors
|
||||||
|
|> Enum.group_by(&List.wrap(&1.path))
|
||||||
|
|> Enum.sort_by(&Enum.count(elem(&1, 0)))
|
||||||
|
|> Enum.map(fn {path, error} ->
|
||||||
|
{List.wrap(load_path) ++ path, error}
|
||||||
|
end)
|
||||||
|
|
||||||
|
"Invalid query\n" <>
|
||||||
|
Enum.map_join(errors_by_path, "\n", fn {key, errors} ->
|
||||||
|
Enum.map_join(errors, "\n", &"* #{inspect(key)} - #{Exception.message(&1)}")
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def stacktrace(_), do: nil
|
def stacktrace(_), do: nil
|
||||||
|
|
|
@ -870,7 +870,7 @@ defmodule Ash.Query do
|
||||||
end
|
end
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
add_error(query, :load, "Invalid load #{inspect(field)}")
|
add_error(query, :load, Ash.Error.Query.InvalidLoad.exception(load: field))
|
||||||
end
|
end
|
||||||
|
|
||||||
field, query ->
|
field, query ->
|
||||||
|
@ -969,7 +969,7 @@ defmodule Ash.Query do
|
||||||
end
|
end
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
add_error(query, :load, "Could not load #{inspect(field)}")
|
add_error(query, :load, Ash.Error.Query.InvalidLoad.exception(load: field))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1577,7 +1577,7 @@ defmodule Ash.Query do
|
||||||
%{query | load: new_loads}
|
%{query | load: new_loads}
|
||||||
else
|
else
|
||||||
{:error, errors} ->
|
{:error, errors} ->
|
||||||
Enum.reduce(errors, query, &add_error(&2, :load, &1))
|
Enum.reduce(errors, query, &add_error(&2, &1))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1594,14 +1594,8 @@ defmodule Ash.Query do
|
||||||
[] ->
|
[] ->
|
||||||
[]
|
[]
|
||||||
|
|
||||||
_errors ->
|
errors ->
|
||||||
[
|
Enum.map(errors, &Ash.Error.set_path(&1, path))
|
||||||
{:error,
|
|
||||||
InvalidQuery.exception(
|
|
||||||
query: load_query,
|
|
||||||
load_path: Enum.reverse(path)
|
|
||||||
)}
|
|
||||||
]
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1610,6 +1604,8 @@ defmodule Ash.Query do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_validate_load(query, loads, path) when is_list(loads) do
|
defp do_validate_load(query, loads, path) when is_list(loads) do
|
||||||
|
query = to_query(query)
|
||||||
|
|
||||||
loads
|
loads
|
||||||
|> List.wrap()
|
|> List.wrap()
|
||||||
|> Enum.flat_map(fn
|
|> Enum.flat_map(fn
|
||||||
|
@ -1617,43 +1613,31 @@ defmodule Ash.Query do
|
||||||
case Ash.Resource.Info.relationship(query.resource, key) do
|
case Ash.Resource.Info.relationship(query.resource, key) do
|
||||||
nil ->
|
nil ->
|
||||||
[
|
[
|
||||||
{:error,
|
|
||||||
NoSuchRelationship.exception(
|
NoSuchRelationship.exception(
|
||||||
resource: query.resource,
|
resource: query.resource,
|
||||||
relationship: key,
|
relationship: key,
|
||||||
load_path: Enum.reverse(path)
|
load_path: Enum.reverse(path)
|
||||||
)}
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
relationship ->
|
relationship ->
|
||||||
cond do
|
cond do
|
||||||
!Ash.Resource.Info.primary_action(relationship.destination, :read) ->
|
!Ash.Resource.Info.primary_action(relationship.destination, :read) ->
|
||||||
[
|
[
|
||||||
{:error,
|
|
||||||
NoReadAction.exception(
|
NoReadAction.exception(
|
||||||
resource: relationship.destination,
|
resource: relationship.destination,
|
||||||
when: "loading relationship #{relationship.name}"
|
when: "loading relationship #{relationship.name}"
|
||||||
)}
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
relationship.type == :many_to_many &&
|
relationship.type == :many_to_many &&
|
||||||
!Ash.Resource.Info.primary_action(relationship.through, :read) ->
|
!Ash.Resource.Info.primary_action(relationship.through, :read) ->
|
||||||
[
|
[
|
||||||
{:error,
|
|
||||||
NoReadAction.exception(
|
NoReadAction.exception(
|
||||||
resource: relationship.destination,
|
resource: relationship.destination,
|
||||||
when: "loading relationship #{relationship.name}"
|
when: "loading relationship #{relationship.name}"
|
||||||
)}
|
|
||||||
]
|
|
||||||
|
|
||||||
match?(%Ash.Query{}, value) ->
|
|
||||||
validate_matching_query_and_continue(
|
|
||||||
value,
|
|
||||||
query.resource,
|
|
||||||
key,
|
|
||||||
path,
|
|
||||||
relationship
|
|
||||||
)
|
)
|
||||||
|
]
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
validate_matching_query_and_continue(
|
validate_matching_query_and_continue(
|
||||||
|
|
|
@ -527,6 +527,14 @@ defmodule Ash.Test.Actions.LoadTest do
|
||||||
assert Enum.sort([category1.id, category2.id]) == Enum.sort([id1, id2])
|
assert Enum.sort([category1.id, category2.id]) == Enum.sort([id1, id2])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it produces a nice error message on loading invalid loads" do
|
||||||
|
assert_raise Ash.Error.Invalid, ~r/:non_existent_thing is not a valid load/, fn ->
|
||||||
|
Post
|
||||||
|
|> Ash.Query.load(categories: [posts: :non_existent_thing])
|
||||||
|
|> Api.read!(authorize?: true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "it allows loading nested many to many relationships" do
|
test "it allows loading nested many to many relationships" do
|
||||||
category1 =
|
category1 =
|
||||||
Category
|
Category
|
||||||
|
|
Loading…
Reference in a new issue