|
1 | 1 | defmodule Gnat.Jetstream.Pager do |
2 | | - @moduledoc false |
| 2 | + @moduledoc """ |
| 3 | + Page through all the messages in a stream |
| 4 | +
|
| 5 | + This module provides a synchronous API to inspect the messages in a stream. |
| 6 | + You can use the reduce module to write a simple function that works like `Enum.reduce` across each message individually. |
| 7 | + If you want to handle messages in batches, you can use the `init` + `page` functions to accomplish that. |
| 8 | + """ |
3 | 9 |
|
4 | 10 | alias Gnat.Jetstream |
5 | 11 | alias Gnat.Jetstream.API.{Consumer, Util} |
6 | 12 |
|
7 | 13 | @opaque pager :: map() |
8 | 14 | @type message :: Gnat.message() |
9 | | - |
| 15 | + @type opts :: list(opt()) |
| 16 | + |
| 17 | + @typedoc """ |
| 18 | + Options you can pass to the pager |
| 19 | +
|
| 20 | + * `batch` controls the maximum number of messages we'll pull in each page/batch (default 10) |
| 21 | + * `domain` You can specify a jetstream domain if needed |
| 22 | + * `from_datetime` Only page through messages recorded on or after this datetime |
| 23 | + * `from_seq` Only page through messages with a sequence number equal or above this option |
| 24 | + * `headers_only` You can pass `true` to this if you only want to see the headers from each message. Can be useful to get metadata without having to receieve large body payloads. |
| 25 | +
|
| 26 | + """ |
| 27 | + @type opt :: |
| 28 | + {:batch, non_neg_integer()} |
| 29 | + | {:domain, String.t()} |
| 30 | + | {:from_datetime, DateTime.t()} |
| 31 | + | {:from_seq, non_neg_integer} |
| 32 | + | {:headers_only, boolean()} |
| 33 | + |
| 34 | + @spec init(Gnat.t(), String.t(), opts()) :: {:ok, pager()} | {:error, term()} |
10 | 35 | def init(conn, stream_name, opts) do |
11 | 36 | domain = Keyword.get(opts, :domainl) |
12 | 37 |
|
@@ -55,6 +80,26 @@ defmodule Gnat.Jetstream.Pager do |
55 | 80 | end |
56 | 81 | end |
57 | 82 |
|
| 83 | + @doc """ |
| 84 | + Similar to Enum.reduce but you can iterate through all messages in a stream |
| 85 | +
|
| 86 | + ``` |
| 87 | + # Assume we have a stream with messages like "1", "2", ... "10" |
| 88 | + Gnat.Jetstream.Pager.reduce(:gnat, "NUMBERS_STREAM", [batch_size: 5], 0, fn(message, total) -> |
| 89 | + num = String.to_integer(message.body) |
| 90 | + total + num |
| 91 | + end) |
| 92 | +
|
| 93 | + # => {:ok, 55} |
| 94 | + ``` |
| 95 | + """ |
| 96 | + @spec reduce( |
| 97 | + Gnat.t(), |
| 98 | + String.t(), |
| 99 | + opts(), |
| 100 | + Enum.acc(), |
| 101 | + (Gnat.message(), Enum.acc() -> Enum.acc()) |
| 102 | + ) :: {:ok, Enum.acc()} | {:error, term()} |
58 | 103 | def reduce(conn, stream_name, opts, initial_state, fun) do |
59 | 104 | with {:ok, pager} <- init(conn, stream_name, opts) do |
60 | 105 | page_through(pager, initial_state, fun) |
@@ -109,12 +154,17 @@ defmodule Gnat.Jetstream.Pager do |
109 | 154 |
|
110 | 155 | ## Helpers for accepting user options |
111 | 156 | defp apply_opts_to_consumer(consumer = %Consumer{}, opts) do |
112 | | - case Keyword.get(opts, :from_seq) do |
113 | | - nil -> |
| 157 | + from = {Keyword.get(opts, :from_seq), Keyword.get(opts, :from_datetime)} |
| 158 | + |
| 159 | + case from do |
| 160 | + {nil, nil} -> |
114 | 161 | consumer |
115 | 162 |
|
116 | | - seq when is_integer(seq) -> |
| 163 | + {seq, _} when is_integer(seq) -> |
117 | 164 | %Consumer{consumer | deliver_policy: :by_start_sequence, opt_start_seq: seq} |
| 165 | + |
| 166 | + {_, %DateTime{} = dt} -> |
| 167 | + %Consumer{consumer | deliver_policy: :by_start_time, opt_start_time: dt} |
118 | 168 | end |
119 | 169 | end |
120 | 170 | end |
0 commit comments