Behaviour for Etcher annotation persistence.
Etcher's <Etcher.layer> component doesn't run any persistence itself
— it fires etcher:created / :updated / :deleted events to the
consumer's LiveView. Consumers persist via either:
- the bundled
Etcher.Storage.Default(writes to theetcher_annotationstable — generated bymix etcher.gen.migration) - their own implementation of this behaviour
Why a behaviour
Etcher is generic; downstream apps often have richer needs than the
default schema can express. PhoenixKit, for example, links each
annotation to a phoenix_kit_comments discussion thread, so its
adapter opens a transaction that creates both rows. Other consumers
might fan out to multi-tenant tables, log to an audit trail, etc.
Etcher itself doesn't depend on any specific Repo — the bundled
Etcher.Storage.Default reads config :etcher, repo: … at runtime.
Example custom adapter
defmodule MyApp.AnnotationStorage do
@behaviour Etcher.Storage
alias MyApp.Repo
alias MyApp.Annotation
def create(attrs) do
%Annotation{}
|> Annotation.changeset(attrs)
|> Repo.insert()
end
def list_for(target_type, target_uuid) do
Repo.all(
from a in Annotation,
where: a.target_type == ^target_type and a.target_uuid == ^target_uuid
)
end
def update(uuid, attrs) do
Repo.get(Annotation, uuid)
|> Annotation.changeset(attrs)
|> Repo.update()
end
def delete(uuid) do
case Repo.get(Annotation, uuid) do
nil -> {:error, :not_found}
row -> Repo.delete(row)
end
end
end
Summary
Types
An annotation as a plain map (or whatever your adapter returns).
Callbacks
Persist a new annotation. Returns {:ok, annotation} or {:error, reason}.
Delete an annotation by uuid. Returns :ok or {:error, reason}.
Return all annotations for a given target. Plain list; order by
position ascending if your schema supports it.
Update an annotation by uuid. Same attrs shape as create/1 but
typically with just :geometry (after the user drags a shape) or
:style.
Types
Callbacks
@callback create(attrs :: map()) :: {:ok, annotation()} | {:error, term()}
Persist a new annotation. Returns {:ok, annotation} or {:error, reason}.
attrs is the payload emitted by the etcher:created event, with
string keys ("target_type", "target_uuid", "kind", "geometry",
"creator_uuid", optionally "style" and "metadata").
Delete an annotation by uuid. Returns :ok or {:error, reason}.
@callback list_for(target_type :: String.t(), target_uuid :: String.t()) :: [annotation()]
Return all annotations for a given target. Plain list; order by
position ascending if your schema supports it.
@callback update(uuid :: String.t(), attrs :: map()) :: {:ok, annotation()} | {:error, term()}
Update an annotation by uuid. Same attrs shape as create/1 but
typically with just :geometry (after the user drags a shape) or
:style.