Current section
Files
Jump to
Current section
Files
lib/graph_api/middleware/error_handling.ex
defmodule GraphApi.Middleware.ErrorHandling do
@moduledoc """
Req response step that parses non-2xx responses into structured error types.
"""
alias GraphApi.Error.{ApiError, RateLimitError}
@doc """
Attaches the error handling step to a Req request.
"""
@spec attach(Req.Request.t()) :: Req.Request.t()
def attach(%Req.Request{} = request) do
Req.Request.append_response_steps(request, microsoft_graph_errors: &handle_errors/1)
end
defp handle_errors({request, %Req.Response{status: status} = response})
when status >= 200 and status < 300 do
{request, response}
end
defp handle_errors({request, %Req.Response{status: 429} = response}) do
retry_after = parse_retry_after(response)
error = %RateLimitError{
retry_after: retry_after,
message: "Rate limited by Microsoft Graph API"
}
{request, %{response | body: {:error, error}}}
end
defp handle_errors({request, %Req.Response{status: status, body: body} = response}) do
{code, message} = extract_error_info(body)
error = %ApiError{
status: status,
code: code,
message: message || "HTTP #{status}",
details: body
}
{request, %{response | body: {:error, error}}}
end
defp extract_error_info(%{"error" => %{"code" => code, "message" => message}}) do
{code, message}
end
defp extract_error_info(%{"error" => %{"code" => code}}) do
{code, nil}
end
defp extract_error_info(_body), do: {nil, nil}
defp parse_retry_after(%Req.Response{} = response) do
case Req.Response.get_header(response, "retry-after") do
[value | _] ->
case Integer.parse(value) do
{seconds, _} -> seconds
:error -> nil
end
_ ->
nil
end
end
end