lib/blockchain_api/blockchain_api.ex

defmodule HeliumElixir.BlockchainApi do
  use HTTPoison.Base
  alias HTTPoison.Response
  alias HTTPoison.Error
  alias HeliumElixir.BlockchainApi.BlockchainRequestConfig
  alias HeliumElixir.BlockchainApi.TakeMinConfig

  defp respond({:ok, %Response{body: body, status_code: status_code}}) do
    case status_code do
      code when code <= 299 ->
        response = body |> Jason.decode!()
        {:ok, response}

      _ ->
        {:error, message: "Request failed with status_code #{status_code}", code: status_code}
    end
  end

  defp respond({:error, %Error{reason: reason}}) do
    {:error, message: reason, code: -1}
  end

  def post_json(
        %BlockchainRequestConfig{headers: headers, base_url: base_url, path: path},
        body \\ %{}
      ) do
    post(base_url <> path, headers, Jason.encode!(body)) |> respond()
  end

  def get_json(
        %BlockchainRequestConfig{headers: headers, base_url: base_url, path: path},
        params \\ %{}
      ) do
    get(base_url <> path, headers, params: params, follow_redirect: true) |> respond()
  end

  @doc """
  Pages the given endpoint until `min_count` is reached.
  The data returned will be at least the amount requested unless no more items exist.

  Returns `{:ok, %{"data" => data, "cursor" => cursor}}`.
  """
  def take_min(
        %BlockchainRequestConfig{} = config,
        %TakeMinConfig{min_count: min_count, params: params, acc: acc, cursor: cursor}
      ) do
    case get_json(config, Map.merge(params, %{cursor: cursor})) do
      {:ok, %{"data" => data, "cursor" => next_cursor}} ->
        next_data = acc ++ data
        acc_count = length(next_data)

        if(acc_count >= min_count || is_nil(next_cursor)) do
          {:ok, %{"data" => next_data, "cursor" => next_cursor}}
        else
          take_min(config, %TakeMinConfig{
            min_count: min_count,
            params: params,
            acc: next_data,
            cursor: next_cursor
          })
        end

      {:ok, %{"data" => data}} ->
        {:ok, %{"data" => acc ++ data, "cursor" => ""}}

      {:error, message: message, code: code} ->
        {:error, message: message, code: code}
    end
  end
end