Current section
Files
Jump to
Current section
Files
lib/walex/event/event.ex
defmodule WalEx.Event do
@moduledoc """
Event DSL and casting
"""
@derive Jason.Encoder
defstruct([:name, :type, :source, :new_record, :old_record, :changes, :timestamp, :lsn])
@type t :: %WalEx.Event{
name: atom(),
type: :insert | :update | :delete,
source: WalEx.Event.Source.t(),
new_record: map() | nil,
old_record: map() | nil,
changes: map() | nil,
timestamp: DateTime.t(),
lsn: {integer(), integer()}
}
require Logger
import WalEx.TransactionFilter
alias WalEx.{Changes, Config, Event, Helpers}
@doc """
Macros for processing events
"""
defmacro __using__(opts) do
app_name = Keyword.get(opts, :name)
quote do
import WalEx.Event.Dsl
def filter_events(txn) do
Event.filter_and_cast(unquote(app_name), txn)
end
def filter_events(txn, name, type, filters) do
Event.filter_and_cast(unquote(app_name), txn, name, type, filters)
end
end
end
def cast(
%Changes.NewRecord{
type: "INSERT",
schema: schema,
table: table,
columns: columns,
record: record,
commit_timestamp: timestamp,
lsn: lsn
},
app_name
) do
%Event{
name: String.to_atom(table),
type: :insert,
source: cast_source(app_name, schema, table, columns),
new_record: record,
timestamp: timestamp,
lsn: lsn
}
end
def cast(
%Changes.UpdatedRecord{
type: "UPDATE",
schema: schema,
table: table,
columns: columns,
record: record,
old_record: old_record,
commit_timestamp: timestamp,
lsn: lsn
},
app_name
) do
%Event{
name: String.to_atom(table),
type: :update,
source: cast_source(app_name, schema, table, columns),
new_record: record,
changes: map_changes(old_record, record),
timestamp: timestamp,
lsn: lsn
}
end
def cast(
%Changes.DeletedRecord{
type: "DELETE",
schema: schema,
table: table,
columns: columns,
old_record: old_record,
commit_timestamp: timestamp,
lsn: lsn
},
app_name
) do
%Event{
name: String.to_atom(table),
type: :delete,
source: cast_source(app_name, schema, table, columns),
old_record: old_record,
timestamp: timestamp,
lsn: lsn
}
end
def cast(_event, _event_name), do: nil
defp cast_source(app_name, schema, table, columns) do
%WalEx.Event.Source{
name: Helpers.get_source_name(),
version: Helpers.get_source_version(),
db: Config.get_database(app_name),
schema: schema,
table: table,
columns: map_columns(columns)
}
end
def cast_events(changes, app_name) do
changes
|> Enum.map(&cast(&1, app_name))
end
@doc """
Filter out events by table and type (optional) from transaction and cast to Event struct
"""
def filter_and_cast(app_name, txn) do
txn
|> filter_subscribed(app_name)
|> cast_events(app_name)
end
# TODO: change order of filter (to before cast!)
def filter_and_cast(app_name, txn, table, type, %{
unwatched_records: unwatched_records,
unwatched_fields: unwatched_fields
}) do
txn
|> filter_changes(table, type, app_name)
|> cast_events(app_name)
|> filter_unwatched_records(unwatched_records)
|> filter_unwatched_fields(unwatched_fields)
end
def filter_and_cast(app_name, txn, table, type, %{unwatched_records: unwatched_records}) do
txn
|> filter_changes(table, type, app_name)
|> cast_events(app_name)
|> filter_unwatched_records(unwatched_records)
end
def filter_and_cast(app_name, txn, table, type, %{unwatched_fields: unwatched_fields}) do
txn
|> filter_changes(table, type, app_name)
|> cast_events(app_name)
|> filter_unwatched_fields(unwatched_fields)
end
def filter_and_cast(app_name, txn, table, type, _filters) do
txn
|> filter_changes(table, type, app_name)
|> cast_events(app_name)
end
@spec lsn_to_string({integer(), integer()}) :: String.t()
def lsn_to_string({high, low}) do
low = Integer.to_string(low, 16)
high = Integer.to_string(high, 16)
"#{high}/#{low}"
end
end