Skip to content

Commit

Permalink
fix: correctly load paginated relationships after create, update, del…
Browse files Browse the repository at this point in the history
…ete (#1229)

Ensure that relationships can be correctly loaded, even with pagination,
on the resources resulting from create, update and delete actions.

Signed-off-by: Davide Briani <davide.briani@secomind.com>
  • Loading branch information
davidebriani authored Jun 10, 2024
1 parent 46450f7 commit ae96711
Show file tree
Hide file tree
Showing 4 changed files with 926 additions and 0 deletions.
2 changes: 2 additions & 0 deletions lib/ash/actions/read/read.ex
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ defmodule Ash.Actions.Read do

query = load_and_select_sort(query)

query = add_relationship_count_aggregates(query)

pkey = Ash.Resource.Info.primary_key(query.resource)

missing_pkeys? =
Expand Down
282 changes: 282 additions & 0 deletions test/actions/create_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,288 @@ defmodule Ash.Test.Actions.CreateTest do
end
end

describe "load" do
test "allows loading has_many relationship on the changeset" do
post1 = Ash.create!(Post, %{title: "Post 1"})
post2 = Ash.create!(Post, %{title: "Post 2"})

load_query =
Post
|> Ash.Query.sort(title: :asc)
|> Ash.Query.select([:title])

author =
Author
|> Ash.Changeset.new()
|> Ash.Changeset.change_attribute(:name, "Author")
|> Ash.Changeset.manage_relationship(:posts, [post2, post1], type: :append_and_remove)
|> Ash.Changeset.load(posts: load_query)
|> Ash.create!()

assert [%Post{title: "Post 1"}, %Post{title: "Post 2"}] = author.posts
end

test "allows loading has_many relationship on the action options" do
post1 = Ash.create!(Post, %{title: "Post 1"})
post2 = Ash.create!(Post, %{title: "Post 2"})

load_query =
Post
|> Ash.Query.sort(title: :asc)
|> Ash.Query.select([:title])

author =
Author
|> Ash.Changeset.new()
|> Ash.Changeset.change_attribute(:name, "Author")
|> Ash.Changeset.manage_relationship(:posts, [post2, post1], type: :append_and_remove)
|> Ash.create!(load: [posts: load_query])

assert [%Post{title: "Post 1"}, %Post{title: "Post 2"}] = author.posts
end

test "allows loading paginated has_many relationship on the changeset" do
post1 = Ash.create!(Post, %{title: "Post 1"})
post2 = Ash.create!(Post, %{title: "Post 2"})

offset_pagination_query =
Post
|> Ash.Query.sort(title: :asc)
|> Ash.Query.select([:title])
|> Ash.Query.page(count: true, limit: 1)

author =
Author
|> Ash.Changeset.new()
|> Ash.Changeset.change_attribute(:name, "Author")
|> Ash.Changeset.manage_relationship(:posts, [post2, post1], type: :append_and_remove)
|> Ash.Changeset.load(posts: offset_pagination_query)
|> Ash.create!()

assert %Ash.Page.Offset{
results: [%Post{title: "Post 1", __metadata__: %{keyset: keyset}}],
limit: 1,
offset: 0,
count: 2,
more?: true
} = author.posts

keyset_pagination_query =
Post
|> Ash.Query.sort(title: :asc)
|> Ash.Query.select([:title])
|> Ash.Query.page(count: true, limit: 1, after: keyset)

author =
Author
|> Ash.Changeset.new()
|> Ash.Changeset.change_attribute(:name, "Author")
|> Ash.Changeset.manage_relationship(:posts, [post2, post1], type: :append_and_remove)
|> Ash.Changeset.load(posts: keyset_pagination_query)
|> Ash.create!()

assert %Ash.Page.Keyset{
results: [%Post{title: "Post 2"}],
limit: 1,
count: 2,
more?: false,
before: nil,
after: ^keyset
} = author.posts
end

test "allows loading paginated has_many relationship on the action options" do
post1 = Ash.create!(Post, %{title: "Post 1"})
post2 = Ash.create!(Post, %{title: "Post 2"})

offset_pagination_query =
Post
|> Ash.Query.sort(title: :asc)
|> Ash.Query.select([:title])
|> Ash.Query.page(count: true, limit: 1)

author =
Author
|> Ash.Changeset.new()
|> Ash.Changeset.change_attribute(:name, "Author")
|> Ash.Changeset.manage_relationship(:posts, [post2, post1], type: :append_and_remove)
|> Ash.create!(load: [posts: offset_pagination_query])

assert %Ash.Page.Offset{
results: [%Post{title: "Post 1", __metadata__: %{keyset: keyset}}],
limit: 1,
offset: 0,
count: 2,
more?: true
} = author.posts

keyset_pagination_query =
Post
|> Ash.Query.sort(title: :asc)
|> Ash.Query.select([:title])
|> Ash.Query.page(count: true, limit: 1, after: keyset)

author =
Author
|> Ash.Changeset.new()
|> Ash.Changeset.change_attribute(:name, "Author")
|> Ash.Changeset.manage_relationship(:posts, [post2, post1], type: :append_and_remove)
|> Ash.create!(load: [posts: keyset_pagination_query])

assert %Ash.Page.Keyset{
results: [%Post{title: "Post 2"}],
limit: 1,
count: 2,
more?: false,
before: nil,
after: ^keyset
} = author.posts
end

test "allows loading many_to_many relationship on the changeset" do
related_post1 = Ash.create!(Post, %{title: "Related 1"})
related_post2 = Ash.create!(Post, %{title: "Related 2"})

load_query =
Post
|> Ash.Query.sort(title: :asc)
|> Ash.Query.select([:title])

post =
Post
|> Ash.Changeset.for_create(:create, %{title: "Post"})
|> Ash.Changeset.manage_relationship(:related_posts, [related_post2, related_post1],
type: :append_and_remove
)
|> Ash.Changeset.load(related_posts: load_query)
|> Ash.create!()

assert [%Post{title: "Related 1"}, %Post{title: "Related 2"}] = post.related_posts
end

test "allows loading many_to_many relationship on the action options" do
related_post1 = Ash.create!(Post, %{title: "Related 1"})
related_post2 = Ash.create!(Post, %{title: "Related 2"})

load_query =
Post
|> Ash.Query.sort(title: :asc)
|> Ash.Query.select([:title])

post =
Post
|> Ash.Changeset.for_create(:create, %{title: "Post"})
|> Ash.Changeset.manage_relationship(:related_posts, [related_post2, related_post1],
type: :append_and_remove
)
|> Ash.create!(load: [related_posts: load_query])

assert [%Post{title: "Related 1"}, %Post{title: "Related 2"}] = post.related_posts
end

test "allows loading paginated many_to_many relationship on the changeset" do
related_post1 = Ash.create!(Post, %{title: "Related 1"})
related_post2 = Ash.create!(Post, %{title: "Related 2"})

offset_pagination_query =
Post
|> Ash.Query.sort(title: :asc)
|> Ash.Query.select([:title])
|> Ash.Query.page(count: true, limit: 1)

post =
Post
|> Ash.Changeset.for_create(:create, %{title: "Post"})
|> Ash.Changeset.manage_relationship(:related_posts, [related_post2, related_post1],
type: :append_and_remove
)
|> Ash.Changeset.load(related_posts: offset_pagination_query)
|> Ash.create!()

assert %Ash.Page.Offset{
results: [%Post{title: "Related 1", __metadata__: %{keyset: keyset}}],
limit: 1,
offset: 0,
count: 2,
more?: true
} = post.related_posts

keyset_pagination_query =
Post
|> Ash.Query.sort(title: :asc)
|> Ash.Query.select([:title])
|> Ash.Query.page(count: true, limit: 1, after: keyset)

post =
Post
|> Ash.Changeset.for_create(:create, %{title: "Post"})
|> Ash.Changeset.manage_relationship(:related_posts, [related_post2, related_post1],
type: :append_and_remove
)
|> Ash.Changeset.load(related_posts: keyset_pagination_query)
|> Ash.create!()

assert %Ash.Page.Keyset{
results: [%Post{title: "Related 2"}],
limit: 1,
count: 2,
more?: false,
before: nil,
after: ^keyset
} = post.related_posts
end

test "allows loading paginated many_to_many relationship on the action options" do
related_post1 = Ash.create!(Post, %{title: "Related 1"})
related_post2 = Ash.create!(Post, %{title: "Related 2"})

offset_pagination_query =
Post
|> Ash.Query.sort(title: :asc)
|> Ash.Query.select([:title])
|> Ash.Query.page(count: true, limit: 1)

post =
Post
|> Ash.Changeset.for_create(:create, %{title: "Post"})
|> Ash.Changeset.manage_relationship(:related_posts, [related_post2, related_post1],
type: :append_and_remove
)
|> Ash.create!(load: [related_posts: offset_pagination_query])

assert %Ash.Page.Offset{
results: [%Post{title: "Related 1", __metadata__: %{keyset: keyset}}],
limit: 1,
offset: 0,
count: 2,
more?: true
} = post.related_posts

keyset_pagination_query =
Post
|> Ash.Query.sort(title: :asc)
|> Ash.Query.select([:title])
|> Ash.Query.page(count: true, limit: 1, after: keyset)

post =
Post
|> Ash.Changeset.for_create(:create, %{title: "Post"})
|> Ash.Changeset.manage_relationship(:related_posts, [related_post2, related_post1],
type: :append_and_remove
)
|> Ash.create!(load: [related_posts: keyset_pagination_query])

assert %Ash.Page.Keyset{
results: [%Post{title: "Related 2"}],
limit: 1,
count: 2,
more?: false,
before: nil,
after: ^keyset
} = post.related_posts
end
end

describe "creating many to many relationships" do
test "allows creating with a many_to_many relationship" do
post2 =
Expand Down
Loading

0 comments on commit ae96711

Please sign in to comment.