defmodule Podbox.Download.Asset do @moduledoc """ An item in the download queue. """ use Ash.Resource, data_layer: AshSqlite.DataLayer, domain: Podbox.Download, notifiers: [Ash.Notifier.PubSub] alias Ash.Query require Ash.Query @type t :: Ash.Resource.record() attributes do uuid_primary_key :id attribute :uri, :string, allow_nil?: false attribute :state, :atom, allow_nil?: false, default: :queued, constraints: [one_of: [:queued, :dequeued, :downloading, :complete, :failed]] attribute :remaining_retries, :integer, allow_nil?: false, constraints: [min: 0] attribute :total_bytes, :integer, allow_nil?: true, constraints: [min: 0] attribute :transferred_bytes, :integer, allow_nil?: true, constraints: [min: 0] attribute :content_type, :string, allow_nil?: true attribute :error_detail, :string, allow_nil?: true attribute :headers, :map, allow_nil?: false, public?: true, sensitive?: true, default: %{} create_timestamp :inserted_at update_timestamp :updated_at end sqlite do table "download_assets" repo Podbox.Repo end pub_sub do module Podbox.Download.PubSub prefix "asset" publish :enqueue, "queued" publish :started, "started" publish :pause, "paused" publish :failed, "failed" publish :progress, "progress" publish :complete, "complete" end actions do create :enqueue do argument :uri, :string, allow_nil?: true, public?: true argument :headers, :map, allow_nil?: true, public?: true, sensitive?: true argument :retries, :integer, allow_nil?: true, default: 5, public?: true change set_attribute(:uri, arg(:uri)) change set_attribute(:headers, arg(:headers)), where: [present(:headers)] change set_attribute(:remaining_retries, arg(:retries)) change set_attribute(:state, :queued) end update :started do argument :total_bytes, :integer, allow_nil?: true, constraints: [min: 0], public?: true argument :transferred_bytes, :integer, allow_nil?: true, constraints: [min: 0], public?: true argument :content_type, :string, allow_nil?: true, public?: true change set_attribute(:state, :downloading) change set_attribute(:content_type, arg(:content_type)), where: [present(:content_type)] change set_attribute(:total_bytes, arg(:total_bytes)), where: [present(:total_bytes)] change set_attribute(:transferred_bytes, arg(:transferred_bytes)), where: [present(:transferred_bytes)] end update :pause do change set_attribute(:state, :queued) end update :failed do argument :error_detail, :string, allow_nil?: false, public?: true change set_attribute(:error_detail, arg(:error_detail)) change set_attribute(:state, :failed), where: [attribute_equals(:remaining_retries, 0)] change set_attribute(:state, :queued), where: [compare(:remaining_retries, greater_than: 0)] change increment(:remaining_retries, amount: -1), where: [compare(:remaining_retries, greater_than: 0)] end update :progress do argument :transferred_bytes, :integer, allow_nil?: false, constraints: [min: 0] change set_attribute(:transferred_bytes, arg(:transferred_bytes)) end update :complete do argument :transferred_bytes, :integer, allow_nil?: true, constraints: [min: 0], public?: true change set_attribute(:state, :complete) change set_attribute(:transferred_bytes, arg(:transferred_bytes)), where: [present(:transferred_bytes)] end read :queued do argument :limit, :integer, allow_nil?: true, constraints: [min: 0] prepare build(sort: [updated_at: :desc], limit: arg(:limit)) end action :dequeue, {:array, :struct} do constraints items: [instance_of: __MODULE__] argument :limit, :integer, allow_nil?: true, constraints: [min: 0] run &dequeue/2 end destroy :destroy do primary? true end read :read do primary? true pagination keyset?: true, required?: false end update :dequeued do change set_attribute(:state, :dequeued) end end @doc false def dequeue(input, _) do __MODULE__ |> Query.for_read(:read) |> Query.sort(updated_at: :desc) |> Query.filter(state == :queued || (state == :dequeued && updated_at <= ago(2, :hour))) |> Query.limit(input.arguments.limit) |> Ash.bulk_update(:dequeued, %{}, return_records?: true) |> case do %{status: :success, records: records} -> {:ok, records} %{errors: errors} -> {:error, errors} end end end