Current section

Files

Jump to
manganese_core_kit lib structs api_error.ex
Raw

lib/structs/api_error.ex

defmodule Manganese.CoreKit.Structs.APIError do
@moduledoc """
Represents an error that occurred in the data layer of the application. All actions should either succeed or fail with an API error.
"""
alias Manganese.CoreKit.Structs
alias Manganese.CoreKit.Enumerations
@type t :: %Structs.APIError{
type: atom,
message: String.t | nil,
status: Enumerations.HttpStatusCode.t | nil
}
@enforce_keys [
:type,
:status
]
defstruct [
:type,
:message,
status: :internal_server_error
]
# Utilities
@doc """
"""
@spec from_changeset(Ecto.Changeset.t) :: t
def from_changeset(changeset) do
if changeset.valid? do
nil
else
Enum.map(
changeset.errors,
fn
{ field, { "has invalid format", _data }} ->
%Structs.APIError{
type: String.to_atom(Atom.to_string(field) <> "_invalid"),
status: :bad_request
}
{ field, { "is invalid", _data }} ->
%Structs.APIError{
type: String.to_atom(Atom.to_string(field) <> "_invalid"),
status: :bad_request
}
{ field, { "should be at least %{count} character(s)", _data }} ->
%Structs.APIError{
type: String.to_atom(Atom.to_string(field) <> "_too_short"),
status: :bad_request
}
{ field, { "should be at most %{count} character(s)", _data }} ->
%Structs.APIError{
type: String.to_atom(Atom.to_string(field) <> "_too_long"),
status: :bad_request
}
{ field, { "has already been taken", _data }} ->
%Structs.APIError{
type: String.to_atom(Atom.to_string(field) <> "_conflict"),
status: :conflict
}
{ _field, { _, api_error: api_error }} -> api_error
{ _field, error } ->
%Structs.APIError{
type: :error,
status: :internal_server_error
}
end
)
|> Enum.reduce(
nil,
fn changeset_api_error, prioritized_api_error -> Structs.APIError.prioritize changeset_api_error, prioritized_api_error end
)
end
end
@doc """
Given two API errors, return the one with the greater HTTP response status.
If the statuses are the same, `api_error1` will take precedence.
"""
@spec prioritize(t | nil, t | nil) :: t | nil
def prioritize(nil, nil), do: nil
def prioritize(nil, api_error2), do: api_error2
def prioritize(api_error1, nil), do: api_error1
def prioritize(api_error1, api_error2) do
status1 = Enumerations.HttpStatusCode.to_integer api_error1.status
status2 = Enumerations.HttpStatusCode.to_integer api_error2.status
cond do
status1 > status2 -> api_error1
status1 < status2 -> api_error2
true ->
api_error1
end
end
end