Utilities for unwrapping paginated Pluggy API responses.
Pluggy list endpoints return a paginated map:
%{results: [items...], page: 1, total_pages: 3, total: 150}This module provides helpers for working with these responses:
results/1— extract the:resultslist from a single page; also accepts cursor tuples fromlist_with_cursorall_results/1— eagerly collect and flatten all pages into one list
All functions accept {:ok, body} or {:error, reason} tuples and
propagate errors unchanged. results/1 also accepts a bare
%Req.Response{} struct and cursor tuples for convenience.
Req plugin
attach/2 adds a response step to a Req.Request that automatically
unwraps paginated responses.
Summary
Functions
Collects all items across every page into a single flat list.
Attaches an unwrap response step to a Req.Request.
Bang variant of results/1 — extracts the :results list on success
or raises on error.
Extracts the :results list from a paginated response.
Unwraps a response tuple, returning the value on success or raising on error.
Functions
@spec all_results(Pluggy.HTTP.cursor_result()) :: {:ok, list()} | {:error, Pluggy.Error.t()}
Collects all items across every page into a single flat list.
Accepts the return value of a list_with_cursor call. Fetches all
remaining pages eagerly and returns {:ok, items} or {:error, reason}
if any page fetch fails.
Examples
Pluggy.Connectors.list_with_cursor(client)
|> Pluggy.Unwrap.all_results()
#=> [%{id: 201, ...}, %{id: 202, ...}, ...]
@spec attach(Req.Request.t(), :results | :all_results | :stream) :: Req.Request.t()
Attaches an unwrap response step to a Req.Request.
The step transforms paginated response bodies according to the given mode. Non-paginated responses (e.g. single-resource GETs) pass through unchanged.
Modes
:results— replaces the body with the:resultslist from the current page:all_results— eagerly fetches every page and replaces the body with a single flat list of all items:stream— replaces the body with a lazyStreamthat yields one paginated map per page (useEnumorStreamto consume)
Non-paginated responses pass through unchanged for all modes.
Usage
req = Pluggy.Unwrap.attach(req, :results)
# Now paginated responses return just the items list:
{:ok, %Req.Response{body: items}} = Req.request(req, url: "/transactions", ...)
# Or eagerly fetch all pages:
req = Pluggy.Unwrap.attach(req, :all_results)
{:ok, %Req.Response{body: all_items}} = Req.request(req, url: "/transactions", ...)
# Or stream pages lazily:
req = Pluggy.Unwrap.attach(req, :stream)
{:ok, %Req.Response{body: stream}} = Req.request(req, url: "/transactions", ...)
stream |> Stream.flat_map(& &1.results) |> Enum.take(50)
Bang variant of results/1 — extracts the :results list on success
or raises on error.
Also accepts cursor tuples from list_with_cursor.
Examples
iex> Pluggy.Unwrap.results!({:ok, %{results: [1, 2], page: 1, total_pages: 1, total: 2}})
[1, 2]
iex> Pluggy.Unwrap.result({:error, %Pluggy.Error{code: 500, message: "boom"}})
{:error, %Pluggy.Error{code: 500, message: "boom"}}
@spec results( Req.Response.t() | {:ok, map(), nil} | {:ok, map(), Pluggy.HTTP.Cursor.t()} | {:ok, map()} | {:error, term()} ) :: {:ok, list() | term()} | {:error, term()}
Extracts the :results list from a paginated response.
Accepts {:ok, body}, {:ok, body, cursor}, {:error, reason}, or a
bare %Req.Response{}. Returns the body unchanged when it is not a
paginated map.
Examples
iex> Pluggy.Unwrap.results({:ok, %{results: [1, 2], page: 1, total_pages: 1, total: 2}})
{:ok, [1, 2]}
iex> Pluggy.Unwrap.results({:ok, %{id: "single"}})
{:ok, %{id: "single"}}
iex> Pluggy.Unwrap.results(%Req.Response{status: 200, body: %{results: [1, 2], page: 1, total_pages: 1, total: 2}})
{:ok, [1, 2]}
iex> Pluggy.Unwrap.results({:error, %Pluggy.Error{code: 500, message: "boom"}})
{:error, %Pluggy.Error{code: 500, message: "boom"}}
@spec results!( Req.Response.t() | {:ok, term(), nil} | {:ok, term(), Pluggy.HTTP.Cursor.t()} | {:ok, term()} | {:error, term()} ) :: term()
Unwraps a response tuple, returning the value on success or raising on error.
Examples
iex> Pluggy.Unwrap.result!({:ok, %{id: "single"}})
%{id: "single"}