Identity for user email will not work during upsert

So, I added the following to my User resource:
identities do
identity :unique_email, [:email] do
eager_check_with Marketplace.Accounts
end
end
identities do
identity :unique_email, [:email] do
eager_check_with Marketplace.Accounts
end
end
Now, in my seeds.exs, I'm trying to add a default user, and I'm using upsert to not create a new one:
Accounts.User
|> Ash.Changeset.new()
|> Ash.Changeset.for_create(:register_with_password, super_user_args)
|> Ash.Changeset.force_change_attribute(:confirmed_at, DateTime.utc_now())
|> Ash.Changeset.force_change_attribute(:roles, [:super_admin])
|> Accounts.create!(upsert?: true, upsert_identity: :unique_email)
Accounts.User
|> Ash.Changeset.new()
|> Ash.Changeset.for_create(:register_with_password, super_user_args)
|> Ash.Changeset.force_change_attribute(:confirmed_at, DateTime.utc_now())
|> Ash.Changeset.force_change_attribute(:roles, [:super_admin])
|> Accounts.create!(upsert?: true, upsert_identity: :unique_email)
I would expect that this would just update the existing user with the same email, but it actually tries to add a new user with the same email:
** (Ash.Error.Invalid) Input Invalid

* email: has already been taken.
(ash 2.6.3) lib/ash/changeset/changeset.ex:834: Ash.Changeset.do_validate_identity/3
(ash 2.6.3) lib/ash/changeset/changeset.ex:756: anonymous fn/2 in Ash.Changeset.eager_validate_identities/1
(elixir 1.14.0) lib/enum.ex:2468: Enum."-reduce/3-lists^foldl/2-0-"/3
** (Ash.Error.Invalid) Input Invalid

* email: has already been taken.
(ash 2.6.3) lib/ash/changeset/changeset.ex:834: Ash.Changeset.do_validate_identity/3
(ash 2.6.3) lib/ash/changeset/changeset.ex:756: anonymous fn/2 in Ash.Changeset.eager_validate_identities/1
(elixir 1.14.0) lib/enum.ex:2468: Enum."-reduce/3-lists^foldl/2-0-"/3
Any idea why?
6 Replies
ZachDaniel
ZachDaniel3y ago
Ah, yeah okay interesting. Its the eager_check_with that is failing it doesn't know that this is an upsert
Blibs
BlibsOP3y ago
Hmm, but if I remove the eager_check_with option the code will not even compile:
** (Spark.Error.DslError) [Marketplace.Accounts.User]
identities -> identity:
The unique_email identity on the resource `Marketplace.Accounts.User` needs the `eager_check_with` property set so that inhibited changes are still validated.
(spark 0.4.4) lib/spark/dsl/extension.ex:605: Spark.Dsl.Extension.raise_transformer_error/2
** (Spark.Error.DslError) [Marketplace.Accounts.User]
identities -> identity:
The unique_email identity on the resource `Marketplace.Accounts.User` needs the `eager_check_with` property set so that inhibited changes are still validated.
(spark 0.4.4) lib/spark/dsl/extension.ex:605: Spark.Dsl.Extension.raise_transformer_error/2
ZachDaniel
ZachDaniel3y ago
I see, you're using the ETS data layer I assume? So the basic problem is that upsert?: true and upsert_identity: :unique_email can be specified when calling .create! but its better to specify them when building the changeset. (for things like this) because the eager validate logic will see that you are upserting on that identity and know not to check it
Accounts.User
|> Ash.Changeset.for_create(:register_with_password, super_user_args, upsert?: true, upsert_identity: :unique_email)
|> Ash.Changeset.force_change_attribute(:confirmed_at, DateTime.utc_now())
|> Ash.Changeset.force_change_attribute(:roles, [:super_admin])
|> Accounts.create!()
Accounts.User
|> Ash.Changeset.for_create(:register_with_password, super_user_args, upsert?: true, upsert_identity: :unique_email)
|> Ash.Changeset.force_change_attribute(:confirmed_at, DateTime.utc_now())
|> Ash.Changeset.force_change_attribute(:roles, [:super_admin])
|> Accounts.create!()
You don't need Ash.Changeset.new() a vast majority of the time
Blibs
BlibsOP3y ago
Nop, I'm using Postgres
ZachDaniel
ZachDaniel3y ago
oh thats the error coming from ash_authentication, I see. the fix I provided should still work
Blibs
BlibsOP3y ago
Yep, it worked great! Thanks!

Did you find this page helpful?