lib/ex_balena/api/client.ex

defmodule ExBalena.Client do
  @moduledoc """
  Implements a `t:Tesla.Client` for making calls to the Balena API.
  """

  @type from :: :host | :remote

  @spec new() :: Tesla.Client.t()
  def new do
    auth_middleware =
      if(is_container?(),
        do: {Tesla.Middleware.Query, [apikey: get_supervisor_api_key()]},
        else: {Tesla.Middleware.BearerAuth, token: token()}
      )

    middleware = [
      {Tesla.Middleware.BaseUrl, base_url()},
      {Tesla.Middleware.Headers, [{"content-type", "application/json"}]},
      auth_middleware,
      {Tesla.Middleware.JSON, []}
    ]

    Tesla.client(middleware)
  end

  @spec get_supervisor_address() :: Balena.supervisor_address() | no_return()
  @spec get_supervisor_api_key() :: String.t() | no_return()

  def get_supervisor_address do
    case System.get_env("BALENA_SUPERVISOR_ADDRESS") do
      "http://" <> _ = url -> url
      _ -> throw("the \"BALENA_SUPERVISOR_ADDRESS\" is not a valid url")
    end
  end

  def get_supervisor_api_key() do
    case System.get_env("BALENA_SUPERVISOR_API_KEY") do
      api_key when is_binary(api_key) and api_key != "" -> api_key
      _ -> throw("\"BALENA_SUPERVISOR_API_KEY\" is invalid")
    end
  end

  @spec is_container?() :: boolean()
  def is_container? do
    case System.get_env("BALENA") do
      env when env in ["1", "true"] -> true
      _ -> false
    end
  end

  @spec base_url(boolean()) :: Balena.supervisor_address()
  @spec base_url() :: Balena.supervisor_address()
  defp base_url(false), do: "https://api.balena-cloud.com/supervisor"
  defp base_url(true), do: get_supervisor_address()
  defp base_url, do: base_url(is_container?())

  @spec token() :: String.t() | no_return()
  defp token() do
    localpath = Path.expand("~/.balena/token")

    if File.exists?(localpath) do
      localpath
      |> File.read!()
      |> String.trim()
    end
  end
end