lib/glific_web/resolvers/media.ex

defmodule GlificWeb.Resolvers.Media do
  @moduledoc """
  Resolver to deal with file uploads, which we send directly to GCS
  """
  alias Glific.{GCS.GcsWorker, Users.User}

  @doc """
  Upload a file given its extension
  """
  @spec upload(Absinthe.Resolution.t(), map(), %{context: map()}) ::
          {:ok, any} | {:error, any}
  def upload(
        _,
        %{media: media, extension: extension, organization_id: organization_id},
        %{context: %{current_user: user}}
      ) do
    GcsWorker.upload_media(media.path, remote_name(user, extension), organization_id)
    |> handle_response()
  end

  @doc """
  Upload a blob encoded in base64 given its extension
  """
  @spec upload_blob(Absinthe.Resolution.t(), map(), %{context: map()}) ::
          {:ok, any} | {:error, any}
  def upload_blob(
        _,
        %{media: media, extension: extension, organization_id: organization_id},
        %{context: %{current_user: user}}
      ) do
    uuid = Ecto.UUID.generate()

    # first decode blob and store in temp file
    local_file = local_name(extension, uuid)

    File.write!(
      local_file,
      Base.decode64!(media)
    )

    GcsWorker.upload_media(local_file, remote_name(user, extension, uuid), organization_id)
    |> handle_response()
  end

  @spec local_name(String.t(), Ecto.UUID.t()) :: String.t()
  defp local_name(extension, uuid),
    do: "#{System.tmp_dir!()}/#{uuid}.#{extension}"

  @spec remote_name(User.t(), String.t(), Ecto.UUID.t() | nil) :: String.t()
  defp remote_name(user, extension, uuid \\ Ecto.UUID.generate()) do
    {year, week} = Timex.iso_week(Timex.now())
    "outbound/#{year}-#{week}/#{user.name}/#{uuid}.#{extension}"
  end

  @spec handle_response(any()) :: {:ok, String.t()} | {:error, String.t()}
  defp handle_response(response) do
    response
    |> case do
      {:ok, %{url: url} = _} -> {:ok, url}
      error -> {:error, "Something went wrong #{inspect(error)}"}
    end
  end
end