Currently migrating from 2.x to 3.x!
My test suite detected a behavior change that I couldn’t explain by looking at the upgrade doc or the new documentation, so I’m really not sure how I should handle this.
I got an update action that starts the email change process, which is basically just my Ash version of what is found in the mix phx.gen.auth
generator.
If the email is already taken, I add an error with Changeset.add_error/2
in the first before_transaction
hook:
update :request_email_change do
description """
Create a user token for changing the credential's email and send a confirmation email.
"""
accept [:email]
argument :current_password, :string, allow_nil?: false, sensitive?: true
argument :update_email_url_fun, :function,
allow_nil?: false,
description: """
A function taking the confirmation token as its only argument and returning the URL to send to the user.
"""
validate changing(:email)
change fn changeset, %{actor: actor} ->
changeset
|> Changeset.before_transaction(fn changeset ->
case changeset
|> Changeset.get_attribute(:email)
|> UserCredential.get_by_email(actor: actor) do
{:ok, _} ->
Changeset.add_error(
changeset,
Ash.Error.Changes.InvalidArgument.exception(
field: :email,
message: "has already been taken"
)
)
{:error, _} ->
changeset
end
end)
|> Changeset.before_transaction(&validate_current_password/1)
|> Changeset.before_action(fn changeset ->
current_email = Changeset.get_data(changeset, :email)
update_email_url_fun = Changeset.get_argument(changeset, :update_email_url_fun)
{:ok, applied_credential} = Ash.Changeset.apply_attributes(changeset)
Accounts.deliver_credential_update_email_instructions(
Ash.load!(applied_credential, [:user], actor: actor),
current_email,
update_email_url_fun
)
# clear the change so no update happens yet
Changeset.clear_change(changeset, :email)
end)
end
end
I’m almost 100% positive that previously this prevented the execution of the next hooks.
The following test makes sure the email validation uniqueness works, and was passing before upgrading to 3.0:
test "validates email uniqueness", %{user: user, credential: credential} do
%{email: email} = user_registration_fixture().credential
assert_raise Ash.Error.Invalid,
~r/email: has already been taken/,
fn ->
UserCredential.request_email_change!(
credential,
email,
valid_user_credential_password(),
&update_email_url_fun/1,
actor: user
)
end
end
Instead of raising the expected Ash.Error.Invalid
as before, I know get a Ash.Error.Unknown
because there is a match error in the before_action
hook (when matching {:ok, applied_credential}
), despite my InvalidArgument
error being present in the changeset:
%Ash.Error.Unknown{
changeset: "#Changeset<>",
errors: [
%Ash.Error.Unknown.UnknownError{
error: %MatchError{
term: {:error,
#Ash.Changeset<
domain: TalentIdeal.Accounts,
action_type: :update,
action: :request_email_change,
attributes: %{
email: #Ash.CiString<"user-576460752303423358@example.com">
},
relationships: %{},
arguments: %{ ... },
errors: [
%Ash.Error.Changes.InvalidArgument{
field: :email,
message: "has already been taken",
value: nil,
splode: nil,
bread_crumbs: [],
vars: [],
path: [],
stacktrace: #Splode.Stacktrace<>,
class: :invalid
}
],
data: #TalentIdeal.Accounts.UserCredential< ... >,
valid?: false
>}
},
field: nil,
splode: Ash.Error,
bread_crumbs: [],
vars: [],
path: [],
stacktrace: #Splode.Stacktrace<>,
class: :unknown
}
]
}
Am I missing something or is there indeed a behavior change, and is it expected? Is it documented somewhere I didn’t find?
Thanks in advance!