lib/ex_open_sea/assets/show.ex

defmodule ExOpenSea.Assets.Show do
  @moduledoc """
  Return information about a single NFT, based on its contract address and token ID. The response
  will contain an Asset Object.

  https://docs.opensea.io/reference/retrieving-a-single-asset
  """

  alias ExOpenSea.Http

  @type api_key :: ExOpenSea.ApiKey.t()
  @type contract_address :: String.t()
  @type token_id :: non_neg_integer
  @type params :: %{
          optional(:account_address) => String.t(),
          optional(:include_orders) => boolean
        }
  @type assets_cursor :: ExOpenSea.AssetsCursor.t()
  @type error_reason :: Maptu.non_strict_error_reason() | String.t()
  @type raw_payload :: map
  @type result :: {:ok, assets_cursor, raw_payload} | {:error, error_reason, raw_payload | nil}

  @spec get(api_key, contract_address, token_id) :: result
  @spec get(api_key, contract_address, token_id, params) :: result
  def get(api_key, contract_address, token_id, params \\ %{}) do
    "/api/v1/asset/#{contract_address}/#{token_id}"
    |> Http.Request.for_path()
    |> Http.Request.with_query(params)
    |> Http.Request.with_auth(api_key)
    |> Http.Client.get()
    |> parse_response()
  end

  defp parse_response({:ok, raw_payload}) do
    {ok_or_error, asset_or_reason} = Mapail.map_to_struct(raw_payload, ExOpenSea.Asset)
    {ok_or_error, asset_or_reason, raw_payload}
  end

  defp parse_response({:error, raw_reasons}) when is_map(raw_reasons) do
    reasons =
      raw_reasons
      |> Enum.reduce(
        [],
        fn {k, v}, acc ->
          acc ++ [{k, v}]
        end
      )

    {:error, reasons, raw_reasons}
  end

  defp parse_response({:error, reason}) do
    {:error, reason, nil}
  end
end