Alembic v4.0.0 Alembic.Error View Source

Error objects provide additional information about problems encountered while performing an operation. Error objects MUST be returned as an array keyed by errors in the top level of a JSON API document.

Link to this section Summary

Types

A single error in Ecto.Changeset.t :errors Keyword.t

A single error field key in the Ecto.Changeset.t :errors Keyword.t

A single error message value in the Ecto.Changeset.t :errors Keyword.t

The name of a JSON type in human-readable terms, such as "array" or "object"

t()

Additional information about problems encountered while performing an operation

Functions

When two or more members can’t be present at the same time

Descends source pointer to child of current source pointer

Converts an Ecto.Changeset.t error composed of the field the error occurred on and the error message

Converts a JSON object into a JSON API Error, t

When the minimum number of children are not present, give the sender a list of the children they could have sent

When a required (MUST in the spec) member is missing

Fills in the format of the error message using the values for the format keys in value_by_key

Error when the JSON type of the field is wrong

Link to this section Types

Link to this type ecto_changeset_error() View Source
ecto_changeset_error() ::
  {ecto_changeset_error_field :: atom(), ecto_changeset_error_message()}

A single error in Ecto.Changeset.t :errors Keyword.t

Link to this type ecto_changeset_error_field() View Source
ecto_changeset_error_field() :: atom()

A single error field key in the Ecto.Changeset.t :errors Keyword.t

Link to this type ecto_changeset_error_message() View Source
ecto_changeset_error_message() ::
  {format :: String.t(), value_by_key :: Keyword.t()}

A single error message value in the Ecto.Changeset.t :errors Keyword.t.

Link to this type human_type() View Source
human_type() :: String.t()

The name of a JSON type in human-readable terms, such as "array" or "object".

Link to this type t() View Source
t() :: %Alembic.Error{
  code: String.t() | nil,
  detail: String.t() | nil,
  id: String.t() | nil,
  links: Alembic.Links.t() | nil,
  meta: %{optional(String.t()) => Alembic.json() | atom()} | nil,
  source: Alembic.Source.t() | nil,
  status: String.t() | nil,
  title: String.t() | nil
}

Additional information about problems encountered while performing an operation.

An error object MAY have the following members:

  • code - an application-specific error code.
  • detail - a human-readable explanation specific to this occurrence of the problem.
  • id - a unique identifier for this particular occurrence of the problem.
  • links - contains the following members:

    • "about" - an Alembic.Link.link leading to further details about this particular occurrence of the problem.
  • meta - non-standard meta-information about the error.
  • source - contains references to the source of the error, optionally including any of the following members:
  • status - the HTTP status code applicable to this problem.
  • title - a short, human-readable summary of the problem that SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization.

Link to this section Functions

Link to this function conflicting(template, children) View Source
conflicting(t(), [String.t()]) :: t()

When two or more members can’t be present at the same time.

iex> Alembic.Error.conflicting(
...>   %Alembic.Error{
...>     source: %Alembic.Source{
...>       pointer: "/errors/0/source"
...>     }
...>   },
...>   ~w{parameter pointer}
...> )
%Alembic.Error{
  detail: "The following members conflict with each other (only one can be present):\nparameter\npointer",
  meta: %{
    "children" => [
      "parameter",
      "pointer"
    ]
  },
  source: %Alembic.Source{
    pointer: "/errors/0/source"
  },
  status: "422",
  title: "Children conflicting"
}
Link to this function descend(error, child) View Source
descend(t(), String.t() | integer()) :: t()

Descends source pointer to child of current source pointer

iex> Alembic.Error.descend(
...>   %Alembic.Error{
...>     source: %Alembic.Source{
...>       pointer: "/data"
...>     }
...>   },
...>   1
...> )
%Alembic.Error{
  source: %Alembic.Source{
    pointer: "/data/1"
  }
}
Link to this function from_ecto_changeset_error(arg, pointer_path_from_ecto_changeset_error_field_options) View Source

Converts an Ecto.Changeset.t error composed of the field the error occurred on and the error message

The field is converted an Alembic.Source.t :pointer. If it cannot be converted, then the returned t will have not :source. The child part of the :pointer is formatted with :format_key.

If field is in association_set in pointer_path_from_ecto_changeset_error_field_options, then the pointer will be under /data/relationships.

iex> format_key = fn key ->
...>   key |> Atom.to_string() |> String.replace("_", "-")
...> end
iex> Alembic.Error.from_ecto_changeset_error(
...>   {:favorite_posts, {"are still associated with this entry", []}},
...>   %{
...>     association_set: MapSet.new([:designated_editor, :favorite_posts]),
...>     association_by_foreign_key: %{designated_editor_id: :designated_editor},
...>     attribute_set: MapSet.new([:first_name, :last_name]),
...>     format_key: format_key
...>   }
...> )
%Alembic.Error{
  detail: "favorite-posts are still associated with this entry",
  source: %Alembic.Source{
    pointer: "/data/relationships/favorite-posts"
  },
  title: "are still associated with this entry"
}

If field is a key in association_by_foreign_key in pointer_path_from_ecto_changeset_error_field_options, then the pointer will be under /data/relationships, but the child will be the name of the (formatted) association instead of the foreign key field itself as JSONAPI attributes should not contain foreign keys.

iex> format_key = fn key ->
...>   key |> Atom.to_string() |> String.replace("_", "-")
...> end
iex> Alembic.Error.from_ecto_changeset_error(
...>   {:designated_editor_id, {"can't be blank", [validation: :required]}},
...>   %{
...>     association_set: MapSet.new([:designated_editor, :favorite_posts]),
...>     association_by_foreign_key: %{designated_editor_id: :designated_editor},
...>     attribute_set: MapSet.new([:first_name, :last_name]),
...>     format_key: format_key
...>   }
...> )
%Alembic.Error{
  detail: "designated-editor can't be blank",
  source: %Alembic.Source{
    pointer: "/data/relationships/designated-editor"
  },
  title: "can't be blank"
}

If field is in attribute_set in pointer_path_from_ecto_changeset_error_field_options, then the pointer will be under /data/attributes.

iex> format_key = fn key ->
...>   key |> Atom.to_string() |> String.replace("_", "-")
...> end
iex> Alembic.Error.from_ecto_changeset_error(
...>   {:first_name, {"should be at least %{count} character(s)", [count: 2, validation: :length, min: 2]}},
...>   %{
...>     association_set: MapSet.new([:designated_editor, :favorite_posts]),
...>     association_by_foreign_key: %{designated_editor_id: :designated_editor},
...>     attribute_set: MapSet.new([:first_name, :last_name]),
...>     format_key: format_key
...>   }
...> )
%Alembic.Error{
  detail: "first-name should be at least 2 character(s)",
  source: %Alembic.Source{
    pointer: "/data/attributes/first-name"
  },
  title: "should be at least 2 character(s)"
}

If field is not in association_set, attribute_set, or a foreign key in association_by_foreign_key in pointer_path_from_ecto_changeset_error_field_options, then the t :source will be nil

iex> format_key = fn key ->
...>   key |> Atom.to_string() |> String.replace("_", "-")
...> end
iex> Alembic.Error.from_ecto_changeset_error(
...>   {:favorite_flavor, {"is not allowed", []}},
...>   %{
...>     association_set: MapSet.new([:designated_editor, :favorite_posts]),
...>     association_by_foreign_key: %{designated_editor_id: :designated_editor},
...>     attribute_set: MapSet.new([:first_name, :last_name]),
...>     format_key: format_key
...>   }
...> )
%Alembic.Error{
  detail: "favorite-flavor is not allowed",
  title: "is not allowed"
}
Link to this function from_json(json_object, template) View Source

Converts a JSON object into a JSON API Error, t.

iex> Alembic.Error.from_json(
...>   %{
...>     "code" => "1",
...>     "detail" => "There was an error in data",
...>     "id" => "2",
...>     "links" => %{
...>       "about" => %{
...>         "href" => "/errors/2",
...>         "meta" => %{
...>           "extra" => "about meta"
...>         }
...>       }
...>     },
...>     "meta" => %{
...>       "extra" => "error meta"
...>     },
...>     "source" => %{
...>       "pointer" => "/data"
...>     },
...>     "status" => "422",
...>     "title" => "There was an error"
...>   },
...>   %Alembic.Error{
...>     source: %Alembic.Source{
...>       pointer: "/errors/0"
...>     }
...>   }
...> )
{
  :ok,
  %Alembic.Error{
    code: "1",
    detail: "There was an error in data",
    id: "2",
    links: %{
      "about" => %Alembic.Link{
        href: "/errors/2",
        meta: %{
          "extra" => "about meta"
        }
      }
    },
    meta: %{
      "extra" => "error meta"
    },
    source: %Alembic.Source{
      pointer: "/data"
    },
    status: "422",
    title: "There was an error"
  }
}
Link to this function minimum_children(template, children) View Source
minimum_children(t(), [String.t()]) :: t()

When the minimum number of children are not present, give the sender a list of the children they could have sent.

iex> Alembic.Error.minimum_children(
...>   %Alembic.Error{
...>     source: %Alembic.Source{
...>       pointer: "/data/relationships/author"
...>     }
...>   },
...>   ~w{data links meta}
...> )
%Alembic.Error{
  detail: "At least one of the following children of `/data/relationships/author` must be present:\n" <>
          "data\n" <>
          "links\n" <>
          "meta",
  meta: %{
    "children" => [
      "data",
      "links",
      "meta"
    ]
  },
  source: %Alembic.Source{
    pointer: "/data/relationships/author"
  },
  status: "422",
  title: "Not enough children"
}
Link to this function missing(template, child) View Source
missing(t(), String.t()) :: t()

When a required (MUST in the spec) member is missing

Top-level member is missing

iex> Alembic.Error.missing(
...>   %Alembic.Error{
...>     source: %Alembic.Source{
...>       pointer: ""
...>     }
...>   },
...>   "data"
...> )
%Alembic.Error{
  detail: "`/data` is missing",
  meta: %{
    "child" => "data"
  },
  source: %Alembic.Source{
    pointer: ""
  },
  status: "422",
  title: "Child missing"
}

Nested member is missing

iex> Alembic.Error.missing(
...>   %Alembic.Error{
...>     source: %Alembic.Source{
...>       pointer: "/data"
...>     }
...>   },
...>   "type"
...> )
%Alembic.Error{
  detail: "`/data/type` is missing",
  meta: %{
    "child" => "type"
  },
  source: %Alembic.Source{
    pointer: "/data"
  },
  status: "422",
  title: "Child missing"
}
Link to this function relationship_path(template \\ %__MODULE__{source: %Source{parameter: "include"}}, unknown_relationship_path) View Source
relationship_path(t(), String.t()) :: t()

When a relationship path in "includes" params is unknown.

If no template is given, it is assumed that the source is the “include” parameter

iex> Alembic.Error.relationship_path("secret")
%Alembic.Error{
  detail: "`secret` is an unknown relationship path",
  meta: %{
    "relationship_path" => "secret"
  },
  source: %Alembic.Source{
    parameter: "include"
  },
  title: "Unknown relationship path"
}

If using a different parameter than recommended in the JSON API spec, a template can be used

iex> Alembic.Error.relationship_path(
...>   %Alembic.Error{
...>     source: %Alembic.Source{
...>       parameter: "relationships"
...>     }
...>   },
...>   "secret"
...> )
%Alembic.Error{
  detail: "`secret` is an unknown relationship path",
  meta: %{
    "relationship_path" => "secret"
  },
  source: %Alembic.Source{
    parameter: "relationships"
  },
  title: "Unknown relationship path"
}
Link to this function title_from_ecto_changeset_error_message(arg) View Source
title_from_ecto_changeset_error_message(ecto_changeset_error_message()) ::
  String.t()

Fills in the format of the error message using the values for the format keys in value_by_key

Link to this function type(error, human_type) View Source
type(t(), human_type()) :: t()

Error when the JSON type of the field is wrong.

NOTE: The JSON type should be used, not the Elixir/Erlang type, so if a member is not a map in Elixir, the human_type should be "object". Likewise, if a member is not a list in Elixir, the human_type should be "array".

When member is not an Elixir list or JSON array

iex> validate_errors = fn
...>   (list) when is_list(list) ->
...>     {:ok, list}
...>   (_) ->
...>     {
...>       :error,
...>       Alembic.Error.type(
...>         %Alembic.Error{
...>           source: %Alembic.Source{
...>             pointer: "/errors"
...>           }
...>         },
...>         "array"
...>       )
...>     }
...> end
iex> json = %{"errors" => "invalid"}
iex> validate_errors.(json["errors"])
{
  :error,
  %Alembic.Error{
    detail: "`/errors` type is not array",
    meta: %{
      "type" => "array"
    },
    source: %Alembic.Source{
      pointer: "/errors"
    },
    status: "422",
    title: "Type is wrong"
  }
}

When member is not an Elixir map or JSON object

iex> validate_meta = fn
...>   (meta) when is_map(meta) ->
...>     {:ok, meta}
...>   (_) ->
...>     {
...>       :error,
...>       Alembic.Error.type(
...>         %Alembic.Error{
...>           source: %Alembic.Source{
...>             pointer: "/meta"
...>           }
...>         },
...>         "object"
...>       )
...>     }
...> end
iex> json = %{"meta" => "invalid"}
iex> validate_meta.(json["meta"])
{
  :error,
  %Alembic.Error{
    detail: "`/meta` type is not object",
    meta: %{
      "type" => "object"
    },
    source: %Alembic.Source{
      pointer: "/meta"
    },
    status: "422",
    title: "Type is wrong"
  }
}