lib/cozy_oss/api_client/finch.ex

defmodule CozyOSS.ApiClient.Finch do
  @moduledoc """
  Finch-based API client for CozyOSS.

      config :cozy_oss, :api_client, CozyOSS.ApiClient.Finch

  In order to use `Finch` API client, you must start `Finch` and provide a `:name`.
  Often in your supervision tree:

      children = [
        {Finch, name: CozyOSS.Finch}
      ]

  Or, in rare cases, dynamically:

      Finch.start_link(name: CozyOSS.Finch)

  If a name different from `CozyOSS.Finch` is used, or you want to use an existing Finch instance,
  you can provide the name via the config:

      config :cozy_oss,
        api_client: CozyOSS.ApiClient.Finch,
        finch_name: My.Custom.Name

  """

  require Logger
  alias CozyOSS.ApiRequest

  @behaviour CozyOSS.ApiClient

  @impl true
  def init do
    unless Code.ensure_loaded?(Finch) do
      Logger.error("""
      Could not find finch dependency.

      Please add :finch to your dependencies:

          {:finch, "~> 0.13"}

      Or set your own CozyOSS.ApiClient:

          config :cozy_oss, :api_client, MyAPIClient

      """)

      raise "missing finch dependency"
    end

    _ = Application.ensure_all_started(:finch)
    :ok
  end

  @impl true
  def request(%ApiRequest{} = req) do
    method = build_method(req)
    url = ApiRequest.to_url(req)
    headers = build_headers(req)
    body = build_body(req)

    request = Finch.build(method, url, headers, body)

    case Finch.request(request, finch_name()) do
      {:ok, response} ->
        {:ok, response.status, response.headers, response.body}

      {:error, reason} ->
        {:error, reason}
    end
  end

  defp build_method(req), do: req.method

  defp build_headers(req) do
    Enum.map(req.headers, fn {k, v} ->
      {to_string(k), to_string(v)}
    end)
  end

  defp build_body(req), do: req.body

  defp finch_name do
    Application.get_env(:cozy_oss, :finch_name, CozyOSS.Finch)
  end
end