mirror of
https://github.com/ash-project/ash.git
synced 2024-09-20 13:33:20 +12:00
fix: Fix composite key in changeset functions (#125)
Co-authored-by: Zach Daniel <zachary.s.daniel@gmail.com>
This commit is contained in:
parent
0f805803e1
commit
a2f5db08f4
2 changed files with 175 additions and 30 deletions
|
@ -635,13 +635,7 @@ defmodule Ash.Changeset do
|
||||||
multiple_primary_keys(relationship, records)
|
multiple_primary_keys(relationship, records)
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
case single_primary_key(relationship, records) do
|
pluck_pk_fields(relationship, records)
|
||||||
{:ok, keys} ->
|
|
||||||
{:ok, keys}
|
|
||||||
|
|
||||||
{:error, _} ->
|
|
||||||
do_primary_key(relationship, records)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -649,13 +643,30 @@ defmodule Ash.Changeset do
|
||||||
do_primary_key(relationship, record)
|
do_primary_key(relationship, record)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp pluck_pk_fields(relationship, records) do
|
||||||
|
Enum.reduce_while(
|
||||||
|
records,
|
||||||
|
{:ok, []},
|
||||||
|
fn
|
||||||
|
record, {:ok, acc} ->
|
||||||
|
case do_primary_key(relationship, record) do
|
||||||
|
{:ok, pk} -> {:cont, {:ok, [pk | acc]}}
|
||||||
|
{:error, error} -> {:halt, {:error, error}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
defp do_primary_key(relationship, record) when is_map(record) do
|
defp do_primary_key(relationship, record) when is_map(record) do
|
||||||
primary_key = Ash.Resource.primary_key(relationship.destination)
|
primary_key = Ash.Resource.primary_key(relationship.destination)
|
||||||
|
|
||||||
is_pkey_map? =
|
is_pkey_map? =
|
||||||
Enum.all?(primary_key, fn key ->
|
Enum.all?(
|
||||||
|
primary_key,
|
||||||
|
fn key ->
|
||||||
Map.has_key?(record, key) || Map.has_key?(record, to_string(key))
|
Map.has_key?(record, key) || Map.has_key?(record, to_string(key))
|
||||||
end)
|
end
|
||||||
|
)
|
||||||
|
|
||||||
if is_pkey_map? do
|
if is_pkey_map? do
|
||||||
pkey =
|
pkey =
|
||||||
|
|
|
@ -123,8 +123,9 @@ defmodule Ash.Test.Changeset.ChangesetTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
|
attribute :serial, :integer, primary_key?: true
|
||||||
attribute :id, :uuid, primary_key?: true, default: &Ecto.UUID.generate/0
|
attribute :id, :uuid, primary_key?: true, default: &Ecto.UUID.generate/0
|
||||||
attribute :title, :string, primary_key?: true
|
attribute :title, :string
|
||||||
attribute :contents, :string
|
attribute :contents, :string
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -446,7 +447,7 @@ defmodule Ash.Test.Changeset.ChangesetTest do
|
||||||
assert %{replace: [%{id: post1.id}]} == changeset.relationships.posts
|
assert %{replace: [%{id: post1.id}]} == changeset.relationships.posts
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it accepts a map %{id: value} representing primary key as a second param only if primary key is a single attribute" do
|
test "it accepts a map %{id: value} representing primary key as a second param" do
|
||||||
post1 =
|
post1 =
|
||||||
Post
|
Post
|
||||||
|> Changeset.new(%{title: "foo"})
|
|> Changeset.new(%{title: "foo"})
|
||||||
|
@ -460,24 +461,157 @@ defmodule Ash.Test.Changeset.ChangesetTest do
|
||||||
assert %{replace: [%{id: post1.id}]} == changeset.relationships.posts
|
assert %{replace: [%{id: post1.id}]} == changeset.relationships.posts
|
||||||
end
|
end
|
||||||
|
|
||||||
# test "it accepts a map %{att1: value1, att2: value2} representing primary key as a second param" do
|
test "it accepts a map %{att1: value1, att2: value2} representing primary key as a second param" do
|
||||||
# post1 = CompositeKeyPost |> Changeset.new(%{title: "foo"}) |> Api.create!()
|
post1 =
|
||||||
#
|
CompositeKeyPost
|
||||||
# assert [:id, :title] == Ash.Resource.primary_key(CompositeKeyPost)
|
|> Changeset.new(%{serial: 1})
|
||||||
#
|
|> Api.create!()
|
||||||
# changeset =
|
|
||||||
# Author
|
changeset =
|
||||||
# |> Changeset.new()
|
Author
|
||||||
# |> Changeset.replace_relationship(:composite_key_posts, %{
|
|> Changeset.new()
|
||||||
# id: post1.id,
|
|> Changeset.replace_relationship(
|
||||||
# title: "some title"
|
:composite_key_posts,
|
||||||
# })
|
%{id: post1.id, serial: post1.serial}
|
||||||
#
|
)
|
||||||
# refute [%Ash.Error.Changes.InvalidRelationship{}] = changeset.errors
|
|
||||||
#
|
assert %{replace: [%{id: post1.id, serial: post1.serial}]} ==
|
||||||
# assert %{replace: [%{id: post1.id, title: post1.title}]} ==
|
changeset.relationships.composite_key_posts
|
||||||
# changeset.relationships.composite_key_posts
|
|
||||||
# end
|
assert [] == changeset.errors
|
||||||
|
|
||||||
|
author =
|
||||||
|
changeset
|
||||||
|
|> Api.create!()
|
||||||
|
|
||||||
|
[fetched_post] =
|
||||||
|
CompositeKeyPost
|
||||||
|
|> Ash.Query.load(author: :composite_key_posts)
|
||||||
|
|> Ash.Query.filter(id: post1.id, serial: post1.serial)
|
||||||
|
|> Api.read!()
|
||||||
|
|
||||||
|
assert author == fetched_post.author
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it accepts a list of maps representing primary_keys as a second param" do
|
||||||
|
post1 =
|
||||||
|
CompositeKeyPost
|
||||||
|
|> Changeset.new(%{serial: 1})
|
||||||
|
|> Api.create!()
|
||||||
|
|
||||||
|
post2 =
|
||||||
|
CompositeKeyPost
|
||||||
|
|> Changeset.new(%{serial: 2})
|
||||||
|
|> Api.create!()
|
||||||
|
|
||||||
|
changeset =
|
||||||
|
Author
|
||||||
|
|> Changeset.new()
|
||||||
|
|> Changeset.replace_relationship(
|
||||||
|
:composite_key_posts,
|
||||||
|
[
|
||||||
|
%{id: post1.id, serial: post1.serial},
|
||||||
|
%{id: post2.id, serial: post2.serial}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert Enum.sort([
|
||||||
|
%{id: post1.id, serial: post1.serial},
|
||||||
|
%{id: post2.id, serial: post2.serial}
|
||||||
|
]) ==
|
||||||
|
Enum.sort(changeset.relationships.composite_key_posts.replace)
|
||||||
|
|
||||||
|
assert [] == changeset.errors
|
||||||
|
|
||||||
|
author =
|
||||||
|
changeset
|
||||||
|
|> Api.create!()
|
||||||
|
|
||||||
|
[fetched_post] =
|
||||||
|
CompositeKeyPost
|
||||||
|
|> Ash.Query.load(author: :composite_key_posts)
|
||||||
|
|> Ash.Query.filter(id: post1.id, serial: post1.serial)
|
||||||
|
|> Api.read!()
|
||||||
|
|
||||||
|
assert author == fetched_post.author
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it accepts mix of entities and maps representing primary_keys as a second param" do
|
||||||
|
post1 =
|
||||||
|
CompositeKeyPost
|
||||||
|
|> Changeset.new(%{serial: 1})
|
||||||
|
|> Api.create!()
|
||||||
|
|
||||||
|
post2 =
|
||||||
|
CompositeKeyPost
|
||||||
|
|> Changeset.new(%{serial: 2})
|
||||||
|
|> Api.create!()
|
||||||
|
|
||||||
|
changeset =
|
||||||
|
Author
|
||||||
|
|> Changeset.new()
|
||||||
|
|> Changeset.replace_relationship(
|
||||||
|
:composite_key_posts,
|
||||||
|
[
|
||||||
|
%{id: post1.id, serial: post1.serial},
|
||||||
|
post2
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert Enum.sort([
|
||||||
|
%{id: post1.id, serial: post1.serial},
|
||||||
|
%{id: post2.id, serial: post2.serial}
|
||||||
|
]) ==
|
||||||
|
Enum.sort(changeset.relationships.composite_key_posts.replace)
|
||||||
|
|
||||||
|
assert [] == changeset.errors
|
||||||
|
|
||||||
|
author =
|
||||||
|
changeset
|
||||||
|
|> Api.create!()
|
||||||
|
|
||||||
|
[fetched_author] =
|
||||||
|
Author
|
||||||
|
|> Ash.Query.load(:composite_key_posts)
|
||||||
|
|> Ash.Query.filter(id: author.id)
|
||||||
|
|> Api.read!()
|
||||||
|
|
||||||
|
assert [post2, post1] = fetched_author.composite_key_posts
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns error if one of relationship entities is invalid" do
|
||||||
|
post1 =
|
||||||
|
CompositeKeyPost
|
||||||
|
|> Changeset.new(%{serial: 1})
|
||||||
|
|> Api.create!()
|
||||||
|
|
||||||
|
post2 =
|
||||||
|
CompositeKeyPost
|
||||||
|
|> Changeset.new(%{serial: 2})
|
||||||
|
|> Api.create!()
|
||||||
|
|
||||||
|
invalid_post =
|
||||||
|
Post
|
||||||
|
|> Changeset.new(%{title: "a title"})
|
||||||
|
|> Api.create!()
|
||||||
|
|
||||||
|
changeset =
|
||||||
|
Author
|
||||||
|
|> Changeset.new()
|
||||||
|
|> Changeset.replace_relationship(
|
||||||
|
:composite_key_posts,
|
||||||
|
[
|
||||||
|
%{id: post1.id, serial: post1.serial},
|
||||||
|
post2,
|
||||||
|
invalid_post
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert Enum.empty?(changeset.relationships)
|
||||||
|
|
||||||
|
assert [%Ash.Error.Changes.InvalidRelationship{} = relation_error] = changeset.errors
|
||||||
|
assert relation_error.message =~ "Invalid identifier"
|
||||||
|
end
|
||||||
|
|
||||||
test "it accepts many-to-many relationship" do
|
test "it accepts many-to-many relationship" do
|
||||||
post1 =
|
post1 =
|
||||||
|
|
Loading…
Reference in a new issue