lib/openagents/transparency/artifact_link.ex

58e6347eeb72 · 2 KB

defmodule OpenAgents.Transparency.ArtifactLink do
  @moduledoc """
  A durable link between an artifact, the account and repository that own it,
  and a transparency tier. The link is the consent-bearing record for any
  public disclosure of the artifact.
  """

  use Ecto.Schema
  import Ecto.Changeset

  @primary_key {:id, :binary_id, autogenerate: true}
  @foreign_key_type :binary_id
  @timestamps_opts [type: :utc_datetime_usec]

  # `changelog`, `release`, `issue`, and `build` name work that finished.
  # `attempt`, `work_job`, `deployment`, and `trace` name work in progress,
  # which is why each needs its own field-by-field schedule in
  # `OpenAgents.Transparency.WorkDisclosure` rather than a single verdict about
  # the record. `trace` has no producer today: the vocabulary admits one and
  # nothing writes one, and `WorkDisclosureTest` asserts exactly that rather
  # than leaving a member that reads as shipped.
  @artifact_types ~w(changelog release issue build attempt work_job deployment trace)
  # A ref kind, not a ref. A work record is addressed by its own identifier,
  # which is why `id` joins the four git ref kinds.
  @artifact_refs ~w(sha tag digest path id)
  @tiers ~w(dark pulse ledger glass)

  schema "artifact_links" do
    field :account_id, :binary_id
    field :repository_id, :binary_id
    field :artifact_type, :string
    field :artifact_ref, :string
    field :tier, :string
    field :consent, :map, default: %{}
    field :authority_snapshot, :map, default: %{}
    field :revoked_at, :utc_datetime_usec
    field :revocation_tombstone, :map, default: %{}
    timestamps(updated_at: false)
  end

  def artifact_types, do: @artifact_types
  def artifact_refs, do: @artifact_refs
  def tiers, do: @tiers

  def changeset(artifact_link, attrs) do
    artifact_link
    |> cast(attrs, [
      :account_id,
      :repository_id,
      :artifact_type,
      :artifact_ref,
      :tier,
      :consent,
      :authority_snapshot
    ])
    |> validate_required([
      :account_id,
      :repository_id,
      :artifact_type,
      :artifact_ref,
      :tier
    ])
    |> validate_inclusion(:artifact_type, @artifact_types)
    |> validate_inclusion(:artifact_ref, @artifact_refs)
    |> validate_inclusion(:tier, @tiers)
  end
end