defmodule GitHub.Error do
@moduledoc """
Exception struct used for communicating errors from the client
> #### Note {:.info}
>
> Functions in this module is unlikely to be used directly by applications. Instead, they are
> useful for plugins. See `GitHub.Plugin` for more information.
This error covers errors generated by the client (for example, HTTP connection errors) as well as
errors returned from the GitHub API (for example, Not Found errors).
## Fields
* `code` (integer): Status code of the API response, if a response was received.
* `message` (string): Human-readable message describing the error. Defaults to a generic
`"Unknown Error"`.
* `operation` (`t:Operation.t/0`): Operation at the time of the error.
* `reason` (atom): Easily-matched atom for common errors, like `:not_found`. Defaults to a
generic `:error`. See **Error Reasons** below for a list of possible values.
* `source` (term): Cause of the error. This could be an operation, an API error response, or
something else.
* `stacktrace` (`t:Exception.stacktrace/0`): Stacktrace from the time of the error. Defaults
to a stack that terminates with the calling function, but can be overridden (for example,
if a `__STACKTRACE__` is available in a `rescue` block).
* `step` (plugin): Plugin active at the time of the error (expressed as a tuple containing the
module and function).
Users of the library can match on the information in the `code` and `source` fields to extract
additional information.
## Error Reasons
Although plugins may use any atom for the `reason` field, the following have predetermined
meanings:
* `:invalid_auth`: The credential (`:auth` option) provided is invalid.
* `:invalid_version`: The version (`:version` option) provided is invalid.
* `:not_found`: A resource or route was not found. Note that GitHub may return this kind of
response when authentication is required to see a resource.
* `:oauth_restricted`: The OAuth credentials are valid, but the requested resource is owned
by an organization that requires admin approval for OAuth apps.
* `:rate_limited`: The client has exceeded a primary or secondary rate limit. Secondary rate
limits have a distinct `message` field with further information.
* `:requires_auth`: The requested endpoint requires an authenticated user, and no auth
credentials were given.
* `:unauthorized`: Valid authentication credentials were given, but the current user does not
have permission to perform this action.
"""
alias GitHub.Operation
@typedoc "GitHub API client error"
@type t :: %__MODULE__{
code: integer | nil,
message: String.t(),
operation: Operation.t(),
reason: atom,
source: term,
stacktrace: Exception.stacktrace(),
step: {module, atom}
}
@derive {Inspect, except: [:operation, :stacktrace]}
defexception [:code, :message, :operation, :reason, :source, :stacktrace, :step]
@doc """
Create a new error struct with the given fields
The current stacktrace is automatically filled in to the resulting error. Callers should specify
the status `code` (if available), a `message`, the original `operation`, the `source` of the
error, and which `step` or plugin is currently active.
"""
@spec new(keyword) :: t
def new(opts) do
{:current_stacktrace, stack} = Process.info(self(), :current_stacktrace)
# Drop `Process.info/2` and `new/1`.
stacktrace = Enum.drop(stack, 2)
fields =
Keyword.merge(
[
message: "Unknown Error",
reason: :error,
stacktrace: stacktrace
],
opts
)
struct!(%__MODULE__{}, fields)
end
end