cover/Elixir.WalletIntegrations.CallbackStore.html

1 defmodule WalletIntegrations.CallbackStore do
2 @moduledoc """
3 ETS-backed GenServer for CallbackRecord persistence.
4
5 Tables:
6 - `:wallet_integrations_callbacks` — keyed by callback_id.
7 - `:wallet_integrations_cb_provider_idx` — bag: {provider, callback_id}.
8 - `:wallet_integrations_cb_msg_id_idx` — set: {inbox_message_id, callback_id} for dedup lookup.
9 """
10
11 use GenServer
12
13 alias WalletIntegrations.CallbackRecord
14
15 @table :wallet_integrations_callbacks
16 @prov_idx :wallet_integrations_cb_provider_idx
17 @msg_idx :wallet_integrations_cb_msg_id_idx
18
19
:-(
def start_link(opts), do: GenServer.start_link(__MODULE__, opts, name: __MODULE__)
20
21 @spec store(CallbackRecord.t()) :: :ok | {:error, :duplicate_callback_id}
22 9 def store(%CallbackRecord{} = cb), do: GenServer.call(__MODULE__, {:store, cb})
23
24 @spec get(callback_id :: String.t()) :: {:ok, CallbackRecord.t()} | {:error, :not_found}
25 2 def get(id), do: GenServer.call(__MODULE__, {:get, id})
26
27 @spec update(CallbackRecord.t()) :: :ok | {:error, :not_found}
28 11 def update(%CallbackRecord{} = cb), do: GenServer.call(__MODULE__, {:update, cb})
29
30 @spec get_by_message_id(message_id :: String.t()) :: {:ok, CallbackRecord.t()} | {:error, :not_found}
31
:-(
def get_by_message_id(message_id), do: GenServer.call(__MODULE__, {:get_by_msg_id, message_id})
32
33 @spec list_by_provider(provider :: atom()) :: [CallbackRecord.t()]
34 1 def list_by_provider(provider), do: GenServer.call(__MODULE__, {:list_by_provider, provider})
35
36 @spec reset() :: :ok
37 23 def reset, do: GenServer.call(__MODULE__, :reset)
38
39 @impl true
40 def init(_opts) do
41
:-(
:ets.new(@table, [:set, :protected, :named_table])
42
:-(
:ets.new(@prov_idx, [:bag, :protected, :named_table])
43
:-(
:ets.new(@msg_idx, [:set, :protected, :named_table])
44 {:ok, %{}}
45 end
46
47 @impl true
48 def handle_call({:store, cb}, _from, state) do
49 9 if :ets.member(@table, cb.callback_id) do
50
:-(
{:reply, {:error, :duplicate_callback_id}, state}
51 else
52 9 :ets.insert(@table, {cb.callback_id, cb})
53 9 :ets.insert(@prov_idx, {cb.provider, cb.callback_id})
54 9 if cb.inbox_message_id do
55 9 :ets.insert(@msg_idx, {cb.inbox_message_id, cb.callback_id})
56 end
57 9 {:reply, :ok, state}
58 end
59 end
60
61 @impl true
62 def handle_call({:get, id}, _from, state) do
63 2 result =
64 case :ets.lookup(@table, id) do
65 2 [{_, cb}] -> {:ok, cb}
66
:-(
[] -> {:error, :not_found}
67 end
68 2 {:reply, result, state}
69 end
70
71 @impl true
72 def handle_call({:update, cb}, _from, state) do
73 11 result =
74 11 case :ets.lookup(@table, cb.callback_id) do
75 [{_, _old}] ->
76 11 :ets.insert(@table, {cb.callback_id, cb})
77 :ok
78
:-(
[] ->
79 {:error, :not_found}
80 end
81 11 {:reply, result, state}
82 end
83
84 @impl true
85 def handle_call({:get_by_msg_id, message_id}, _from, state) do
86
:-(
result =
87 case :ets.lookup(@msg_idx, message_id) do
88 [{_, cb_id}] ->
89
:-(
case :ets.lookup(@table, cb_id) do
90
:-(
[{_, cb}] -> {:ok, cb}
91
:-(
[] -> {:error, :not_found}
92 end
93
:-(
[] -> {:error, :not_found}
94 end
95
:-(
{:reply, result, state}
96 end
97
98 @impl true
99 def handle_call({:list_by_provider, provider}, _from, state) do
100 1 ids = :ets.lookup(@prov_idx, provider) |> Enum.map(fn {_, id} -> id end)
101 1 items = Enum.flat_map(ids, fn id ->
102 1 case :ets.lookup(@table, id) do
103 1 [{_, cb}] -> [cb]
104
:-(
[] -> []
105 end
106 end)
107 1 {:reply, items, state}
108 end
109
110 @impl true
111 def handle_call(:reset, _from, state) do
112 23 :ets.delete_all_objects(@table)
113 23 :ets.delete_all_objects(@prov_idx)
114 23 :ets.delete_all_objects(@msg_idx)
115 23 {:reply, :ok, state}
116 end
117 end
Line Hits Source