Let me preface this all with: I’m sorry this is v2 code. I don’t have much expectation of support for it and upgrading to v3 is definitely on my todo list.
I have a resource called Role that I would like to set a locked
status with respect to an actor’s company_id. For reasons, I want to keep track of this outside of the current table so I’m not going to e.g. add a company_id column and a locked (bool) column. Instead I would like to create a resource LockedStatus that just has a role_id and company_id. If the actor’s company_id shows up for a given role_id, it means that role is locked for that actor’s company.
My LockedStatus resource looks like this
defmodule MyApp.LockedStatus do
// ...
attributes do
attribute(:company_id, :integer, primary_key?: true, allow_nil?: false)
end
// ...
relationships do
belongs_to(:role, Role, primary_key?: true, allow_nil?: false)
end
/ ...
end
I’m trying to update it by adding a locked
argument to an update action I have on Role, which looks something like this
defmodule MyApp.Role do
// ...
relationships do
has_many(:locked_statuses, LockedStatus)
end
calculations do
calculate(
:locked_status,
:boolean,
expr(exists(locked_statuses, company_id == ^actor(:company_id)))
)
end
actions do
create :my_create do
argument(:locked, :boolean, allow_nil?: true)
change(&update_locked_status/2)
end
end
defp update_locked_status(
%{arguments: %{locked_status: true}, changeset,
%{actor: actor}
) do
changeset
|> Ash.Changeset.manage_relationship(
:locked_statuses,
%{company_id: actor.company_id},
on_no_match: :create,
)
end
defp update_locked_status(%{arguments: %{locked_status: false}} = changeset, %{actor: actor}) do
changeset
|> Ash.Changeset.manage_relationship(
:locked_statuses,
%{company_id: actor.company_id},
on_match: {:destroy, :destroy}
)
end
defp update_locked_status(changeset, _), do: changeset)
end
For the most part, everything works. However, if I try to lock something that is already locked, i.e. create a relationship that already exists, I get a (Ash.Error.Invalid) Invalid input Invalid value provided for company_id: has already been taken
error.
There are a lot of ways I’ve tried to get around this error, like passing the role_id
in the input so the lookup works correctly, but it always seems to be falling through to the on_missing
clause.
I feel like there is something essential I’m missing about manage_relationship. How would you create & destroy these records on the LockedStatus resource?