Packages
ash_phoenix
2.3.21
2.3.24
2.3.23
2.3.22
2.3.21
2.3.20
2.3.19
2.3.18
2.3.17
2.3.16
2.3.15
2.3.14
2.3.13
2.3.12
2.3.11
2.3.10
2.3.9
2.3.8
2.3.7
2.3.6
2.3.5
2.3.4
2.3.3
2.3.2
2.3.1
2.3.0
2.2.0
2.1.26
2.1.25
2.1.24
2.1.23
2.1.22
2.1.21
2.1.20
2.1.19
2.1.18
2.1.17
2.1.16
2.1.15
2.1.14
2.1.13
2.1.12
2.1.11
2.1.10
2.1.9
2.1.8
2.1.7
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
2.0.0-rc.8
2.0.0-rc.7
2.0.0-rc.6
2.0.0-rc.5
2.0.0-rc.4
2.0.0-rc.3
2.0.0-rc.2
2.0.0-rc.1
2.0.0-rc.0
1.3.7
1.3.6
1.3.5
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.2.26
1.2.25
1.2.24
1.2.23
1.2.22
1.2.21
1.2.20
1.2.19
1.2.18
1.2.17
1.2.16
1.2.15
1.2.14
1.2.13
1.2.12
1.2.11
1.2.10
1.2.9
1.2.8
1.2.7
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.1.2
1.1.1
1.1.0
1.1.0-rc.3
1.1.0-rc.2
1.1.0-rc.1
1.1.0-rc.0
1.0.0-rc.1
1.0.0-rc.0
1.0.0-pre.2
1.0.0-pre.1
1.0.0-pre.0
0.7.7
0.7.6-rc.0
0.7.5
0.7.4
0.7.3
0.7.2-rc.2
0.7.2-rc.1
0.7.2-rc.0
0.7.1
0.7.0
0.6.0-rc.7
0.6.0-rc.6
0.6.0-rc.5
0.6.0-rc.4
0.6.0-rc.3
0.6.0-rc.2
0.6.0-rc.1
0.6.0-rc.0
0.5.19-rc.2
0.5.19-rc.1
0.5.19-rc.0
0.5.18
0.5.17
0.5.16
0.5.15
0.5.14
0.5.13
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.24
0.4.23
0.4.23-rc.1
0.4.23-rc.0
0.4.22-rc2
0.4.22-rc1
0.4.21
0.4.20
0.4.19
0.4.18
0.4.17
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
Utilities for integrating Ash and Phoenix
Current section
Files
Jump to
Current section
Files
lib/ash_phoenix/form_data/helpers.ex
# SPDX-FileCopyrightText: 2020 ash_phoenix contributors <https://github.com/ash-project/ash_phoenix/graphs/contributors>
#
# SPDX-License-Identifier: MIT
defmodule AshPhoenix.FormData.Helpers do
require Logger
@moduledoc false
def get_argument(nil, _), do: nil
def get_argument(action, field) when is_atom(field) do
Enum.find(action.arguments, &(&1.name == field))
end
def get_argument(action, field) when is_binary(field) do
Enum.find(action.arguments, &(to_string(&1.name) == field))
end
def type_to_form_type(type) do
case Ash.Type.ecto_type(type) do
:integer -> :number_input
:boolean -> :checkbox
:date -> :date_select
:time -> :time_select
:utc_datetime -> :datetime_select
:naive_datetime -> :datetime_select
_ -> :text_input
end
end
def form_for_name(resource) do
resource
|> Module.split()
|> List.last()
|> Macro.underscore()
end
defp match_path_filter?(path, path_filter, form_keys \\ []) do
path == path_filter ||
(!Enum.empty?(form_keys) && Enum.count(path) > Enum.count(path_filter) &&
!form_would_capture?(form_keys, path, path_filter))
end
defp form_would_capture?(form_keys, path, path_filter) do
Enum.any?(form_keys, fn {key, config} ->
key = config[:for] || key
if config[:type] == :list do
List.starts_with?(path, path_filter ++ [key]) &&
is_integer(path |> Enum.drop(Enum.count(path_filter) + 1) |> Enum.at(0))
else
List.starts_with?(path, path_filter ++ [key])
end
end)
end
@doc false
def transform_errors(
form,
errors,
path_filter \\ nil,
form_keys \\ [],
post_process_errors \\ nil
) do
errors = if List.wrap(errors) == [], do: [], else: Ash.Error.to_error_class(errors).errors
additional_path_filters =
form_keys
|> Enum.filter(fn {_key, config} -> config[:type] == :list end)
|> Enum.map(fn {key, _config} ->
[key]
end)
errors
|> Enum.filter(fn error ->
if Map.has_key?(error, :path) && path_filter do
match? =
match_path_filter?(error.path, path_filter, form_keys) ||
Enum.any?(additional_path_filters, &match_path_filter?(error.path, &1))
match?
else
if form.warn_on_unhandled_errors? do
Logger.warning("""
Unhandled error in form submission for #{inspect(form.resource)}.#{form.action}
This error was unhandled because it did not have a `path` key.
#{Exception.format(:error, error)}
""")
nil
end
false
end
end)
|> Enum.map(fn error ->
if error.path in additional_path_filters do
error =
if Map.has_key?(error, :field) do
%{error | field: List.last(Enum.at(additional_path_filters, 0))}
else
error
end
if Map.has_key?(error, :fields) do
%{error | fields: [List.last(Enum.at(additional_path_filters, 0))]}
else
error
end
else
error
end
end)
|> Enum.flat_map(&transform_error(form, &1))
|> Enum.filter(fn
error when is_exception(error) ->
if AshPhoenix.FormData.Error.impl_for(error) do
true
else
if form.warn_on_unhandled_errors? do
Logger.warning("""
Unhandled error in form submission for #{inspect(form.resource)}.#{form.action}
This error was unhandled because #{inspect(error.__struct__)} does not implement the `AshPhoenix.FormData.Error` protocol.
#{Exception.format(:error, error)}
""")
nil
end
end
{_key, _value, _vars} ->
true
nil ->
false
error ->
if form.warn_on_unhandled_errors? do
Logger.warning("""
Unhandled error in form submission for #{form.resource}.#{form.action}
This error was unhandled because it was not an exception that implemented the `AshPhoenix.FormData.Error`
protocol, or a tuple in the form of {:field, "message", [replacement: :var]}.
#{inspect(error)}
""")
end
false
end)
|> Enum.flat_map(fn
exception when is_exception(exception) ->
exception
|> AshPhoenix.FormData.Error.to_form_error()
|> List.wrap()
|> Enum.map(fn {field, message, vars} ->
{field, {message, transform_vars(vars)}}
end)
{field, message, vars} ->
[{field, {message || "", transform_vars(vars)}}]
end)
|> Enum.uniq()
|> apply_post_process_errors(form, path_filter, post_process_errors)
end
defp apply_post_process_errors(errors, _form, _path_filter, nil), do: errors
defp apply_post_process_errors(errors, form, path_filter, post_process_errors) do
path = path_filter || []
errors
|> Enum.flat_map(fn {field, {message, vars}} ->
# Convert back to triple format for processing
case List.wrap(post_process_errors.(form, path, {field, message, vars})) do
[] ->
[]
errors ->
Enum.map(errors, fn {new_field, new_message, new_vars} ->
{new_field, {new_message || "", transform_vars(new_vars)}}
end)
end
end)
end
defp transform_vars(vars) do
vars
|> List.wrap()
|> Enum.flat_map(fn {key, value} ->
try do
if is_integer(value) do
[{key, value}]
else
[{key, to_string(value)}]
end
rescue
_ ->
[]
end
end)
# Drop known system added vars
|> Keyword.drop([:path, :index, :field, :message])
end
def transform_error(form, error) do
case form.transform_errors do
transformer when is_function(transformer, 2) ->
case transformer.(form.source, error) do
error when is_exception(error) ->
if AshPhoenix.FormData.Error.impl_for(error) do
List.wrap(to_form_error(error))
else
[]
end
{key, value, vars} ->
[{key, value, vars}]
list when is_list(list) ->
Enum.flat_map(list, fn
error when is_exception(error) ->
if AshPhoenix.FormData.Error.impl_for(error) do
List.wrap(to_form_error(error))
else
[]
end
{key, value, vars} ->
[{key, value, vars}]
end)
end
nil ->
List.wrap(error)
end
end
def transform_predicate_error(predicate, error, transform_errors) do
case transform_errors do
transformer when is_function(transformer, 2) ->
case transformer.(predicate, error) do
error when is_exception(error) ->
if AshPhoenix.FormData.Error.impl_for(error) do
List.wrap(to_form_error(error))
else
[]
end
{key, value, vars} ->
[{key, value, vars}]
list when is_list(list) ->
Enum.flat_map(list, fn
error when is_exception(error) ->
if AshPhoenix.FormData.Error.impl_for(error) do
List.wrap(to_form_error(error))
else
[]
end
{key, value, vars} ->
[{key, value, vars}]
end)
end
nil ->
case error do
{_key, _value, _vars} = error ->
[error]
error ->
if AshPhoenix.FormData.Error.impl_for(error) do
List.wrap(to_form_error(error))
else
[]
end
end
end
end
def transform_arguments_error(arguments, error, transform_errors) do
case transform_errors do
transformer when is_function(transformer, 2) ->
case transformer.(arguments, error) do
error when is_exception(error) ->
if AshPhoenix.FormData.Error.impl_for(error) do
List.wrap(to_form_error(error))
else
[]
end
{key, value, vars} ->
[{key, value, vars}]
list when is_list(list) ->
Enum.flat_map(list, fn
error when is_exception(error) ->
if AshPhoenix.FormData.Error.impl_for(error) do
List.wrap(to_form_error(error))
else
[]
end
{key, value, vars} ->
[{key, value, vars}]
end)
end
nil ->
case error do
{_key, _value, _vars} = error ->
[error]
error ->
if AshPhoenix.FormData.Error.impl_for(error) do
List.wrap(to_form_error(error))
else
[]
end
end
end
end
defp to_form_error(exception) when is_exception(exception) do
case AshPhoenix.FormData.Error.to_form_error(exception) do
nil ->
nil
{field, message} ->
{field, message, []}
{field, message, vars} ->
{field, message, vars}
list when is_list(list) ->
Enum.map(list, fn item ->
case item do
{field, message} ->
{field, message, []}
{field, message, vars} ->
{field, message, vars}
end
end)
end
end
end