Skip to content

Commit 643a147

Browse files
authored
fix: produce correct error on restrict managed_relationship behavior (#690)
1 parent 4688dd1 commit 643a147

File tree

2 files changed

+47
-2
lines changed

2 files changed

+47
-2
lines changed

lib/data_layer.ex

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2668,7 +2668,15 @@ defmodule AshPostgres.DataLayer do
26682668
) do
26692669
case Ecto.Adapters.Postgres.Connection.to_constraints(error, []) do
26702670
[] ->
2671-
{:error, Ash.Error.to_ash_error(error, stacktrace)}
2671+
constraints = maybe_foreign_key_violation_constraints(error)
2672+
if constraints != [] do
2673+
{:error,
2674+
changeset
2675+
|> constraints_to_errors(:delete, constraints, resource, error)
2676+
|> Ash.Error.to_ash_error()}
2677+
else
2678+
{:error, Ash.Error.to_ash_error(error, stacktrace)}
2679+
end
26722680

26732681
constraints ->
26742682
{:error,
@@ -2682,6 +2690,20 @@ defmodule AshPostgres.DataLayer do
26822690
{:error, Ash.Error.to_ash_error(error, stacktrace)}
26832691
end
26842692

2693+
defp maybe_foreign_key_violation_constraints(%Postgrex.Error{postgres: postgres})
2694+
when is_map(postgres) do
2695+
code = postgres[:code] || postgres["code"]
2696+
constraint = postgres[:constraint] || postgres["constraint"]
2697+
2698+
if code in ["23503", 23503, :foreign_key_violation] and is_binary(constraint) do
2699+
[{:foreign_key, constraint}]
2700+
else
2701+
[]
2702+
end
2703+
end
2704+
2705+
defp maybe_foreign_key_violation_constraints(_), do: []
2706+
26852707
defp constraints_to_errors(
26862708
%{constraints: user_constraints} = changeset,
26872709
action,

test/destroy_test.exs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,30 @@
44

55
defmodule AshPostgres.DestroyTest do
66
use AshPostgres.RepoCase, async: false
7-
alias AshPostgres.Test.Post
7+
alias AshPostgres.Test.{Post, Permalink}
8+
9+
test "destroy with restrict on_delete returns would leave records behind error" do
10+
post =
11+
Post
12+
|> Ash.Changeset.for_create(:create, %{})
13+
|> Ash.create!()
14+
15+
Permalink |> Ash.Changeset.for_create(:create, %{post_id: post.id}) |> Ash.create!()
16+
17+
assert {:error, %Ash.Error.Invalid{errors: errors}} =
18+
post
19+
|> Ash.Changeset.for_destroy(:destroy, %{})
20+
|> Ash.destroy()
21+
22+
assert Enum.any?(errors, fn
23+
%Ash.Error.Changes.InvalidAttribute{message: msg} ->
24+
msg =~ "would leave records behind"
25+
26+
_ ->
27+
false
28+
end),
29+
"Expected 'would leave records behind' error, got: #{inspect(errors)}"
30+
end
831

932
test "destroy action destroys the record" do
1033
post =

0 commit comments

Comments
 (0)