Packages
ash
2.6.30
3.29.3
3.29.2
3.29.1
3.29.0
3.28.0
3.27.8
3.27.7
3.27.6
3.27.5
3.27.4
3.27.3
3.27.2
3.27.1
3.27.0
3.26.0
3.25.2
3.25.1
3.25.0
retired
3.24.7
3.24.6
3.24.5
3.24.4
3.24.3
3.24.2
3.24.1
3.24.0
3.23.1
3.23.0
3.22.2
3.22.1
3.22.0
3.21.3
3.21.2
3.21.1
3.21.0
3.20.0
3.19.3
3.19.2
3.19.1
3.19.0
3.18.0
3.17.1
3.17.0
3.16.0
3.15.0
3.14.1
3.14.0
retired
3.13.2
3.13.1
3.13.0
3.12.0
3.11.3
3.11.2
3.11.1
3.11.0
3.10.1
3.10.0
3.9.0
3.8.0
3.7.6
3.7.5
3.7.4
3.7.3
3.7.2
3.7.1
3.7.0
retired
3.6.3
retired
3.6.2
3.6.1
3.6.0
3.5.43
3.5.42
3.5.41
3.5.40
3.5.39
3.5.38
3.5.37
3.5.36
3.5.35
3.5.34
3.5.33
3.5.32
3.5.31
3.5.30
3.5.29
3.5.28
3.5.27
3.5.26
3.5.25
3.5.24
3.5.23
3.5.22
3.5.21
3.5.20
3.5.19
3.5.18
3.5.17
3.5.16
3.5.15
3.5.14
3.5.13
3.5.12
3.5.11
3.5.10
3.5.9
3.5.8
3.5.7
3.5.6
3.5.5
3.5.4
3.5.3
3.5.2
3.5.1
3.5.0
3.4.74
retired
3.4.73
3.4.72
3.4.71
3.4.70
3.4.69
3.4.68
3.4.67
3.4.66
3.4.65
3.4.64
3.4.63
3.4.62
3.4.61
3.4.60
3.4.59
3.4.58
3.4.57
3.4.56
3.4.55
3.4.54
3.4.53
3.4.52
3.4.51
3.4.50
3.4.49
3.4.48
3.4.47
3.4.46
3.4.45
3.4.44
3.4.43
3.4.42
3.4.41
3.4.40
3.4.39
3.4.38
3.4.37
3.4.36
3.4.35
3.4.34
3.4.33
3.4.32
3.4.31
3.4.30
3.4.29
3.4.28
3.4.27
3.4.26
3.4.25
3.4.24
3.4.23
3.4.22
3.4.21
3.4.20
3.4.19
3.4.18
3.4.17
3.4.16
3.4.15
3.4.14
3.4.13
3.4.12
3.4.11
3.4.10
3.4.9
3.4.8
3.4.7
3.4.6
3.4.5
3.4.4
3.4.3
3.4.2
3.4.1
3.4.0
3.3.3
3.3.2
3.3.1
3.3.0
3.2.6
3.2.5
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.1.8
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.16
3.0.15
3.0.14
3.0.13
3.0.12
3.0.11
3.0.10
3.0.9
3.0.8
3.0.7
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
3.0.0-rc.46
3.0.0-rc.45
3.0.0-rc.44
3.0.0-rc.43
3.0.0-rc.42
3.0.0-rc.41
3.0.0-rc.40
3.0.0-rc.39
3.0.0-rc.38
3.0.0-rc.37
3.0.0-rc.36
3.0.0-rc.35
3.0.0-rc.34
3.0.0-rc.33
3.0.0-rc.32
3.0.0-rc.31
3.0.0-rc.29
3.0.0-rc.28
3.0.0-rc.27
3.0.0-rc.26
3.0.0-rc.25
3.0.0-rc.24
3.0.0-rc.23
3.0.0-rc.22
3.0.0-rc.21
3.0.0-rc.20
3.0.0-rc.19
3.0.0-rc.18
3.0.0-rc.17
3.0.0-rc.16
3.0.0-rc.15
3.0.0-rc.14
3.0.0-rc.13
3.0.0-rc.12
3.0.0-rc.11
3.0.0-rc.10
3.0.0-rc.9
3.0.0-rc.8
3.0.0-rc.7
3.0.0-rc.6
3.0.0-rc.5
3.0.0-rc.4
3.0.0-rc.3
3.0.0-rc.1
3.0.0-rc.0
2.21.15
2.21.14
2.21.13
2.21.12
2.21.11
2.21.10
2.21.9
2.21.8
2.21.7
2.21.6
2.21.5
2.21.4
2.21.3
2.21.2
2.21.1
2.21.0
2.20.3
2.20.2
2.20.1
2.20.0
2.19.14
2.19.13
2.19.12
2.19.11
2.19.10
2.19.9
2.19.8
2.19.7
2.19.6
2.19.5
2.19.4
2.19.3
retired
2.19.2
retired
2.19.1
retired
2.19.0
retired
2.18.2
2.18.1
2.18.0
2.17.24
2.17.23
2.17.22
2.17.21
2.17.20
2.17.19
2.17.18
2.17.17
2.17.16
2.17.15
2.17.14
2.17.13
2.17.12
2.17.11
2.17.10
2.17.9
2.17.8
2.17.7
2.17.6
2.17.5
2.17.4
2.17.3
2.17.2
2.17.1
2.17.0
2.16.1
2.16.0
2.15.20
2.15.19
2.15.18
2.15.17
2.15.16
2.15.15
2.15.14
2.15.13
2.15.12
2.15.11
2.15.10
2.15.9
2.15.8
2.15.7
2.15.6
2.15.5
2.15.4
2.15.2
2.15.1
2.15.0
2.14.21
2.14.20
2.14.19
2.14.18
2.14.17
2.14.16
2.14.15
2.14.14
2.14.13
2.14.12
2.14.11
2.14.10
2.14.9
2.14.8
2.14.7
2.14.6
2.14.5
2.14.4
2.14.3
2.14.2
2.14.1
2.14.0
2.13.4
retired
2.13.3
2.13.2
2.13.1
2.13.0
2.12.1
2.12.0
2.11.11
2.11.10
2.11.9
2.11.8
2.11.7
2.11.6
2.11.5
2.11.4
2.11.3
2.11.2
2.11.1
2.11.0
2.11.0-rc.3
2.11.0-rc.2
2.11.0-rc.1
2.11.0-rc.0
2.10.2
2.10.1
2.10.0
2.9.29
2.9.28
2.9.27
2.9.26
2.9.25
2.9.24
2.9.23
2.9.22
2.9.21
2.9.20
2.9.19
2.9.18
2.9.17
2.9.16
2.9.15
2.9.14
2.9.13
2.9.12
2.9.11
2.9.10
2.9.9
2.9.8
2.9.7
2.9.6
2.9.5
2.9.4
2.9.3
2.9.2
2.9.1
2.9.0
2.8.1
2.8.0
2.7.1
2.7.0
2.6.31
2.6.30
2.6.29
2.6.28
2.6.27
2.6.26
2.6.25
2.6.24
2.6.23
2.6.22
2.6.21
2.6.20
2.6.19
2.6.18
2.6.17
2.6.16
2.6.15
2.6.14
2.6.13
2.6.11
2.6.10
2.6.9
2.6.8
2.6.7
2.6.6
2.6.5
2.6.4
2.6.3
2.6.2
2.6.1
2.6.0
2.5.16
2.5.15
2.5.14
2.5.13
2.5.12
2.5.11
2.5.10
2.5.9
2.5.8
2.5.7
2.5.6
2.5.5
2.5.4
2.5.3
2.5.2
2.5.1
2.5.0
2.5.0-rc.6
2.5.0-rc.5
2.5.0-rc.4
2.5.0-rc.3
2.5.0-rc.2
2.5.0-rc.1
2.5.0-rc.0
2.4.30
2.4.29
2.4.28
2.4.27
2.4.26
2.4.25
2.4.24
2.4.23
2.4.22
2.4.21
2.4.20
2.4.19
2.4.18
2.4.17
2.4.16
2.4.15
2.4.14
2.4.13
2.4.12
2.4.11
2.4.10
2.4.9
2.4.8
2.4.7
2.4.6
2.4.5
2.4.4
2.4.3
2.4.2
2.4.1
2.4.0
2.3.0
2.2.0
2.1.0
2.0.0
2.0.0-rc.15
2.0.0-rc.14
2.0.0-rc.13
2.0.0-rc.12
2.0.0-rc.11
2.0.0-rc.10
2.0.0-rc.9
2.0.0-rc.8
2.0.0-rc.7
2.0.0-rc.6
2.0.0-rc.5
2.0.0-rc.4
2.0.0-rc.3
2.0.0-rc.2
2.0.0-rc.1
2.0.0-rc.0
2.0.0-pre.8
2.0.0-pre.7
2.0.0-pre.6
2.0.0-pre.5
2.0.0-pre.4
2.0.0-pre.3
2.0.0-pre.2
2.0.0-pre.1
2.0.0-pre.0
1.53.3
1.53.2
1.53.0
1.52.0-rc.22
1.52.0-rc.21
1.52.0-rc.20
1.52.0-rc.19
1.52.0-rc.18
1.52.0-rc.17
1.52.0-rc.16
1.52.0-rc.15
1.52.0-rc.14
1.52.0-rc.13
1.52.0-rc.12
1.52.0-rc.11
1.52.0-rc.10
1.52.0-rc.9
1.52.0-rc.8
1.52.0-rc.7
1.52.0-rc.6
1.52.0-rc.5
1.52.0-rc.4
1.52.0-rc.3
1.52.0-rc.2
1.52.0-rc.1
1.52.0-rc.0
1.51.2
1.51.1
retired
1.51.0
1.50.21
1.50.20
1.50.19
1.50.18
1.50.17
1.50.16
1.50.15
1.50.14
1.50.13
1.50.12
1.50.11
1.50.10
1.50.9
1.50.8
1.50.7
1.50.6
1.50.5
1.50.4
1.50.3
1.50.2
1.50.1
1.50.0
1.49.0
1.48.0-rc.30
1.48.0-rc.29
1.48.0-rc.28
1.48.0-rc.27
1.48.0-rc.26
1.48.0-rc.25
1.48.0-rc.24
1.48.0-rc.23
1.48.0-rc.22
1.48.0-rc.21
1.48.0-rc.20
1.48.0-rc.19
1.48.0-rc.18
1.48.0-rc.17
1.48.0-rc.16
1.48.0-rc.15
1.48.0-rc.14
1.48.0-rc.13
1.48.0-rc.12
1.48.0-rc.11
1.48.0-rc.10
1.48.0-rc.9
1.48.0-rc.8
1.48.0-rc.7
1.48.0-rc.6
1.48.0-rc.5
1.48.0-rc.4
1.48.0-rc.3
1.48.0-rc.2
1.48.0-rc.1
1.48.0-rc.0
1.47.12
1.47.11
1.47.10
1.47.9
1.47.8
1.47.7
1.47.6
1.47.5
1.47.4
1.47.3
1.47.2
1.47.1
1.47.0
1.46.13
1.46.12
1.46.11
1.46.10
1.46.9
1.46.8
1.46.7
1.46.6
1.46.5
1.46.4
1.46.3
1.46.2
1.46.1
1.46.0
1.45.0-rc9
1.45.0-rc8
1.45.0-rc7
1.45.0-rc6
1.45.0-rc5
1.45.0-rc4
1.45.0-rc3
1.45.0-rc20
1.45.0-rc2
1.45.0-rc19
1.45.0-rc18
1.45.0-rc17
1.45.0-rc16
1.45.0-rc15
1.45.0-rc14
1.45.0-rc13
1.45.0-rc12
1.45.0-rc11
1.45.0-rc10
1.45.0-rc1
1.45.0-rc0
1.44.13
1.44.12
1.44.11
1.44.10
1.44.9
1.44.8
1.44.7
1.44.6
1.44.5
1.44.4
1.44.3
1.44.2
1.44.1
1.44.0
1.43.12
1.43.11
1.43.10
1.43.9
1.43.8
1.43.7
1.43.6
1.43.5
1.43.4
1.43.3
1.43.2
1.43.1
1.43.0
1.42.0
1.41.12
1.41.11
1.41.10
1.41.9
1.41.8
1.41.7
1.41.6
1.41.5
1.41.4
1.41.3
1.41.2
1.41.1
1.41.0
1.40.0
1.39.7
1.39.6
1.39.5
1.39.4
1.39.3
1.39.2
1.39.1
1.39.0
1.38.0
1.37.2
1.37.1
1.37.0
1.36.22
1.36.21
1.36.19
1.36.18
1.36.17
1.36.16
1.36.15
1.36.14
1.36.13
1.36.12
1.36.11
1.36.10
1.36.9
1.36.8
1.36.7
1.36.6
1.36.5
1.36.4
1.36.3
1.36.2
1.36.0
1.35.1
1.35.0
1.34.9
1.34.8
1.34.7
1.34.6
1.34.5
1.34.4
1.34.3
1.34.2
1.34.1
1.34.0
1.33.0
1.32.2
1.32.1
1.32.0
1.31.1
1.31.0
1.30.2
1.30.1
1.29.0-rc1
1.29.0-rc0
1.28.1
1.28.0
1.27.1
1.27.0
1.26.13
1.26.12
1.26.11
1.26.10
1.26.9
1.26.8
1.26.7
1.26.6
1.26.5
1.26.4
1.26.2
1.26.1
1.26.0
1.25.8
1.25.7
1.25.6
1.25.5
1.25.4
1.25.3
1.25.2
1.25.1
1.25.0
1.24.2
1.24.1
1.24.0
1.23.3
1.23.2
1.23.1
1.23.0
1.22.1
1.22.0
1.20.1
1.20.0
1.19.1
1.19.0
1.18.1
1.18.0
1.17.1
1.17.0
1.16.2
1.15.1
1.15.0
1.14.0
1.13.4
1.13.3
1.13.2
1.13.1
1.13.0
1.12.0
1.11.1
1.11.0
1.10.0
1.9.0
1.8.0
1.7.0
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.4.1
1.4.0
1.3.1
1.3.0
1.2.1
1.2.0
1.1.3
1.1.2
1.1.0
1.0.3
1.0.2
1.0.1
1.0.0
0.13.1
0.13.0
0.12.0
0.10.0
0.9.1
0.9.0
0.8.0
0.7.0
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.0
0.3.0
0.2.0
0.1.9
0.1.8
0.1.3
0.1.1
0.1.0
A declarative, extensible framework for building Elixir applications.
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/ash/actions/helpers.ex
defmodule Ash.Actions.Helpers do
@moduledoc false
require Logger
def validate_calculation_load!(%{__struct__: Ash.Query}, module) do
raise """
`#{inspect(module)}.load/3` returned a query.
Returning a query from the `load/3` callback of a calculation is now deprecated.
Instead, return the load statement itself, i.e instead of `Ash.Query.load(query, [...])`,
just return `[...]`. This is so that Ash can examine the requirements of just this single
calculation to ensure that all required values are present
"""
end
def validate_calculation_load!(other, _), do: other
defp set_context(%{__struct__: Ash.Changeset} = changeset, context),
do: Ash.Changeset.set_context(changeset, context)
defp set_context(%{__struct__: Ash.Query} = query, context),
do: Ash.Query.set_context(query, context)
def add_process_context(api, query_or_changeset, opts) do
query_or_changeset = set_context(query_or_changeset, opts[:context] || %{})
api = api || query_or_changeset.api
opts =
case query_or_changeset.context do
%{
private: %{
actor: actor
}
} ->
Keyword.put_new(opts, :actor, actor)
_ ->
opts
end
opts =
case query_or_changeset.context do
%{
private: %{
authorize?: authorize?
}
} ->
Keyword.put_new(opts, :authorize?, authorize?)
_ ->
opts
end
opts =
case query_or_changeset.context do
%{
private: %{
tracer: tracer
}
} ->
Keyword.put_new(opts, :tracer, tracer)
_ ->
opts
end
opts =
opts
|> add_actor(query_or_changeset, api)
|> add_authorize?(api)
|> add_tenant()
|> add_tracer()
query_or_changeset = add_context(query_or_changeset, opts)
{query_or_changeset, opts}
end
defp add_context(query_or_changeset, opts) do
context = Process.get(:ash_context, %{}) || %{}
private_context = Map.new(Keyword.take(opts, [:actor, :authorize?]))
case query_or_changeset do
%{__struct__: Ash.Query} ->
query_or_changeset
|> Ash.Query.set_context(context)
|> Ash.Query.set_context(%{private: private_context})
%{__struct__: Ash.Changeset} ->
query_or_changeset
|> Ash.Changeset.set_context(context)
|> Ash.Changeset.set_context(%{
private: private_context
})
end
end
defp add_actor(opts, query_or_changeset, api) do
opts =
if Keyword.has_key?(opts, :actor) do
opts
else
case Process.get(:ash_actor) do
{:actor, value} ->
Keyword.put(opts, :actor, value)
_ ->
opts
end
end
if api do
if !internal?(query_or_changeset) && !Keyword.has_key?(opts, :actor) &&
Ash.Api.Info.require_actor?(api) do
raise Ash.Error.Forbidden.ApiRequiresActor, api: api
end
opts
else
# The only time api would be nil here is when we call this helper inside of `Changeset.for_*` and `Query.for_read`
# meaning this will be run again later with the api, so we skip the validations on the api
opts
end
end
defp internal?(%{context: %{private: %{internal?: true}}}), do: true
defp internal?(_), do: false
defp add_authorize?(opts, api) do
opts =
if Keyword.has_key?(opts, :authorize?) do
opts
else
case Process.get(:ash_authorize?) do
{:authorize?, value} ->
Keyword.put(opts, :authorize?, value)
_ ->
opts
end
end
if api do
case Ash.Api.Info.authorize(api) do
:always ->
Keyword.put(opts, :authorize?, true)
:by_default ->
Keyword.put_new(opts, :authorize?, true)
:when_requested ->
if Keyword.has_key?(opts, :actor) do
Keyword.put_new(opts, :authorize?, true)
else
Keyword.put(opts, :authorize?, opts[:authorize?] || Keyword.has_key?(opts, :actor))
end
end
else
# The only time api would be nil here is when we call this helper inside of `Changeset.for_*` and `Query.for_read`
# meaning this will be run again later with the api, so we skip the validations on the api
opts
end
end
defp add_tenant(opts) do
if Keyword.has_key?(opts, :tenant) do
opts
else
case Process.get(:ash_tenant) do
{:tenant, value} ->
Keyword.put(opts, :tenant, value)
_ ->
opts
end
end
end
defp add_tracer(opts) do
if Keyword.has_key?(opts, :tracer) do
opts
else
case Process.get(:ash_tracer) do
{:tracer, value} ->
Keyword.put(opts, :tracer, value)
_ ->
case Application.get_env(:ash, :tracer) do
nil ->
opts
tracer ->
Keyword.put(opts, :tracer, tracer)
end
end
end
end
def warn_missed!(resource, action, result) do
case Map.get(result, :resource_notifications, []) do
empty when empty in [nil, []] ->
:ok
missed ->
case Application.get_env(:ash, :missed_notifications, :warn) do
:ignore ->
:ok
:raise ->
raise """
Missed #{Enum.count(missed)} notifications in action #{inspect(resource)}.#{action.name}.
This happens when the resources are in a transaction, and you did not pass
`return_notifications?: true`. If you are in a changeset hook, you can
return the notifications. If not, you can send the notifications using
`Ash.Notifier.notify/1` once your resources are out of a transaction.
"""
:warn ->
{:current_stacktrace, stacktrace} = Process.info(self(), :current_stacktrace)
Logger.warn("""
Missed #{Enum.count(missed)} notifications in action #{inspect(resource)}.#{action.name}.
This happens when the resources are in a transaction, and you did not pass
`return_notifications?: true`. If you are in a changeset hook, you can
return the notifications. If not, you can send the notifications using
`Ash.Notifier.notify/1` once your resources are out of a transaction.
#{Exception.format_stacktrace(stacktrace)}
""")
end
end
end
def process_errors(changeset, [error]) do
%{changeset | errors: []}
|> Ash.Changeset.add_error(error)
|> Map.get(:errors)
|> case do
[error] ->
error
errors ->
errors
end
end
def process_errors(changeset, errors) when is_list(errors) do
%{changeset | errors: []}
|> Ash.Changeset.add_error(errors)
|> Map.get(:errors)
end
def process_errors(changeset, error), do: process_errors(changeset, [error])
def load_runtime_types({:ok, results}, query, attributes?) do
load_runtime_types(results, query, attributes?)
end
def load_runtime_types({:error, error}, _query, _attributes?) do
{:error, error}
end
def load_runtime_types(results, query, attributes?) when is_list(results) do
attributes = runtime_attributes(query, attributes?)
calcs = runtime_calculations(query)
if Enum.empty?(attributes) && Enum.empty?(calcs) do
{:ok, results}
else
Enum.reduce_while(results, {:ok, []}, fn result, {:ok, results} ->
case do_load_runtime_types(result, attributes, calcs) do
{:ok, result} ->
{:cont, {:ok, [result | results]}}
other ->
{:halt, other}
end
end)
|> case do
{:ok, results} -> {:ok, Enum.reverse(results)}
other -> other
end
end
end
def load_runtime_types(nil, _, _attributes?), do: {:ok, nil}
def load_runtime_types(result, query, attributes?) do
do_load_runtime_types(
result,
runtime_attributes(query, attributes?),
runtime_calculations(query)
)
end
defp runtime_attributes(query, true) do
case query.select do
nil ->
Ash.Resource.Info.attributes(query.resource)
select ->
Enum.map(select, &Ash.Resource.Info.attribute(query.resource, &1))
end
|> Enum.reject(fn %{type: type, constraints: constraints} ->
Ash.Type.cast_in_query?(type, constraints)
end)
end
defp runtime_attributes(_, _), do: []
defp runtime_calculations(query) do
query.calculations
|> Kernel.||(%{})
|> Enum.reject(fn {_name, calc} ->
constraints = Map.get(calc, :constraints, [])
if function_exported?(Ash.Type, :cast_in_query?, 2) do
Ash.Type.cast_in_query?(calc.type, constraints)
else
Ash.Type.cast_in_query?(calc.type)
end
end)
end
defp do_load_runtime_types(record, select, calculations) do
select
|> Enum.reduce_while({:ok, record}, fn attr, {:ok, record} ->
case Ash.Type.cast_stored(attr.type, Map.get(record, attr.name), attr.constraints) do
{:ok, value} ->
{:cont, {:ok, Map.put(record, attr.name, value)}}
other ->
{:halt, other}
end
end)
|> case do
{:ok, record} ->
Enum.reduce_while(calculations, {:ok, record}, fn {name, calc}, {:ok, record} ->
case calc.load do
nil ->
case Ash.Type.cast_stored(
calc.type,
Map.get(record.calculations || %{}, calc.name),
Map.get(calc, :constraints, [])
) do
{:ok, value} ->
{:cont, {:ok, Map.update!(record, :calculations, &Map.put(&1, name, value))}}
other ->
{:halt, other}
end
load ->
case Ash.Type.cast_stored(
calc.type,
Map.get(record, load),
Map.get(calc, :constraints, [])
) do
{:ok, casted} ->
{:cont, {:ok, Map.put(record, load, casted)}}
other ->
{:halt, other}
end
end
end)
other ->
other
end
end
def select({:ok, results}, query) do
{:ok, select(results, query)}
end
def select({:error, error}, _query) do
{:error, error}
end
def select(results, query) when is_list(results) do
Enum.map(results, &select(&1, query))
end
def select(nil, _), do: nil
def select(result, %{select: nil}) do
result
end
def select(result, %{resource: resource, select: select}) do
resource
|> Ash.Resource.Info.attributes()
|> Enum.flat_map(fn attribute ->
if attribute.always_select? || attribute.primary_key? || attribute.name in select do
[]
else
[attribute.name]
end
end)
|> Enum.reduce(result, fn key, record ->
Map.put(record, key, nil)
end)
|> Ash.Resource.put_metadata(:selected, select)
end
def attributes_to_select(%{select: nil, resource: resource}) do
resource
|> Ash.Resource.Info.attributes()
|> Enum.map(& &1.name)
end
def attributes_to_select(%{select: select, resource: resource}) do
resource
|> Ash.Resource.Info.attributes()
|> Enum.flat_map(fn attribute ->
if attribute.always_select? || attribute.primary_key? || attribute.name in select do
[attribute.name]
else
[]
end
end)
end
end