mirror of
https://github.com/ash-project/ash.git
synced 2024-09-20 05:23:03 +12:00
improvement: return invalid primary key errors for Api.get
when the input can't be cast
This commit is contained in:
parent
a479970e23
commit
865f9aa253
3 changed files with 59 additions and 9 deletions
|
@ -9,8 +9,10 @@ defmodule Ash.Error.Invalid.InvalidPrimaryKey do
|
|||
|
||||
def code(_), do: "invalid_primary_key"
|
||||
|
||||
def message(%{resource: resource, value: value}) do
|
||||
"invalid primary key #{inspect(value)} provided for resource #{inspect(resource)}"
|
||||
def message(%{resource: _resource, value: value}) do
|
||||
# TODO: removed the resource so as not to leak it, but it would be nice to show it internally somehow
|
||||
# perhaps an `internal_message` that is only shown on raised exceptions
|
||||
"invalid primary key #{inspect(value)} provided"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -409,14 +409,26 @@ defmodule Ash.Filter do
|
|||
|
||||
case {primary_key, id} do
|
||||
{[field], [{field, value}]} ->
|
||||
{:ok, %{field => value}}
|
||||
case cast_value(resource, field, value, id) do
|
||||
{:ok, value} ->
|
||||
{:ok, %{field => value}}
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
end
|
||||
|
||||
{[field], value} when not keyval? ->
|
||||
{:ok, %{field => value}}
|
||||
case cast_value(resource, field, value, id) do
|
||||
{:ok, value} ->
|
||||
{:ok, %{field => value}}
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
end
|
||||
|
||||
{fields, value} ->
|
||||
if keyval? do
|
||||
with :error <- get_keys(value, fields),
|
||||
with :error <- get_keys(value, fields, resource),
|
||||
:error <- get_identity_filter(resource, id) do
|
||||
{:error, InvalidPrimaryKey.exception(resource: resource, value: id)}
|
||||
end
|
||||
|
@ -426,16 +438,30 @@ defmodule Ash.Filter do
|
|||
end
|
||||
end
|
||||
|
||||
defp get_keys(value, fields) do
|
||||
defp get_keys(value, fields, resource) do
|
||||
original_value = value
|
||||
|
||||
Enum.reduce_while(fields, {:ok, %{}}, fn field, {:ok, vals} ->
|
||||
case fetch(value, field) do
|
||||
{:ok, value} ->
|
||||
{:cont, {:ok, Map.put(vals, field, value)}}
|
||||
case cast_value(resource, field, value, original_value) do
|
||||
{:ok, value} ->
|
||||
{:cont, {:ok, Map.put(vals, field, value)}}
|
||||
|
||||
{:error, error} ->
|
||||
{:halt, {:error, error}}
|
||||
end
|
||||
|
||||
:error ->
|
||||
case fetch(value, to_string(field)) do
|
||||
{:ok, value} ->
|
||||
{:cont, {:ok, Map.put(vals, field, value)}}
|
||||
case cast_value(resource, field, value, original_value) do
|
||||
{:ok, value} ->
|
||||
{:cont, {:ok, Map.put(vals, field, value)}}
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
end
|
||||
|
||||
:error ->
|
||||
{:halt, :error}
|
||||
|
@ -444,6 +470,22 @@ defmodule Ash.Filter do
|
|||
end)
|
||||
end
|
||||
|
||||
defp cast_value(resource, field, value, id) do
|
||||
attribute = Ash.Resource.Info.attribute(resource, field)
|
||||
|
||||
if attribute do
|
||||
case Ash.Type.cast_input(attribute.type, value, attribute.constraints) do
|
||||
{:ok, value} ->
|
||||
{:ok, value}
|
||||
|
||||
_ ->
|
||||
{:error, InvalidPrimaryKey.exception(resource: resource, value: id)}
|
||||
end
|
||||
else
|
||||
{:error, InvalidPrimaryKey.exception(resource: resource, value: id)}
|
||||
end
|
||||
end
|
||||
|
||||
defp fetch(val, key) when is_map(val), do: Map.fetch(val, key)
|
||||
defp fetch(val, key) when is_list(val) and is_atom(key), do: Keyword.fetch(val, key)
|
||||
defp fetch(_, _), do: :error
|
||||
|
@ -454,7 +496,7 @@ defmodule Ash.Filter do
|
|||
|> Enum.find_value(
|
||||
:error,
|
||||
fn identity ->
|
||||
case get_keys(id, identity.keys) do
|
||||
case get_keys(id, identity.keys, resource) do
|
||||
{:ok, key} ->
|
||||
{:ok, key}
|
||||
|
||||
|
|
|
@ -143,6 +143,12 @@ defmodule Ash.Test.Actions.ReadTest do
|
|||
assert ^post = strip_metadata(Api.get!(Post, post.id))
|
||||
end
|
||||
|
||||
test "it gives an invalid primary key error when invalid input is provided" do
|
||||
assert_raise Ash.Error.Invalid, ~r/invalid primary key "not good"/, fn ->
|
||||
Api.get!(Post, "not good")
|
||||
end
|
||||
end
|
||||
|
||||
test "it raises when there is no matching record" do
|
||||
res =
|
||||
assert_raise Ash.Error.Invalid, fn ->
|
||||
|
|
Loading…
Reference in a new issue