mirror of
https://github.com/ash-project/ash.git
synced 2024-09-19 13:03:02 +12:00
fix: properly pass actor when running filters at runtime
fix: misplaced curly bracket when handling struct type casting
This commit is contained in:
parent
ec12332e95
commit
b6e1e80fc2
12 changed files with 211 additions and 81 deletions
|
@ -67,10 +67,12 @@ defmodule Ash.Actions.Destroy.Bulk do
|
|||
opts
|
||||
end
|
||||
|
||||
query =
|
||||
{query, opts} =
|
||||
if query.__validated_for_action__ do
|
||||
query
|
||||
{query, opts}
|
||||
else
|
||||
{query, opts} = Ash.Actions.Helpers.set_context_and_get_opts(domain, query, opts)
|
||||
|
||||
query =
|
||||
Ash.Query.for_read(
|
||||
query,
|
||||
|
@ -80,9 +82,7 @@ defmodule Ash.Actions.Destroy.Bulk do
|
|||
tenant: opts[:tenant]
|
||||
)
|
||||
|
||||
{query, _opts} = Ash.Actions.Helpers.set_context_and_get_opts(domain, query, opts)
|
||||
|
||||
query
|
||||
{query, opts}
|
||||
end
|
||||
|
||||
query = %{query | domain: domain}
|
||||
|
|
|
@ -999,7 +999,9 @@ defmodule Ash.Actions.Read.Relationships do
|
|||
case Ash.Filter.Runtime.filter_matches(
|
||||
related_query.domain,
|
||||
value,
|
||||
query.filter
|
||||
query.filter,
|
||||
tenant: query.tenant,
|
||||
actor: query.actor
|
||||
) do
|
||||
{:ok, value} ->
|
||||
value
|
||||
|
|
|
@ -22,10 +22,12 @@ defmodule Ash.Actions.Update.Bulk do
|
|||
opts
|
||||
end
|
||||
|
||||
query =
|
||||
{query, opts} =
|
||||
if query.__validated_for_action__ do
|
||||
query
|
||||
{query, opts}
|
||||
else
|
||||
{query, opts} = Ash.Actions.Helpers.set_context_and_get_opts(domain, query, opts)
|
||||
|
||||
query =
|
||||
Ash.Query.for_read(
|
||||
query,
|
||||
|
@ -35,9 +37,7 @@ defmodule Ash.Actions.Update.Bulk do
|
|||
tenant: opts[:tenant]
|
||||
)
|
||||
|
||||
{query, _opts} = Ash.Actions.Helpers.set_context_and_get_opts(domain, query, opts)
|
||||
|
||||
query
|
||||
{query, opts}
|
||||
end
|
||||
|
||||
query = %{query | domain: domain}
|
||||
|
|
|
@ -339,7 +339,12 @@ defmodule Ash.DataLayer.Ets do
|
|||
},
|
||||
{:ok, acc} ->
|
||||
results
|
||||
|> filter_matches(Map.get(query || %{}, :filter), domain, context[:tenant])
|
||||
|> filter_matches(
|
||||
Map.get(query || %{}, :filter),
|
||||
domain,
|
||||
context[:tenant],
|
||||
context[:actor]
|
||||
)
|
||||
|> case do
|
||||
{:ok, matches} ->
|
||||
field = field || Enum.at(Ash.Resource.Info.primary_key(resource), 0)
|
||||
|
@ -386,7 +391,14 @@ defmodule Ash.DataLayer.Ets do
|
|||
) do
|
||||
with {:ok, records} <- get_records(resource, tenant),
|
||||
{:ok, records} <-
|
||||
filter_matches(records, filter, domain, context[:private][:tenant], parent),
|
||||
filter_matches(
|
||||
records,
|
||||
filter,
|
||||
domain,
|
||||
context[:private][:tenant],
|
||||
context[:private][:actor],
|
||||
parent
|
||||
),
|
||||
records <- Sort.runtime_sort(records, distinct_sort || sort, domain: domain),
|
||||
records <- Sort.runtime_distinct(records, distinct, domain: domain),
|
||||
records <- Sort.runtime_sort(records, sort, domain: domain),
|
||||
|
@ -625,7 +637,9 @@ defmodule Ash.DataLayer.Ets do
|
|||
case Ash.Expr.eval_hydrated(expression,
|
||||
record: record,
|
||||
resource: resource,
|
||||
domain: domain
|
||||
domain: domain,
|
||||
actor: calculation.context.actor,
|
||||
tenant: calculation.context.tenant
|
||||
) do
|
||||
{:ok, value} ->
|
||||
if calculation.load do
|
||||
|
@ -722,7 +736,13 @@ defmodule Ash.DataLayer.Ets do
|
|||
domain
|
||||
),
|
||||
{:ok, filtered} <-
|
||||
filter_matches(related, query.filter, domain, context[:tenant]),
|
||||
filter_matches(
|
||||
related,
|
||||
query.filter,
|
||||
domain,
|
||||
context[:tenant],
|
||||
context[:actor]
|
||||
),
|
||||
sorted <- Sort.runtime_sort(filtered, query.sort, domain: domain) do
|
||||
field = field || Enum.at(Ash.Resource.Info.primary_key(query.resource), 0)
|
||||
|
||||
|
@ -1015,27 +1035,38 @@ defmodule Ash.DataLayer.Ets do
|
|||
filter,
|
||||
domain,
|
||||
_tenant,
|
||||
actor,
|
||||
parent \\ nil,
|
||||
conflicting_upsert_values \\ nil
|
||||
)
|
||||
|
||||
defp filter_matches([], _, _domain, _tenant, _parent, _conflicting_upsert_values),
|
||||
defp filter_matches([], _, _domain, _tenant, _actor, _parent, _conflicting_upsert_values),
|
||||
do: {:ok, []}
|
||||
|
||||
defp filter_matches(records, nil, _domain, _tenant, _parent, _conflicting_upsert_values),
|
||||
do: {:ok, records}
|
||||
defp filter_matches(
|
||||
records,
|
||||
nil,
|
||||
_domain,
|
||||
_tenant,
|
||||
_actor,
|
||||
_parent,
|
||||
_conflicting_upsert_values
|
||||
),
|
||||
do: {:ok, records}
|
||||
|
||||
defp filter_matches(
|
||||
records,
|
||||
filter,
|
||||
domain,
|
||||
tenant,
|
||||
actor,
|
||||
parent,
|
||||
conflicting_upsert_values
|
||||
) do
|
||||
Ash.Filter.Runtime.filter_matches(domain, records, filter,
|
||||
parent: parent,
|
||||
tenant: tenant,
|
||||
actor: actor,
|
||||
conflicting_upsert_values: conflicting_upsert_values
|
||||
)
|
||||
end
|
||||
|
@ -1137,8 +1168,9 @@ defmodule Ash.DataLayer.Ets do
|
|||
[result],
|
||||
filter,
|
||||
domain,
|
||||
context.private[:tenant],
|
||||
context.private[:actor],
|
||||
nil,
|
||||
context[:tenant],
|
||||
conflicting_upsert_values
|
||||
)
|
||||
end
|
||||
|
@ -1383,10 +1415,17 @@ defmodule Ash.DataLayer.Ets do
|
|||
@doc false
|
||||
@impl true
|
||||
def destroy(resource, %{data: record, filter: filter} = changeset) do
|
||||
do_destroy(resource, record, changeset.tenant, filter, changeset.domain)
|
||||
do_destroy(
|
||||
resource,
|
||||
record,
|
||||
changeset.tenant,
|
||||
filter,
|
||||
changeset.domain,
|
||||
changeset.context[:private][:actor]
|
||||
)
|
||||
end
|
||||
|
||||
defp do_destroy(resource, record, tenant, filter, domain) do
|
||||
defp do_destroy(resource, record, tenant, filter, domain, actor) do
|
||||
with {:ok, table} <- wrap_or_create_table(resource, tenant) do
|
||||
pkey = Map.take(record, Ash.Resource.Info.primary_key(resource))
|
||||
|
||||
|
@ -1394,7 +1433,7 @@ defmodule Ash.DataLayer.Ets do
|
|||
case ETS.Set.get(table, pkey) do
|
||||
{:ok, {_key, record}} when is_map(record) ->
|
||||
with {:ok, record} <- cast_record(record, resource),
|
||||
{:ok, [_]} <- filter_matches([record], filter, domain, tenant) do
|
||||
{:ok, [_]} <- filter_matches([record], filter, domain, tenant, actor) do
|
||||
with {:ok, _} <- ETS.Set.delete(table, pkey) do
|
||||
:ok
|
||||
end
|
||||
|
@ -1489,7 +1528,8 @@ defmodule Ash.DataLayer.Ets do
|
|||
{pkey, changeset.attributes, changeset.atomics, changeset.filter},
|
||||
changeset.domain,
|
||||
changeset.tenant,
|
||||
resource
|
||||
resource,
|
||||
changeset.context[:private][:actor]
|
||||
),
|
||||
{:ok, record} <- cast_record(record, resource) do
|
||||
new_pkey = pkey_map(resource, record)
|
||||
|
@ -1534,7 +1574,14 @@ defmodule Ash.DataLayer.Ets do
|
|||
end)
|
||||
end
|
||||
|
||||
defp do_update(table, {pkey, record, atomics, changeset_filter}, domain, tenant, resource) do
|
||||
defp do_update(
|
||||
table,
|
||||
{pkey, record, atomics, changeset_filter},
|
||||
domain,
|
||||
tenant,
|
||||
resource,
|
||||
actor
|
||||
) do
|
||||
attributes = resource |> Ash.Resource.Info.attributes()
|
||||
|
||||
case dump_to_native(record, attributes) do
|
||||
|
@ -1543,7 +1590,7 @@ defmodule Ash.DataLayer.Ets do
|
|||
{:ok, {_key, record}} when is_map(record) ->
|
||||
with {:ok, casted_record} <- cast_record(record, resource),
|
||||
{:ok, [casted_record]} <-
|
||||
filter_matches([casted_record], changeset_filter, domain, tenant) do
|
||||
filter_matches([casted_record], changeset_filter, domain, tenant, actor) do
|
||||
case atomics do
|
||||
empty when empty in [nil, []] ->
|
||||
data = Map.merge(record, casted)
|
||||
|
|
|
@ -70,6 +70,7 @@ defmodule Ash.DataLayer.Mnesia do
|
|||
:limit,
|
||||
:tenant,
|
||||
:sort,
|
||||
context: %{},
|
||||
relationships: %{},
|
||||
offset: 0,
|
||||
aggregates: [],
|
||||
|
@ -203,7 +204,12 @@ defmodule Ash.DataLayer.Mnesia do
|
|||
},
|
||||
{:ok, acc} ->
|
||||
results
|
||||
|> filter_matches(Map.get(query || %{}, :filter), domain, query.tenant)
|
||||
|> filter_matches(
|
||||
Map.get(query || %{}, :filter),
|
||||
domain,
|
||||
query.tenant,
|
||||
query.context[:private][:actor]
|
||||
)
|
||||
|> case do
|
||||
{:ok, matches} ->
|
||||
field = field || Enum.at(Ash.Resource.Info.primary_key(resource), 0)
|
||||
|
@ -243,6 +249,12 @@ defmodule Ash.DataLayer.Mnesia do
|
|||
{:ok, %{query | tenant: tenant}}
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def set_context(_resource, query, context) do
|
||||
{:ok, %{query | context: context}}
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def run_query(
|
||||
|
@ -255,7 +267,8 @@ defmodule Ash.DataLayer.Mnesia do
|
|||
limit: limit,
|
||||
sort: sort,
|
||||
aggregates: aggregates,
|
||||
tenant: tenant
|
||||
tenant: tenant,
|
||||
context: context
|
||||
},
|
||||
_resource
|
||||
) do
|
||||
|
@ -265,7 +278,8 @@ defmodule Ash.DataLayer.Mnesia do
|
|||
end),
|
||||
{:ok, records} <-
|
||||
records |> Enum.map(&elem(&1, 2)) |> Ash.DataLayer.Ets.cast_records(resource),
|
||||
{:ok, filtered} <- filter_matches(records, filter, domain, tenant),
|
||||
{:ok, filtered} <-
|
||||
filter_matches(records, filter, domain, tenant, context[:private][:actor]),
|
||||
offset_records <-
|
||||
filtered |> Sort.runtime_sort(sort, domain: domain) |> Enum.drop(offset || 0),
|
||||
limited_records <- do_limit(offset_records, limit),
|
||||
|
@ -296,10 +310,10 @@ defmodule Ash.DataLayer.Mnesia do
|
|||
defp do_limit(records, nil), do: records
|
||||
defp do_limit(records, limit), do: Enum.take(records, limit)
|
||||
|
||||
defp filter_matches(records, nil, _domain, _tenant), do: {:ok, records}
|
||||
defp filter_matches(records, nil, _domain, _tenant, _), do: {:ok, records}
|
||||
|
||||
defp filter_matches(records, filter, domain, tenant) do
|
||||
Ash.Filter.Runtime.filter_matches(domain, records, filter, tenant: tenant)
|
||||
defp filter_matches(records, filter, domain, tenant, actor) do
|
||||
Ash.Filter.Runtime.filter_matches(domain, records, filter, tenant: tenant, actor: actor)
|
||||
end
|
||||
|
||||
@doc false
|
||||
|
|
|
@ -29,7 +29,18 @@ defmodule Ash.DataLayer.Simple do
|
|||
|
||||
defmodule Query do
|
||||
@moduledoc false
|
||||
defstruct [:data, :resource, :filter, :domain, :limit, :offset, sort: [], data_set?: false]
|
||||
defstruct [
|
||||
:data,
|
||||
:resource,
|
||||
:filter,
|
||||
:domain,
|
||||
:limit,
|
||||
:offset,
|
||||
:tenant,
|
||||
sort: [],
|
||||
data_set?: false,
|
||||
context: %{}
|
||||
]
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
@ -60,11 +71,20 @@ defmodule Ash.DataLayer.Simple do
|
|||
end
|
||||
|
||||
def run_query(
|
||||
%{data: data, sort: sort, domain: domain, filter: filter, limit: limit, offset: offset},
|
||||
%{
|
||||
data: data,
|
||||
sort: sort,
|
||||
domain: domain,
|
||||
filter: filter,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
tenant: tenant,
|
||||
context: context
|
||||
},
|
||||
_resource
|
||||
) do
|
||||
data
|
||||
|> do_filter_matches(filter, domain)
|
||||
|> do_filter_matches(filter, domain, tenant, context)
|
||||
|> case do
|
||||
{:ok, results} ->
|
||||
{:ok,
|
||||
|
@ -90,8 +110,11 @@ defmodule Ash.DataLayer.Simple do
|
|||
end
|
||||
end
|
||||
|
||||
defp do_filter_matches(data, filter, domain) do
|
||||
Ash.Filter.Runtime.filter_matches(domain, data, filter)
|
||||
defp do_filter_matches(data, filter, domain, tenant, context) do
|
||||
Ash.Filter.Runtime.filter_matches(domain, data, filter,
|
||||
actor: context[:private][:actor],
|
||||
tenant: tenant
|
||||
)
|
||||
end
|
||||
|
||||
@doc false
|
||||
|
@ -105,7 +128,7 @@ defmodule Ash.DataLayer.Simple do
|
|||
end
|
||||
|
||||
@doc false
|
||||
def set_tenant(_, query, _), do: {:ok, query}
|
||||
def set_tenant(_, query, tenant), do: {:ok, %{query | tenant: tenant}}
|
||||
|
||||
@doc false
|
||||
def filter(query, filter, _resource) do
|
||||
|
@ -122,7 +145,7 @@ defmodule Ash.DataLayer.Simple do
|
|||
with {:ok, data_layer_context} <- Map.fetch(context, :data_layer),
|
||||
{:ok, data} <- Map.fetch(data_layer_context, :data),
|
||||
{:ok, resource_data} <- Map.fetch(data, query.resource) do
|
||||
{:ok, %{query | data_set?: true, data: resource_data || []}}
|
||||
{:ok, %{query | data_set?: true, data: resource_data || [], context: context}}
|
||||
else
|
||||
_ ->
|
||||
{:ok, query}
|
||||
|
|
|
@ -111,7 +111,9 @@ defmodule Ash.Expr do
|
|||
opts[:parent],
|
||||
opts[:resource],
|
||||
opts[:domain],
|
||||
opts[:unknown_on_unknown_refs?]
|
||||
opts[:unknown_on_unknown_refs?],
|
||||
opts[:actor],
|
||||
opts[:tenant]
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -54,7 +54,12 @@ defmodule Ash.Filter.Runtime do
|
|||
|> load_all(refs_to_load)
|
||||
|> Ash.Query.set_context(%{private: %{internal?: true}})
|
||||
|
||||
Ash.load!(records, load, authorize?: false, domain: domain, tenant: opts[:tenant])
|
||||
Ash.load!(records, load,
|
||||
authorize?: false,
|
||||
domain: domain,
|
||||
tenant: opts[:tenant],
|
||||
actor: opts[:actor]
|
||||
)
|
||||
end
|
||||
|
||||
Enum.reduce_while(records, {:ok, []}, fn record, {:ok, records} ->
|
||||
|
@ -166,7 +171,9 @@ defmodule Ash.Filter.Runtime do
|
|||
parent \\ nil,
|
||||
resource \\ nil,
|
||||
domain \\ nil,
|
||||
unknown_on_unknown_refs? \\ false
|
||||
unknown_on_unknown_refs? \\ false,
|
||||
actor \\ nil,
|
||||
tenant \\ nil
|
||||
) do
|
||||
if domain && record do
|
||||
refs_to_load =
|
||||
|
@ -187,7 +194,12 @@ defmodule Ash.Filter.Runtime do
|
|||
|> load_all(refs)
|
||||
|> Ash.Query.set_context(%{private: %{internal?: true}})
|
||||
|
||||
Ash.load!(record, load, domain: domain, authorize?: false)
|
||||
Ash.load!(record, load,
|
||||
domain: domain,
|
||||
authorize?: false,
|
||||
tenant: tenant,
|
||||
actor: actor
|
||||
)
|
||||
end
|
||||
|
||||
do_match(record, expression, parent, resource, unknown_on_unknown_refs?)
|
||||
|
@ -451,7 +463,7 @@ defmodule Ash.Filter.Runtime do
|
|||
if unknown_on_unknown_refs? do
|
||||
:unknown
|
||||
else
|
||||
nil
|
||||
{:ok, nil}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@ defmodule Ash.Policy.FilterCheck do
|
|||
|
||||
defp try_eval(expression, %{
|
||||
resource: resource,
|
||||
action_input: %Ash.ActionInput{} = action_input,
|
||||
action_input: %Ash.ActionInput{tenant: tenant} = action_input,
|
||||
actor: actor
|
||||
}) do
|
||||
expression =
|
||||
|
@ -140,22 +140,12 @@ defmodule Ash.Policy.FilterCheck do
|
|||
public?: false
|
||||
}) do
|
||||
{:ok, hydrated} ->
|
||||
Ash.Expr.eval_hydrated(hydrated, resource: resource, unknown_on_unknown_refs?: true)
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
defp try_eval(expression, %{resource: resource, query: %Ash.Query{} = query}) do
|
||||
case Ash.Filter.hydrate_refs(expression, %{
|
||||
resource: resource,
|
||||
aggregates: query.aggregates,
|
||||
calculations: query.calculations,
|
||||
public?: false
|
||||
}) do
|
||||
{:ok, hydrated} ->
|
||||
Ash.Expr.eval_hydrated(hydrated, resource: resource, unknown_on_unknown_refs?: true)
|
||||
Ash.Expr.eval_hydrated(hydrated,
|
||||
resource: resource,
|
||||
unknown_on_unknown_refs?: true,
|
||||
actor: actor,
|
||||
tenant: tenant
|
||||
)
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
|
@ -164,7 +154,31 @@ defmodule Ash.Policy.FilterCheck do
|
|||
|
||||
defp try_eval(expression, %{
|
||||
resource: resource,
|
||||
changeset: %Ash.Changeset{action_type: :create} = changeset,
|
||||
query: %Ash.Query{tenant: tenant} = query,
|
||||
actor: actor
|
||||
}) do
|
||||
case Ash.Filter.hydrate_refs(expression, %{
|
||||
resource: resource,
|
||||
aggregates: query.aggregates,
|
||||
calculations: query.calculations,
|
||||
public?: false
|
||||
}) do
|
||||
{:ok, hydrated} ->
|
||||
Ash.Expr.eval_hydrated(hydrated,
|
||||
resource: resource,
|
||||
unknown_on_unknown_refs?: true,
|
||||
actor: actor,
|
||||
tenant: tenant
|
||||
)
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
defp try_eval(expression, %{
|
||||
resource: resource,
|
||||
changeset: %Ash.Changeset{action_type: :create, tenant: tenant} = changeset,
|
||||
actor: actor
|
||||
}) do
|
||||
expression =
|
||||
|
@ -184,7 +198,12 @@ defmodule Ash.Policy.FilterCheck do
|
|||
public?: false
|
||||
}) do
|
||||
{:ok, hydrated} ->
|
||||
Ash.Expr.eval_hydrated(hydrated, resource: resource, unknown_on_unknown_refs?: true)
|
||||
Ash.Expr.eval_hydrated(hydrated,
|
||||
resource: resource,
|
||||
unknown_on_unknown_refs?: true,
|
||||
actor: actor,
|
||||
tenant: tenant
|
||||
)
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
|
@ -193,7 +212,8 @@ defmodule Ash.Policy.FilterCheck do
|
|||
|
||||
defp try_eval(expression, %{
|
||||
resource: resource,
|
||||
changeset: %Ash.Changeset{data: data} = changeset
|
||||
changeset: %Ash.Changeset{data: data, tenant: tenant} = changeset,
|
||||
actor: actor
|
||||
}) do
|
||||
case Ash.Filter.hydrate_refs(expression, %{
|
||||
resource: resource,
|
||||
|
@ -204,7 +224,9 @@ defmodule Ash.Policy.FilterCheck do
|
|||
{:ok, hydrated} ->
|
||||
opts = [
|
||||
resource: resource,
|
||||
unknown_on_unknown_refs?: true
|
||||
unknown_on_unknown_refs?: true,
|
||||
actor: actor,
|
||||
tenant: tenant
|
||||
]
|
||||
|
||||
# We don't want to authorize on stale data in real life
|
||||
|
@ -231,7 +253,7 @@ defmodule Ash.Policy.FilterCheck do
|
|||
end
|
||||
end
|
||||
|
||||
defp try_eval(expression, %{resource: resource}) do
|
||||
defp try_eval(expression, %{resource: resource, actor: actor}) do
|
||||
case Ash.Filter.hydrate_refs(expression, %{
|
||||
resource: resource,
|
||||
aggregates: %{},
|
||||
|
@ -239,7 +261,11 @@ defmodule Ash.Policy.FilterCheck do
|
|||
public?: false
|
||||
}) do
|
||||
{:ok, hydrated} ->
|
||||
Ash.Expr.eval_hydrated(hydrated, resource: resource, unknown_on_unknown_refs?: true)
|
||||
Ash.Expr.eval_hydrated(hydrated,
|
||||
resource: resource,
|
||||
unknown_on_unknown_refs?: true,
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
|
|
|
@ -2913,7 +2913,11 @@ defmodule Ash.Query do
|
|||
"Could not determine domain for #{inspect(query)}, please provide the `:domain` option."
|
||||
|
||||
with {:ok, records} <-
|
||||
Ash.Filter.Runtime.filter_matches(domain, records, query.filter, parent: opts[:parent]),
|
||||
Ash.Filter.Runtime.filter_matches(domain, records, query.filter,
|
||||
parent: opts[:parent],
|
||||
actor: opts[:actor] || query.context[:private][:actor],
|
||||
tenant: opts[:tenant] || query.tenant
|
||||
),
|
||||
records <- Sort.runtime_sort(records, query.distinct_sort || query.sort, domain: domain),
|
||||
records <- Sort.runtime_distinct(records, query.distinct, domain: domain),
|
||||
records <- Sort.runtime_sort(records, query.sort, domain: domain),
|
||||
|
|
|
@ -45,6 +45,7 @@ defmodule Ash.Resource.Calculation.Expression do
|
|||
case Ash.Expr.eval_hydrated(expression,
|
||||
record: record,
|
||||
resource: resource,
|
||||
actor: context.actor,
|
||||
unknown_on_unknown_refs?: true
|
||||
) do
|
||||
{:ok, value} ->
|
||||
|
|
|
@ -264,21 +264,20 @@ defmodule Ash.Type.Struct do
|
|||
else
|
||||
keys = Map.keys(value)
|
||||
|
||||
cond do
|
||||
Enum.all?(keys, &is_atom/1) ->
|
||||
{:ok, struct(struct, value)}
|
||||
if Enum.all?(keys, &is_atom/1) do
|
||||
{:ok, struct(struct, value)}
|
||||
else
|
||||
{:ok,
|
||||
Map.delete(struct.__struct__, :__struct__)
|
||||
|> Enum.reduce({:ok, struct(struct)}, fn {key, _value}, {:ok, acc} ->
|
||||
case Map.fetch(value, to_string(key)) do
|
||||
{:ok, val} ->
|
||||
{:ok, Map.put(acc, key, val)}
|
||||
|
||||
Enum.all?(keys, &is_binary/1) ->
|
||||
{:ok, Map.delete(struct.__struct__, :__struct__)}
|
||||
|> Enum.reduce({:ok, struct(struct)}, fn {key, _value}, {:ok, acc} ->
|
||||
case Map.fetch(value, to_string(key)) do
|
||||
{:ok, val} ->
|
||||
{:ok, Map.put(acc, key, val)}
|
||||
|
||||
:error ->
|
||||
{:ok, acc}
|
||||
end
|
||||
end)
|
||||
:error ->
|
||||
{:ok, acc}
|
||||
end
|
||||
end)}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue