Packages
ash
1.52.0-rc.3
3.29.3
3.29.2
3.29.1
3.29.0
3.28.0
3.27.8
3.27.7
3.27.6
3.27.5
3.27.4
3.27.3
3.27.2
3.27.1
3.27.0
3.26.0
3.25.2
3.25.1
3.25.0
retired
3.24.7
3.24.6
3.24.5
3.24.4
3.24.3
3.24.2
3.24.1
3.24.0
3.23.1
3.23.0
3.22.2
3.22.1
3.22.0
3.21.3
3.21.2
3.21.1
3.21.0
3.20.0
3.19.3
3.19.2
3.19.1
3.19.0
3.18.0
3.17.1
3.17.0
3.16.0
3.15.0
3.14.1
3.14.0
retired
3.13.2
3.13.1
3.13.0
3.12.0
3.11.3
3.11.2
3.11.1
3.11.0
3.10.1
3.10.0
3.9.0
3.8.0
3.7.6
3.7.5
3.7.4
3.7.3
3.7.2
3.7.1
3.7.0
retired
3.6.3
retired
3.6.2
3.6.1
3.6.0
3.5.43
3.5.42
3.5.41
3.5.40
3.5.39
3.5.38
3.5.37
3.5.36
3.5.35
3.5.34
3.5.33
3.5.32
3.5.31
3.5.30
3.5.29
3.5.28
3.5.27
3.5.26
3.5.25
3.5.24
3.5.23
3.5.22
3.5.21
3.5.20
3.5.19
3.5.18
3.5.17
3.5.16
3.5.15
3.5.14
3.5.13
3.5.12
3.5.11
3.5.10
3.5.9
3.5.8
3.5.7
3.5.6
3.5.5
3.5.4
3.5.3
3.5.2
3.5.1
3.5.0
3.4.74
retired
3.4.73
3.4.72
3.4.71
3.4.70
3.4.69
3.4.68
3.4.67
3.4.66
3.4.65
3.4.64
3.4.63
3.4.62
3.4.61
3.4.60
3.4.59
3.4.58
3.4.57
3.4.56
3.4.55
3.4.54
3.4.53
3.4.52
3.4.51
3.4.50
3.4.49
3.4.48
3.4.47
3.4.46
3.4.45
3.4.44
3.4.43
3.4.42
3.4.41
3.4.40
3.4.39
3.4.38
3.4.37
3.4.36
3.4.35
3.4.34
3.4.33
3.4.32
3.4.31
3.4.30
3.4.29
3.4.28
3.4.27
3.4.26
3.4.25
3.4.24
3.4.23
3.4.22
3.4.21
3.4.20
3.4.19
3.4.18
3.4.17
3.4.16
3.4.15
3.4.14
3.4.13
3.4.12
3.4.11
3.4.10
3.4.9
3.4.8
3.4.7
3.4.6
3.4.5
3.4.4
3.4.3
3.4.2
3.4.1
3.4.0
3.3.3
3.3.2
3.3.1
3.3.0
3.2.6
3.2.5
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.1.8
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.16
3.0.15
3.0.14
3.0.13
3.0.12
3.0.11
3.0.10
3.0.9
3.0.8
3.0.7
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
3.0.0-rc.46
3.0.0-rc.45
3.0.0-rc.44
3.0.0-rc.43
3.0.0-rc.42
3.0.0-rc.41
3.0.0-rc.40
3.0.0-rc.39
3.0.0-rc.38
3.0.0-rc.37
3.0.0-rc.36
3.0.0-rc.35
3.0.0-rc.34
3.0.0-rc.33
3.0.0-rc.32
3.0.0-rc.31
3.0.0-rc.29
3.0.0-rc.28
3.0.0-rc.27
3.0.0-rc.26
3.0.0-rc.25
3.0.0-rc.24
3.0.0-rc.23
3.0.0-rc.22
3.0.0-rc.21
3.0.0-rc.20
3.0.0-rc.19
3.0.0-rc.18
3.0.0-rc.17
3.0.0-rc.16
3.0.0-rc.15
3.0.0-rc.14
3.0.0-rc.13
3.0.0-rc.12
3.0.0-rc.11
3.0.0-rc.10
3.0.0-rc.9
3.0.0-rc.8
3.0.0-rc.7
3.0.0-rc.6
3.0.0-rc.5
3.0.0-rc.4
3.0.0-rc.3
3.0.0-rc.1
3.0.0-rc.0
2.21.15
2.21.14
2.21.13
2.21.12
2.21.11
2.21.10
2.21.9
2.21.8
2.21.7
2.21.6
2.21.5
2.21.4
2.21.3
2.21.2
2.21.1
2.21.0
2.20.3
2.20.2
2.20.1
2.20.0
2.19.14
2.19.13
2.19.12
2.19.11
2.19.10
2.19.9
2.19.8
2.19.7
2.19.6
2.19.5
2.19.4
2.19.3
retired
2.19.2
retired
2.19.1
retired
2.19.0
retired
2.18.2
2.18.1
2.18.0
2.17.24
2.17.23
2.17.22
2.17.21
2.17.20
2.17.19
2.17.18
2.17.17
2.17.16
2.17.15
2.17.14
2.17.13
2.17.12
2.17.11
2.17.10
2.17.9
2.17.8
2.17.7
2.17.6
2.17.5
2.17.4
2.17.3
2.17.2
2.17.1
2.17.0
2.16.1
2.16.0
2.15.20
2.15.19
2.15.18
2.15.17
2.15.16
2.15.15
2.15.14
2.15.13
2.15.12
2.15.11
2.15.10
2.15.9
2.15.8
2.15.7
2.15.6
2.15.5
2.15.4
2.15.2
2.15.1
2.15.0
2.14.21
2.14.20
2.14.19
2.14.18
2.14.17
2.14.16
2.14.15
2.14.14
2.14.13
2.14.12
2.14.11
2.14.10
2.14.9
2.14.8
2.14.7
2.14.6
2.14.5
2.14.4
2.14.3
2.14.2
2.14.1
2.14.0
2.13.4
retired
2.13.3
2.13.2
2.13.1
2.13.0
2.12.1
2.12.0
2.11.11
2.11.10
2.11.9
2.11.8
2.11.7
2.11.6
2.11.5
2.11.4
2.11.3
2.11.2
2.11.1
2.11.0
2.11.0-rc.3
2.11.0-rc.2
2.11.0-rc.1
2.11.0-rc.0
2.10.2
2.10.1
2.10.0
2.9.29
2.9.28
2.9.27
2.9.26
2.9.25
2.9.24
2.9.23
2.9.22
2.9.21
2.9.20
2.9.19
2.9.18
2.9.17
2.9.16
2.9.15
2.9.14
2.9.13
2.9.12
2.9.11
2.9.10
2.9.9
2.9.8
2.9.7
2.9.6
2.9.5
2.9.4
2.9.3
2.9.2
2.9.1
2.9.0
2.8.1
2.8.0
2.7.1
2.7.0
2.6.31
2.6.30
2.6.29
2.6.28
2.6.27
2.6.26
2.6.25
2.6.24
2.6.23
2.6.22
2.6.21
2.6.20
2.6.19
2.6.18
2.6.17
2.6.16
2.6.15
2.6.14
2.6.13
2.6.11
2.6.10
2.6.9
2.6.8
2.6.7
2.6.6
2.6.5
2.6.4
2.6.3
2.6.2
2.6.1
2.6.0
2.5.16
2.5.15
2.5.14
2.5.13
2.5.12
2.5.11
2.5.10
2.5.9
2.5.8
2.5.7
2.5.6
2.5.5
2.5.4
2.5.3
2.5.2
2.5.1
2.5.0
2.5.0-rc.6
2.5.0-rc.5
2.5.0-rc.4
2.5.0-rc.3
2.5.0-rc.2
2.5.0-rc.1
2.5.0-rc.0
2.4.30
2.4.29
2.4.28
2.4.27
2.4.26
2.4.25
2.4.24
2.4.23
2.4.22
2.4.21
2.4.20
2.4.19
2.4.18
2.4.17
2.4.16
2.4.15
2.4.14
2.4.13
2.4.12
2.4.11
2.4.10
2.4.9
2.4.8
2.4.7
2.4.6
2.4.5
2.4.4
2.4.3
2.4.2
2.4.1
2.4.0
2.3.0
2.2.0
2.1.0
2.0.0
2.0.0-rc.15
2.0.0-rc.14
2.0.0-rc.13
2.0.0-rc.12
2.0.0-rc.11
2.0.0-rc.10
2.0.0-rc.9
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
2.0.0-pre.8
2.0.0-pre.7
2.0.0-pre.6
2.0.0-pre.5
2.0.0-pre.4
2.0.0-pre.3
2.0.0-pre.2
2.0.0-pre.1
2.0.0-pre.0
1.53.3
1.53.2
1.53.0
1.52.0-rc.22
1.52.0-rc.21
1.52.0-rc.20
1.52.0-rc.19
1.52.0-rc.18
1.52.0-rc.17
1.52.0-rc.16
1.52.0-rc.15
1.52.0-rc.14
1.52.0-rc.13
1.52.0-rc.12
1.52.0-rc.11
1.52.0-rc.10
1.52.0-rc.9
1.52.0-rc.8
1.52.0-rc.7
1.52.0-rc.6
1.52.0-rc.5
1.52.0-rc.4
1.52.0-rc.3
1.52.0-rc.2
1.52.0-rc.1
1.52.0-rc.0
1.51.2
1.51.1
retired
1.51.0
1.50.21
1.50.20
1.50.19
1.50.18
1.50.17
1.50.16
1.50.15
1.50.14
1.50.13
1.50.12
1.50.11
1.50.10
1.50.9
1.50.8
1.50.7
1.50.6
1.50.5
1.50.4
1.50.3
1.50.2
1.50.1
1.50.0
1.49.0
1.48.0-rc.30
1.48.0-rc.29
1.48.0-rc.28
1.48.0-rc.27
1.48.0-rc.26
1.48.0-rc.25
1.48.0-rc.24
1.48.0-rc.23
1.48.0-rc.22
1.48.0-rc.21
1.48.0-rc.20
1.48.0-rc.19
1.48.0-rc.18
1.48.0-rc.17
1.48.0-rc.16
1.48.0-rc.15
1.48.0-rc.14
1.48.0-rc.13
1.48.0-rc.12
1.48.0-rc.11
1.48.0-rc.10
1.48.0-rc.9
1.48.0-rc.8
1.48.0-rc.7
1.48.0-rc.6
1.48.0-rc.5
1.48.0-rc.4
1.48.0-rc.3
1.48.0-rc.2
1.48.0-rc.1
1.48.0-rc.0
1.47.12
1.47.11
1.47.10
1.47.9
1.47.8
1.47.7
1.47.6
1.47.5
1.47.4
1.47.3
1.47.2
1.47.1
1.47.0
1.46.13
1.46.12
1.46.11
1.46.10
1.46.9
1.46.8
1.46.7
1.46.6
1.46.5
1.46.4
1.46.3
1.46.2
1.46.1
1.46.0
1.45.0-rc9
1.45.0-rc8
1.45.0-rc7
1.45.0-rc6
1.45.0-rc5
1.45.0-rc4
1.45.0-rc3
1.45.0-rc20
1.45.0-rc2
1.45.0-rc19
1.45.0-rc18
1.45.0-rc17
1.45.0-rc16
1.45.0-rc15
1.45.0-rc14
1.45.0-rc13
1.45.0-rc12
1.45.0-rc11
1.45.0-rc10
1.45.0-rc1
1.45.0-rc0
1.44.13
1.44.12
1.44.11
1.44.10
1.44.9
1.44.8
1.44.7
1.44.6
1.44.5
1.44.4
1.44.3
1.44.2
1.44.1
1.44.0
1.43.12
1.43.11
1.43.10
1.43.9
1.43.8
1.43.7
1.43.6
1.43.5
1.43.4
1.43.3
1.43.2
1.43.1
1.43.0
1.42.0
1.41.12
1.41.11
1.41.10
1.41.9
1.41.8
1.41.7
1.41.6
1.41.5
1.41.4
1.41.3
1.41.2
1.41.1
1.41.0
1.40.0
1.39.7
1.39.6
1.39.5
1.39.4
1.39.3
1.39.2
1.39.1
1.39.0
1.38.0
1.37.2
1.37.1
1.37.0
1.36.22
1.36.21
1.36.19
1.36.18
1.36.17
1.36.16
1.36.15
1.36.14
1.36.13
1.36.12
1.36.11
1.36.10
1.36.9
1.36.8
1.36.7
1.36.6
1.36.5
1.36.4
1.36.3
1.36.2
1.36.0
1.35.1
1.35.0
1.34.9
1.34.8
1.34.7
1.34.6
1.34.5
1.34.4
1.34.3
1.34.2
1.34.1
1.34.0
1.33.0
1.32.2
1.32.1
1.32.0
1.31.1
1.31.0
1.30.2
1.30.1
1.29.0-rc1
1.29.0-rc0
1.28.1
1.28.0
1.27.1
1.27.0
1.26.13
1.26.12
1.26.11
1.26.10
1.26.9
1.26.8
1.26.7
1.26.6
1.26.5
1.26.4
1.26.2
1.26.1
1.26.0
1.25.8
1.25.7
1.25.6
1.25.5
1.25.4
1.25.3
1.25.2
1.25.1
1.25.0
1.24.2
1.24.1
1.24.0
1.23.3
1.23.2
1.23.1
1.23.0
1.22.1
1.22.0
1.20.1
1.20.0
1.19.1
1.19.0
1.18.1
1.18.0
1.17.1
1.17.0
1.16.2
1.15.1
1.15.0
1.14.0
1.13.4
1.13.3
1.13.2
1.13.1
1.13.0
1.12.0
1.11.1
1.11.0
1.10.0
1.9.0
1.8.0
1.7.0
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.4.1
1.4.0
1.3.1
1.3.0
1.2.1
1.2.0
1.1.3
1.1.2
1.1.0
1.0.3
1.0.2
1.0.1
1.0.0
0.13.1
0.13.0
0.12.0
0.10.0
0.9.1
0.9.0
0.8.0
0.7.0
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.0
0.3.0
0.2.0
0.1.9
0.1.8
0.1.3
0.1.1
0.1.0
A declarative, extensible framework for building Elixir applications.
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/ash/error/error.ex
defmodule Ash.Error do
@moduledoc """
Tools and utilities used by Ash to manage and conform errors
"""
alias Ash.Error.{Forbidden, Framework, Invalid, Unknown}
@type error_class() :: :invalid | :authorization | :framework | :unknown
@type t :: struct
# We use these error classes also to choose a single error
# to raise when multiple errors have occured. We raise them
# sorted by their error classes
@error_classes [
:forbidden,
:invalid,
:framework,
:unknown
]
@error_modules [
forbidden: Forbidden,
invalid: Invalid,
framework: Framework,
unknown: Unknown
]
@error_class_indices @error_classes |> Enum.with_index() |> Enum.into(%{})
@doc false
def error_modules, do: Keyword.values(@error_modules)
defmodule Stacktrace do
@moduledoc "A placeholder for a stacktrace so that we can avoid printing it everywhere"
defstruct [:stacktrace]
defimpl Inspect do
def inspect(_, _) do
"#Stacktrace<>"
end
end
end
@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) when is_map(error) do
path = List.wrap(path)
error =
if Map.has_key?(error, :path) && is_list(error.path) do
%{error | path: path ++ error.path}
else
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
error
end
end
def set_path(error, _), do: error
def ash_error?(value) do
!!Ash.ErrorKind.impl_for(value)
end
def to_error_class(values, opts \\ [])
def to_error_class(values, opts) when is_list(values) do
values =
values
|> flatten_preserving_keywords()
|> Enum.uniq_by(&clear_stacktraces/1)
|> Enum.map(fn value ->
if ash_error?(value) do
value
else
Unknown.exception(errors: values)
end
end)
|> Enum.uniq()
choose_error(values, opts[:changeset] || opts[:query])
end
def to_error_class(value, opts) do
if ash_error?(value) && value.__struct__ in Keyword.values(@error_modules) do
add_changeset_or_query(value, [value], opts[:changeset] || opts[:query])
else
to_error_class([value], opts)
end
end
def to_ash_error(list, stacktrace \\ nil)
def to_ash_error(list, stacktrace) when is_list(list) do
if Keyword.keyword?(list) do
error =
list
|> Keyword.take([:error, :vars])
|> Keyword.put_new(:error, list[:message])
|> Unknown.exception()
if stacktrace do
%{error | stacktrace: stacktrace}
else
stacktrace
end
else
Enum.map(list, &to_ash_error(&1, stacktrace))
end
end
def to_ash_error(error, stacktrace) when is_binary(error) do
if stacktrace do
error = Unknown.exception(error: error)
%{error | stacktrace: %Stacktrace{stacktrace: stacktrace}}
else
Unknown.exception(error: error)
end
end
def to_ash_error(other, stacktrace) do
cond do
ash_error?(other) ->
if stacktrace do
Map.put(other, :stacktrace, %Stacktrace{stacktrace: stacktrace})
else
other
end
is_exception(other) ->
if stacktrace do
other = Unknown.exception(error: Exception.format(:error, other))
Map.put(other, :stacktrace, %Stacktrace{stacktrace: stacktrace})
else
Unknown.exception(error: Exception.format(:error, other))
end
true ->
other = Unknown.exception(error: "unknown error: #{inspect(other)}")
if stacktrace do
Map.put(other, :stacktrace, %Stacktrace{stacktrace: stacktrace})
else
other
end
end
end
@doc "A utility to flatten a list, but preserve keyword list elements"
def flatten_preserving_keywords(list) do
if Keyword.keyword?(list) do
[list]
else
Enum.flat_map(list, fn item ->
cond do
Keyword.keyword?(item) ->
[item]
is_list(item) ->
item
true ->
[item]
end
end)
end
end
def clear_stacktraces(%{stacktrace: stacktrace} = error) when not is_nil(stacktrace) do
clear_stacktraces(%{error | stacktrace: nil})
end
def clear_stacktraces(%{errors: errors} = exception) when is_list(errors) do
%{exception | errors: Enum.map(errors, &clear_stacktraces/1)}
end
def clear_stacktraces(error), do: error
def choose_error(errors, changeset_or_query \\ nil)
def choose_error([], changeset_or_query) do
error = Ash.Error.Unknown.exception([])
add_changeset_or_query(error, [], changeset_or_query)
end
def choose_error(errors, changeset_or_query) do
errors = Enum.map(errors, &to_ash_error/1)
[error | other_errors] =
Enum.sort_by(errors, fn error ->
# the second element here sorts errors that are already parent errors
{Map.get(@error_class_indices, error.class),
@error_modules[error.class] != error.__struct__}
end)
parent_error_module = @error_modules[error.class]
top_level_error =
if parent_error_module == error.__struct__ do
%{error | errors: (error.errors || []) ++ other_errors}
else
parent_error_module.exception(errors: errors)
end
add_changeset_or_query(top_level_error, errors, changeset_or_query)
end
defp add_changeset_or_query(error, errors, changeset_or_query) do
changeset = error.changeset || error.query || changeset_or_query
if changeset_or_query do
changeset_or_query = %{
changeset_or_query
| action_failed?: true,
errors: List.wrap(errors) ++ changeset.errors
}
case changeset_or_query do
%Ash.Changeset{} = changeset ->
%{error | changeset: %{changeset | errors: Enum.uniq(changeset.errors)}}
%Ash.Query{} = query ->
%{error | query: %{query | errors: Enum.uniq(query.errors)}}
end
else
error
end
end
def error_messages(errors, custom_message, stacktraces?) do
errors = Enum.map(errors, &to_ash_error/1)
generic_message =
errors
|> List.wrap()
|> Enum.group_by(& &1.class)
|> Enum.sort_by(fn {group, _} -> Map.get(@error_class_indices, group) end)
|> Enum.map_join("\n\n", fn {class, class_errors} ->
header = header(class) <> "\n\n"
if stacktraces? do
header <>
Enum.map_join(class_errors, "\n", fn
error when is_binary(error) ->
"* #{error}"
%{stacktrace: %Stacktrace{stacktrace: stacktrace}} = class_error ->
"* #{Exception.message(class_error)}\n" <>
path(class_error) <>
Enum.map_join(stacktrace, "\n", fn stack_item ->
" " <> Exception.format_stacktrace_entry(stack_item)
end)
end)
else
header <>
Enum.map_join(class_errors, "\n", fn
class_error when is_binary(class_error) ->
"* #{class_error}"
class_error ->
"* #{Exception.message(class_error)}"
end)
end
end)
if custom_message do
custom =
custom_message
|> List.wrap()
|> Enum.map_join("\n", &"* #{&1}")
"\n\n" <> custom <> generic_message
else
generic_message
end
end
def error_descriptions(errors) do
errors
|> Kernel.||([])
|> Enum.group_by(& &1.class)
|> Enum.sort_by(fn {group, _} -> Map.get(@error_class_indices, group) end)
|> Enum.map_join("\n\n", fn {class, class_errors} ->
header = header(class) <> "\n\n"
header <> Enum.map_join(class_errors, "\n", &"* #{Ash.ErrorKind.message(&1)}")
end)
end
defp path(%{path: path}) when path not in [[], nil] do
" at " <> to_path(path) <> "\n"
end
defp path(_), do: ""
defp to_path(path) do
Enum.map_join(path, ", ", fn item ->
if is_list(item) do
"[#{to_path(item)}]"
else
if is_binary(item) || is_atom(item) || is_number(item) do
item
else
inspect(item)
end
end
end)
end
defp header(:invalid), do: "Input Invalid"
defp header(:forbidden), do: "Forbidden"
defp header(:framework), do: "Framework Error"
defp header(:unknown), do: "Unknown Error"
end