ML-162: enable /api/v1/errors endpoint

This commit is contained in:
Claudio Ortolina
2026-05-04 13:14:40 +01:00
parent 213ca31a42
commit 51bd24d5b4
8 changed files with 611 additions and 15 deletions
@@ -0,0 +1,65 @@
defmodule MusicLibraryWeb.ErrorController do
use MusicLibraryWeb, :controller
alias MusicLibrary.Errors
def index(conn, params) do
status = parse_status(params["status"])
muted = parse_muted(params["muted"])
search = params["search"]
limit = parse_int(params["limit"], 50)
offset = parse_int(params["offset"], 0)
opts =
[]
|> maybe_put(:status, status)
|> maybe_put(:muted, muted)
|> Keyword.put(:search, search)
|> Keyword.put(:limit, limit)
|> Keyword.put(:offset, offset)
%{errors: errors, total: total} = Errors.list_errors(opts)
render(conn, :index, errors: errors, total: total, limit: limit, offset: offset)
end
def show(conn, %{"id" => id}) do
{id_int, ""} = Integer.parse(id)
case Errors.get_error(id_int) do
{:ok, error} ->
render(conn, :show, error: error)
{:error, :not_found} ->
conn
|> put_status(:not_found)
|> json(%{error: "Not Found"})
end
end
# -- private helpers --
defp parse_status(nil), do: nil
defp parse_status("resolved"), do: :resolved
defp parse_status("unresolved"), do: :unresolved
defp parse_status(_), do: nil
defp parse_muted(nil), do: nil
defp parse_muted("true"), do: true
defp parse_muted("false"), do: false
defp parse_muted(_), do: nil
defp parse_int(nil, default), do: default
defp parse_int(value, default) when is_binary(value) do
case Integer.parse(value) do
{int, ""} -> int
_ -> default
end
end
defp parse_int(_, default), do: default
defp maybe_put(kw, _key, nil), do: kw
defp maybe_put(kw, key, value), do: Keyword.put(kw, key, value)
end
+77 -13
View File
@@ -1,21 +1,85 @@
defmodule MusicLibraryWeb.ErrorJSON do
@moduledoc """
This module is invoked by your endpoint in case of errors on JSON requests.
JSON rendering for production errors via the API and for Phoenix error responses.
See config/config.exs.
This module serves dual purpose:
- Renders error/occurrence data for the /api/v1/errors endpoints
- Renders generic error responses (404, 500, etc.) for the Phoenix endpoint
when the request accepts JSON (configured in config/config.exs render_errors)
"""
use MusicLibraryWeb, :json
# If you want to customize a particular status code,
# you may add your own clauses, such as:
#
# def render("500.json", _assigns) do
# %{errors: %{detail: "Internal Server Error"}}
# end
# By default, Phoenix returns the status message from
# the template name. For example, "404.json" becomes
# "Not Found".
# Catch-all for Phoenix error template rendering (404, 500, etc.)
def render(template, _assigns) do
%{errors: %{detail: Phoenix.Controller.status_message_from_template(template)}}
%{error: Phoenix.Controller.status_message_from_template(template)}
end
def index(%{errors: errors, total: total, limit: limit, offset: offset}) do
%{
errors: Enum.map(errors, &error/1),
total: total,
limit: limit,
offset: offset
}
end
def show(%{error: error}) do
%{
error: error_with_occurrences(error)
}
end
defp error(e) do
%{
id: e.id,
kind: e.kind,
reason: e.reason,
source_line: e.source_line,
source_function: e.source_function,
status: atom_to_string(e.status),
fingerprint: e.fingerprint,
last_occurrence_at: datetime_to_iso8601(e.last_occurrence_at),
muted: e.muted,
inserted_at: datetime_to_iso8601(e.inserted_at),
updated_at: datetime_to_iso8601(e.updated_at)
}
end
defp error_with_occurrences(e) do
error(e)
|> Map.put(:occurrence_count, Map.get(e, :occurrence_count, 0))
|> Map.put(:first_occurrence_at, datetime_to_iso8601(Map.get(e, :first_occurrence_at)))
|> Map.put(:occurrences, Enum.map(Map.get(e, :occurrences, []), &occurrence/1))
end
defp occurrence(o) do
%{
id: o.id,
reason: o.reason,
context: o.context,
breadcrumbs: o.breadcrumbs,
stacktrace: %{
lines: Enum.map(o.stacktrace.lines, &stacktrace_line/1)
},
error_id: o.error_id,
inserted_at: datetime_to_iso8601(o.inserted_at)
}
end
defp stacktrace_line(line) do
%{
application: line.application,
module: line.module,
function: line.function,
arity: line.arity,
file: line.file,
line: line.line
}
end
defp datetime_to_iso8601(nil), do: nil
defp datetime_to_iso8601(dt), do: DateTime.to_iso8601(dt)
defp atom_to_string(nil), do: nil
defp atom_to_string(atom), do: Atom.to_string(atom)
end
+2
View File
@@ -130,6 +130,8 @@ defmodule MusicLibraryWeb.Router do
get "/collection/random", CollectionController, :random
get "/collection/on_this_day", CollectionController, :on_this_day
get "/collection", CollectionController, :index
get "/errors", ErrorController, :index
get "/errors/:id", ErrorController, :show
get "/assets/:transform_payload", AssetController, :show
get "/backup", ArchiveController, :backup
end