Current section

Files

Jump to
ex_teal lib ex_teal api error_serializer.ex
Raw

lib/ex_teal/api/error_serializer.ex

defmodule ExTeal.Api.ErrorSerializer do
@moduledoc """
Translates a changeset into an error
"""
alias Ecto.Changeset
alias ExTeal.Resource.Serializer
@doc """
Traverses and translates changeset errors.
See `Ecto.Changeset.traverse_errors/2`
"""
def translate_errors(%Changeset{} = changeset) do
Changeset.traverse_errors(changeset, &parse_changeset_error/1)
end
defp parse_changeset_error({msg, opts}) do
Enum.reduce(opts, msg, fn {key, value}, acc ->
String.replace(acc, "%{#{key}}", to_string(value))
end)
end
defp parse_changeset_error(msg), do: msg
def render(%Changeset{} = changeset) do
# When encoded, the changeset returns its errors
# as a JSON object. So we just pass it forward.
errors =
changeset
|> translate_errors()
|> flatten()
%{
errors: errors
}
end
def render(data) do
if Keyword.keyword?(data) do
data |> Enum.into(%{}) |> Jason.encode!()
else
Jason.encode(data)
end
end
def flatten(data) do
Enum.reduce(data, %{}, &flatten_element/2)
end
defp flatten_element({key, value}, acc) when is_map(value) do
Enum.reduce(value, acc, fn {subkey, subvalue}, acc ->
Map.put(acc, "#{key}.#{subkey}", subvalue)
end)
end
defp flatten_element({key, value}, acc) do
Map.put(acc, key, value)
end
def handle_error(conn, {:error, %Changeset{} = cs}) do
body = cs |> render() |> Jason.encode!()
Serializer.as_json(conn, body, 422)
end
def handle_error(conn, :not_authorized) do
body =
Jason.encode!(%{
id: "NOT_AUTHORIZED",
title: "Not Authorized",
status: 403
})
Serializer.as_json(conn, body, 403)
end
def handle_error(conn, _reason) do
body =
Jason.encode!(%{
id: "NOT_FOUND",
title: "Resource not found",
status: 404
})
Serializer.as_json(conn, body, 404)
end
end