Cookbook: Autopaginate using "Link" header (sentry version) - elixir-tesla/tesla GitHub Wiki
Cookbook: Autopaginate using "Link" header shows an outdated version that doesn't use the up to date Tesla API.
This showcases a recursive middleware that handles pagination through Sentry's specific implementation of the Link
header which contains indication whether the next page contains results in a results=true/false
attribute.
It makes new requests from within the middleware to browse all available pages.
https://docs.sentry.io/api/pagination/
defmodule SentryPaginate do
@behaviour Tesla.Middleware
@impl Tesla.Middleware
def call(env, next, _opts) do
env
|> Tesla.run(next)
|> parse_rels()
|> maybe_get_next(env, next)
end
defp maybe_get_next({:ok, %Tesla.Env{status: 200, opts: opts} = res} = env, original_env, next) do
cond do
opts[:rels]["next"] ->
# Copy opts and query params from the response env,
# these are not modified in the adapters, but middlewares
# that come after might store state there
new_env = %{original_env | opts: res.opts, url: opts[:rels]["next"]}
case call(new_env, next, []) do
{:ok, %Tesla.Env{status: 200} = new_res} ->
{:ok, %{new_res | body: res.body ++ new_res.body}}
error ->
error
end
true ->
env
end
end
defp parse_rels({:ok, env}), do: {:ok, parse_rels(env)}
defp parse_rels({:error, reason}), do: {:error, reason}
defp parse_rels(env) do
if link = Tesla.get_header(env, "link") do
Tesla.put_opt(env, :rels, rels(link))
else
env
end
end
defp rels(link) do
link
|> String.split(",")
|> Enum.map(&String.trim/1)
|> Enum.map(&rel/1)
|> Enum.reject(&is_nil/1)
|> Enum.into(%{})
end
defp rel(item) do
case Regex.run(~r/\A<(.+)>; rel=["]?([^"]+)["]?; results=["]?([^"]+)["]?/, item, capture: :all_but_first) |> Enum.reverse() do
["false", _, _] ->
nil
[_ | rest] ->
List.to_tuple(rest)
end
end
end