cover/Elixir.DaProductApp.TransactionEventChainService.html

1 defmodule DaProductApp.TransactionEventChainService do
2 @moduledoc """
3 Helper functions to work with the transaction_event_chain table.
4
5 append_event/1 will insert a new chain row and link it to the previous event
6 for the same transaction in a DB transaction to keep ordering deterministic.
7 """
8
9 alias DaProductApp.{Repo, TransactionEventChain}
10 import Ecto.Query, only: [from: 2]
11
12 @doc """
13 Append an event to the chain.
14
15 attrs must include: :transaction_id, :event_type, :event_ref_table
16 optional: :event_ref_id, :payload
17
18 Returns {:ok, chain_record} or {:error, reason}
19 """
20 def append_event(attrs) when is_map(attrs) do
21 1 try do
22 1 Repo.transaction(fn ->
23 1 tx_id = Map.fetch!(attrs, :transaction_id)
24
25 1 prev =
26 from(c in TransactionEventChain,
27 where: c.transaction_id == ^tx_id,
28 order_by: [desc: c.inserted_at],
29 limit: 1
30 )
31 |> Repo.one()
32
33 1 attrs = Map.put_new(attrs, :prev_event_id, prev && prev.id)
34
35 %TransactionEventChain{}
36 |> TransactionEventChain.changeset(attrs)
37 1 |> Repo.insert()
38 end)
39 rescue
40
:-(
e in DBConnection.ConnectionError ->
41 # Generic DB error (connection/table missing) - return an error tuple so callers can continue
42 {:error, :db_connection_error}
43
:-(
e in Ecto.QueryError ->
44 # Likely missing table or malformed query
45
:-(
Logger.warn("[TransactionEventChainService] append_event failed: #{inspect(e)}")
46 {:error, :no_table}
47 end
48 end
49
50 @doc """
51 Traverse the chain for a transaction in ascending order (oldest first).
52 Returns a list of chain entries.
53 """
54 def get_chain(transaction_id) do
55 from(c in TransactionEventChain,
56 where: c.transaction_id == ^transaction_id,
57 order_by: c.inserted_at
58 )
59
:-(
|> Repo.all()
60 end
61
62 @doc """
63 Convenience method to append an event with transaction_id and event_type.
64
65 ## Parameters
66 - `transaction_id`: The transaction ID
67 - `event_type`: String describing the event type
68 - `payload`: Optional map of event data
69
70 ## Returns
71 - `{:ok, chain_record}` on success
72 - `{:error, reason}` on failure
73 """
74
:-(
def append_event(transaction_id, event_type, payload \\ %{}) do
75 1 attrs = %{
76 transaction_id: transaction_id,
77 event_type: event_type,
78 event_ref_table: "transaction_events", # Default reference table
79 payload: payload
80 }
81
82 1 append_event(attrs)
83 end
84 end
Line Hits Source