defmodule HeliumElixir.BlockchainApi do
use HTTPoison.Base
require HTTPoison.Retry
alias HTTPoison.Retry
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, Jason.encode!(body), headers) |> respond()
end
def get_json(
%BlockchainRequestConfig{
headers: headers,
base_url: base_url,
path: path,
recv_timeout: recv_timeout,
max_retries: max_retries,
hackney: hackney
},
params \\ %{}
) do
get(base_url <> path, headers,
params: params,
follow_redirect: true,
recv_timeout: recv_timeout,
hackney: hackney
)
|> Retry.autoretry(
max_attempts: max_retries,
wait: 15000,
retry_unknown_errors: 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
def each_page(%BlockchainRequestConfig{} = config, fun, cursor \\ nil) do
case get_json(config, %{cursor: cursor}) do
{:ok, %{"data" => data, "cursor" => next_cursor}} ->
fun.(data)
each_page(config, fun, next_cursor)
{:ok, %{"data" => data}} ->
fun.(data)
:ok
{:error, error} ->
{:error, error}
end
end
end