lib/horizon/ledgers.ex

defmodule Stellar.Horizon.Ledgers do
  @moduledoc """
  Exposes functions to interact with Ledgers in Horizon.

  You can:
  * Retrieve a ledger.
  * List all ledgers.
  * List a ledger's effects.
  * List a ledger's transactions.
  * List a ledger's operations.

  Horizon API reference: https://developers.stellar.org/api/resources/ledgers/
  """

  alias Stellar.Horizon.{
    Collection,
    Effect,
    Error,
    Ledger,
    Operation,
    Request,
    Transaction
  }

  @type sequence :: non_neg_integer()
  @type params :: Keyword.t()
  @type resource :: Ledger.t() | Collection.t()
  @type response :: {:ok, resource()} | {:error, Error.t()}

  @endpoint "ledgers"

  @doc """
  Retrieves information of a specific ledger.

  ## Parameters:
    * `sequence`: The sequence number of a specific ledger.

  ## Examples

      iex> Ledgers.retrieve(27147222)
      {:ok, %Ledger{}}
  """
  @spec retrieve(sequence :: sequence()) :: response()
  def retrieve(sequence) do
    :get
    |> Request.new(@endpoint, path: sequence)
    |> Request.perform()
    |> Request.results(as: Ledger)
  end

  @doc """
  Lists all ledgers.

  ## Options

    * `cursor`: A number that points to a specific location in a collection of responses and is pulled from the `paging_token` value of a record.
    * `order`: A designation of the order in which records should appear. Options include `asc` (ascending) or `desc` (descending).
    * `limit`: The maximum number of records returned. The limit can range from 1 to 200. Defaults to 10.

  ## Examples

      iex> Ledgers.all(limit: 10, order: :asc)
      {:ok, %Collection{records: [%Ledger{}, ...]}}
  """
  @spec all(params :: params()) :: response()
  def all(params \\ []) do
    :get
    |> Request.new(@endpoint)
    |> Request.add_query(params)
    |> Request.perform()
    |> Request.results(collection: {Ledger, &all/1})
  end

  @doc """
  Lists successful transactions in a specific ledger.

  ## Parameters
    * `sequence`: The sequence number of a specific ledger.

  ## Options
    * `cursor`: A number that points to a specific location in a collection of responses and is pulled from the `paging_token` value of a record.
    * `order`: A designation of the order in which records should appear. Options include `asc` (ascending) or `desc` (descending).
    * `limit`: The maximum number of records returned. The limit can range from 1 to 200. Defaults to 10.
    * `include_failed`: Set to true to include failed operations in results.

  ## Examples

      iex> Ledgers.list_transactions(27147222, limit: 20)
      {:ok, %Collection{records: [%Transaction{}, ...]}}
  """
  @spec list_transactions(sequence :: sequence(), params :: params()) :: response()
  def list_transactions(sequence, params \\ []) do
    :get
    |> Request.new(@endpoint, path: sequence, segment: "transactions")
    |> Request.add_query(params, extra_params: [:include_failed])
    |> Request.perform()
    |> Request.results(collection: {Transaction, &list_transactions(sequence, &1)})
  end

  @doc """
  Lists successful operations in a specific ledger.

  ## Parameters
    * `sequence`: The sequence number of a specific ledger.

  ## Options
    * `cursor`: A number that points to a specific location in a collection of responses and is pulled from the `paging_token` value of a record.
    * `order`: A designation of the order in which records should appear. Options include `asc` (ascending) or `desc` (descending).
    * `limit`: The maximum number of records returned. The limit can range from 1 to 200. Defaults to 10.
    * `include_failed`: Set to true to include failed operations in results.
    * `join`: Set to `transactions` to include the transactions which created each of the operations in the response.

  ## Examples

      iex> Ledgers.list_operations(27147222, limit: 20)
      {:ok, %Collection{records: [%Operation{}, ...]}}

      # join transactions
      iex> Ledgers.list_operations(27147222, join: "transactions")
      {:ok, %Collection{records: [%Operation{transaction: %Transaction{}}, ...]}}
  """
  @spec list_operations(sequence :: sequence(), params :: params()) :: response()
  def list_operations(sequence, params \\ []) do
    :get
    |> Request.new(@endpoint, path: sequence, segment: "operations")
    |> Request.add_query(params, extra_params: [:include_failed, :join])
    |> Request.perform()
    |> Request.results(collection: {Operation, &list_operations(sequence, &1)})
  end

  @doc """
  Lists successful payments in a specific ledger.

  ## Parameters
    * `sequence`: The sequence number of a specific ledger.

  ## Options
    * `cursor`: A number that points to a specific location in a collection of responses and is pulled from the `paging_token` value of a record.
    * `order`: A designation of the order in which records should appear. Options include `asc` (ascending) or `desc` (descending).
    * `limit`: The maximum number of records returned. The limit can range from 1 to 200. Defaults to 10.
    * `include_failed`: Set to true to include failed operations in results.
    * `join`: Set to `transactions` to include the transactions which created each of the operations in the response.

  ## Examples

      iex> Ledgers.list_payments(27147222, limit: 20)
      {:ok, %Collection{records: [%Operation{body: %Payment{}}, ...]}}

      # include failed
      iex> Ledgers.list_payments(27147222, include_failed: true)
      {:ok, %Collection{records: [%Operation{body: %Payment{}}, ...]}}
  """
  @spec list_payments(sequence :: sequence(), params :: params()) :: response()
  def list_payments(sequence, params \\ []) do
    :get
    |> Request.new(@endpoint, path: sequence, segment: "payments")
    |> Request.add_query(params, extra_params: [:include_failed, :join])
    |> Request.perform()
    |> Request.results(collection: {Operation, &list_payments(sequence, &1)})
  end

  @doc """
  Lists the effects of a specific ledger.

  ## Parameters
    * `sequence`: The sequence number of a specific ledger.

  ## Options
    * `cursor`: A number that points to a specific location in a collection of responses and is pulled from the `paging_token` value of a record.
    * `order`: A designation of the order in which records should appear. Options include `asc` (ascending) or `desc` (descending).
    * `limit`: The maximum number of records returned. The limit can range from 1 to 200. Defaults to 10.

  ## Examples

      iex> Ledgers.list_effects(27147222, limit: 20)
      {:ok, %Collection{records: [%Effect{}, ...]}}
  """
  @spec list_effects(sequence :: sequence(), params :: params()) :: response()
  def list_effects(sequence, params \\ []) do
    :get
    |> Request.new(@endpoint, path: sequence, segment: "effects")
    |> Request.add_query(params)
    |> Request.perform()
    |> Request.results(collection: {Effect, &list_effects(sequence, &1)})
  end
end