I have function in changeset pipeline to encrypt token.
def changeset(user, params \\ %{}) do
user
|> cast(params, [:id, :token]
|> encrypt(:token)
end
Since this function is new, the old data is still not encrypted and I need to do it manually. Then I met the problem.
If I pass the original token
to changeset, it will treat the value as if no changes. The encrypt
in pipeline is not working and value not update.
If I encrypted the token
and pass to changeset, it marked as changes, and encrypt
function applied. However, it encrypted twice
.
One clumsy way is to add a checking on encrypt
to check if the token
is encrypted or not. But remember before we passed new token to changeset, we still need to check if the token is encrypted. Which means, we do the checking twice.
So I'm looking for a simple solution if someone have the idea.
Cheers
Since this is a run-once action, I would go with a migration, that directly calls Ecto.Repo.update/2
(unfortunately I did not find a way to call Ecto.Repo.update_all/3
here unless you can do encrypt
directly in the database.)
defmodule My.Repo.Migrations.EncryptTokens do
@moduledoc false
use Ecto.Migration
def up do
for unencrypted <- from(...) |> Repo.all() do
unencrypted
|> Ecto.Changeset.change(token: encrypt(unencrypted.token))
|> Repo.update()
end
end
def down, do: raise "unrevertable"
end
Sidenote: you might also take a look at Ecto.Changeset.force_change/3
fwiw.