Skip to content

Commit

Permalink
improvement: allow data loading when no primary read action exists
Browse files Browse the repository at this point in the history
this is still a bit experimental, so there may be some sharp edges,
but it makes sense for a resource with only a create action to be able to
load data, but to do so it currently needs to add a read action that will
never actually be called, only "used" as the action being performed, for things
like policies etc. We may even want to make this logic the default in a future
major version.
  • Loading branch information
zachdaniel committed Oct 24, 2024
1 parent 57c2a76 commit 62ca570
Show file tree
Hide file tree
Showing 10 changed files with 58 additions and 12 deletions.
2 changes: 1 addition & 1 deletion documentation/dsls/DSL-Ash.Reactor.md
Original file line number Diff line number Diff line change
Expand Up @@ -760,7 +760,7 @@ end
| [`max_concurrency`](#reactor-bulk_update-max_concurrency){: #reactor-bulk_update-max_concurrency } | `non_neg_integer` | `0` | If set to a value greater than 0, up to that many tasks will be started to run batches asynchronously. |
| [`notification_metadata`](#reactor-bulk_update-notification_metadata){: #reactor-bulk_update-notification_metadata } | `map \| Reactor.Template.Element \| Reactor.Template.Input \| Reactor.Template.Result \| Reactor.Template.Value` | `%{}` | Metadata to be merged into the metadata field for all notifications sent from this operation. |
| [`notify?`](#reactor-bulk_update-notify?){: #reactor-bulk_update-notify? } | `boolean` | `false` | Whether or not to generate any notifications. This may be intensive for large bulk actions. |
| [`page`](#reactor-bulk_update-page){: #reactor-bulk_update-page } | `keyword` | `[]` | Pagination options, see [the pagination docs for more](read-actions.md#pagination). |
| [`page`](#reactor-bulk_update-page){: #reactor-bulk_update-page } | `keyword` | `[]` | Pagination options, see `Ash.read/2` for more. |
| [`read_action`](#reactor-bulk_update-read_action){: #reactor-bulk_update-read_action } | `atom` | | The action to use when building the read query. |
| [`return_errors?`](#reactor-bulk_update-return_errors?){: #reactor-bulk_update-return_errors? } | `boolean` | `false` | Whether or not to return all of the errors that occur. Defaults to false to account for large inserts. |
| [`return_records?`](#reactor-bulk_update-return_records?){: #reactor-bulk_update-return_records? } | `boolean` | `false` | Whether or not to return all of the records that were inserted. Defaults to false to account for large inserts. |
Expand Down
2 changes: 2 additions & 0 deletions lib/ash/actions/create/bulk.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1438,6 +1438,7 @@ defmodule Ash.Actions.Create.Bulk do
context: %{private: %{just_created_by_action: action.name}},
reuse_values?: true,
domain: domain,
action: Ash.Resource.Info.primary_action(resource, :read) || action,
tenant: opts[:tenant],
actor: opts[:actor],
authorize?: opts[:authorize?],
Expand All @@ -1450,6 +1451,7 @@ defmodule Ash.Actions.Create.Bulk do
context: %{private: %{just_created_by_action: action.name}},
domain: domain,
tenant: opts[:tenant],
action: Ash.Resource.Info.primary_action(resource, :read) || action,
reuse_values?: true,
actor: opts[:actor],
authorize?: opts[:authorize?],
Expand Down
1 change: 1 addition & 0 deletions lib/ash/actions/create/create.ex
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,7 @@ defmodule Ash.Actions.Create do
}),
domain,
actor: opts[:actor],
action: Ash.Resource.Info.primary_action(changeset.resource, :read) || changeset.action,
reuse_values?: true,
authorize?: opts[:authorize?],
tracer: opts[:tracer]
Expand Down
3 changes: 3 additions & 0 deletions lib/ash/actions/destroy/bulk.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2139,6 +2139,8 @@ defmodule Ash.Actions.Destroy.Bulk do
reuse_values?: true,
domain: domain,
tenant: opts[:tenant],
action:
Ash.Resource.Info.primary_action(changeset.resource, :read) || changeset.action,
actor: opts[:actor],
authorize?: opts[:authorize?],
tracer: opts[:tracer]
Expand All @@ -2149,6 +2151,7 @@ defmodule Ash.Actions.Destroy.Bulk do
List.wrap(changeset.load),
reuse_values?: true,
tenant: opts[:tenant],
action: Ash.Resource.Info.primary_action(changeset.resource, :read) || changeset.action,
domain: domain,
actor: opts[:actor],
authorize?: opts[:authorize?],
Expand Down
2 changes: 2 additions & 0 deletions lib/ash/actions/destroy/destroy.ex
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ defmodule Ash.Actions.Destroy do
case Helpers.load({:ok, changeset.data, %{}}, changeset, domain,
actor: opts[:actor],
reuse_values?: true,
action:
Ash.Resource.Info.primary_action(changeset.resource, :read) || changeset.action,
authorize?: opts[:authorize?],
tracer: opts[:tracer]
) do
Expand Down
25 changes: 16 additions & 9 deletions lib/ash/actions/read/read.ex
Original file line number Diff line number Diff line change
Expand Up @@ -132,14 +132,18 @@ defmodule Ash.Actions.Read do
action = get_action(query.resource, query.action || action)

query =
for_read(
query,
action,
actor: opts[:actor],
authorize?: opts[:authorize?],
timeout: opts[:timeout],
tenant: opts[:tenant]
)
if action.type == :read do
for_read(
query,
action,
actor: opts[:actor],
authorize?: opts[:authorize?],
timeout: opts[:timeout],
tenant: opts[:tenant]
)
else
query
end

initial_query = query

Expand All @@ -148,7 +152,7 @@ defmodule Ash.Actions.Read do
query = %{
query
| timeout:
opts[:timeout] || query.timeout || query.action.timeout ||
opts[:timeout] || query.timeout || (query.action && query.action.timeout) ||
Ash.Domain.Info.timeout(query.domain)
}

Expand Down Expand Up @@ -2420,6 +2424,9 @@ defmodule Ash.Actions.Read do

def page_opts(action, page_opts, relationship?) do
cond do
action.type != :read ->
nil

action.pagination == false ->
nil

Expand Down
3 changes: 3 additions & 0 deletions lib/ash/actions/update/bulk.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2494,6 +2494,8 @@ defmodule Ash.Actions.Update.Bulk do
reuse_values?: true,
domain: domain,
tenant: opts[:tenant],
action:
Ash.Resource.Info.primary_action(changeset.resource, :read) || changeset.action,
actor: opts[:actor],
authorize?: opts[:authorize?],
tracer: opts[:tracer]
Expand All @@ -2504,6 +2506,7 @@ defmodule Ash.Actions.Update.Bulk do
List.wrap(changeset.load),
reuse_values?: true,
tenant: opts[:tenant],
action: Ash.Resource.Info.primary_action(changeset.resource, :read) || changeset.action,
domain: domain,
actor: opts[:actor],
authorize?: opts[:authorize?],
Expand Down
1 change: 1 addition & 0 deletions lib/ash/actions/update/update.ex
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,7 @@ defmodule Ash.Actions.Update do
|> Helpers.load(changeset, domain,
actor: opts[:actor],
reuse_values?: true,
action: Ash.Resource.Info.primary_action(changeset.resource, :read) || changeset.action,
authorize?: opts[:authorize?],
tracer: opts[:tracer]
)
Expand Down
3 changes: 1 addition & 2 deletions lib/ash/reactor/dsl/bulk_update.ex
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,7 @@ defmodule Ash.Reactor.Dsl.BulkUpdate do
],
page: [
type: :keyword_list,
doc:
"Pagination options, see `Ash.read/2` for more.",
doc: "Pagination options, see `Ash.read/2` for more.",
required: false,
default: []
],
Expand Down
28 changes: 28 additions & 0 deletions test/actions/load_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,24 @@ defmodule Ash.Test.Actions.LoadTest do

alias Ash.Test.Domain, as: Domain

defmodule ExampleWithNoPrimaryRead do
use Ash.Resource,
domain: Domain,
data_layer: Ash.DataLayer.Ets

actions do
defaults [:create]
end

attributes do
uuid_primary_key :id
end

calculations do
calculate :id_calc, :uuid, expr(id)
end
end

defmodule Campaign do
@moduledoc false
use Ash.Resource,
Expand Down Expand Up @@ -1927,4 +1945,14 @@ defmodule Ash.Test.Actions.LoadTest do
assert post.contents == loaded_post.contents
end
end

test "you can load data on create with no default read action" do
record =
ExampleWithNoPrimaryRead
|> Ash.Changeset.for_create(:create, %{})
|> Ash.Changeset.load(:id_calc)
|> Ash.create!()

assert record.id == record.id_calc
end
end

0 comments on commit 62ca570

Please sign in to comment.