Packages

Core utilities for LushrLux

Current section

Files

Jump to
lushrlux lib errors.ex
Raw

lib/errors.ex

defmodule LushrLux.Core.Errors do
import Ecto.Changeset
@moduledoc """
To make all the endpoints in a file more readable some of the error formatting has been moved here.
"""
@doc """
This might not be the most elegant solution but it works for what it needs to do. Returning a status code and
an error message which is done dynamically.
* It takes the changeset from ecto when it fails. It taverse it to find the error section.
* From that error section it finds the fields.
* From the same error section it finds the message.
As from my experience ecto changesets only happen as one _event_ i.e. you're either going to get say a conflict
error on a `unique_constrait` or you're going to be missing required fields. It then looks at the error message
and pattern matches on the error message and the appropriate http status code for that error is returned along
with the generated message based on the fields that were either say, conflicting, or missing.
"""
def read_errors(changeset) do
decoded = changeset |> decode_errors()
fields = error_keys(decoded)
msg = error_messages(decoded)
case msg do
"can't be blank" -> {400, "Missing fields #{fields}"}
"has already been taken" -> {409, "That #{fields} already exists"}
end
end
defp decode_errors(changeset) do
traverse_errors(changeset, fn {msg, opts} ->
Enum.reduce(opts, msg, fn {key, value}, acc ->
String.replace(acc, "%{#{key}}", to_string(value))
end)
end)
end
defp error_keys(decoded_errors) do
decoded_errors
|> Map.keys()
|> Enum.map(&Atom.to_string/1)
|> Enum.map(fn m -> String.replace(m, "_", " ") end)
|> Enum.join(", ")
end
defp error_messages(decoded_errors) do
decoded_errors
|> Map.values()
|> List.flatten()
|> List.first()
end
end