lib/active_storage.ex

defmodule ActiveStorage do
  @moduledoc """
  The ActiveStorage context.
  """

  import Ecto.Query, warn: false
  alias Chaskiq.Repo

  alias ActiveStorage.{Attachment, Blob}

  def verifier do
    Chaskiq.Verifier
  end

  def graphql_resolver(
        _a,
        %{
          input:
            %{
              byte_size: _byte_size,
              checksum: _checksum,
              content_type: _content_type,
              filename: _filename
            } = input
        },
        %{context: %{current_user: _current_user}}
      ) do
    direct_upload_response = %{direct_upload: create_direct_upload(input)}

    {:ok, direct_upload_response}
  end

  def create_direct_upload(%{
        byte_size: byte_size,
        checksum: checksum,
        content_type: content_type,
        filename: filename
      }) do
    service = "amazon"

    {:ok, blob} =
      ActiveStorage.create_storage_blob(%{
        byte_size: byte_size,
        checksum: checksum,
        content_type: content_type,
        filename: filename,
        metadata: %{},
        service_name: service
      })

    _service = blob |> ActiveStorage.Blob.service()

    ActiveStorage.Service.S3Service.create_direct_upload(blob, %{
      byte_size: byte_size,
      checksum: checksum,
      content_type: content_type,
      filename: filename
    })
  end

  # def url(blob) do
  #  service(blob).url(blob)
  # end

  def url(blob) do
    ActiveStorage.Service.url(blob)
  end

  def service_url(blob) do
    signed_blob_id = Chaskiq.Verifier.sign(blob.id)
    "/active_storage/blobs/redirect/#{signed_blob_id}"
  end

  @doc """
  Returns the list of storage_blob.

  ## Examples

      iex> list_storage_blob()
      [%Blob{}, ...]

  """
  def list_storage_blob do
    Repo.all(Blob)
  end

  @doc """
  Gets a single storage_blob.

  Raises `Ecto.NoResultsError` if the Storage blob does not exist.

  ## Examples

      iex> get_storage_blob!(123)
      %Blob{}

      iex> get_storage_blob!(456)
      ** (Ecto.NoResultsError)

  """
  def get_storage_blob!(id), do: Repo.get!(Blob, id)

  @doc """
  Creates a storage_blob.

  ## Examples

      iex> create_storage_blob(%{field: value})
      {:ok, %Blob{}}

      iex> create_storage_blob(%{field: bad_value})
      {:error, %Ecto.Changeset{}}

  """
  def create_storage_blob(attrs \\ %{}) do
    %Blob{}
    |> Blob.changeset(attrs)
    |> Repo.insert()
  end

  @doc """
  Updates a storage_blob.

  ## Examples

      iex> update_storage_blob(storage_blob, %{field: new_value})
      {:ok, %Blob{}}

      iex> update_storage_blob(storage_blob, %{field: bad_value})
      {:error, %Ecto.Changeset{}}

  """
  def update_storage_blob(%Blob{} = storage_blob, attrs) do
    storage_blob
    |> Blob.changeset(attrs)
    |> Repo.update()
  end

  @doc """
  Deletes a storage_blob.

  ## Examples

      iex> delete_storage_blob(storage_blob)
      {:ok, %Blob{}}

      iex> delete_storage_blob(storage_blob)
      {:error, %Ecto.Changeset{}}

  """
  def delete_storage_blob(%Blob{} = storage_blob) do
    Repo.delete(storage_blob)
  end

  @doc """
  Returns an `%Ecto.Changeset{}` for tracking storage_blob changes.

  ## Examples

      iex> change_storage_blob(storage_blob)
      %Ecto.Changeset{data: %Blob{}}

  """
  def change_storage_blob(%Blob{} = storage_blob, attrs \\ %{}) do
    Blob.changeset(storage_blob, attrs)
  end

  @doc """
  Gets a record's attachment.

  Returns `nil` if attachment doesn't exist.

  ## Examples

      iex> get_attachment(user, "avatar")
      %ActiveStorage.Attachment{}
  """
  def get_attachment(record, attachment_name) do
    attachment_query(record, attachment_name)
    |> repo().one()
  end

  def get_attachments(record, attachment_name) do
    attachment_query(record, attachment_name)
    |> repo().all()
  end

  @doc """
  Checks if attachment(s) exists (works for `has_one_attached` and `has_many_attached`).

  ## Examples

      iex> attached?(user, "avatar")
      true
  """
  def attached?(record, attachment_name) do
    attachment_query(record, attachment_name)
    |> repo().exists?()
  end

  defp attachment_query(%mod{id: record_id}, attachment_name) do
    record_type = mod.record_type()

    from(a in Attachment, where: a.name == ^attachment_name and a.record_type == ^record_type and a.record_id == ^record_id)
    |> preload(:blob)
  end

  defp repo do
    Application.fetch_env!(:active_storage, :repo)
  end

  # Downloads the blob to a tempfile on disk. Yields the tempfile.
  #
  # The tempfile's name is prefixed with +ActiveStorage-+ and the blob's ID. Its extension matches that of the blob.
  #
  # By default, the tempfile is created in <tt>Dir.tmpdir</tt>. Pass +tmpdir:+ to create it in a different directory:
  #
  #   blob.open(tmpdir: "/path/to/tmp") do |file|
  #     # ...
  #   end
  #
  # The tempfile is automatically closed and unlinked after the given block is executed.
  #
  # Raises ActiveStorage::IntegrityError if the downloaded data does not match the blob's checksum.
  # def open(tmpdir: nil, &block) do
  # service.open(
  #   key,
  #   checksum: checksum,
  #   verify: !composed,
  #   name: [ "ActiveStorage-#{id}-", filename.extension_with_delimiter ],
  #   tmpdir: tmpdir,
  #   &block
  # )
  # end

  # def mirror_later # :nodoc:
  #  ActiveStorage::MirrorJob.perform_later(key, checksum: checksum) if service.respond_to?(:mirror)
  # end

  def variable_content_types,
    do: [
      "image/png",
      "image/gif",
      "image/jpg",
      "image/jpeg",
      "image/pjpeg",
      "image/tiff",
      "image/bmp",
      "image/vnd.adobe.photoshop",
      "image/vnd.microsoft.icon",
      "image/webp"
    ]

  def web_image_content_types,
    do: [
      "image/png",
      "image/jpeg",
      "image/jpg",
      "image/gif"
    ]

  def content_types_to_serve_as_binary,
    do: [
      "text/html",
      "text/javascript",
      "image/svg+xml",
      "application/postscript",
      "application/x-shockwave-flash",
      "text/xml",
      "application/xml",
      "application/xhtml+xml",
      "application/mathml+xml",
      "text/cache-manifest"
    ]

  def content_types_allowed_inline,
    do: [
      "image/png",
      "image/gif",
      "image/jpg",
      "image/jpeg",
      "image/tiff",
      "image/bmp",
      "image/vnd.adobe.photoshop",
      "image/vnd.microsoft.icon",
      "application/pdf"
    ]

  def previewers,
    do: [
      ActiveStorage.Previewer.PopplerPDFPreviewer,
      ActiveStorage.Previewer.MuPDFPreviewer,
      ActiveStorage.Previewer.VideoPreviewer
    ]

  def analyzers,
    do: [ActiveStorage.Analyzer.ImageAnalyzer, ActiveStorage.Analyzer.VideoAnalyzer]

  # def paths, do: ActiveSupport :: OrderedOptions.new()
  # def queues, do: ActiveSupport :: InheritableOptions.new()
end