Using Ecto Multi, and the to_list()
method, I am trying to assert the form of the Multi before committing the transaction. The example from the docs (https://hexdocs.pm/ecto/Ecto.Multi.html#module-example) made me expect a form as follows:
multi_result = Ecto.Multi.new() |> Domain.Helpers.DeviceHistoryRecord.build_multi(normalized_data)
assert [
{:registry, {:run, result_registry, []}},
{:insert_registry, {:insert_or_update, _registry_changeset, []}},
{:update_registry_name, {:update, registry_changeset, []}},
{:item, {:run, result_item, []}},
{:insert_item, {:insert_or_update, item_changeset, []}},
{:manufacturer, {:run, result_manufacturer, []}},
{:insert_manufacturer, {:insert_or_update, manufacturer_changeset, []}},
{:dhr, {:insert_or_update, dhr_changeset, []}}
] = Ecto.Multi.to_list(multi_result)
Instead the result is a set of :run
operations, with a function reference, rather than the relevant changesets to check.
pry(6)> multi_result.operations
[
dhr: {:run, #Function<6.131623037/2 in Ecto.Multi.operation_fun/3>},
insert_manufacturer: {:run,
#Function<6.131623037/2 in Ecto.Multi.operation_fun/3>},
manufacturer: {:run,
#Function<5.99861518/2 in Domain.Helpers.DeviceHistoryRecord.build_multi/2>},
insert_item: {:run, #Function<6.131623037/2 in Ecto.Multi.operation_fun/3>},
item: {:run,
#Function<3.99861518/2 in Domain.Helpers.DeviceHistoryRecord.build_multi/2>},
update_registry_name: {:run,
#Function<6.131623037/2 in Ecto.Multi.operation_fun/3>},
insert_registry: {:run,
#Function<6.131623037/2 in Ecto.Multi.operation_fun/3>},
registry: {:run,
#Function<0.99861518/2 in Domain.Helpers.DeviceHistoryRecord.build_multi/2>}
]
Here is the function I use to compose the Multi transaction:
def build_multi(ecto_multi, normalized_data) do
ecto_multi
|> Ecto.Multi.run(:registry, fn _repo, _args ->
case Repo.get_by(Registry, sheet_id: normalized_data[:registry]) do
nil -> {:ok, nil}
result -> {:ok, result}
end
end)
|> Ecto.Multi.insert_or_update(:insert_registry, fn %{registry: registry} ->
case registry do
nil -> Registry.changeset(%Registry{sheet_id: normalized_data[:registry]})
_ -> Ecto.Changeset.change(registry)
end
end)
|> Ecto.Multi.update(:update_registry_name, fn %{insert_registry: registry} ->
Domain.Helpers.Registry.update_registry_name_changeset(registry)
end)
|> Ecto.Multi.run(:item, fn _repo, _args ->
case Repo.get_by(Item, item_id: normalized_data[:item]) do
nil -> {:ok, nil}
result -> {:ok, result}
end
end)
|> Ecto.Multi.insert_or_update(:insert_item, fn %{item: item} ->
case item do
nil -> Item.changeset(%Item{item_id: normalized_data[:item]})
_ -> Ecto.Changeset.change(item)
end
end)
|> Ecto.Multi.run(:manufacturer, fn _repo, _args ->
case Repo.get_by(Manufacturer, name: normalized_data[:manufacturer]) do
nil -> {:ok, nil}
result -> {:ok, result}
end
end)
|> Ecto.Multi.insert_or_update(:insert_manufacturer, fn %{manufacturer: manufacturer} ->
case manufacturer do
nil -> Manufacturer.changeset(%Manufacturer{name: normalized_data[:manufacturer]})
_ -> Ecto.Changeset.change(manufacturer)
end
end)
|> Ecto.Multi.insert_or_update(:dhr, fn %{
insert_registry: registry,
insert_item: item,
insert_manufacturer: manufacturer
} ->
lot_number = normalized_data[:lot_number]
schema = (Repo.get_by(DeviceHistoryRecord, %{registry_id: registry.id, lot_number: lot_number}) || %DeviceHistoryRecord{item_id: item.id, lot_number: lot_number})
|> Repo.preload(:components)
|> Repo.preload(:equipment)
|> Ecto.Changeset.change()
normalized_data = %{
normalized_data
| registry_id: registry.id,
item_id: item.id,
manufacturer_id: manufacturer.id
}
DeviceHistoryRecord.embed_confirmations(schema, normalized_data)
|> DeviceHistoryRecord.embed_failures(normalized_data)
|> DeviceHistoryRecord.embed_revision_metadata(normalized_data)
# |> DeviceHistoryRecord.embed_metadata(normalized_data)
|> DeviceHistoryRecord.set_conditions(normalized_data)
|> DeviceHistoryRecord.changeset(normalized_data)
end)
end
I’d love some feedback on ways I could simplify or make the above multi transaction more robust, or something I am missing re. the to_list()
return value. I’d like to give feedback to the user of the system about any issues before committing the valid transactions.