fix: handle filter condition on create (#368)

This commit is contained in:
Jonatan Männchen 2024-08-12 20:48:45 +02:00 committed by GitHub
parent 73c7a915e0
commit a539f6443e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 136 additions and 2 deletions

View file

@ -2539,6 +2539,28 @@ defmodule AshPostgres.DataLayer do
upsert_fields: upsert_fields,
return_records?: true
}) do
{:ok, []} ->
key_filters =
Enum.map(keys, fn key ->
{key,
Ash.Changeset.get_attribute(changeset, key) || Map.get(changeset.params, key) ||
Map.get(changeset.params, to_string(key))}
end)
ash_query =
resource
|> Ash.Query.do_filter(and: [key_filters])
|> then(fn
query when is_nil(identity) or is_nil(identity.where) -> query
query -> Ash.Query.do_filter(query, identity.where)
end)
|> Ash.Query.set_tenant(changeset.tenant)
with {:ok, ecto_query} <- Ash.Query.data_layer_query(ash_query),
{:ok, [result]} <- run_query(ecto_query, resource) do
{:ok, Ash.Resource.put_metadata(result, :upsert_skipped, true)}
end
{:ok, [result]} ->
{:ok, result}

View file

@ -65,6 +65,50 @@ defmodule AshPostgres.BulkCreateTest do
end)
end
test "bulk upsert skips with filter" do
assert [
{:ok, %{title: "fredfoo", uniq_if_contains_foo: "1foo", price: 10}},
{:ok, %{title: "georgefoo", uniq_if_contains_foo: "2foo", price: 20}},
{:ok, %{title: "herbert", uniq_if_contains_foo: "3", price: 30}}
] =
Ash.bulk_create!(
[
%{title: "fredfoo", uniq_if_contains_foo: "1foo", price: 10},
%{title: "georgefoo", uniq_if_contains_foo: "2foo", price: 20},
%{title: "herbert", uniq_if_contains_foo: "3", price: 30}
],
Post,
:create,
return_stream?: true,
return_records?: true
)
|> Enum.sort_by(fn {:ok, result} -> result.title end)
assert [
{:ok, %{title: "georgefoo", uniq_if_contains_foo: "2foo", price: 20_000}},
{:ok, %{title: "herbert", uniq_if_contains_foo: "3", price: 30}}
] =
Ash.bulk_create!(
[
%{title: "fredfoo", uniq_if_contains_foo: "1foo", price: 10},
%{title: "georgefoo", uniq_if_contains_foo: "2foo", price: 20_000},
%{title: "herbert", uniq_if_contains_foo: "3", price: 30}
],
Post,
:upsert_with_filter,
return_stream?: true,
return_errors?: true,
return_records?: true
)
|> Enum.sort_by(fn
{:ok, result} ->
result.title
_ ->
nil
end)
end
# confirmed that this doesn't work because it can't. An upsert must map to a potentially successful insert.
# leaving this test here for posterity
# test "bulk creates can upsert with id" do

58
test/create_test.exs Normal file
View file

@ -0,0 +1,58 @@
defmodule AshPostgres.CreateTest do
use AshPostgres.RepoCase, async: false
alias AshPostgres.Test.Post
test "creates insert" do
assert {:ok, %Post{}} =
Post
|> Ash.Changeset.for_create(:create, %{title: "fred"})
|> Ash.create()
assert [%{title: "fred"}] =
Post
|> Ash.Query.sort(:title)
|> Ash.read!()
end
test "upserts entry" do
assert {:ok, %Post{id: id}} =
Post
|> Ash.Changeset.for_create(:create, %{
title: "fredfoo",
uniq_if_contains_foo: "foo",
price: 10
})
|> Ash.create()
assert {:ok, %Post{id: ^id, price: 20}} =
Post
|> Ash.Changeset.for_create(:upsert_with_filter, %{
title: "fredfoo",
uniq_if_contains_foo: "foo",
price: 20
})
|> Ash.create()
end
test "skips upsert with filter" do
assert {:ok, %Post{id: id}} =
Post
|> Ash.Changeset.for_create(:create, %{
title: "fredfoo",
uniq_if_contains_foo: "foo",
price: 10
})
|> Ash.create()
assert {:ok, %Post{id: ^id} = post} =
Post
|> Ash.Changeset.for_create(:upsert_with_filter, %{
title: "fredfoo",
uniq_if_contains_foo: "foo",
price: 10
})
|> Ash.create()
assert Ash.Resource.get_metadata(post, :upsert_skipped)
end
end

View file

@ -258,6 +258,16 @@ defmodule AshPostgres.Test.Post do
)
end
create :upsert_with_filter do
upsert?(true)
upsert_identity(:uniq_if_contains_foo)
upsert_fields([:price])
change(fn changeset, _ ->
Ash.Changeset.filter(changeset, expr(price != fragment("EXCLUDED.price")))
end)
end
update :set_title_from_author do
change(atomic_update(:title, expr(author.first_name)))
end
@ -292,7 +302,7 @@ defmodule AshPostgres.Test.Post do
identity(:uniq_on_upper, [:upper_thing])
identity(:uniq_if_contains_foo, [:uniq_if_contains_foo]) do
where expr(contains(title, "foo"))
where expr(contains(uniq_if_contains_foo, "foo"))
end
end

View file

@ -1,4 +1,4 @@
ExUnit.start()
ExUnit.start(capture_log: true)
ExUnit.configure(stacktrace_depth: 100)
AshPostgres.TestRepo.start_link()