Unicom360.Error exception (Unicom360 v0.1.0)

Copy Markdown View Source

The one error type of the SDK.

Every function returns {:error, %Unicom360.Error{}} on failure, and its ! variant raises the same struct. type says what went wrong; class/1 names the canonical error class the other Unicom360 SDKs use, which also tells the business refusals apart.

typeclass/1When
:configuration_errorConfigurationErrorno API key, an unusable option, a blank webhook secret
:connection_errorAPIConnectionErrorno complete response (refused, DNS, reset)
:timeout_errorAPITimeoutErrorthe connection or the response timed out
:api_status_errorAPIStatusErrorany other non-2xx (3xx, 403, 404...), or a 2xx that is not an envelope
:authentication_errorAuthenticationErrorHTTP 401
:rate_limit_errorRateLimitErrorHTTP 429; retry_after is set
:invalid_request_errorInvalidRequestErrorHTTP 400: the request's shape is wrong
:api_server_errorAPIServerErrorHTTP 5xx
:api_errorAPIError, InsufficientBundleError, DuplicateReferenceError, NoValidRecipientsError, SessionWindowClosedErrorHTTP 200 with success: false (or status: false): a business refusal
:signature_verification_errorSignatureVerificationErrora webhook failed verification; reason says which check
:unicom360_errorUnicom360Errora webhook or callback body that is not a JSON object

HTTP 200 is not success: a refusal such as an unknown sender ID arrives as HTTP 200 with success: false, and is an :api_error whose code says why (see Unicom360.Codes).

Fields:

  • message: human-readable text; for API errors, the gateway's message (an array of messages is joined with "; "). Never contains the API key.
  • code: the normalised code (Unicom360.Codes.normalize/1) on API errors, else nil.
  • http_status, api_reference (quote it to support), raw_body, headers (the response's, names lower-cased) and data (the envelope's data, raw) on API errors.
  • attempts: the HTTP attempts an operation made, retries included.
  • retry_after: on a :rate_limit_error, milliseconds until the next UTC minute, when the gateway's per-minute window resets.
  • original_message_id: on a DuplicateReferenceError, the message_id the earlier send returned, when the gateway says (session sends).
  • results: on a NoValidRecipientsError, why each recipient was rejected.
  • reason: on a :signature_verification_error, one of :missing_signature, :missing_timestamp, :invalid_timestamp, :malformed_signature, :timestamp_out_of_tolerance, :signature_mismatch.
  • cause: on a :connection_error or :timeout_error, the underlying exception.

A DuplicateReferenceError with attempts greater than 1 means an earlier attempt of the same call very probably succeeded: the retry was refused because that attempt had already used the reference. Treat the message as sent.

Summary

Functions

The canonical class name of an error, as the other Unicom360 SDKs name it.

Whether the error is a :connection_error or a :timeout_error (a timeout is a connection error too, as APITimeoutError extends APIConnectionError).

Whether the error came from a non-2xx response, or a 2xx that was not an envelope (APIStatusError and its subclasses).

Types

reason()

@type reason() ::
  :missing_signature
  | :missing_timestamp
  | :invalid_timestamp
  | :malformed_signature
  | :timestamp_out_of_tolerance
  | :signature_mismatch

t()

@type t() :: %Unicom360.Error{
  __exception__: true,
  api_reference: String.t() | nil,
  attempts: pos_integer() | nil,
  cause: Exception.t() | nil,
  code: String.t() | nil,
  data: term(),
  headers: %{optional(String.t()) => [String.t()]} | nil,
  http_status: non_neg_integer() | nil,
  message: String.t(),
  original_message_id: String.t() | nil,
  raw_body: String.t() | nil,
  reason: reason() | nil,
  results: [Unicom360.WhatsApp.TemplateResult.t()] | nil,
  retry_after: non_neg_integer() | nil,
  type: type()
}

type()

@type type() ::
  :unicom360_error
  | :configuration_error
  | :connection_error
  | :timeout_error
  | :api_status_error
  | :authentication_error
  | :rate_limit_error
  | :invalid_request_error
  | :api_server_error
  | :api_error
  | :signature_verification_error

Functions

class(error)

@spec class(t()) :: String.t()

The canonical class name of an error, as the other Unicom360 SDKs name it.

An :api_error is classed by its code: INSUFFICIENT_BUNDLE is "InsufficientBundleError", DUPLICATE_REFERENCE "DuplicateReferenceError", NO_VALID_RECIPIENTS "NoValidRecipientsError", SESSION_WINDOW_CLOSED "SessionWindowClosedError", anything else "APIError".

iex> Unicom360.Error.class(%Unicom360.Error{type: :api_error, code: "INSUFFICIENT_BUNDLE"})
"InsufficientBundleError"
iex> Unicom360.Error.class(%Unicom360.Error{type: :rate_limit_error})
"RateLimitError"

connection_error?(error)

@spec connection_error?(t()) :: boolean()

Whether the error is a :connection_error or a :timeout_error (a timeout is a connection error too, as APITimeoutError extends APIConnectionError).

status_error?(error)

@spec status_error?(t()) :: boolean()

Whether the error came from a non-2xx response, or a 2xx that was not an envelope (APIStatusError and its subclasses).