mirror of
https://github.com/ash-project/ash.git
synced 2024-09-20 05:23:03 +12:00
fix: properly error on types when evaluating expressions at runtime
This commit is contained in:
parent
4b88300e00
commit
a5f51e8f1b
5 changed files with 57 additions and 10 deletions
|
@ -195,12 +195,21 @@ defmodule Ash.Filter.Runtime do
|
|||
nil ->
|
||||
{:ok, true}
|
||||
|
||||
%op{__operator__?: true, left: left, right: right} = operator ->
|
||||
%op{__operator__?: true, left: left, right: right} ->
|
||||
with {:ok, [left, right]} <-
|
||||
resolve_exprs([left, right], record),
|
||||
{:known, val} <- op.evaluate(%{operator | left: left, right: right}) do
|
||||
{:op, {:ok, %op{} = new_operator}} <-
|
||||
{:op, Ash.Query.Operator.try_cast_with_ref(op, left, right)},
|
||||
{:known, val} <-
|
||||
op.evaluate(new_operator) do
|
||||
{:ok, val}
|
||||
else
|
||||
{:op, {:error, error}} ->
|
||||
{:error, error}
|
||||
|
||||
{:op, {:ok, expr}} ->
|
||||
do_match(record, expr)
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
|
||||
|
@ -213,9 +222,15 @@ defmodule Ash.Filter.Runtime do
|
|||
|
||||
%func{__function__?: true, arguments: arguments} = function ->
|
||||
with {:ok, args} <- resolve_exprs(arguments, record),
|
||||
{:args, args} when not is_nil(args) <-
|
||||
{:args, try_cast_arguments(func.args(), args)},
|
||||
{:known, val} <- func.evaluate(%{function | arguments: args}) do
|
||||
{:ok, val}
|
||||
else
|
||||
{:args, nil} ->
|
||||
{:error,
|
||||
"Could not cast function arguments for #{func.name()}/#{Enum.count(arguments)}"}
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
|
||||
|
@ -333,11 +348,19 @@ defmodule Ash.Filter.Runtime do
|
|||
end)
|
||||
end
|
||||
|
||||
defp resolve_expr(%mod{__predicate__?: _, left: left, right: right} = pred, record) do
|
||||
defp resolve_expr(%mod{__predicate__?: _, left: left, right: right}, record) do
|
||||
with {:ok, [left, right]} <- resolve_exprs([left, right], record),
|
||||
{:known, val} <- mod.evaluate(%{pred | left: left, right: right}) do
|
||||
{:op, {:ok, %mod{} = new_pred}} <-
|
||||
{:op, Ash.Query.Operator.try_cast_with_ref(mod, left, right)},
|
||||
{:known, val} <- mod.evaluate(new_pred) do
|
||||
{:ok, val}
|
||||
else
|
||||
{:op, {:error, error}} ->
|
||||
{:error, error}
|
||||
|
||||
{:op, {:ok, expr}} ->
|
||||
resolve_expr(expr, record)
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
|
||||
|
@ -351,9 +374,14 @@ defmodule Ash.Filter.Runtime do
|
|||
|
||||
defp resolve_expr(%mod{__predicate__?: _, arguments: args} = pred, record) do
|
||||
with {:ok, args} <- resolve_exprs(args, record),
|
||||
{:args, args} when not is_nil(args) <-
|
||||
{:args, try_cast_arguments(mod.args(), args)},
|
||||
{:known, val} <- mod.evaluate(%{pred | arguments: args}) do
|
||||
{:ok, val}
|
||||
else
|
||||
{:args, nil} ->
|
||||
{:error, "Could not cast function arguments for #{mod.name()}/#{Enum.count(args)}"}
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
|
||||
|
@ -367,6 +395,16 @@ defmodule Ash.Filter.Runtime do
|
|||
|
||||
defp resolve_expr(other, _), do: {:ok, other}
|
||||
|
||||
defp try_cast_arguments(configured_args, args) do
|
||||
given_arg_count = Enum.count(args)
|
||||
|
||||
configured_args
|
||||
|> Enum.filter(fn args ->
|
||||
Enum.count(args) == given_arg_count
|
||||
end)
|
||||
|> Enum.find_value(&Ash.Query.Function.try_cast_arguments(&1, args))
|
||||
end
|
||||
|
||||
defp resolve_ref(%Ref{attribute: attribute, relationship_path: path}, record) do
|
||||
name =
|
||||
case attribute do
|
||||
|
|
|
@ -80,7 +80,7 @@ defmodule Ash.Query.Function do
|
|||
end
|
||||
end
|
||||
|
||||
defp try_cast_arguments(configured_args, args) do
|
||||
def try_cast_arguments(configured_args, args) do
|
||||
args
|
||||
|> Enum.zip(configured_args)
|
||||
|> Enum.reduce_while({:ok, []}, fn
|
||||
|
|
|
@ -71,7 +71,8 @@ defmodule Ash.Query.Operator do
|
|||
end
|
||||
end
|
||||
|
||||
defp try_cast_with_ref(mod, left, right) do
|
||||
@doc false
|
||||
def try_cast_with_ref(mod, left, right) do
|
||||
Enum.find_value(mod.types(), fn type ->
|
||||
try_cast(left, right, type)
|
||||
end)
|
||||
|
|
|
@ -14,6 +14,8 @@ defmodule Ash.Query.Type do
|
|||
:error -> :error
|
||||
{:ok, val} -> {:ok, val}
|
||||
end
|
||||
else
|
||||
:error
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -104,12 +104,18 @@ defmodule Ash.Test.CalculationTest do
|
|||
|
||||
calculate :conditional_full_name,
|
||||
:string,
|
||||
expr(if(first_name and last_name, first_name <> " " <> last_name, "(none)"))
|
||||
expr(
|
||||
if(
|
||||
not is_nil(first_name) and not is_nil(last_name),
|
||||
first_name <> " " <> last_name,
|
||||
"(none)"
|
||||
)
|
||||
)
|
||||
|
||||
calculate :conditional_full_name_block,
|
||||
:string,
|
||||
expr(
|
||||
if first_name and last_name do
|
||||
if not is_nil(first_name) and not is_nil(last_name) do
|
||||
first_name <> " " <> last_name
|
||||
else
|
||||
"(none)"
|
||||
|
@ -120,10 +126,10 @@ defmodule Ash.Test.CalculationTest do
|
|||
:string,
|
||||
expr(
|
||||
cond do
|
||||
first_name and last_name ->
|
||||
not is_nil(first_name) and not is_nil(last_name) ->
|
||||
first_name <> " " <> last_name
|
||||
|
||||
first_name ->
|
||||
not is_nil(first_name) ->
|
||||
first_name
|
||||
|
||||
true ->
|
||||
|
|
Loading…
Reference in a new issue