Add base infra to store assets

This commit is contained in:
Claudio Ortolina
2025-09-01 10:43:47 +03:00
parent 74fa3b7a6f
commit a605610c9d
4 changed files with 94 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
defmodule MusicLibrary.Assets do
alias MusicLibrary.Assets.Asset
alias MusicLibrary.Repo
def store(params) do
%Asset{}
|> Asset.changeset(params)
|> Repo.insert()
end
end
+36
View File
@@ -0,0 +1,36 @@
defmodule MusicLibrary.Assets.Asset do
use Ecto.Schema
import Ecto.Changeset
@primary_key {:hash, :string, autogenerate: false}
schema "assets" do
field :content, :binary
field :format, :string
field :properties, :map, default: %{}, read_after_writes: true
timestamps(type: :utc_datetime)
end
def changeset(asset, attrs) do
asset
|> cast(attrs, [:content, :format, :properties])
|> validate_required([:content, :format])
|> generate_hash()
|> unique_constraint(:hash)
end
defp generate_hash(changeset) do
case get_change(changeset, :content) do
nil ->
changeset
content ->
put_change(changeset, :hash, hash(content))
end
end
defp hash(content) do
:crypto.hash(:sha256, content) |> Base.encode16()
end
end
@@ -0,0 +1,14 @@
defmodule MusicLibrary.Repo.Migrations.CreateAssets do
use Ecto.Migration
def change do
create table(:assets, primary_key: false) do
add :hash, :string, primary_key: true
add :content, :binary, null: false
add :format, :string, null: false
add :properties, :map, default: %{}
timestamps(type: :utc_datetime)
end
end
end
+34
View File
@@ -0,0 +1,34 @@
defmodule MusicLibrary.AssetsTest do
use MusicLibrary.DataCase
alias MusicLibrary.Assets
describe "store/1" do
test "stores using the hash as key" do
params = %{
content: "some content",
format: "text/plain",
properties: %{language: "english"}
}
assert {:ok, asset} = Assets.store(params)
assert asset.hash == "290F493C44F5D63D06B374D0A5ABD292FAE38B92CAB2FAE5EFEFE1B0E9347F56"
assert asset.properties == %{"language" => "english"}
assert asset.content == "some content"
assert asset.format == "text/plain"
end
test "prevents duplicates" do
params = %{
content: "some content",
format: "text/plain",
properties: %{language: "english"}
}
assert {:ok, _asset} = Assets.store(params)
assert {:error, changeset} = Assets.store(params)
assert [hash: {"has already been taken", _}] = changeset.errors
end
end
end