Current section
Files
Jump to
Current section
Files
lib/retort/response/error/ecto/changeset.ex
defmodule Retort.Response.Error.Ecto.Changeset do
@moduledoc """
Extensions to `Ecto.Changeset` to allow `Retort.Response.Error.t` to be turned (back) into `Ecto.Changeset.t` errors.
"""
require Logger
import Calcinator.Resources, only: [attribute_to_field: 2]
# Functions
@doc """
Converts `alembic_error` to equivalent `Ecto.Changeset.t` validation error and adds it to `changesset`
"""
@spec add_alembic_error(Ecto.Changeset.t(), Alembic.Error.t()) :: Ecto.Changeset.t()
def add_alembic_error(
changeset = %Ecto.Changeset{data: %ecto_schema_module{}},
%Alembic.Error{
source: %Alembic.Source{
pointer: "/data/relationships/" <> relationship
},
title: title
}
) do
relationship
|> relationship_to_association(ecto_schema_module)
|> case do
{:ok, association} ->
Ecto.Changeset.add_error(changeset, association, title)
{:error, relationship} ->
Logger.error(fn ->
[
inspect(ecto_schema_module),
" does not have association corresponding to the ",
relationship,
" relationship"
]
end)
changeset
end
end
def add_alembic_error(
changeset = %Ecto.Changeset{data: %ecto_schema_module{}},
%Alembic.Error{
source: %Alembic.Source{
pointer: "/data/attributes/" <> attribute
},
title: title
}
) do
attribute
|> attribute_to_field(ecto_schema_module)
|> case do
{:ok, field} ->
Ecto.Changeset.add_error(changeset, field, title)
{:error, attribute} ->
Logger.error(fn ->
[inspect(ecto_schema_module), " does not have field corresponding to the ", attribute, " attribute"]
end)
changeset
end
end
def add_alembic_error(changeset, %Alembic.Error{title: "Invalid field value", detail: detail}) do
%{"key" => key, "value" => value} =
Regex.named_captures(
~r/(?<value>.+) is not a valid value for (?<key>.+)\./,
detail
)
Ecto.Changeset.add_error(changeset, String.to_existing_atom(key), "cannot be #{value}")
end
@doc """
Converts `alembic_errors` to equivalent `Ecto.Changeset.t` validation errors and adds them to `changeset`
"""
@spec add_alembic_errors(Ecto.Changeset.t(), [Alembic.Error.t()]) :: Ecto.Changeset.t()
def add_alembic_errors(changeset, alembic_errors) when is_list(alembic_errors) do
Enum.reduce(alembic_errors, changeset, &add_alembic_error(&2, &1))
end
## Private Functions
defp relationship_to_association(relationship, ecto_schema_module)
when is_binary(relationship) and is_atom(ecto_schema_module) do
association_string = JaSerializer.ParamParser.Utils.format_key(relationship)
for(
potential_association <- ecto_schema_module.__schema__(:associations),
potential_association_string = to_string(potential_association),
potential_association_string == association_string,
do: potential_association
)
|> case do
[association] ->
{:ok, association}
[] ->
{:error, relationship}
end
end
end