defmodule Kevo.ApiError do
@moduledoc """
Represents a failed response from Kevo's HTTP/2 API.
"""
defexception [
# :network_error, :unexpected_status, :unexpected_body
:reason,
# map of request parameters
:request,
# response received
:response,
# some expected value, status or header
:expected,
# root error given
:caused_by,
# interstitial step if applicable
:step
]
@type t :: %Kevo.ApiError{
reason: :network_error | :unexpected_status | :unexpected_body,
request: map(),
response: any(),
expected: integer() | list({String.t(), String.t()}),
caused_by: any(),
step: {function :: String.t(), arity :: integer()}
}
# network error
def message(%__MODULE__{reason: :network_error} = err) do
step_prefix(err.step) <> "network error: #{inspect(err.caused_by)}"
end
# unexpected status
def message(%__MODULE__{reason: :unexpected_status} = err) do
{:response, _, status, _} = err.response
step_prefix(err.step) <> "expected response status #{err.expected}, got: #{status}"
end
# unexpected body
def message(%__MODULE__{reason: :unexpected_body} = err) do
step_prefix(err.step) <> "invalid json response: #{Jason.DecodeError.message(err.caused_by)}"
end
# missing header
def message(%__MODULE__{reason: :missing_header} = err) do
{:response, _, _, headers} = err.response
headers = Enum.map_join(headers, ", ", fn {header, _} -> header end)
step_prefix(err.step) <> "expected header `#{err.expected}` not present in: [#{headers}]"
end
def from_network(request, error, step \\ nil) do
{:error,
%__MODULE__{
reason: :network_error,
request: request,
caused_by: error,
step: step
}}
end
def from_body(request, %Jason.DecodeError{} = error, step \\ nil) do
{:error,
%__MODULE__{
reason: :unexpected_body,
request: request,
caused_by: error,
step: step
}}
end
def from_status(request, response, expected, step \\ nil) do
{:error,
%__MODULE__{
reason: :unexpected_status,
request: request,
response: response,
expected: expected,
step: step
}}
end
def from_headers(request, response, expected, step \\ nil) do
{:error,
%__MODULE__{
reason: :missing_header,
request: request,
response: response,
expected: expected,
step: step
}}
end
defp step_prefix(nil), do: ""
defp step_prefix({name, arity}), do: "#{name}/#{arity}: "
end