improvement: add in error paths for managed relationships

This commit is contained in:
Zach Daniel 2021-07-14 20:14:40 -04:00
parent 38a08bf2a6
commit 19199f5b00
7 changed files with 55 additions and 26 deletions

View file

@ -161,8 +161,7 @@ defmodule Ash.Actions.ManagedRelationships do
{:error, error} ->
{:halt,
{Ash.Changeset.add_error(changeset, error, [
opts[:meta][:id] || relationship.name,
index
opts[:meta][:id] || relationship.name
]), instructions}}
end
@ -238,7 +237,7 @@ defmodule Ash.Actions.ManagedRelationships do
primary_key: input,
resource: relationship.destination
),
[opts[:meta][:id], index]
[opts[:meta][:id] || relationship.name]
)
|> Ash.Changeset.put_context(:private, %{error: %{relationship.name => true}})
@ -251,7 +250,7 @@ defmodule Ash.Actions.ManagedRelationships do
relationship: relationship.name,
message: "Changes would create a new related record"
),
[opts[:meta][:id], index]
[opts[:meta][:id] || relationship.name]
)
|> Ash.Changeset.put_context(:private, %{error: %{relationship.name => true}})
@ -315,7 +314,8 @@ defmodule Ash.Actions.ManagedRelationships do
{:error, error} ->
{:halt,
{Ash.Changeset.add_error(changeset, error, [opts[:meta][:id], index]), instructions}}
{Ash.Changeset.add_error(changeset, error, [opts[:meta][:id] || relationship.name]),
instructions}}
end
end
@ -392,9 +392,10 @@ defmodule Ash.Actions.ManagedRelationships do
end
inputs
|> Enum.with_index()
|> Enum.reduce_while(
{:ok, [], [], []},
fn input, {:ok, current_value, all_notifications, all_used} ->
fn {input, input_index}, {:ok, current_value, all_notifications, all_used} ->
case handle_input(
record,
current_value,
@ -411,7 +412,9 @@ defmodule Ash.Actions.ManagedRelationships do
{:cont, {:ok, new_value, all_notifications ++ notifications, all_used ++ used}}
{:error, error} ->
{:halt, {:error, error}}
{:halt,
{:error,
Ash.Changeset.set_path(error, [opts[:meta][:id] || relationship.name, input_index])}}
end
end
)
@ -432,7 +435,7 @@ defmodule Ash.Actions.ManagedRelationships do
all_notifications ++ notifications}
{:error, error} ->
{:error, error}
{:error, Ash.Changeset.set_path(error, [opts[:meta][:id] || relationship.name])}
end
{:error, error} ->
@ -486,7 +489,8 @@ defmodule Ash.Actions.ManagedRelationships do
{:cont, {:ok, new_value, all_notifications ++ notifications, all_used ++ used}}
{:error, error} ->
{:halt, {:error, error}}
{:halt,
{:error, Ash.Changeset.set_path(error, [opts[:meta][:id] || relationship.name])}}
end
end
)
@ -507,11 +511,11 @@ defmodule Ash.Actions.ManagedRelationships do
all_notifications ++ notifications}
{:error, error} ->
{:error, error}
{:error, Ash.Changeset.set_path(error, [opts[:meta][:id] || relationship.name])}
end
{:error, error} ->
{:error, error}
{:error, Ash.Changeset.set_path(error, [opts[:meta][:id] || relationship.name])}
end
end

View file

@ -537,6 +537,7 @@ defmodule Ash.Changeset do
add_error(
changeset,
Ash.Error.Changes.Required.exception(
resource: changeset.resource,
field: argument.name,
type: :argument
)
@ -820,7 +821,11 @@ defmodule Ash.Changeset do
if is_nil(get_attribute(changeset, required_attribute)) do
add_error(
changeset,
Required.exception(field: required_attribute, type: :attribute)
Required.exception(
resource: changeset.resource,
field: required_attribute,
type: :attribute
)
)
else
changeset
@ -832,7 +837,11 @@ defmodule Ash.Changeset do
else
add_error(
changeset,
Required.exception(field: required_attribute.name, type: :attribute)
Required.exception(
resource: changeset.resource,
field: required_attribute.name,
type: :attribute
)
)
end
end
@ -847,7 +856,11 @@ defmodule Ash.Changeset do
if is_nil(get_attribute(changeset, required_attribute)) do
add_error(
changeset,
Required.exception(field: required_attribute, type: :attribute)
Required.exception(
resource: changeset.resource,
field: required_attribute,
type: :attribute
)
)
else
changeset
@ -857,7 +870,11 @@ defmodule Ash.Changeset do
if is_nil(get_attribute(changeset, required_attribute.name)) do
add_error(
changeset,
Required.exception(field: required_attribute.name, type: :attribute)
Required.exception(
resource: changeset.resource,
field: required_attribute.name,
type: :attribute
)
)
else
changeset
@ -2081,7 +2098,12 @@ defmodule Ash.Changeset do
|> handle_error(changeset)
end
defp set_path(error, path) do
@doc false
def set_path(errors, path) when is_list(errors) do
Enum.map(errors, &set_path(&1, path))
end
def set_path(error, path) do
error =
if Map.has_key?(error, :path) && is_list(error.path) do
%{error | path: path ++ error.path}
@ -2089,6 +2111,13 @@ defmodule Ash.Changeset do
error
end
error =
if Map.has_key?(error, :changeset) && error.changeset do
%{error | changeset: %{error.changeset | errors: set_path(error.changeset.errors, path)}}
else
error
end
if Map.has_key?(error, :errors) && is_list(error.errors) do
%{error | errors: Enum.map(error.errors, &set_path(&1, path))}
else

View file

@ -567,11 +567,9 @@ defmodule Ash.Engine do
Enum.reduce(errors, state, &add_error(&2, path, &1))
end
defp add_error(state, path, error) do
path = List.wrap(path)
defp add_error(state, _path, error) do
error = Ash.Error.to_ash_error(error)
%{state | errors: [Map.put(error, :path, path) | state.errors]}
%{state | errors: [error | state.errors]}
end
end

View file

@ -573,10 +573,8 @@ defmodule Ash.Engine.Runner do
Enum.reduce(errors, state, &add_error(&2, path, &1))
end
defp add_error(state, path, error) do
path = List.wrap(path)
defp add_error(state, _path, error) do
error = Ash.Error.to_ash_error(error)
error = Map.put(error, :path, path)
if error in state.errors do
state

View file

@ -2,7 +2,7 @@ defmodule Ash.Error.Changes.Required do
@moduledoc "Used when an attrbute or relationship is required"
use Ash.Error.Exception
def_ash_error([:field, :type], class: :invalid)
def_ash_error([:field, :type, :resource], class: :invalid)
defimpl Ash.ErrorKind do
def id(_), do: Ash.UUID.generate()

View file

@ -290,6 +290,7 @@ defmodule Ash.Query do
add_error(
query,
Required.exception(
resource: query.resource,
field: argument.name,
type: :argument
)

View file

@ -303,8 +303,7 @@ defmodule Ash.Type do
do: cast_input({:array, type}, [], constraints)
def cast_input({:array, _type}, term, _) when not is_list(term) do
{:error,
message: "must be a list or a map of integer indices (string encoded or not) to values"}
{:error, "is invalid"}
end
def cast_input({:array, type}, term, constraints) do