I have a File resource with a create action that has a change that uploads the file to storage inside Ash.Changeset.before_transaction/2. This works fine, no errors or warnings.
I then have a Project resource which has a many_to_many relation to File through ProjectFile. In the Project create action I’m using manage_relationship to create files which uses the File creation action that uploads the file. This is when it triggers this warning:
One or more
before_transaction
hooks onMyApp.Files.File.create
are being executed, but there is an ongoing transaction already happening.
The warning does suggest a few options like silencing the warning or creating another action that is safe to use in a surrounding transaction, and use that instead of this one.
Does Ash use nested transactions when inserting relationships? Or is it just using a nested transaction because I’ve added some hooks?
I’m guessing in my situation it’s best not to silence this warning because we don’t want to be hitting external apis from within transactions? My alternative approach is to upload the file in a before_transaction from the Project like so:
defmodule MyApp.Projects.Project do
use Ash.Resource,
domain: MyApp.Project,
authorizers: [Ash.Policy.Authorizer],
data_layer: AshPostgres.DataLayer
postgres do
table "projects"
repo MyApp.Repo
end
actions do
defaults [:read, :update]
create :create_with_files do
accept [:title]
argument :files, {:array, :map} do
allow_nil? false
end
change relate_actor(:user)
change fn changeset, _ ->
Ash.Changeset.before_transaction(changeset, fn changeset ->
# upload files but don't create the File record as we're not in a transaction
# put the uploaded files in changeset context to access later
nil
end)
end
change fn changeset, result ->
Ash.Changeset.after_transaction(changeset, fn changeset, result ->
# if result has an error delete the file from storage
nil
end)
end
change fn changeset, _ ->
# during the main transaction get the files from the changeset context
# manage the realtionship manually using Ash.Changeset.manage_relationship
nil
end
end
end
relationships do
belongs_to :user, MyApp.Accounts.User
many_to_many :files, MyApp.Files.File do
through MyApp.Projects.ProjectFile
source_attribute_on_join_resource :project_id
destination_attribute_on_join_resource :file_id
public? true
end
end
policies do
policy action([:create_with_files]) do
authorize_if always()
end
end
end
As an Ash newbie does this seem like the correct way to approach this with Ash?
Side note anyone want to make an ash_file_storage extension?