Ash FrameworkAF
Ash Framework8mo ago
13 replies
Joan Gavelán

Working with Transactions

Hello!

I'm working on converting the following Elixir/Ecto function into an Ash action:
def create_team(attrs, user_id) do
  Ecto.Multi.new()
  |> Ecto.Multi.insert(:team, Team.changeset(%Team{}, attrs))
  |> Ecto.Multi.insert(:member, fn %{team: team} ->
    TeamMember.changeset(%TeamMember{}, %{
      team_id: team.id,
      user_id: user_id,
      role: "admin"
    })
  end)
  |> Repo.transaction()
end

This function performs two operations within a transaction:

Creates a new Team with the provided attributes.

Creates a new TeamMember associated with the newly created team and assigns the "admin" role to the specified user.

Here's my current Team resource definition in Ash:
defmodule Noted.Workspace.Team do
  use Ash.Resource,
    otp_app: :noted,
    domain: Noted.Workspace,
    authorizers: [Ash.Policy.Authorizer],
    data_layer: AshPostgres.DataLayer

  postgres do
    table "teams"
    repo Noted.Repo
  end

  actions do
    defaults [:read]

    create :create_team do
      # 1. Create new Team
      # 2. Create a new TeamMember, relate it to the actor and newly created team, and assign it an admin role (this must be done in a transaction)
    end
  end

  attributes do
    uuid_v7_primary_key :id

    attribute :name, :string do
      allow_nil? false
      public? true
      constraints min_length: 2, max_length: 50
    end

    create_timestamp :created_at
    update_timestamp :updated_at
  end

  relationships do
    has_many :invitations, Noted.Workspace.Invitation

    many_to_many :users, Noted.Accounts.User do
      through Noted.Workspace.TeamMember
    end
  end
end

I'm looking for guidance on how to implement the :create_team action in Ash to replicate the behavior of the original Ecto.Multi function. Specifically, I want to ensure that both the team creation and the associated team member creation occur within the same transaction.
Solution
 change fn changeset, context ->
  Ash.Changeset.after_action(changeset, fn changeset, team ->
    team_member_attrs = %{
      # actor is in the context
      user_id: context.actor.id,
      team_id: team.id,
      role: "admin"
    }

    IO.inspect(team_member_attrs, label: "Team member")

    {:ok, team}
  end)
end
Was this page helpful?