diff --git a/CHANGELOG.md b/CHANGELOG.md index d8c2f0c..645ee24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Fixes and improvements - Fix datetime being reset at every changes +- Fix account creation with a soft-deleted email ## v.3.0.0 diff --git a/lib/claper/accounts/user.ex b/lib/claper/accounts/user.ex index abcc923..cf8f427 100644 --- a/lib/claper/accounts/user.ex +++ b/lib/claper/accounts/user.ex @@ -2,6 +2,7 @@ defmodule Claper.Accounts.User do use Ecto.Schema import Ecto.Changeset + import Ecto.Query @derive { Flop.Schema, @@ -129,7 +130,9 @@ defmodule Claper.Accounts.User do |> validate_required([:email]) |> validate_format(:email, ~r/^[^\s]+@[^\s]+$/, message: "must have the @ sign and no spaces") |> validate_length(:email, max: 160) - |> unsafe_validate_unique(:email, Claper.Repo) + |> unsafe_validate_unique(:email, Claper.Repo, + query: from(user in __MODULE__, where: is_nil(user.deleted_at)) + ) |> unique_constraint(:email) end diff --git a/priv/repo/migrations/20260807052500_allow_reusing_deleted_user_emails.exs b/priv/repo/migrations/20260807052500_allow_reusing_deleted_user_emails.exs new file mode 100644 index 0000000..c722e9a --- /dev/null +++ b/priv/repo/migrations/20260807052500_allow_reusing_deleted_user_emails.exs @@ -0,0 +1,8 @@ +defmodule Claper.Repo.Migrations.AllowReusingDeletedUserEmails do + use Ecto.Migration + + def change do + drop unique_index(:users, [:email]) + create unique_index(:users, [:email], where: "deleted_at IS NULL") + end +end diff --git a/test/claper/accounts_test.exs b/test/claper/accounts_test.exs index dea7237..2cbca22 100644 --- a/test/claper/accounts_test.exs +++ b/test/claper/accounts_test.exs @@ -186,6 +186,20 @@ defmodule Claper.AccountsTest do assert oidc_user.user.first_name == "Local" assert oidc_user.user.last_name == "Name" end + + test "creates a new account when the previous account with that email was deleted" do + deleted_user = user_fixture() + assert {:ok, deleted_user} = Accounts.delete_user(deleted_user) + + assert {:ok, oidc_user} = + Accounts.get_or_create_user_with_oidc( + oidc_user_attrs(%{email: deleted_user.email}) + ) + + refute oidc_user.user.id == deleted_user.id + refute Accounts.deleted?(oidc_user.user) + assert Accounts.get_user_by_email(deleted_user.email).id == oidc_user.user.id + end end describe "change_user_email/2" do