improvement: expose union errors with path when tag is set

This commit is contained in:
Zach Daniel 2023-08-15 14:07:28 -07:00
parent 5549bbf290
commit 8c1f334075
3 changed files with 75 additions and 9 deletions

View file

@ -455,10 +455,10 @@ defmodule Ash.Type do
|> Ash.Error.flatten_preserving_keywords()
|> Enum.map(fn
message when is_binary(message) ->
[message: message, index: index]
[message: message, index: index, path: [index]]
keyword ->
Keyword.put(keyword, :index, index)
Keyword.merge(keyword, index: index, path: [index])
end)
{:halt, {:error, errors}}

View file

@ -234,10 +234,15 @@ defmodule Ash.Type.Union do
Map.get(value, config[:tag], Map.get(value, to_string(config[:tag])))
tags_equal? =
if is_atom(tag_value) do
their_tag_value == tag_value || their_tag_value == to_string(tag_value)
else
their_tag_value == tag_value
cond do
is_atom(tag_value) ->
their_tag_value == tag_value || their_tag_value == to_string(tag_value)
is_binary(tag_value) && is_atom(their_tag_value) ->
their_tag_value == tag_value || to_string(their_tag_value) == tag_value
true ->
their_tag_value == tag_value
end
if tags_equal? do
@ -253,11 +258,11 @@ defmodule Ash.Type.Union do
}}}
{:error, other} ->
{:halt, {:error, "is not a valid #{type_name}: #{inspect(other)}"}}
{:halt, {:expose_error, other}}
end
{:error, other} ->
{:halt, {:error, "is not a valid #{type_name}: #{inspect(other)}"}}
{:halt, {:expose_error, other}}
:error ->
{:halt, {:error, "is not a valid #{type_name}"}}
@ -302,6 +307,9 @@ defmodule Ash.Type.Union do
{:error, errors} ->
{:error, error_message(errors)}
{:expose_error, errors} ->
{:error, errors}
{:ok, value} ->
{:ok, value}

View file

@ -2,6 +2,32 @@ defmodule Ash.Test.Type.UnionTest do
@moduledoc false
use ExUnit.Case, async: true
defmodule Foo do
use Ash.Resource, data_layer: :embedded
attributes do
attribute :foo, :string, constraints: [match: ~r/foo/]
attribute :type, :string do
writable? false
default "foo"
end
end
end
defmodule Bar do
use Ash.Resource, data_layer: :embedded
attributes do
attribute :bar, :string, constraints: [match: ~r/bar/]
attribute :type, :string do
writable? false
default "bar"
end
end
end
test "it handles simple types" do
constraints = [
types: [
@ -50,6 +76,38 @@ defmodule Ash.Test.Type.UnionTest do
assert {:ok, %Ash.Union{value: %{type: :bar, bar: 1}, type: :bar}} =
Ash.Type.cast_input(:union, %{type: :bar, bar: 1}, constraints)
assert {:error, _} = Ash.Type.cast_input(:union, %{type: :baz, bar: 1}, constraints)
assert {:error, _} =
Ash.Type.cast_input(:union, %{type: :baz, bar: 1}, constraints)
end
test "it handles paths" do
constraints = [
items: [
types: [
foo: [
type: Foo,
tag: :type,
tag_value: :foo
],
bar: [
type: Bar,
tag: :type,
tag_value: :bar
]
]
]
]
{:ok, %{type: type, constraints: constraints}} =
Ash.Type.set_type_transformation(%{
type: {:array, Ash.Type.Union},
constraints: constraints
})
assert {:ok, [%Ash.Union{value: %{type: "foo", foo: "foo"}, type: :foo}]} =
Ash.Type.cast_input(type, [%{type: :foo, foo: "foo"}], constraints)
# assert {:ok, [%Ash.Union{value: %{type: "foo", foo: "foo"}, type: :foo}]} =
Ash.Type.cast_input(type, [%{type: :foo, foo: "bar"}], constraints) |> IO.inspect()
end
end