lib/utils/http_impl.ex

defmodule Web3AptosEx.HTTPImpl do
  @moduledoc """
  the encapsulation of http
  """

  require Logger

  alias Web3AptosEx.HTTP

  @retries 5
  @behaviour HTTP

  @doc """
    +----------------------+
    | json_rpc_constructer |
    +----------------------+
  """
  @impl HTTP
  def json_rpc(method, id) when is_integer(id) do
    %{method: method, jsonrpc: "2.0", id: id}
  end

  def json_rpc(method, params) do
    %{method: method, params: params, jsonrpc: "2.0", id: 1}
  end

  @doc """
    +-------------+
    | get request |
    +-------------+
  """
  @impl HTTP
  def get(url) do
    do_get(url, @retries)
  end

  defp do_get(_url, retries) when retries == 0 do
    {:error, "retires #{@retries} times and not success"}
  end

  defp do_get(url, retries) do
    url
    |> HTTPoison.get([])
    |> handle_response()
    |> case do
      {:ok, body} ->
        {:ok, body}

      {:error, _} ->
        Process.sleep(500)
        do_get(url, retries - 1)
    end
  end

  @doc """
    +--------------+
    | post request |
    +--------------+
  """
  @impl HTTP
  def post(url, body) do
    do_post(url, body, @retries)
  end

  @impl HTTP
  def post(url, body, :urlencoded) do
    do_post(url, body, @retries, :urlencoded)
  end

  defp do_post(_url, _body, retires) when retires == 0 do
    {:error, "retires #{@retries} times and not success"}
  end

  defp do_post(url, body, retries) do
    url
    |> HTTPoison.post(
      Poison.encode!(body),
      [{"Content-Type", "application/json"}]
    )
    |> handle_response()
    |> case do
      {:ok, body} ->
        {:ok, body}

      {:error, _} ->
        Process.sleep(500)
        do_post(url, body, retries - 1)
    end
  end

  defp do_post(_url, _body, retires, :urlencoded) when retires == 0 do
    {:error, "retires #{@retries} times and not success"}
  end

  defp do_post(url, body, retries, :urlencoded) do
    url
    |> HTTPoison.post(
      body,
      [{"Content-Type", "application/x-www-form-urlencoded"}]
    )
    |> handle_response()
    |> case do
      {:ok, body} ->
        {:ok, body}

      {:error, _} ->
        Process.sleep(500)
        do_post(url, body, retries - 1)
    end
  end

  # normal
  defp handle_response({:ok, %HTTPoison.Response{status_code: status_code, body: body}})
       when status_code in 200..299 do
    case Poison.decode(body) do
      {:ok, json_body} ->
        {:ok, ExStructTranslator.to_atom_struct(json_body)}

      {:error, payload} ->
        Logger.error("Reason: #{inspect(payload)}")
        {:error, :network_error}
    end
  end

  # 404 or sth else
  defp handle_response({:ok, %HTTPoison.Response{status_code: status_code, body: _}}) do
    Logger.error("Reason: #{status_code} ")
    {:error, :network_error}
  end

  defp handle_response(error) do
    Logger.error("Reason: other_error")
    error
  end
end