Packages
ash_phoenix
2.3.24
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/inertia/error.ex
# SPDX-FileCopyrightText: 2020 ash_phoenix contributors <https://github.com/ash-project/ash_phoenix/graphs/contributors>
#
# SPDX-License-Identifier: MIT
if Code.ensure_loaded?(Inertia.Errors) do
defmodule AshPhoenix.Inertia.Error do
@moduledoc ~S"""
Provides a mapping from an Ash Error type to a plain map that can be used with the `Inertia.Controller.assign_errors/2` function.
Note this module is only available when the `:inertia` dependency is included in your application.
## Typical usage with Inertia
Inertia users will typically pass Ash errors directly into the `Inertia.Controller.assign_errors/2` function in their controllers.
Internally the Inertia library will use the `Inertia.Errors` protocol to transform the Ash Error to a plain map for JSON serialization.
```elixir
def create(conn, params) do
case MyApp.Posts.create(params, actor: conn.assigns.current_user) do
{:ok, post} ->
redirect(conn, to: ~p"/posts/#{post.slug}")
{:error, errors} ->
conn
|> assign_errors(errors)
|> render_inertia("CreatePost")
end
end
```
The `Inertia.Errors` protocol is implemented for common error types and the Ash error classes, such as `Ash.Error.Invalid` and `Ash.Error.Forbidden`.
If you have a situation where there is no protocol implementation for your error type, you may need to call `Ash.Error.to_error_class/1` on the error
first, before passing it to ` Inertia.Controller.assign_errors/2`, or providing an implementation of the `Inertia.Errors` protocol for the error type.
"""
@doc """
Converts an error, or list of errors to a map of error field to error message.
Nested field errors are flattened with the error path added as a dotted prefex, eg
```elixir
%{
"user.contact.email_address" => "Is required"
}
```
## Parameters
- `error_or_errors` (required) The error struct or list
- `message_func` (optional) A function to transform a tuple of message string and variables map to a single string.
## Examples
iex> AshPhoenix.Inertia.Error.to_errors(
iex> %Ash.Error.Action.InvalidArgument{
iex> path: [:customer, :contact],
iex> field: :email,
iex> message: "%{email} is already taken",
iex> vars: %{email: "acme@example.com"}
iex> }
iex>)
%{"customer.contact.email" => "acme@example.com is already taken"}
iex> AshPhoenix.Inertia.Error.to_errors(
iex> %Ash.Error.Invalid{
iex> errors: [
iex> %Ash.Error.Action.InvalidArgument{
iex> path: [:customer, :contact],
iex> field: :email,
iex> message: "%{email} is already taken",
iex> vars: %{email: "acme@example.com"}
iex> },
iex> %Ash.Error.Action.InvalidArgument{
iex> path: [:product],
iex> field: :sku,
iex> message: "%{product_name} is out of stock",
iex> vars: %{product_name: "acme powder"}
iex> }
iex> ]
iex> }
iex>)
%{
"customer.contact.email" => "acme@example.com is already taken",
"product.sku" => "acme powder is out of stock"
}
"""
@spec to_errors(error_or_errors :: term, message_func :: function) :: %{
String.t() => String.t()
}
def to_errors(error_or_errors, message_func \\ &default_message_func/1)
def to_errors(%AshPhoenix.Form{} = form, message_func) do
form
|> AshPhoenix.Form.raw_errors(for_path: :all)
|> Enum.flat_map(fn {path, errors} ->
Enum.map(errors, &Ash.Error.set_path(&1, path))
end)
|> to_errors(message_func)
end
def to_errors(error_or_errors, message_func) do
error_or_errors
|> Ash.Error.to_error_class()
|> Map.get(:errors)
|> Enum.flat_map(fn error ->
if AshPhoenix.FormData.Error.impl_for(error) do
error
|> AshPhoenix.FormData.Error.to_form_error()
|> List.wrap()
|> Enum.map(fn {field, message, vars} ->
{Enum.join(error.path ++ [field], "."), message_func.({message, vars})}
end)
else
[]
end
end)
|> Map.new()
end
@doc false
# Accepts a tuple for consistency with the Ecto.Changeset.traverse_errors convention.
def default_message_func({message, vars}) do
Enum.reduce(vars || [], message, fn
{key, %Regex{} = value}, acc ->
String.replace(acc, "%{#{key}}", Regex.source(value))
{key, value}, acc when is_list(value) ->
String.replace(acc, "%{#{key}}", Enum.join(value, ","))
{key, value}, acc ->
String.replace(acc, "%{#{key}}", to_string(value))
end)
end
end
Code.ensure_compiled!(AshPhoenix.Form)
# The list of types this protocol is implemented for was determined empirically based on
# usage with actions exposed through a code_interface. Additional impls may be required.
defimpl Inertia.Errors,
for: [
Ash.Error,
Ash.Error.Invalid,
Ash.Error.Unknown,
Ash.Error.Forbidden,
AshPhoenix.Form,
Ash.Changeset,
Ash.Query,
Ash.ActionInput
] do
def to_errors(value) do
value
|> AshPhoenix.Inertia.Error.to_errors()
end
def to_errors(value, message_func) do
value
|> AshPhoenix.Inertia.Error.to_errors(message_func)
end
end
end