View Source IdempotencyPlug (IdempotencyPlug v0.2.1)

Plug that handles Idempotency-Key HTTP headers.

A single Idempotency-Key HTTP header is required for POST and PATCH requests.

Handling of requests is based on https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key-header/

idempotency-key

Idempotency Key

The value of the Idempotency-Key HTTP header is combined with a URI to produce a unique sha256 hash for the request. This will be used to store the response for first-time requests. The key is used to fetch this response in all subsequent requests.

A sha256 hash of the request payload is generated and used to ensure the key is not reused with a different request payload.

error-handling

Error handling

By default, errors are raised and handled by the Plug.Exception protocol:

Setting :with option with an MFA will catch and pass the error to the MFA.

cached-responses

Cached responses

Cached responses returns an Expires header in the response. See IdempotencyPlug.RequestTracker for more on expiration.

options

Options

  • :tracker - must be a name or PID for the IdempotencyPlug.RequestTracker GenServer, required.

  • :idempotency_key - should be a MFA callback to process idempotency key. Defaults to {Elixir.IdempotencyPlug, :idempotency_key}.

  • :request_payload - should be a MFA to parse request payload. Defaults to {Elixir.IdempotencyPlug, :request_payload}.

  • :hash - should be a MFA to hash an Erlang term. Defaults to {Elixir.IdempotencyPlug, :sha256_hash}.

  • :with - should be one of :exception or MFA. Defaults to :exception.

    • :exception - raises an error.
    • {mod, fun, args} - calls the MFA to process the conn with error, the connection MUST be halted.

examples

Examples

plug IdempotencyPlug,
  tracker: IdempotencyPlug.RequestTracker,
  idempotency_key: {__MODULE__, :scope_idempotency_key},
  request_payload: {__MODULE__, :limit_request_payload},
  hash: {__MODULE__, :sha512_hash},
  with: {__MODULE__, :handle_error}

def scope_idempotency_key(conn, key) do
  {conn.assigns.user.id, key}
end

def limit_request_payload(conn) do
  Map.drop(conn.params, ["value"])
end

def sha512_hash(_key, value) do
  :sha512
  |> :crypto.hash(:erlang.term_to_binary(value))
  |> Base.encode16()
  |> String.downcase()
end

def handle_error(conn, error) do
  conn
  |> put_status(error.plug_status)
  |> json(%{message: error.message})
  |> halt()
end

Link to this section Summary

Functions

Returns the key as-is.

Sorts the request params in a deterministic order.

Encodes the value from an Erlang term to a binary and generates a sha256 hash from it.

Link to this section Functions

Link to this function

idempotency_key(conn, key)

View Source

Returns the key as-is.

Sorts the request params in a deterministic order.

Encodes the value from an Erlang term to a binary and generates a sha256 hash from it.