mirror of
https://github.com/ash-project/ash_phoenix.git
synced 2024-09-19 06:42:47 +12:00
improvement: properly return nested errors with for_path: :all
This commit is contained in:
parent
bace1b1ec4
commit
c292848c57
3 changed files with 74 additions and 69 deletions
|
@ -1634,8 +1634,6 @@ defmodule AshPhoenix.Form do
|
|||
before_submit = opts[:before_submit] || (& &1)
|
||||
|
||||
if form.valid? || opts[:force?] do
|
||||
form = clear_errors(form)
|
||||
|
||||
unless form.api do
|
||||
raise """
|
||||
No Api configured, but one is required to submit the form.
|
||||
|
@ -1732,7 +1730,7 @@ defmodule AshPhoenix.Form do
|
|||
if opts[:raise?] do
|
||||
raise Ash.Error.to_error_class(query.errors, query: query)
|
||||
else
|
||||
query = %{(query || original_changeset_or_query) | errors: []}
|
||||
query = query || original_changeset_or_query
|
||||
|
||||
errors =
|
||||
error
|
||||
|
@ -1754,7 +1752,7 @@ defmodule AshPhoenix.Form do
|
|||
if opts[:raise?] do
|
||||
raise Ash.Error.to_error_class(changeset.errors, changeset: changeset)
|
||||
else
|
||||
changeset = %{(changeset || original_changeset_or_query) | errors: []}
|
||||
changeset = changeset || original_changeset_or_query
|
||||
|
||||
errors =
|
||||
error
|
||||
|
@ -2137,20 +2135,16 @@ defmodule AshPhoenix.Form do
|
|||
gather_errors(form, opts[:format])
|
||||
|
||||
[] ->
|
||||
errors =
|
||||
if form.errors do
|
||||
if form.just_submitted? do
|
||||
form.submit_errors
|
||||
else
|
||||
transform_errors(form, form.source.errors, [], form.form_keys)
|
||||
end
|
||||
else
|
||||
[]
|
||||
end
|
||||
if form.errors do
|
||||
errors =
|
||||
transform_errors(form, form.source.errors, [], form.form_keys)
|
||||
|
||||
errors
|
||||
|> List.wrap()
|
||||
|> format_errors(opts[:format])
|
||||
errors
|
||||
|> List.wrap()
|
||||
|> format_errors(opts[:format])
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
path ->
|
||||
form
|
||||
|
@ -2199,12 +2193,42 @@ defmodule AshPhoenix.Form do
|
|||
forms when is_list(forms) ->
|
||||
forms
|
||||
|> Enum.with_index()
|
||||
|> Enum.reduce(acc, fn {form, i}, acc ->
|
||||
gather_errors(form, format, acc, trail ++ [key, i])
|
||||
|> Enum.reduce(acc, fn {nested_form, i}, acc ->
|
||||
nested_errors =
|
||||
form.source.errors
|
||||
|> Enum.filter(&List.starts_with?(&1.path || [], [key, i]))
|
||||
|> Enum.map(fn form ->
|
||||
%{form | path: Enum.drop(form.path, 1)}
|
||||
end)
|
||||
|
||||
nested_form = %{
|
||||
nested_form
|
||||
| source: %{
|
||||
nested_form.source
|
||||
| errors: Enum.uniq(nested_errors ++ (nested_form.source.errors || []))
|
||||
}
|
||||
}
|
||||
|
||||
gather_errors(nested_form, format, acc, trail ++ [key, i])
|
||||
end)
|
||||
|
||||
form ->
|
||||
gather_errors(form, format, acc, trail ++ [key])
|
||||
nested_form ->
|
||||
nested_errors =
|
||||
form.source.errors
|
||||
|> Enum.filter(&List.starts_with?(&1.path || [], [key]))
|
||||
|> Enum.map(fn form ->
|
||||
%{form | path: Enum.drop(form.path, 1)}
|
||||
end)
|
||||
|
||||
nested_form = %{
|
||||
nested_form
|
||||
| source: %{
|
||||
nested_form.source
|
||||
| errors: Enum.uniq(nested_errors ++ (nested_form.source.errors || []))
|
||||
}
|
||||
}
|
||||
|
||||
gather_errors(nested_form, format, acc, trail ++ [key])
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
@ -3575,26 +3599,6 @@ defmodule AshPhoenix.Form do
|
|||
|
||||
defp expand_error(other), do: List.wrap(other)
|
||||
|
||||
defp clear_errors(nil), do: nil
|
||||
|
||||
defp clear_errors(forms) when is_list(forms) do
|
||||
Enum.map(forms, &clear_errors/1)
|
||||
end
|
||||
|
||||
defp clear_errors(form) do
|
||||
%{
|
||||
form
|
||||
| forms:
|
||||
Map.new(form.forms, fn {k, v} ->
|
||||
{k, clear_errors(v)}
|
||||
end),
|
||||
source: %{
|
||||
form.source
|
||||
| errors: []
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
A utility for parsing paths of nested forms in query encoded format.
|
||||
|
||||
|
@ -4619,6 +4623,13 @@ defmodule AshPhoenix.Form do
|
|||
end
|
||||
|
||||
@impl true
|
||||
@spec input_type(AshPhoenix.Form.t(), any(), atom() | binary()) ::
|
||||
:checkbox
|
||||
| :date_select
|
||||
| :datetime_select
|
||||
| :number_input
|
||||
| :text_input
|
||||
| :time_select
|
||||
def input_type(%{resource: resource, action: action}, _, field) do
|
||||
attribute = Ash.Resource.Info.attribute(resource, field)
|
||||
|
||||
|
|
|
@ -50,7 +50,24 @@ defmodule AshPhoenix.FormData.Helpers do
|
|||
end)
|
||||
end
|
||||
|
||||
defp unwrap_errors(errors) do
|
||||
Enum.flat_map(errors, &unwrap_error/1)
|
||||
end
|
||||
|
||||
defp unwrap_error(%class{errors: errors})
|
||||
when class in [
|
||||
Ash.Error.Invalid,
|
||||
Ash.Error.Forbidden,
|
||||
Ash.Error.Unknown,
|
||||
Ash.Error.Framework
|
||||
],
|
||||
do: unwrap_errors(errors)
|
||||
|
||||
defp unwrap_error(error), do: [error]
|
||||
|
||||
def transform_errors(form, errors, path_filter \\ nil, form_keys \\ []) do
|
||||
errors = unwrap_errors(errors)
|
||||
|
||||
additional_path_filters =
|
||||
form_keys
|
||||
|> Enum.filter(fn {_key, config} -> config[:type] == :list end)
|
||||
|
@ -110,7 +127,7 @@ defmodule AshPhoenix.FormData.Helpers do
|
|||
Logger.warning("""
|
||||
Unhandled error in form submission for #{inspect(form.resource)}.#{form.action}
|
||||
|
||||
This error was unhandled because it did not implement the `AshPhoenix.FormData.Error` protocol.
|
||||
This error was unhandled because #{inspect(error.__struct__)} does not implement the `AshPhoenix.FormData.Error` protocol.
|
||||
|
||||
#{Exception.format(:error, error)}
|
||||
""")
|
||||
|
|
|
@ -174,7 +174,6 @@ defmodule AshPhoenix.FormTest do
|
|||
|> Form.for_create(:create_author_required, api: Api, forms: [auto?: true])
|
||||
|> Form.validate(%{"list_of_ints" => %{"0" => %{"map" => "of stuff"}}})
|
||||
|
||||
# TODO: this might be wrong
|
||||
assert AshPhoenix.Form.value(form, :list_of_ints) == %{"0" => %{"map" => "of stuff"}}
|
||||
end
|
||||
end
|
||||
|
@ -795,33 +794,11 @@ defmodule AshPhoenix.FormTest do
|
|||
|> Form.validate(%{"embedded_argument" => %{"value" => "you@example.com"}})
|
||||
|> form_for("action")
|
||||
|
||||
[nested_form] = inputs_for(form, :embedded_argument)
|
||||
assert AshPhoenix.Form.errors(form, for_path: [:embedded_argument]) == [
|
||||
value: "must match email"
|
||||
]
|
||||
|
||||
# This is the top level error with a path to the nest form.
|
||||
assert [
|
||||
%Ash.Error.Changes.InvalidArgument{
|
||||
field: :value,
|
||||
message: "must match email",
|
||||
value: "you@example.com",
|
||||
path: [:embedded_argument],
|
||||
class: :invalid
|
||||
}
|
||||
] = form.source.source.errors
|
||||
|
||||
assert form.errors == []
|
||||
|
||||
# This is the error on the nested form.
|
||||
assert [
|
||||
%Ash.Error.Changes.InvalidArgument{
|
||||
field: :value,
|
||||
message: "must match email",
|
||||
value: "you@example.com",
|
||||
path: [],
|
||||
class: :invalid
|
||||
}
|
||||
] = nested_form.source.source.errors
|
||||
|
||||
assert nested_form.errors == [{:value, {"must match email", []}}]
|
||||
assert AshPhoenix.Form.errors(form) == []
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue