mirror of
https://github.com/ash-project/ash_phoenix.git
synced 2024-09-19 06:42:47 +12:00
improvement: add to_filter_map/1
to filter_form
This commit is contained in:
parent
cb9fc8758b
commit
ecdce6e894
2 changed files with 117 additions and 0 deletions
|
@ -118,6 +118,93 @@ defmodule AshPhoenix.FilterForm do
|
|||
|> set_validity()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a filter map that can be provided to `Ash.Filter.parse`
|
||||
|
||||
This allows for things like saving a stored filter. Does not currently support parameterizing calculations or functions.
|
||||
"""
|
||||
def to_filter_map(form) do
|
||||
if form.valid? do
|
||||
case do_to_filter_map(form, form.resource) do
|
||||
{:ok, expr} ->
|
||||
{:ok, expr}
|
||||
|
||||
{:error, %__MODULE__{} = form} ->
|
||||
{:error, form}
|
||||
|
||||
{:error, error} ->
|
||||
{:error, %{form | errors: List.wrap(error)}}
|
||||
end
|
||||
else
|
||||
{:error, form}
|
||||
end
|
||||
end
|
||||
|
||||
defp do_to_filter_map(%__MODULE__{components: []}, _), do: {:ok, true}
|
||||
|
||||
defp do_to_filter_map(
|
||||
%__MODULE__{components: components, operator: operator, negated?: negated?} = form,
|
||||
resource
|
||||
) do
|
||||
{filters, components, errors?} =
|
||||
Enum.reduce(components, {[], [], false}, fn component, {filters, components, errors?} ->
|
||||
case do_to_filter_map(component, resource) do
|
||||
{:ok, component_filter} ->
|
||||
{filters ++ [component_filter], components ++ [component], errors?}
|
||||
|
||||
{:error, component} ->
|
||||
{filters, components ++ [component], true}
|
||||
end
|
||||
end)
|
||||
|
||||
if errors? do
|
||||
{:error, %{form | components: components}}
|
||||
else
|
||||
expr = %{to_string(operator) => filters}
|
||||
|
||||
if negated? do
|
||||
{:ok, %{"not" => expr}}
|
||||
else
|
||||
{:ok, expr}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp do_to_filter_map(
|
||||
%Predicate{
|
||||
field: field,
|
||||
value: value,
|
||||
operator: operator,
|
||||
negated?: negated?,
|
||||
path: path
|
||||
},
|
||||
_resource
|
||||
) do
|
||||
expr =
|
||||
put_at_path(%{}, Enum.map(path, &to_string/1), %{
|
||||
to_string(field) => %{to_string(operator) => value}
|
||||
})
|
||||
|
||||
if negated? do
|
||||
{:ok, %{"not" => expr}}
|
||||
else
|
||||
{:ok, expr}
|
||||
end
|
||||
end
|
||||
|
||||
defp put_at_path(_, [], value), do: value
|
||||
|
||||
defp put_at_path(map, [key], value) do
|
||||
Map.put(map || %{}, key, value)
|
||||
end
|
||||
|
||||
defp put_at_path(map, [key | rest], value) do
|
||||
map
|
||||
|> Kernel.||(%{})
|
||||
|> Map.put_new(key, %{})
|
||||
|> Map.update!(key, &put_at_path(&1, rest, value))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a filter expression that can be provided to Ash.Query.filter/2
|
||||
|
||||
|
|
|
@ -107,6 +107,8 @@ defmodule AshPhoenix.FilterFormTest do
|
|||
test "An empty form returns the filter `true`" do
|
||||
form = FilterForm.new(Post)
|
||||
|
||||
assert FilterForm.to_filter_map(form) == {:ok, true}
|
||||
|
||||
assert Ash.Query.equivalent_to?(
|
||||
FilterForm.filter!(Post, form),
|
||||
true
|
||||
|
@ -122,6 +124,9 @@ defmodule AshPhoenix.FilterFormTest do
|
|||
}
|
||||
)
|
||||
|
||||
assert FilterForm.to_filter_map(form) ==
|
||||
{:ok, %{"and" => [%{"title" => %{"eq" => "new post"}}]}}
|
||||
|
||||
assert Ash.Query.equivalent_to?(
|
||||
FilterForm.filter!(Post, form),
|
||||
title == "new post"
|
||||
|
@ -185,12 +190,37 @@ defmodule AshPhoenix.FilterFormTest do
|
|||
}
|
||||
)
|
||||
|
||||
assert {:ok, %{"and" => [%{"comments" => %{"text" => %{"contains" => "new"}}}]} = filter} =
|
||||
FilterForm.to_filter_map(form)
|
||||
|
||||
Ash.Filter.parse!(Post, filter) |> IO.inspect()
|
||||
|
||||
assert Ash.Query.equivalent_to?(
|
||||
FilterForm.filter!(Post, form),
|
||||
contains(comments.text, "new")
|
||||
)
|
||||
end
|
||||
|
||||
test "predicates can reference paths for to_filter_map" do
|
||||
form =
|
||||
FilterForm.new(Post,
|
||||
params: %{
|
||||
field: :text,
|
||||
operator: :eq,
|
||||
path: "comments",
|
||||
value: "new"
|
||||
}
|
||||
)
|
||||
|
||||
assert {:ok, %{"and" => [%{"comments" => %{"text" => %{"eq" => "new"}}}]} = filter} =
|
||||
FilterForm.to_filter_map(form)
|
||||
|
||||
assert Ash.Query.equivalent_to?(
|
||||
Ash.Query.filter(Post, ^Ash.Filter.parse!(Post, filter)),
|
||||
comments.text == "new"
|
||||
)
|
||||
end
|
||||
|
||||
test "predicates with fields that refer to a relationship will be appended to the path" do
|
||||
form =
|
||||
FilterForm.new(Post,
|
||||
|
|
Loading…
Reference in a new issue