How to pass second actor parameter to the ash_paper trail

Hello,

I am using ash_papertrail for audit logs. But I have setup 2 actors like this:

paper_trail do
    # default is :uuid
    primary_key_type(:uuid_v7)
    # default is :snapshot
    change_tracking_mode(:changes_only)
    # default is false
    store_action_name?(true)
    # the primary keys are always ignored
    ignore_attributes([:inserted_at, :updated_at])
    # default is []
    ignore_actions([:destroy])

    store_action_inputs?(true)

    store_resource_identifier?(true)

# Passed through actor
    belongs_to_actor(:user, Mgx.Accounts.User, domain: Mgx.Accounts)

# Have existing relastionship
    belongs_to_actor(:company_information, Mgx.Company.CompanyInformation, domain: Mgx.Company)
  end

So in the LiveView, I am passing actor as current_user like this which works, but user_id,

form =
      company_information
      |> AshPhoenix.Form.for_update(:update_company_documents,
        as: "company-information",
        forms: [auto?: true],
        actor: socket.assigns.current_user
      )

Since actor can be only one, is there is any way I can pass company_information_id ?
image. Or any other way, by removing actor looked into mixin, But didn’t understand properly. I tried putting company_information_id in the params but didn’t work either :slight_smile:

Appreciate for any help. Thank you

There is only ever one actor as you say, and I don’t think there is way to pass inputs or data through to the version itself. Can you describe more specifically what you’d like to accomplish? You could try adding a “last_updated_by_company” relationship to the main resource, at which point each version would have that available in its changes, if that is what you’re looking for.

1 Like

Sure,

I have an update action from the company_information resource that manages company_documents. This is the action:

update :update_company_documents do
      argument :company_documents, {:array, :map}

      require_atomic? false

      change manage_relationship(:company_documents, type: :direct_control)
    end

I think since because of manage_relationship, I can not find audit version record in the relevant table. So i added paper_trail section in the company_documents resource so that i can track the changes.

The business logic is, a company_information can have many_to_many with users, but company_documents belongs_to company_information only, not users, Although I can get the to which company he made through company_inforamtion and company_documents relationship, I thought it might be better if we just add the company_information_id and get direct queries with user_id and company_information_id, without extra join.

Hmm…you can use the version mixin to add an extra attribute company_information_id, and a change to look it up for the given inputs.

defmodule YourMixin do
  defmacro __using__(_) do
    relationships do
      belongs_to ...
    end

    changes do
       change ..., on: [:create]
    end
  end
end

Sorry Zach, I tried what you said, but somehow not even new record inserting into the database, maybe syntax issue?

defmodule Mgx.PaperTrail.CompanyInformationIdMixin do
  use Ash.Resource

  defmacro __using__(_opts) do
    quote do
      relationships do
        belongs_to :company_information, Mgx.Company.CompanyInformation
      end

      changes do
        # Get the argument from the ash changeset, company information id, and set it in the changeset
        change fn changeset, _context ->
                 company_information_id =
                   Ash.Changeset.fetch_argument(changeset, :company_information_id)

                 Ash.Changeset.force_change_attribute(
                   changeset,
                   :company_information_id,
                   company_information_id
                 )
               end,
               on: [:create]
      end
    end
  end
end

This is how I am able to use paper trail:

paper_trail do
    primary_key_type(:uuid_v7)
    change_tracking_mode(:changes_only)
    store_action_name?(true)
    ignore_attributes([:inserted_at, :updated_at])
    ignore_actions([:destroy])

    belongs_to_actor(:user, Mgx.Accounts.User, domain: Mgx.Accounts)

    mixin(Mgx.PaperTrail.CompanyInformationIdMixin)
  end

and tried to put in company_information_id in the params like this

def handle_event("save", %{"company-information" => params}, socket) do
    params =
      params
      |> Map.put("company_information_id", socket.assigns.company_information.id)
# ........

Now even the new record is not inserting.

Are you getting some kind of error?

No error. The save event is successful, as a well as relevant data (company_documents) in db. But I can’t see new version of audit log in the table

Can you try main of ash_paper_trail?

Yeah I tried to install from the main. It Still not working. This is the logs: Although I got error, where Ash.Changeset.fetch_argument gives tuple, Once i fixed and tried it, Now the the record are succesfully implemented but, company_information_id is still nil.
I also tried to put in the company_information_id in the nested form params, but no result i am getting company_information_id in the mixin

Mixin:

defmodule Mgx.PaperTrail.CompanyInformationIdMixin do
  use Ash.Resource

  defmacro __using__(_opts) do
    quote do
      relationships do
        belongs_to :company_information, Mgx.Company.CompanyInformation
      end

      changes do
        # Get the argument from the ash changeset, company information id, and set it in the changeset
        change fn changeset, _context ->
                 company_information_id =
                   Ash.Changeset.get_argument_or_attribute(changeset, :company_information_id)

                 dbg(company_information_id)

                 Ash.Changeset.force_change_attribute(
                   changeset,
                   :company_information_id,
                   company_information_id
                 )
               end,
               on: [:create]
      end
    end
  end
end

Here are the logs:

[(mgx 0.1.0) deps/ash_paper_trail/lib/resource/transformers/create_version_resource.ex:335: Mgx.Company.BusinessAddress.Version.change_0_generated_598ECEEE256D572A87FEABA91E2A2838/2]
company_information_id #=> nil

Params:

params #=> %{
  "business_address" => %{
    "_form_type" => "create",
    "_persistent_id" => "0",
    "_touched" => "company_information_id,_form_type,_persistent_id,_touched,_unused_city,_unused_country_code,_unused_postal_code,_unused_state,_unused_street,city,company_information_id,country_code,postal_code,state,street",
    "city" => "Bentley Dennis Plc",
    "company_information_id" => "653bdcc3-52eb-429d-b774-2cf646a72057",
    "country_code" => "Mccray Moss LLC",
    "postal_code" => "Lee Giles Traders",
    "state" => "Sullivan and Farley Associates",
    "street" => "Walters Conrad Traders"
  },
  "website" => "https://www.nuzejiha.mobi"
}

[debug] QUERY OK db=0.7ms idle=1973.5ms
begin []

Create Action:

create :create do
      primary? true
      accept [:company_information_id, :country_code, :state, :city, :street, :postal_code]
    end

Yeah, you won’t get the arguments from the main action call in the version mixin. You may have to run a query there to fetch the company_information_id based on the other attributes available to you there. Otherwise we would need to add some kind of feature where the version action gets context set on it like the parent changeset.

Thanks Zach, I already have context that has actor current_user that has company_information_id :smile: