Add value to manage_relationship input from current resource

I have this resource
defmodule Aldente.Management.Invitation do
use Ash.Resource,
domain: Aldente.Management,
data_layer: AshPostgres.DataLayer,
extensions: [AshGraphql.Resource]

import Aldente.Changes

alias Aldente.{Management, Accounts}

graphql do
type :invitation
end

postgres do
table "invitations"
repo Aldente.Repo
end

actions do
defaults [:read, :destroy]

create :create do
primary? true
accept [:role]
end

update :accept do
argument :code, :uuid, allow_nil?: false

validate attribute_equals(:code, arg(:code))
validate attributes_absent(:member_id)
# TODO: make a new Member with `role` and `entity_id`, for current actor
# change relation(:member, %{user_id: actor(:id), role: attr(:role)}, type: :create)
end
end

multitenancy do
strategy :attribute
attribute :entity_id
end

attributes do
uuid_primary_key :id

attribute :code, :uuid, generated?: true, allow_nil?: false
attribute :role, Management.Role, allow_nil?: false
end

relationships do
belongs_to :entity, Management.Entity, allow_nil?: false, public?: true
belongs_to :member, Management.Member, public?: true
end

defp generate_code do
Ash.UUID.generate()
end
end
defmodule Aldente.Management.Invitation do
use Ash.Resource,
domain: Aldente.Management,
data_layer: AshPostgres.DataLayer,
extensions: [AshGraphql.Resource]

import Aldente.Changes

alias Aldente.{Management, Accounts}

graphql do
type :invitation
end

postgres do
table "invitations"
repo Aldente.Repo
end

actions do
defaults [:read, :destroy]

create :create do
primary? true
accept [:role]
end

update :accept do
argument :code, :uuid, allow_nil?: false

validate attribute_equals(:code, arg(:code))
validate attributes_absent(:member_id)
# TODO: make a new Member with `role` and `entity_id`, for current actor
# change relation(:member, %{user_id: actor(:id), role: attr(:role)}, type: :create)
end
end

multitenancy do
strategy :attribute
attribute :entity_id
end

attributes do
uuid_primary_key :id

attribute :code, :uuid, generated?: true, allow_nil?: false
attribute :role, Management.Role, allow_nil?: false
end

relationships do
belongs_to :entity, Management.Entity, allow_nil?: false, public?: true
belongs_to :member, Management.Member, public?: true
end

defp generate_code do
Ash.UUID.generate()
end
end
I am creating the :accept for invitation, which should create a Member where the role will be the same as the Invitation's role, something like arg or actor but an attr (it does not exist, I just invented it to show what I am looking for (PS. relation function is just making a manage_relationship with passed arguments)
9 Replies
ZachDaniel
ZachDaniel4mo ago
change fn changeset, _ ->
Ash.Changeset.manage_relationship(...)
end
change fn changeset, _ ->
Ash.Changeset.manage_relationship(...)
end
you can use the lower level Ash.Changeset.manage_relationship in a custom change which accepts whatever value you want
Vahagn
VahagnOP4mo ago
but how do I get the role attribute from the original resource which is going to be updated is it always in the changeset? or do I have to load it somehow
ZachDaniel
ZachDaniel4mo ago
You have a couple ways its in changeset.data You can also do an after action hook where you get the result
Vahagn
VahagnOP4mo ago
will it always be in changeset.data? Does it always load the resource? I am just thinking of atomic updates, Should I say somewhere that this change is not atomic?
ZachDaniel
ZachDaniel4mo ago
it will happen automatically if you add a change that can't be done atomically it will yell at you to add require_atomic? false
Vahagn
VahagnOP4mo ago
so manage_relationship can sometimes be atomic and sometimes not, right?
ZachDaniel
ZachDaniel4mo ago
currently its never atomic If you use an after action hook we allow it to be annotated as atomic for convenience which is a slightly less strict version of atomicity but its important for practicality
Vahagn
VahagnOP4mo ago
understood, thanks a lot
ZachDaniel
ZachDaniel4mo ago
but you still have to make a custom change and declare that your after action logic is safe in that way
defmodule MyChange do
use Ash.Resource.Change

def change(_, _, _) do
...
end

def atomic(changeset, opts, context) do
{:ok, change(changeset, opts, context)}
end
end
defmodule MyChange do
use Ash.Resource.Change

def change(_, _, _) do
...
end

def atomic(changeset, opts, context) do
{:ok, change(changeset, opts, context)}
end
end

Did you find this page helpful?