mirror of
https://github.com/ash-project/ash.git
synced 2024-09-20 05:23:03 +12:00
improvement: replace templates in change/preparation/validation opts
Previously, only a few specific changes supported using `arg/1` or `context/1` in their options. Now, those templates can be used in any change options, built-in or not.
This commit is contained in:
parent
e18a8782c7
commit
113d2452d2
5 changed files with 77 additions and 46 deletions
|
@ -520,7 +520,7 @@ defmodule Ash.Changeset do
|
|||
opts[:tracer],
|
||||
metadata
|
||||
)
|
||||
|> add_validations(opts[:tracer], metadata)
|
||||
|> add_validations(opts[:tracer], metadata, opts[:actor])
|
||||
|> mark_validated(action.name)
|
||||
end
|
||||
end
|
||||
|
@ -650,7 +650,7 @@ defmodule Ash.Changeset do
|
|||
opts[:tracer],
|
||||
metadata
|
||||
)
|
||||
|> add_validations(opts[:tracer], metadata)
|
||||
|> add_validations(opts[:tracer], metadata, opts[:actor])
|
||||
|> mark_validated(action.name)
|
||||
|> eager_validate_identities()
|
||||
|
||||
|
@ -942,6 +942,14 @@ defmodule Ash.Changeset do
|
|||
} do
|
||||
Ash.Tracer.set_metadata(opts[:tracer], :validation, metadata)
|
||||
|
||||
opts =
|
||||
Ash.Filter.build_filter_from_template(
|
||||
opts,
|
||||
actor,
|
||||
changeset.arguments,
|
||||
changeset.context
|
||||
)
|
||||
|
||||
module.validate(changeset, opts) == :ok
|
||||
end
|
||||
end
|
||||
|
@ -955,6 +963,14 @@ defmodule Ash.Changeset do
|
|||
|
||||
Ash.Tracer.set_metadata(opts[:tracer], :change, metadata)
|
||||
|
||||
opts =
|
||||
Ash.Filter.build_filter_from_template(
|
||||
opts,
|
||||
actor,
|
||||
changeset.arguments,
|
||||
changeset.context
|
||||
)
|
||||
|
||||
module.change(changeset, opts, %{
|
||||
actor: actor,
|
||||
authorize?: authorize? || false,
|
||||
|
@ -967,7 +983,7 @@ defmodule Ash.Changeset do
|
|||
end
|
||||
|
||||
%{validation: _} = validation, changeset ->
|
||||
validate(changeset, validation, tracer, metadata)
|
||||
validate(changeset, validation, tracer, metadata, actor)
|
||||
end)
|
||||
end
|
||||
|
||||
|
@ -1113,34 +1129,42 @@ defmodule Ash.Changeset do
|
|||
|
||||
defp default(:update, %{update_default: value}), do: value
|
||||
|
||||
defp add_validations(changeset, tracer, metadata) do
|
||||
defp add_validations(changeset, tracer, metadata, actor) do
|
||||
changeset.resource
|
||||
# We use the `changeset.action_type` to support soft deletes
|
||||
# Because a delete is an `update` with an action type of `update`
|
||||
|> Ash.Resource.Info.validations(changeset.action_type)
|
||||
|> Enum.reduce(changeset, &validate(&2, &1, tracer, metadata))
|
||||
|> Enum.reduce(changeset, &validate(&2, &1, tracer, metadata, actor))
|
||||
end
|
||||
|
||||
defp validate(changeset, validation, tracer, metadata) do
|
||||
defp validate(changeset, validation, tracer, metadata, actor) do
|
||||
if validation.before_action? do
|
||||
before_action(changeset, fn changeset ->
|
||||
if validation.only_when_valid? and not changeset.valid? do
|
||||
changeset
|
||||
else
|
||||
do_validation(changeset, validation, tracer, metadata)
|
||||
do_validation(changeset, validation, tracer, metadata, actor)
|
||||
end
|
||||
end)
|
||||
else
|
||||
if validation.only_when_valid? and not changeset.valid? do
|
||||
changeset
|
||||
else
|
||||
do_validation(changeset, validation, tracer, metadata)
|
||||
do_validation(changeset, validation, tracer, metadata, actor)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp do_validation(changeset, validation, tracer, metadata) do
|
||||
defp do_validation(changeset, validation, tracer, metadata, actor) do
|
||||
if Enum.all?(validation.where || [], fn {module, opts} ->
|
||||
opts =
|
||||
Ash.Filter.build_filter_from_template(
|
||||
opts,
|
||||
actor,
|
||||
changeset.arguments,
|
||||
changeset.context
|
||||
)
|
||||
|
||||
module.validate(changeset, opts) == :ok
|
||||
end) do
|
||||
Ash.Tracer.span :validation, "validate: #{inspect(validation.module)}", tracer do
|
||||
|
@ -1150,7 +1174,15 @@ defmodule Ash.Changeset do
|
|||
} do
|
||||
Ash.Tracer.set_metadata(tracer, :validation, metadata)
|
||||
|
||||
case validation.module.validate(changeset, validation.opts) do
|
||||
opts =
|
||||
Ash.Filter.build_filter_from_template(
|
||||
validation.opts,
|
||||
actor,
|
||||
changeset.arguments,
|
||||
changeset.context
|
||||
)
|
||||
|
||||
case validation.module.validate(changeset, opts) do
|
||||
:ok ->
|
||||
changeset
|
||||
|
||||
|
|
|
@ -565,6 +565,16 @@ defmodule Ash.Filter do
|
|||
Enum.any?(args, &template_references_actor?/1)
|
||||
end
|
||||
|
||||
def template_references_actor?(list) when is_list(list) do
|
||||
Enum.any?(list, &template_references_actor?/1)
|
||||
end
|
||||
|
||||
def template_references_actor?(tuple) when is_tuple(tuple) do
|
||||
tuple
|
||||
|> Tuple.to_list()
|
||||
|> Enum.any?(&template_references_actor?/1)
|
||||
end
|
||||
|
||||
def template_references_actor?(_), do: false
|
||||
|
||||
@doc false
|
||||
|
|
|
@ -422,6 +422,14 @@ defmodule Ash.Query do
|
|||
|
||||
case module.init(opts) do
|
||||
{:ok, opts} ->
|
||||
opts =
|
||||
Ash.Filter.build_filter_from_template(
|
||||
opts,
|
||||
actor,
|
||||
query.arguments,
|
||||
query.context
|
||||
)
|
||||
|
||||
case module.prepare(query, opts, %{
|
||||
actor: actor,
|
||||
authorize?: authorize?,
|
||||
|
|
|
@ -21,40 +21,20 @@ defmodule Ash.Resource.Change.SetAttribute do
|
|||
defp validate_value(_), do: :ok
|
||||
|
||||
def change(changeset, opts, _) do
|
||||
case opts[:value] do
|
||||
{arg_key, arg} when arg_key in [:arg, :_arg] ->
|
||||
case Ash.Changeset.fetch_argument(changeset, arg) do
|
||||
{:ok, value} ->
|
||||
if opts[:new?] do
|
||||
if Ash.Changeset.changing_attribute?(changeset, opts[:attribute]) do
|
||||
changeset
|
||||
else
|
||||
Changeset.force_change_attribute(changeset, opts[:attribute], value)
|
||||
end
|
||||
else
|
||||
Changeset.force_change_attribute(changeset, opts[:attribute], value)
|
||||
end
|
||||
value =
|
||||
case opts[:value] do
|
||||
value when is_function(value) -> value.()
|
||||
value -> value
|
||||
end
|
||||
|
||||
_ ->
|
||||
changeset
|
||||
end
|
||||
|
||||
_ ->
|
||||
value =
|
||||
case opts[:value] do
|
||||
value when is_function(value) -> value.()
|
||||
value -> value
|
||||
end
|
||||
|
||||
if opts[:new?] do
|
||||
if Ash.Changeset.changing_attribute?(changeset, opts[:attribute]) do
|
||||
changeset
|
||||
else
|
||||
Changeset.force_change_attribute(changeset, opts[:attribute], value)
|
||||
end
|
||||
else
|
||||
Changeset.force_change_attribute(changeset, opts[:attribute], value)
|
||||
end
|
||||
if opts[:new?] do
|
||||
if Ash.Changeset.changing_attribute?(changeset, opts[:attribute]) do
|
||||
changeset
|
||||
else
|
||||
Changeset.force_change_attribute(changeset, opts[:attribute], value)
|
||||
end
|
||||
else
|
||||
Changeset.force_change_attribute(changeset, opts[:attribute], value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -43,7 +43,7 @@ defmodule Ash.Test.Actions.UpdateTest do
|
|||
|
||||
update :set_private_attribute_from_arg do
|
||||
argument :private, :string
|
||||
change set_attribute(:private, {:arg, :private})
|
||||
change set_attribute(:private, arg(:private))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -300,8 +300,9 @@ defmodule Ash.Test.Actions.UpdateTest do
|
|||
|
||||
profile =
|
||||
profile
|
||||
|> new(%{bio: "foobar", private: "blah"})
|
||||
|> Api.update!(action: :set_private_attribute_from_arg)
|
||||
|> new(%{bio: "foobar"})
|
||||
|> for_update(:set_private_attribute_from_arg, %{private: "blah"})
|
||||
|> Api.update!()
|
||||
|
||||
assert profile.private == "blah"
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue