Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions documentation/dsls/DSL-Ash.Resource.md
Original file line number Diff line number Diff line change
Expand Up @@ -880,12 +880,14 @@ multiple actions of each type in a large application.
* pipe_through
* metadata
* argument
* filter
* [destroy](#actions-destroy)
* change
* validate
* pipe_through
* metadata
* argument
* filter


### Examples
Expand Down Expand Up @@ -1823,6 +1825,7 @@ Declares a `update` action. For calling this action, see the `Ash.Domain` docume
* [pipe_through](#actions-update-pipe_through)
* [metadata](#actions-update-metadata)
* [argument](#actions-update-argument)
* [filter](#actions-update-filter)


### Examples
Expand Down Expand Up @@ -2088,6 +2091,40 @@ argument :password_confirmation, :string

Target: `Ash.Resource.Actions.Argument`

### actions.update.filter
```elixir
filter filter
```


Applies a filter. Can use `^arg/1`, `^context/1` and `^actor/1` templates. Multiple filters are combined with *and*.



### Examples
```
filter expr(first_name == "fred")
filter expr(last_name == "weasley" and magician == true)

```



### Arguments

| Name | Type | Default | Docs |
|------|------|---------|------|
| [`filter`](#actions-update-filter-filter){: #actions-update-filter-filter .spark-required} | `any` | | The filter to apply. Can use `^arg/1`, `^context/1` and `^actor/1` templates. Multiple filters are combined with *and*. |






### Introspection

Target: `Ash.Resource.Dsl.Filter`




Expand All @@ -2112,6 +2149,7 @@ See `Ash.Resource.Change.Builtins.cascade_destroy/2` for cascading destroy opera
* [pipe_through](#actions-destroy-pipe_through)
* [metadata](#actions-destroy-metadata)
* [argument](#actions-destroy-argument)
* [filter](#actions-destroy-filter)


### Examples
Expand Down Expand Up @@ -2381,6 +2419,40 @@ argument :password_confirmation, :string

Target: `Ash.Resource.Actions.Argument`

### actions.destroy.filter
```elixir
filter filter
```


Applies a filter. Can use `^arg/1`, `^context/1` and `^actor/1` templates. Multiple filters are combined with *and*.



### Examples
```
filter expr(first_name == "fred")
filter expr(last_name == "weasley" and magician == true)

```



### Arguments

| Name | Type | Default | Docs |
|------|------|---------|------|
| [`filter`](#actions-destroy-filter-filter){: #actions-destroy-filter-filter .spark-required} | `any` | | The filter to apply. Can use `^arg/1`, `^context/1` and `^actor/1` templates. Multiple filters are combined with *and*. |






### Introspection

Target: `Ash.Resource.Dsl.Filter`




Expand Down
7 changes: 7 additions & 0 deletions lib/ash/actions/destroy/bulk.ex
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,13 @@ defmodule Ash.Actions.Destroy.Bulk do

query = %{query | domain: domain}

query =
if Map.get(action, :filter) do
Ash.Query.do_filter(query, action.filter)
else
query
end

fully_atomic_changeset =
cond do
not_atomic_reason ->
Expand Down
7 changes: 7 additions & 0 deletions lib/ash/actions/update/bulk.ex
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ defmodule Ash.Actions.Update.Bulk do

query = %{query | domain: domain}

query =
if is_map(action) && Map.get(action, :filter) do
Ash.Query.do_filter(query, action.filter)
else
query
end

fully_atomic_changeset =
cond do
not_atomic_reason ->
Expand Down
10 changes: 10 additions & 0 deletions lib/ash/changeset/changeset.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3136,6 +3136,16 @@ defmodule Ash.Changeset do
|> timeout(changeset.timeout || opts[:timeout])
|> set_tenant(opts[:tenant] || changeset.tenant || changeset.data.__metadata__[:tenant])
|> Map.put(:action_type, action.type)
|> apply_action_filter(action)
end

defp apply_action_filter(changeset, %{filter: nil}), do: changeset

defp apply_action_filter(changeset, action) do
case Map.get(action, :filter) do
nil -> changeset
action_filter -> filter(changeset, action_filter)
end
end

defp reset_arguments(%{arguments: arguments} = changeset) do
Expand Down
23 changes: 21 additions & 2 deletions lib/ash/resource/actions/destroy.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ defmodule Ash.Resource.Actions.Destroy do
:description,
:error_handler,
:multitenancy,
:filter,
manual: nil,
require_atomic?: Application.compile_env(:ash, :require_atomic_by_default?, true),
skip_unknown_inputs: [],
Expand All @@ -27,6 +28,7 @@ defmodule Ash.Resource.Actions.Destroy do
require_attributes: [],
allow_nil_input: [],
changes: [],
filters: [],
reject: [],
transaction?: true,
public?: true,
Expand All @@ -40,6 +42,8 @@ defmodule Ash.Resource.Actions.Destroy do
name: atom,
manual: module | nil,
multitenancy: atom,
filter: any,
filters: [any],
action_select: list(atom) | nil,
notifiers: list(module),
arguments: list(Ash.Resource.Actions.Argument.t()),
Expand Down Expand Up @@ -118,8 +122,23 @@ defmodule Ash.Resource.Actions.Destroy do
def opt_schema, do: @opt_schema

def transform(%{manual: manual} = action) when not is_nil(manual) do
{:ok, %{action | require_atomic?: false}}
{:ok, %{action | require_atomic?: false} |> concat_filters()}
end

def transform(action), do: {:ok, action}
def transform(action), do: {:ok, concat_filters(action)}

defp concat_filters(%{filters: [filter]} = action) do
%{action | filter: filter.filter}
end

defp concat_filters(%{filters: [first | rest]} = action) do
filter =
Enum.reduce(rest, first.filter, fn filter, acc ->
Ash.Query.BooleanExpression.new(:and, filter.filter, acc)
end)

%{action | filter: filter}
end

defp concat_filters(action), do: action
end
23 changes: 21 additions & 2 deletions lib/ash/resource/actions/update.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ defmodule Ash.Resource.Actions.Update do
:description,
:error_handler,
:multitenancy,
:filter,
accept: nil,
require_attributes: [],
allow_nil_input: [],
Expand All @@ -27,6 +28,7 @@ defmodule Ash.Resource.Actions.Update do
skip_global_validations?: false,
arguments: [],
changes: [],
filters: [],
reject: [],
metadata: [],
transaction?: true,
Expand All @@ -41,6 +43,8 @@ defmodule Ash.Resource.Actions.Update do
name: atom,
manual: module | nil,
multitenancy: atom,
filter: any,
filters: [any],
skip_unknown_inputs: list(atom | String.t()),
atomic_upgrade?: boolean(),
action_select: list(atom) | nil,
Expand Down Expand Up @@ -115,8 +119,23 @@ defmodule Ash.Resource.Actions.Update do
def opt_schema, do: @opt_schema

def transform(%{manual: manual} = action) when not is_nil(manual) do
{:ok, %{action | require_atomic?: false}}
{:ok, %{action | require_atomic?: false} |> concat_filters()}
end

def transform(action), do: {:ok, action}
def transform(action), do: {:ok, concat_filters(action)}

defp concat_filters(%{filters: [filter]} = action) do
%{action | filter: filter.filter}
end

defp concat_filters(%{filters: [first | rest]} = action) do
filter =
Enum.reduce(rest, first.filter, fn filter, acc ->
Ash.Query.BooleanExpression.new(:and, filter.filter, acc)
end)

%{action | filter: filter}
end

defp concat_filters(action), do: action
end
4 changes: 2 additions & 2 deletions lib/ash/resource/change/builtins.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ defmodule Ash.Resource.Change.Builtins do

This ensures that only things matching the provided filter are updated or destroyed.
"""
@spec filter(expr :: Ash.Expr.t()) :: Ash.Resource.Change.ref()
def filter(filter) do
@spec filter_where(expr :: Ash.Expr.t()) :: Ash.Resource.Change.ref()
def filter_where(filter) do
{Ash.Resource.Change.Filter, filter: filter}
end

Expand Down
6 changes: 6 additions & 0 deletions lib/ash/resource/dsl.ex
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,9 @@ defmodule Ash.Resource.Dsl do
],
arguments: [
@action_argument
],
filters: [
@filter
]
],
deprecations: [
Expand Down Expand Up @@ -773,6 +776,9 @@ defmodule Ash.Resource.Dsl do
],
arguments: [
@action_argument
],
filters: [
@filter
]
],
target: Ash.Resource.Actions.Destroy,
Expand Down
2 changes: 1 addition & 1 deletion test/actions/bulk/bulk_destroy_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ defmodule Ash.Test.Actions.BulkDestroyTest do
end

destroy :destroy_with_filter do
change filter(expr(title == "foo"))
change filter_where(expr(title == "foo"))
end

destroy :soft do
Expand Down
2 changes: 1 addition & 1 deletion test/actions/bulk/bulk_update_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ defmodule Ash.Test.Actions.BulkUpdateTest do
end

update :update_with_filter do
change filter(expr(title == "foo"))
change filter_where(expr(title == "foo"))
end

update :update_fails_on_title2 do
Expand Down