Ltix.Errors (Ltix v0.2.0)

Copy Markdown View Source

Structured error types for LTI 1.3 validation.

Uses Splode for composable, class-based errors. There are three error classes, each with a default HTTP status code.

ClassStatusMeaning
:invalid400Spec-violating input (bad claims, missing params)
:security401Security framework violations (signature, nonce, expiry)
:unknown500Unexpected / catch-all errors

A single failure is returned directly. Several failures found at once are returned together in their class, such as %Ltix.Errors.Invalid{errors: [...]}.

Use status_code/1 to get the HTTP status for any Ltix error.

error = Ltix.Errors.Invalid.MissingClaim.exception(claim: "sub", spec_ref: "Core §5.3")
Ltix.Errors.status_code(error)
#=> 400

When Plug is available, all errors also implement Plug.Exception, so Phoenix error views use the correct status automatically.

Summary

Functions

Returns the HTTP status code for an Ltix error.

Traverses errors, calling fun for each leaf error, and returns a nested map of results grouped by each error's path.

Raises an error if the result is an error, otherwise returns the result

Types

class()

@type class() :: %{
  :__struct__ => class_module(),
  :__exception__ => true,
  :errors => [t()],
  :class => error_class(),
  :bread_crumbs => [String.t()],
  :vars => Keyword.t(),
  :stacktrace => Splode.Stacktrace.t() | nil,
  :context => map(),
  optional(atom()) => any()
}

class_module()

@type class_module() ::
  Ltix.Errors.Unknown | Ltix.Errors.Security | Ltix.Errors.Invalid

error_class()

@type error_class() :: :unknown | :security | :invalid

t()

@type t() :: %{
  :__struct__ => module(),
  :__exception__ => true,
  :class => error_class(),
  :bread_crumbs => [String.t()],
  :vars => Keyword.t(),
  :stacktrace => Splode.Stacktrace.t() | nil,
  :context => map(),
  optional(atom()) => any()
}

Functions

splode_error?(arg1, splode)

status_code(error)

@spec status_code(Exception.t()) :: pos_integer()

Returns the HTTP status code for an Ltix error.

Each error has a status code derived from its class.

ClassDefault status
:invalid400
:security401
:unknown500

Individual errors may override the default for their class. Exceptions that are not Ltix errors return 500.

Examples

iex> error = Ltix.Errors.Invalid.MissingClaim.exception(claim: "sub", spec_ref: "Core §5.3")
iex> Ltix.Errors.status_code(error)
400

traverse_errors(error_or_errors, fun)

Traverses errors, calling fun for each leaf error, and returns a nested map of results grouped by each error's path.

See Splode.traverse_errors/2 for full documentation.

Example

iex> Elixir.Ltix.Errors.traverse_errors(error, fn error ->
...>   Exception.message(error)
...> end)
%{name: ["name is required"]}

unwrap!(result, opts \\ nil)

Raises an error if the result is an error, otherwise returns the result

Alternatively, you can use the defsplode macro, which does this automatically.

Options

  • :error_opts - Options to pass to to_error/2 when converting the returned error
  • :unknown_error_opts - Options to pass to the unknown error if the function returns only :error. not necessary if your function always returns {:error, error}.

Examples

def function(arg) do

case do_something(arg) do
  :success -> :ok
  {:success, result} -> {:ok, result}
  {:error, error} -> {:error, error}
end

end

def function!(arg) do

YourErrors.unwrap!(function(arg))

end