cover/Elixir.WalletRisk.RiskSignalStore.html

1 defmodule WalletRisk.RiskSignalStore do
2 @moduledoc """
3 ETS-backed GenServer for RiskSignal persistence.
4
5 Tables:
6 - `:wallet_risk_signals` — keyed by signal_id.
7 - `:wallet_risk_signal_user_idx` — bag: {user_id, signal_id}.
8 """
9
10 use GenServer
11
12 alias WalletRisk.RiskSignal
13
14 @table :wallet_risk_signals
15 @user_idx :wallet_risk_signal_user_idx
16
17
:-(
def start_link(opts), do: GenServer.start_link(__MODULE__, opts, name: __MODULE__)
18
19 @spec store(RiskSignal.t()) :: :ok | {:error, :duplicate_signal_id}
20 11 def store(%RiskSignal{} = s), do: GenServer.call(__MODULE__, {:store, s})
21
22 @spec get(signal_id :: String.t()) :: {:ok, RiskSignal.t()} | {:error, :not_found}
23
:-(
def get(id), do: GenServer.call(__MODULE__, {:get, id})
24
25 @spec list_by_user(user_id :: String.t()) :: [RiskSignal.t()]
26 26 def list_by_user(uid), do: GenServer.call(__MODULE__, {:list_by_user, uid})
27
28 @spec list_all() :: [RiskSignal.t()]
29
:-(
def list_all, do: GenServer.call(__MODULE__, :list_all)
30
31 @spec reset() :: :ok
32 32 def reset, do: GenServer.call(__MODULE__, :reset)
33
34 @impl true
35 def init(_opts) do
36
:-(
:ets.new(@table, [:set, :protected, :named_table])
37
:-(
:ets.new(@user_idx, [:bag, :protected, :named_table])
38 {:ok, %{}}
39 end
40
41 @impl true
42 def handle_call({:store, s}, _from, state) do
43 11 if :ets.member(@table, s.signal_id) do
44
:-(
{:reply, {:error, :duplicate_signal_id}, state}
45 else
46 11 :ets.insert(@table, {s.signal_id, s})
47 11 :ets.insert(@user_idx, {s.user_id, s.signal_id})
48 11 {:reply, :ok, state}
49 end
50 end
51
52 @impl true
53 def handle_call({:get, id}, _from, state) do
54
:-(
result =
55 case :ets.lookup(@table, id) do
56
:-(
[{_, s}] -> {:ok, s}
57
:-(
[] -> {:error, :not_found}
58 end
59
:-(
{:reply, result, state}
60 end
61
62 @impl true
63 def handle_call({:list_by_user, uid}, _from, state) do
64 26 ids = :ets.lookup(@user_idx, uid) |> Enum.map(fn {_, id} -> id end)
65 26 items =
66 Enum.flat_map(ids, fn id ->
67 26 case :ets.lookup(@table, id) do
68 26 [{_, s}] -> [s]
69
:-(
[] -> []
70 end
71 end)
72 26 {:reply, items, state}
73 end
74
75 @impl true
76 def handle_call(:list_all, _from, state) do
77
:-(
items = :ets.tab2list(@table) |> Enum.map(fn {_, s} -> s end)
78
:-(
{:reply, items, state}
79 end
80
81 @impl true
82 def handle_call(:reset, _from, state) do
83 32 :ets.delete_all_objects(@table)
84 32 :ets.delete_all_objects(@user_idx)
85 32 {:reply, :ok, state}
86 end
87 end
Line Hits Source