# `AshRateLimiter`

An extension for `Ash.Resource` which adds the ability to rate limit access to actions.

# `keyfun`

```elixir
@type keyfun() ::
  (Ash.Query.t() | Ash.Changeset.t() | Ash.ActionInput.t() -&gt; String.t())
  | (Ash.Query.t() | Ash.Changeset.t() | Ash.ActionInput.t(), map() -&gt;
       String.t())
```

# `t`

```elixir
@type t() :: %AshRateLimiter{
  __identifier__: any(),
  __spark_metadata__: Spark.Dsl.Entity.spark_meta(),
  action: atom(),
  description: nil | String.t(),
  key: String.t() | keyfun(),
  limit: pos_integer(),
  on: term(),
  per: pos_integer() | Duration.t()
}
```

# `key_for_action`

```elixir
@spec key_for_action(
  Ash.Query.t() | Ash.Changeset.t() | Ash.ActionInput.t(),
  map(),
  keyword()
) ::
  String.t()
```

The default bucket key generation function for `AshRateLimiter`.

Generates a key based on the domain, resource and action names. For update and destroy actions it will also include the primary key(s) in the key.

Works with `Ash.Query`, `Ash.Changeset`, and `Ash.ActionInput` (for generic actions).

## Options

* `:include_pk?` (`t:boolean/0`) - Whether or not to include the primary keys in the generated key in update/destroy actions. The default value is `true`.

* `:include_actor_attributes` (one or a list of `t:atom/0`) - A list of attributes to include from the current actor The default value is `[]`.

* `:include_tenant?` (`t:boolean/0`) - Whether or not to include the tenant in the bucket key. The default value is `false`.

## Examples

    iex> Example.Post
    ...> |> Ash.Changeset.for_create(:create, post_attrs())
    ...> |> key_for_action(%{})
    "example/post/create"

    iex> Example.Post
    ...> |> Ash.Query.for_read(:read)
    ...> |> key_for_action(%{})
    "example/post/read"

    iex> generate_post(id: "0196ebc0-83b4-7938-bc9d-22ae65dbec0b")
    ...> |> Ash.Changeset.for_update(:update, %{title: "Local Farmer Claims 'Space Zombie' Wrecked His Barn"})
    ...> |> key_for_action(%{})
    "example/post/update?id=0196ebc0-83b4-7938-bc9d-22ae65dbec0b"

    iex> generate_post(id: "0196ebc6-4839-7a9e-b2a6-feb0eabc8772")
    ...> |> Ash.Changeset.for_destroy(:destroy)
    ...> |> key_for_action(%{})
    "example/post/destroy?id=0196ebc6-4839-7a9e-b2a6-feb0eabc8772"

    iex> generate_post(id: "0196ebcd-fabf-74c3-9585-8b13c5f07068")
    ...> |> Ash.Changeset.for_destroy(:destroy)
    ...> |> key_for_action(%{}, include_pk?: false)
    "example/post/destroy"

    iex> Example.Post
    ...> |> Ash.Changeset.for_create(:create, post_attrs())
    ...> |> key_for_action(%{actor: %{role: :time_traveller}}, include_actor_attributes: [:role])
    "example/post/create?actor%5Brole%5D=time_traveller"

    iex> Example.Post
    ...> |> Ash.Changeset.new()
    ...> |> Ash.Changeset.set_tenant("Hill Valley Telegraph")
    ...> |> Ash.Changeset.for_create(:create, post_attrs())
    ...> |> key_for_action(%{}, include_tenant?: true)
    "example/post/create?tenant=Hill%20Valley%20Telegraph"

# `rate_limit`
*macro* 

---

*Consult [api-reference.md](api-reference.md) for complete listing*
