Current section
Files
Jump to
Current section
Files
lib/xdk/paginator.ex
# AUTO-GENERATED FILE - DO NOT EDIT
# This file was automatically generated by the XDK build tool.
# Any manual changes will be overwritten on the next generation.
defmodule Xdk.Paginator do
@moduledoc """
Cursor-based pagination helper.
Wraps any paginated endpoint function into a lazy `Stream` that
automatically follows `next_token` / `pagination_token`.
## Example
fetch = fn token ->
opts = [query: "elixir", max_results: 100]
opts = if token, do: Keyword.put(opts, :pagination_token, token), else: opts
Xdk.Posts.search_recent(client, opts)
end
Xdk.Paginator.stream(fetch)
|> Stream.flat_map(fn {:ok, page} -> page["data"] || [] end)
|> Enum.take(500)
"""
@type fetch_fun :: (String.t() | nil -> {:ok, map()} | {:error, term()})
@spec stream(fetch_fun(), keyword()) :: Enumerable.t()
def stream(fetch_fun, _opts \\ []) do
Stream.resource(
fn -> nil end,
fn
:halt ->
{:halt, :halt}
token ->
case fetch_fun.(token) do
{:ok, page} ->
next = extract_next_token(page)
next_state = if next, do: next, else: :halt
{[{:ok, page}], next_state}
{:error, _} = err ->
{[err], :halt}
end
end,
fn _ -> :ok end
)
end
@spec items(fetch_fun(), keyword()) :: Enumerable.t()
def items(fetch_fun, opts \\ []) do
data_key = Keyword.get(opts, :data_key, "data")
stream(fetch_fun, opts)
|> Stream.flat_map(fn
{:ok, page} -> page[data_key] || []
{:error, _} -> []
end)
end
defp extract_next_token(page) do
meta = page["meta"] || %{}
meta["next_token"] || meta["pagination_token"]
end
end