fix: load relationships for management properly

fix: `fetch_key` bug in embedded types
fix: handle_indexed_maps for embedded types
This commit is contained in:
Zach Daniel 2021-04-14 23:38:51 -04:00
parent f97caad908
commit 5425660752
3 changed files with 39 additions and 20 deletions

View file

@ -5,18 +5,35 @@ defmodule Ash.Actions.ManagedRelationships do
alias Ash.Error.Changes.InvalidRelationship alias Ash.Error.Changes.InvalidRelationship
alias Ash.Error.Query.NotFound alias Ash.Error.Query.NotFound
def load(_api, created, %{relationships: rels}, _) when rels == %{}, do: {:ok, created} def load(_api, created, %{relationships: rels}, _) when rels == %{},
do: {:ok, created}
def load(_api, created, %{relationships: nil}, _), do: {:ok, created} def load(_api, created, %{relationships: nil}, _), do: {:ok, created}
def load(api, created, changeset, opts) do def load(api, created, changeset, engine_opts) do
if Ash.Changeset.ManagedRelationshipHelpers.must_load?(opts) do Enum.reduce_while(changeset.relationships, {:ok, created}, fn {key, value}, {:ok, acc} ->
api.load(created, Map.keys(changeset.relationships), relationship = Ash.Resource.Info.relationship(changeset.resource, key)
authorize?: opts[:authorize?],
actor: opts[:actor] case Enum.filter(value, fn {_, opts} ->
) opts = Ash.Changeset.ManagedRelationshipHelpers.sanitize_opts(relationship, opts)
else Ash.Changeset.ManagedRelationshipHelpers.must_load?(opts)
{:ok, created} end) do
end [] ->
{:cont, {:ok, acc}}
relationships ->
authorize? =
engine_opts[:authorize?] &&
Enum.any?(relationships, fn {_, opts} -> opts[:authorize?] end)
actor = engine_opts[:actor]
case api.load(acc, key, authorize?: authorize?, actor: actor) do
{:ok, loaded} -> {:cont, {:ok, loaded}}
{:error, error} -> {:halt, {:error, error}}
end
end
end)
end end
def setup_managed_belongs_to_relationships(changeset, actor, engine_opts) do def setup_managed_belongs_to_relationships(changeset, actor, engine_opts) do

View file

@ -1718,7 +1718,8 @@ defmodule Ash.Changeset do
add_invalid_errors(:attribute, changeset, attribute, "Attribute is not writable") add_invalid_errors(:attribute, changeset, attribute, "Attribute is not writable")
attribute -> attribute ->
with {:ok, prepared} <- with value <- handle_indexed_maps(attribute.type, value),
{:ok, prepared} <-
prepare_change(changeset, attribute, value, attribute.constraints), prepare_change(changeset, attribute, value, attribute.constraints),
{:ok, casted} <- {:ok, casted} <-
cast_input(attribute.type, prepared, attribute.constraints, true), cast_input(attribute.type, prepared, attribute.constraints, true),
@ -1844,7 +1845,8 @@ defmodule Ash.Changeset do
%{changeset | attributes: Map.put(changeset.attributes, attribute.name, nil)} %{changeset | attributes: Map.put(changeset.attributes, attribute.name, nil)}
attribute -> attribute ->
with {:ok, prepared} <- with value <- handle_indexed_maps(attribute.type, value),
{:ok, prepared} <-
prepare_change(changeset, attribute, value, attribute.constraints), prepare_change(changeset, attribute, value, attribute.constraints),
{:ok, casted} <- {:ok, casted} <-
cast_input(attribute.type, prepared, attribute.constraints), cast_input(attribute.type, prepared, attribute.constraints),

View file

@ -128,7 +128,7 @@ defmodule Ash.EmbeddableType do
defmacro single_embed_implementation do defmacro single_embed_implementation do
# credo:disable-for-next-line Credo.Check.Refactor.LongQuoteBlocks # credo:disable-for-next-line Credo.Check.Refactor.LongQuoteBlocks
quote do quote location: :keep do
alias __MODULE__.ShadowApi alias __MODULE__.ShadowApi
def storage_type, do: :map def storage_type, do: :map
@ -181,7 +181,7 @@ defmodule Ash.EmbeddableType do
def fetch_key(map, atom) do def fetch_key(map, atom) do
case Map.fetch(map, atom) do case Map.fetch(map, atom) do
{:ok, value} -> {:ok, value} ->
value {:ok, value}
:error -> :error ->
Map.fetch(map, to_string(atom)) Map.fetch(map, to_string(atom))
@ -321,7 +321,7 @@ defmodule Ash.EmbeddableType do
:error -> :error ->
{pkey_field, :error} {pkey_field, :error}
value -> {:ok, value} ->
attribute = Ash.Resource.Info.attribute(__MODULE__, pkey_field) attribute = Ash.Resource.Info.attribute(__MODULE__, pkey_field)
case Ash.Type.cast_input(attribute.type, value, attribute.constraints) do case Ash.Type.cast_input(attribute.type, value, attribute.constraints) do
@ -362,7 +362,7 @@ defmodule Ash.EmbeddableType do
defmacro array_embed_implementation do defmacro array_embed_implementation do
# credo:disable-for-next-line Credo.Check.Refactor.LongQuoteBlocks # credo:disable-for-next-line Credo.Check.Refactor.LongQuoteBlocks
quote do quote location: :keep do
alias __MODULE__.ShadowApi alias __MODULE__.ShadowApi
def array_constraints, do: Ash.EmbeddableType.embedded_resource_array_constraints() def array_constraints, do: Ash.EmbeddableType.embedded_resource_array_constraints()
@ -482,9 +482,9 @@ defmodule Ash.EmbeddableType do
Enum.into(pkey_fields, %{}, fn pkey_field -> Enum.into(pkey_fields, %{}, fn pkey_field ->
case fetch_key(new, pkey_field) do case fetch_key(new, pkey_field) do
:error -> :error ->
:error {pkey_field, :error}
value -> {:ok, value} ->
attr = Map.get(pkey_attributes, pkey_field) attr = Map.get(pkey_attributes, pkey_field)
case Ash.Type.cast_input(attr.type, value, attr.constraints) do case Ash.Type.cast_input(attr.type, value, attr.constraints) do
@ -492,7 +492,7 @@ defmodule Ash.EmbeddableType do
{pkey_field, casted} {pkey_field, casted}
_ -> _ ->
:error {pkey_field, :error}
end end
end end
end) end)
@ -538,7 +538,7 @@ defmodule Ash.EmbeddableType do
end end
defmacro define_embeddable_type do defmacro define_embeddable_type do
quote do quote location: :keep do
use Ash.Type use Ash.Type
parent = __MODULE__ parent = __MODULE__