improvement: include actor in all calculation context

This commit is contained in:
Zach Daniel 2023-02-23 09:45:27 -05:00
parent 86d2297018
commit f060a2bdc4
2 changed files with 97 additions and 0 deletions

View file

@ -281,6 +281,8 @@ defmodule Ash.Actions.Read do
)
end
query = add_calculation_context(query, actor, authorize?, tenant, tracer)
query = %{
query
| api: api,
@ -424,6 +426,77 @@ defmodule Ash.Actions.Read do
[fetch, process]
end
defp add_calculation_context(query, actor, authorize?, tenant, tracer) do
query =
if query.calculations do
%{
query
| calculations:
Map.new(query.calculations, fn {name, calc} ->
{name,
%{
calc
| context:
Map.merge(
%{actor: actor, authorize?: authorize?, tenant: tenant, tracer: tracer},
calc.context
)
}}
end)
}
end
if query.filter do
%{
query
| filter: add_calc_context_to_filter(query.filter, actor, authorize?, tenant, tracer)
}
else
query
end
end
defp add_calc_context_to_filter(filter, actor, authorize?, tenant, tracer) do
Ash.Filter.map(filter, fn
%Ash.Query.Parent{} = parent ->
%{
parent
| expr: add_calc_context_to_filter(parent.expr, actor, authorize?, tenant, tracer)
}
%Ash.Query.Exists{} = exists ->
%{
exists
| expr: add_calc_context_to_filter(exists.expr, actor, authorize?, tenant, tracer)
}
%Ash.Query.Ref{attribute: %Ash.Resource.Calculation{}} = ref ->
raise Ash.Error.Framework.AssumptionFailed,
message: "Unhandled calculation in filter statement #{inspect(ref)}"
%Ash.Query.Ref{attribute: %Ash.Query.Calculation{} = calc} = ref ->
%{
ref
| attribute: %{
calc
| context:
Map.merge(
%{
actor: actor,
authorize?: authorize?,
tenant: tenant,
tracer: tracer
},
calc.context
)
}
}
other ->
other
end)
end
defp unwrap_for_get({:ok, [value | _]}, true, _, _resource), do: {:ok, value}
defp unwrap_for_get({:ok, []}, true, true, resource),

View file

@ -25,6 +25,21 @@ defmodule Ash.Test.CalculationTest do
end
end
defmodule NameWithUsersName do
# Don't do this kind of thing in real life
use Ash.Calculation
def load(_, _, _) do
[:full_name]
end
def calculate(records, _opts, %{actor: actor}) do
Enum.map(records, fn record ->
record.full_name <> " " <> actor.first_name <> " " <> actor.last_name
end)
end
end
defmodule ConcatWithLoad do
# An example concatenation calculation, that accepts the delimiter as an argument
use Ash.Calculation
@ -160,6 +175,7 @@ defmodule Ash.Test.CalculationTest do
calculate :best_friends_name, :string, BestFriendsName
calculate :names_of_best_friends_of_me, :string, NamesOfBestFriendsOfMe
calculate :name_with_users_name, :string, NameWithUsersName
calculate :conditional_full_name,
:string,
@ -258,6 +274,14 @@ defmodule Ash.Test.CalculationTest do
assert %{slug: "zach daniel123"} = Api.load!(user, [:slug])
end
test "calculations can access the actor", %{user1: user1, user2: user2} do
assert %{
name_with_users_name: "zach daniel brian cranston"
} =
user1
|> Api.load!(:name_with_users_name, actor: user2)
end
test "it loads anything specified by the load callback" do
full_names =
User