Skip to content

Commit

Permalink
improvement: StaleRecordError on skipped upsert
Browse files Browse the repository at this point in the history
  • Loading branch information
zachdaniel committed Oct 10, 2024
1 parent c5dc325 commit 6ff4621
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 0 deletions.
10 changes: 10 additions & 0 deletions lib/ash.ex
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,12 @@ defmodule Ash do
doc:
"If a conflict is found based on the primary key, the record is updated in the database (requires upsert support)"
],
return_skipped_upsert?: [
type: :boolean,
default: false,
doc:
"If `true`, and a record was *not* upserted because its filter prevented the upsert, the original record (which was *not* upserted) will be returned."
],
upsert_identity: [
type: :atom,
doc:
Expand Down Expand Up @@ -578,6 +584,10 @@ defmodule Ash do
doc:
"If a conflict is found based on the primary key, the record is updated in the database (requires upsert support)"
],
return_skipped_upsert?: [
type: :boolean,
hide: true
],
upsert_identity: [
type: :atom,
doc:
Expand Down
3 changes: 3 additions & 0 deletions lib/ash/actions/create/bulk.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1263,6 +1263,9 @@ defmodule Ash.Actions.Create.Bulk do
end

case result do
{:ok, %{__metadata__: %{upsert_skipped: true}}} ->
[]

{:ok, result} ->
{:ok,
[
Expand Down
15 changes: 15 additions & 0 deletions lib/ash/actions/create/create.ex
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,21 @@ defmodule Ash.Actions.Create do
opts[:upsert_identity] || changeset.action.upsert_identity
)
)
|> case do
{:ok, %{__metadata__: %{upsert_skipped: true}}} = result ->
if opts[:return_skipped_upsert?] do
result
else
{:error,
Ash.Error.Changes.StaleRecord.exception(
resource: changeset.resource,
filter: changeset.filter
)}
end

other ->
other
end
|> Helpers.rollback_if_in_transaction(
changeset.resource,
changeset
Expand Down
28 changes: 28 additions & 0 deletions test/actions/create_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,7 @@ defmodule Ash.Test.Actions.CreateTest do
})
|> Ash.create!(
upsert?: true,
return_skipped_upsert?: true,
upsert_identity: :unique_title,
upsert_fields: [:contents, :updated_at],
upsert_condition: expr(contents != upsert_conflict(:contents))
Expand All @@ -624,6 +625,33 @@ defmodule Ash.Test.Actions.CreateTest do
assert Ash.Resource.get_metadata(post, :upsert_skipped)
end

test "skips upsert when condition doesn't match, returning error unless asked for" do
Post
|> Ash.Changeset.new()
|> Ash.Changeset.change_attributes(%{
title: "foo",
contents: "bar",
tag: "before"
})
|> Ash.create!()

assert_raise Ash.Error.Invalid, ~r/Stale/, fn ->
Post
|> Ash.Changeset.new()
|> Ash.Changeset.change_attributes(%{
title: "foo",
contents: "bar",
tag: "after"
})
|> Ash.create!(
upsert?: true,
upsert_identity: :unique_title,
upsert_fields: [:contents, :updated_at],
upsert_condition: expr(contents != upsert_conflict(:contents))
)
end
end

test "timestamps will match each other" do
post =
Post
Expand Down

0 comments on commit 6ff4621

Please sign in to comment.