lib/graphql.ex

defmodule GraphQL do
  @moduledoc """
  Useful functions to parse data from GraphQL response data
  """

  @doc """
  Find the "pageInfo" data within a GraphQL response.

  ## Examples

      iex> %{
      ...>   "a" => %{
      ...>     "b" => %{
      ...>       "pageInfo" => %{
      ...>         "hasNextPage" => true,
      ...>         "endCursor" => "abc123"
      ...>       },
      ...>       "nodes" => [:one, :two, :three, :etc]
      ...>     }
      ...>   }
      ...> } |> GraphQL.find_pageinfo()
      %{
        "hasNextPage" => true,
        "endCursor" => "abc123"
      }

      iex> GraphQL.find_pageinfo(%{})
      nil
  """
  def find_pageinfo(data) when map_size(data) == 0, do: nil

  def find_pageinfo(data) when is_map(data) do
    data
    |> Enum.reduce(%{}, fn {key, nested_data}, _acc ->
      case key do
        "pageInfo" ->
          nested_data

        _ ->
          find_pageinfo(nested_data)
      end
    end)
  end

  def find_pageinfo(_) do
    nil
  end

  @doc """
  Find "nodes" within a GraphQL response.

  ## Examples

      iex> %{
      ...>   "a" => %{
      ...>     "b" => %{
      ...>       "pageInfo" => %{
      ...>         "hasNextPage" => true,
      ...>         "endCursor" => "abc123"
      ...>       },
      ...>       "nodes" => [:one, :two, :three, :etc]
      ...>     }
      ...>   }
      ...> } |> GraphQL.find_nodes()
      [:one, :two, :three, :etc]

      iex> GraphQL.find_nodes(%{})
      []
  """
  def find_nodes(%{"nodes" => nodes}) do
    nodes
  end

  def find_nodes(map) when is_map(map) do
    Enum.map(map, fn {_key, data} ->
      find_nodes(data)
    end)
    |> List.flatten()
    |> Enum.reject(&is_nil/1)
  end

  def find_nodes(_) do
    nil
  end
end