mirror of
https://github.com/ash-project/ash.git
synced 2024-09-20 05:23:03 +12:00
fix: properly handle limit/offset for aggregates
This commit is contained in:
parent
adbac1b7da
commit
eab194fac5
3 changed files with 41 additions and 19 deletions
|
@ -5,8 +5,6 @@ defmodule Ash.Actions.Aggregate do
|
||||||
def run(api, query, aggregates, opts) do
|
def run(api, query, aggregates, opts) do
|
||||||
query = %{query | api: api}
|
query = %{query | api: api}
|
||||||
{query, opts} = Ash.Actions.Helpers.add_process_context(query.api, query, opts)
|
{query, opts} = Ash.Actions.Helpers.add_process_context(query.api, query, opts)
|
||||||
action = query.action || Ash.Resource.Info.primary_action!(query.resource, :read)
|
|
||||||
opts = Keyword.put_new(opts, :read_action, action.name)
|
|
||||||
|
|
||||||
with %{valid?: true} = query <- Ash.Actions.Read.handle_attribute_multitenancy(query) do
|
with %{valid?: true} = query <- Ash.Actions.Read.handle_attribute_multitenancy(query) do
|
||||||
aggregates
|
aggregates
|
||||||
|
@ -15,19 +13,24 @@ defmodule Ash.Actions.Aggregate do
|
||||||
agg_authorize? = aggregate.authorize? && opts[:authorize?]
|
agg_authorize? = aggregate.authorize? && opts[:authorize?]
|
||||||
|
|
||||||
read_action =
|
read_action =
|
||||||
aggregate.read_action || query.action ||
|
aggregate.read_action || (query.action && query.action.name) ||
|
||||||
Ash.Resource.Info.primary_action!(query.resource, :read).name
|
Ash.Resource.Info.primary_action!(query.resource, :read).name
|
||||||
|
|
||||||
{agg_authorize?, read_action}
|
{agg_authorize?, read_action}
|
||||||
|
|
||||||
{_name, _kind} ->
|
{_name, _kind} ->
|
||||||
{!!opts[:authorize?], opts[:read_action]}
|
{!!opts[:authorize?],
|
||||||
|
opts[:read_action] || opts[:action] || (query.action && query.action.name) ||
|
||||||
|
Ash.Resource.Info.primary_action!(query.resource, :read).name}
|
||||||
|
|
||||||
{_name, _kind, agg_opts} ->
|
{_name, _kind, agg_opts} ->
|
||||||
authorize? =
|
authorize? =
|
||||||
Keyword.get(agg_opts, :authorize?, true) && opts[:authorize?]
|
Keyword.get(agg_opts, :authorize?, true) && opts[:authorize?]
|
||||||
|
|
||||||
{authorize?, agg_opts[:read_action] || opts[:read_action] || action.name}
|
{authorize?,
|
||||||
|
agg_opts[:read_action] || opts[:read_action] || agg_opts[:action] || opts[:action] ||
|
||||||
|
(query.action && query.action.name) ||
|
||||||
|
Ash.Resource.Info.primary_action!(query.resource, :read).name}
|
||||||
end)
|
end)
|
||||||
|> Enum.reduce_while({:ok, %{}}, fn
|
|> Enum.reduce_while({:ok, %{}}, fn
|
||||||
{{agg_authorize?, read_action}, aggregates}, {:ok, acc} ->
|
{{agg_authorize?, read_action}, aggregates}, {:ok, acc} ->
|
||||||
|
@ -54,7 +57,7 @@ defmodule Ash.Actions.Aggregate do
|
||||||
aggregates: List.wrap(aggregates),
|
aggregates: List.wrap(aggregates),
|
||||||
actor: opts[:actor],
|
actor: opts[:actor],
|
||||||
tenant: opts[:tenant],
|
tenant: opts[:tenant],
|
||||||
action: action.name,
|
action: read_action,
|
||||||
authorize?: opts[:authorize?]
|
authorize?: opts[:authorize?]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +68,11 @@ defmodule Ash.Actions.Aggregate do
|
||||||
with {:ok, query} <- authorize_query(query, opts, agg_authorize?),
|
with {:ok, query} <- authorize_query(query, opts, agg_authorize?),
|
||||||
{:ok, aggregates} <- validate_aggregates(query, aggregates, opts),
|
{:ok, aggregates} <- validate_aggregates(query, aggregates, opts),
|
||||||
{:ok, data_layer_query} <-
|
{:ok, data_layer_query} <-
|
||||||
Ash.Query.data_layer_query(%Ash.Query{resource: query.resource}),
|
Ash.Query.data_layer_query(%Ash.Query{
|
||||||
|
resource: query.resource,
|
||||||
|
limit: query.limit,
|
||||||
|
offset: query.offset
|
||||||
|
}),
|
||||||
{:ok, result} <-
|
{:ok, result} <-
|
||||||
Ash.DataLayer.run_aggregate_query(
|
Ash.DataLayer.run_aggregate_query(
|
||||||
data_layer_query,
|
data_layer_query,
|
||||||
|
@ -88,16 +95,10 @@ defmodule Ash.Actions.Aggregate do
|
||||||
|> Ash.Query.do_filter(right.filter)
|
|> Ash.Query.do_filter(right.filter)
|
||||||
|> Ash.Query.sort(right.sort, prepend?: true)
|
|> Ash.Query.sort(right.sort, prepend?: true)
|
||||||
|> Ash.Query.distinct_sort(right.distinct_sort, prepend?: true)
|
|> Ash.Query.distinct_sort(right.distinct_sort, prepend?: true)
|
||||||
|> Ash.Query.limit(right.limit)
|
|
||||||
|> Ash.Query.set_tenant(right.tenant)
|
|> Ash.Query.set_tenant(right.tenant)
|
||||||
|> merge_offset(right.offset)
|
|
||||||
|> Ash.Query.set_context(right.context)
|
|> Ash.Query.set_context(right.context)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp merge_offset(query, offset) do
|
|
||||||
Ash.Query.offset(query, query.offset + (offset || 0))
|
|
||||||
end
|
|
||||||
|
|
||||||
defp authorize_query(query, opts, agg_authorize?) do
|
defp authorize_query(query, opts, agg_authorize?) do
|
||||||
if agg_authorize? do
|
if agg_authorize? do
|
||||||
case query.api.can(query, opts[:actor],
|
case query.api.can(query, opts[:actor],
|
||||||
|
@ -155,6 +156,7 @@ defmodule Ash.Actions.Aggregate do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp set_opts(query, specified, others) do
|
defp set_opts(query, specified, others) do
|
||||||
|
query = Ash.Query.unset(query, [:limit, :offset])
|
||||||
{agg_opts, _} = Ash.Query.Aggregate.split_aggregate_opts(others)
|
{agg_opts, _} = Ash.Query.Aggregate.split_aggregate_opts(others)
|
||||||
|
|
||||||
agg_opts = Keyword.merge(agg_opts, specified)
|
agg_opts = Keyword.merge(agg_opts, specified)
|
||||||
|
|
|
@ -384,6 +384,14 @@ defmodule Ash.Query.Aggregate do
|
||||||
defp build_query(_resource, nil), do: {:ok, nil}
|
defp build_query(_resource, nil), do: {:ok, nil}
|
||||||
|
|
||||||
defp build_query(resource, build_opts) when is_list(build_opts) do
|
defp build_query(resource, build_opts) when is_list(build_opts) do
|
||||||
|
cond do
|
||||||
|
build_opts[:limit] ->
|
||||||
|
{:error, "Cannot set limit on aggregate query"}
|
||||||
|
|
||||||
|
build_opts[:offset] && build_opts[:offset] != 0 ->
|
||||||
|
{:error, "Cannot set offset on aggregate query"}
|
||||||
|
|
||||||
|
true ->
|
||||||
case Ash.Query.build(resource, build_opts) do
|
case Ash.Query.build(resource, build_opts) do
|
||||||
%{valid?: true} = query ->
|
%{valid?: true} = query ->
|
||||||
build_query(resource, query)
|
build_query(resource, query)
|
||||||
|
@ -392,10 +400,20 @@ defmodule Ash.Query.Aggregate do
|
||||||
{:error, query.errors}
|
{:error, query.errors}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp build_query(_resource, %Ash.Query{} = query) do
|
defp build_query(_resource, %Ash.Query{} = query) do
|
||||||
|
cond do
|
||||||
|
query.limit ->
|
||||||
|
{:error, "Cannot set limit on aggregate query"}
|
||||||
|
|
||||||
|
query.offset && query.offset != 0 ->
|
||||||
|
{:error, "Cannot set offset on aggregate query"}
|
||||||
|
|
||||||
|
true ->
|
||||||
{:ok, Ash.Query.unset(query, [:load, :limit, :offset])}
|
{:ok, Ash.Query.unset(query, [:load, :limit, :offset])}
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@doc false
|
@doc false
|
||||||
def kind_to_type({:custom, type}, _attribute_type, _attribute_constraints), do: {:ok, type, []}
|
def kind_to_type({:custom, type}, _attribute_type, _attribute_constraints), do: {:ok, type, []}
|
||||||
|
|
|
@ -195,6 +195,8 @@ defmodule Ash.Test.Actions.AggregateTest do
|
||||||
assert %{count: 0} = Api.aggregate!(Post, {:count, :count}, authorize?: true)
|
assert %{count: 0} = Api.aggregate!(Post, {:count, :count}, authorize?: true)
|
||||||
assert 0 = Api.count!(Post, authorize?: true)
|
assert 0 = Api.count!(Post, authorize?: true)
|
||||||
|
|
||||||
|
Application.put_env(:foo, :bar, true)
|
||||||
|
|
||||||
assert %{count: 1} =
|
assert %{count: 1} =
|
||||||
Post
|
Post
|
||||||
|> Ash.Query.for_read(:unpublic)
|
|> Ash.Query.for_read(:unpublic)
|
||||||
|
|
Loading…
Reference in a new issue