Pluggy.Unwrap (PluggyAI v0.1.0)

Copy Markdown View Source

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 :results list from a single page; also accepts cursor tuples from list_with_cursor
  • all_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

all_results(error)

@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, ...}, ...]

attach(req, mode)

@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 :results list 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 lazy Stream that yields one paginated map per page (use Enum or Stream to 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)

result(ok)

@spec result({:ok, term()} | {:error, term()}) :: {:ok, term()} | {:error, term()}

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"}}

results(response)

@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"}}

results!(response)

@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"}