Current section

Files

Jump to
ex_esdb lib repl subscriber.ex
Raw

lib/repl/subscriber.ex

defmodule ExESDB.Repl.Subscriber do
@moduledoc """
The Repl.Subscriber is a GenServer that:
- creates a permanent subscription to the store.
- subscribes to the events emitted by the store, by handling events directly.
"""
use GenServer
alias ExESDB.GatewayAPI, as: API
alias ExESDB.Themes, as: Themes
alias ExESDB.Options, as: Options
@impl true
def handle_info({:event_emitted, event}, state) do
%{
event_stream_id: stream_id,
event_type: event_type,
event_number: version,
data: payload
} = event
%{
store: store,
subscription_name: subscription_name
} = state
IO.puts("\nCONSUMED #{stream_id}:#{event_type}
version #{inspect(version)}
payload: #{inspect(payload, pretty: true)}")
store
|> API.ack_event(subscription_name, event, self())
{:noreply, state}
end
@impl true
def handle_info({:DOWN, _ref, :process, _pid, _reason}, state) do
{:stop, :normal, state}
end
@impl true
def handle_info(msg, state) do
IO.puts("UNHANDLED MESSAGE #{inspect(msg)}")
{:noreply, state}
end
@spec start(
subscription_name :: String.t(),
start_from :: integer(),
selector :: String.t()
) :: pid()
def start(subscription_name, start_from \\ 0, selector \\ "$all") do
args = [
subscription_name: subscription_name,
start_from: start_from,
selector: selector
]
case start_link(args) do
{:ok, pid} ->
IO.puts("#{Themes.subscriber(pid)} for [#{inspect(subscription_name)}] is UP!")
pid
{:error, {:already_started, pid}} ->
IO.puts("#{Themes.subscriber(pid)} for [#{inspect(subscription_name)}] is UP!")
pid
{:error, reason} ->
raise "Failed to start subscriber. Reason: #{inspect(reason)}"
end
end
############## PLUMBING ##############
@impl true
def init(args) do
store = Options.store_id()
selector = selector(args)
subscription_name = subscription_name(args)
start_from = start_from(args)
store
|> API.save_subscription(
:by_stream,
selector,
subscription_name,
start_from,
self()
)
{:ok,
%{
store: store,
subscription_name: subscription_name,
selector: selector
}}
end
def start_link(args) do
GenServer.start_link(
__MODULE__,
args,
name: Module.concat(__MODULE__, subscription_name(args))
)
end
def child_spec(init_arg) do
%{
id: __MODULE__,
start: {__MODULE__, :start_link, [init_arg]},
type: :worker,
restart: :permanent,
shutdown: 5000
}
end
defp selector(args), do: Keyword.get(args, :selector, "$all")
defp subscription_name(args), do: Keyword.get(args, :subscription_name, "transient")
defp start_from(args), do: Keyword.get(args, :start_from, 0)
end