defmodule Stellar.Horizon.Operation do @moduledoc """ Represents a `Operation` resource from Horizon API. """ @behaviour Stellar.Horizon.Resource alias Stellar.Horizon.{Mapping, Transaction} alias Stellar.Horizon.Operation.{ AccountMerge, AllowTrust, BeginSponsoringFutureReserves, BumpSequence, ChangeTrust, CreateAccount, CreateClaimableBalance, ClaimClaimableBalance, CreatePassiveSellOffer, EndSponsoringFutureReserves, LiquidityPoolDeposit, LiquidityPoolWithdraw, ManageBuyOffer, ManageSellOffer, ManageData, PathPaymentStrictReceive, PathPaymentStrictSend, Payment, RevokeSponsorship, SetOptions } @type body :: AccountMerge.t() | AllowTrust.t() | BeginSponsoringFutureReserves.t() | BumpSequence.t() | ChangeTrust.t() | CreateAccount.t() | CreateClaimableBalance.t() | ClaimClaimableBalance.t() | CreatePassiveSellOffer.t() | EndSponsoringFutureReserves.t() | LiquidityPoolDeposit.t() | LiquidityPoolWithdraw.t() | ManageBuyOffer.t() | ManageSellOffer.t() | ManageData.t() | PathPaymentStrictReceive.t() | PathPaymentStrictSend.t() | Payment.t() | RevokeSponsorship.t() | SetOptions.t() @type t :: %__MODULE__{ id: String.t(), paging_token: String.t(), source_account: String.t(), type_i: non_neg_integer(), type: String.t(), transaction_hash: String.t(), transaction_successful: boolean(), body: body(), transaction: Transaction.t() | nil, created_at: DateTime.t() } defstruct [ :id, :paging_token, :source_account, :type, :type_i, :transaction_hash, :transaction_successful, :body, :transaction, :created_at ] @mapping [ id: :integer, type_i: :integer, transaction: {:struct, Transaction}, created_at: :date_time ] @impl true def new(attrs, opts \\ []) def new(attrs, _opts) do mapping = attrs |> Map.get(:type) |> operation_type_mapping() attrs |> operation_attrs() |> (&Mapping.build(%__MODULE__{}, &1)).() |> Mapping.parse(mapping) end @spec operation_attrs(attrs :: map()) :: map() defp operation_attrs(attrs) do body_attrs = %__MODULE__{} |> Map.from_struct() |> Map.keys() |> (&Map.drop(attrs, &1 ++ [:_links])).() Map.put(attrs, :body, body_attrs) end @spec operation_type_mapping(type :: any()) :: Keyword.t() defp operation_type_mapping(nil), do: @mapping defp operation_type_mapping(type), do: Keyword.merge(@mapping, body: {:struct, operation_type(type)}) @spec operation_type(type :: String.t()) :: module() defp operation_type("create_account"), do: CreateAccount defp operation_type("payment"), do: Payment defp operation_type("path_payment_strict_receive"), do: PathPaymentStrictReceive defp operation_type("path_payment_strict_send"), do: PathPaymentStrictSend defp operation_type("manage_sell_offer"), do: ManageSellOffer defp operation_type("manage_buy_offer"), do: ManageBuyOffer defp operation_type("create_passive_sell_offer"), do: CreatePassiveSellOffer defp operation_type("set_options"), do: SetOptions defp operation_type("change_trust"), do: ChangeTrust defp operation_type("allow_trust"), do: AllowTrust defp operation_type("account_merge"), do: AccountMerge defp operation_type("manage_data"), do: ManageData defp operation_type("bump_sequence"), do: BumpSequence defp operation_type("create_claimable_balance"), do: CreateClaimableBalance defp operation_type("claim_claimable_balance"), do: ClaimClaimableBalance defp operation_type("begin_sponsoring_future_reserves"), do: BeginSponsoringFutureReserves defp operation_type("end_sponsoring_future_reserves"), do: EndSponsoringFutureReserves defp operation_type("revoke_sponsorship"), do: RevokeSponsorship defp operation_type("liquidity_pool_deposit"), do: LiquidityPoolDeposit defp operation_type("liquidity_pool_withdraw"), do: LiquidityPoolWithdraw end