Hi!
I’m trying to create a custom ID field for my (RSS/Atom) Feed model. The ID is supposed to be a hash of the feed URL, which is a field of the model as well.
I could be going about this wrong, and I’m very keen on getting some feedback if so. I’ve created a custom Ecto.Type that will be a proxy for a :binary_id
. It’ll require a string to be passed (the feed URL in my case) and then hash the string, and also encode/decode the string into binary and back.
As I believed my hashing method was the issue, I edited my code to simply use the Ecto.UUID
library underneath. However, the issue remains regardless of whether I use a binary
or bitstring
type:
value
<<193, 9, 196, 77, 174, 162, 186, 72, 139, 190, 5, 61, 28, 3, 245, 39>>
forTwigAdmin.News.Feed.id
ininsert
does not match type :binary_id
migration file
defmodule TwigAdmin.Repo.Migrations.CreateFeeds do
use Ecto.Migration
def change do
create table(:feeds, primary_key: false) do
add :id, :binary_id, primary_key: true, null: false
add :uri, :string
add :source_uri, :string
add :name, :string
add :type, :string
add :source_updated, :utc_datetime_usec
add :last_retrieved, :utc_datetime_usec
# belongs to a user
add :user_id, references(:users)
timestamps()
end
end
end
feed id
defmodule TwigAdmin.News.Feed.FeedId do
use Ecto.Type
def type, do: :binary_id
def cast(uri) when is_bitstring(uri) do
{:ok, encode_id(uri)}
end
def cast(_), do: :error
def dump(id) when is_binary(id) do
Ecto.UUID.dump(id)
end
def dump(_), do: :error
def load(uri) when is_bitstring(uri) do
{:ok, encode_id(uri)}
end
defp encode_id(uri) do
{:ok, id} =
uri
|> String.trim()
|> Kernel.then(&:crypto.hash(:md5, &1))
|> Ecto.UUID.load()
id
end
end
feed schema
defmodule TwigAdmin.News.Feed.Schema do
defmacro __using__(_) do
quote do
use Ecto.Schema
alias TwigAdmin.News.Feed.FeedId
@primary_key {:id, :binary_id, autogenerate: false}
@foreign_key_type :id
# @derive {Phoenix.Param, key: :hash_id}
end
end
end
defmodule TwigAdmin.News.Feed do
use TwigAdmin.News.Feed.Schema
import Ecto.Changeset
schema "feeds" do
field(:source_uri, :string)
field(:uri, :string)
field(:name, :string)
field(:type, :string)
field(:source_updated, :utc_datetime_usec)
field(:last_retrieved, :utc_datetime_usec)
belongs_to :user, TwigAdmin.Accounts.User
timestamps()
end
@doc false
def validate_changeset(feed, attrs) do
feed
|> cast(attrs, [:source_uri, :type])
|> validate_required([:source_uri, :type])
end
@doc false
def changeset(feed, attrs) do
feed
|> cast(attrs, [:source_uri, :uri, :name, :type, :source_updated, :last_retrieved])
|> validate_required([:source_uri, :uri, :name, :type, :source_updated, :last_retrieved])
|> put_id()
end
defp put_id(%Ecto.Changeset{} = changeset) do
{:ok, id_str} = FeedId.cast(get_field(changeset, :uri))
{:ok, id} = FeedId.dump(id_str)
changeset
|> validate_required(:uri)
|> put_change(:id, id)
end
end
I’ve been bashing my head against this issue for a few days now, so any help would be greatly appreciated. Thank you in advance!