Ltix.Pagination (Ltix v0.2.0)

Copy Markdown View Source

Lazy paginated fetching for LTI Advantage endpoints.

LTI Advantage services use RFC 8288 Link headers with rel="next" for pagination. This module handles link parsing and builds a lazy stream that fetches pages on demand. A next link back to a page already fetched is a Ltix.Errors.Unknown.TransportError.

Summary

Functions

Extract the rel="next" target from response headers.

Extract the rel="next" URL, resolving relative targets against response_url.

Fetch a paginated endpoint as a lazy stream of response bodies.

Fetch a paginated endpoint as a lazy stream of result tuples.

Functions

parse_next_link(headers)

@spec parse_next_link(%{required(String.t()) => [String.t()]}) :: String.t() | nil

Extract the rel="next" target from response headers.

Parses RFC 8288 Link headers and returns the target as supplied, or nil if not present. Ignores links with an anchor parameter. Use parse_next_link/2 to resolve relative targets.

iex> Ltix.Pagination.parse_next_link(%{"link" => ["<https://example.com/p2>; rel=\"next\""]})
"https://example.com/p2"

iex> Ltix.Pagination.parse_next_link(%{"link" => ["<?page=2>; rel=next"]})
"?page=2"

iex> Ltix.Pagination.parse_next_link(%{})
nil

parse_next_link(headers, response_url)

@spec parse_next_link(%{required(String.t()) => [String.t()]}, String.t()) ::
  String.t() | nil

Extract the rel="next" URL, resolving relative targets against response_url.

Pass the URL of the response that has the headers. Returns nil if no next link is present. Ignores links with an anchor parameter.

iex> Ltix.Pagination.parse_next_link(
...>   %{"link" => ["<../three>; rel=next"]},
...>   "https://example.com/pages/two"
...> )
"https://example.com/three"

stream(url, headers, opts \\ [])

@spec stream(String.t(), [{String.t(), String.t()}], keyword()) ::
  {:ok, Enumerable.t()} | {:error, Exception.t()}

Fetch a paginated endpoint as a lazy stream of response bodies.

Fetches the first page eagerly. If it succeeds, returns {:ok, stream} where each element is a decoded JSON response body. Subsequent pages are fetched lazily as the stream is consumed. If the first page fails, returns {:error, reason} immediately. Later failures raise during enumeration.

Options

  • :params (map/0) - Query parameters for the first page only. The default value is %{}.

  • :req_options (keyword/0) - HTTP request options, merged over config :ltix, req_options. The default value is [].

  • :local_network_access (nil | :none | :local | :loopback) - Addresses platform requests may connect to besides public ones. :local adds private network addresses, and :loopback adds those and this machine. Defaults to config :ltix, :local_network_access, then :none.

stream_results(url, headers, opts \\ [])

@spec stream_results(String.t(), [{String.t(), String.t()}], keyword()) ::
  {:ok, Enumerable.t()} | {:error, Exception.t()}

Fetch a paginated endpoint as a lazy stream of result tuples.

Fetches the first page eagerly. Returns {:ok, stream} with {:ok, body} for each page, or {:error, error} if the first page fails. A later failure emits one {:error, error} and ends the stream.

Options

  • :params (map/0) - Query parameters for the first page only. The default value is %{}.

  • :req_options (keyword/0) - HTTP request options, merged over config :ltix, req_options. The default value is [].

  • :local_network_access (nil | :none | :local | :loopback) - Addresses platform requests may connect to besides public ones. :local adds private network addresses, and :loopback adds those and this machine. Defaults to config :ltix, :local_network_access, then :none.