Publish :create, ["created", deeply_nested_item_id]

Let's say my resources have this relationship, chat_room has many posts which then has many comments. and I want to have this pub_sub definedin comment.ex.
pub_sub do
...
publish :create, ["created", :chat_room_id]

relationships do
belongs_to :post, MyApp.Chat.Post do
api MyApp.Chat
..

aggregates do
first :chat_room_id, :post, :chat_room_id
pub_sub do
...
publish :create, ["created", :chat_room_id]

relationships do
belongs_to :post, MyApp.Chat.Post do
api MyApp.Chat
..

aggregates do
first :chat_room_id, :post, :chat_room_id
Unfortuantely, this gives me the following error. What's the right way to do this?
** (Protocol.UndefinedError) protocol String.Chars not implemented for #Ash.NotLoaded<:aggregate> of type Ash.NotLoaded (a struct)
** (Protocol.UndefinedError) protocol String.Chars not implemented for #Ash.NotLoaded<:aggregate> of type Ash.NotLoaded (a struct)
My chat_room_load() function that feeds the data to the form is defined like this.
def chat_room_load() do
comments =
Comment
|> Ash.Query.load([:post, :chat_room_id])
posts =
Post
|> Ash.Query.load([
comment: comments
...
])
def chat_room_load() do
comments =
Comment
|> Ash.Query.load([:post, :chat_room_id])
posts =
Post
|> Ash.Query.load([
comment: comments
...
])
6 Replies
ZachDaniel
ZachDaniel3y ago
This one is interesting. I think what you might need to do is something a bit ugly, but basically two things need to happen. 1. the pub sub notifier is going to have to ignore not loaded values. 2. you'll need to load the value on anything before you change it and after you change it.
changes do
change fn changeset ->
# if its an update or destroy, load the value on `changeset.data`
# for everything else, add an after action hook that loads the value
end
end
changes do
change fn changeset ->
# if its an update or destroy, load the value on `changeset.data`
# for everything else, add an after action hook that loads the value
end
end
Jason
JasonOP3y ago
Thank you! I tried these and :update works well, but not :create. Can you please see what I'm doing wrong?
update :update do
...
change fn changeset, _ ->
chat_room_id =
MyApp.Repo.get!(Post, changeset.data.post_id, load: [:chat_room_id]).chat_room_id

%{changeset | data: %{changeset.data | chat_room_id: chat_room_id}}
end
end

create :create do
...
change fn changeset, _ ->
Ash.Changeset.after_action(changeset, fn changeset, _result ->
chat_room_id =
MyApp.Repo.get!(Post, changeset.attributes.post_id,
load: [:chat_room_id]
).chat_room_id


%{changeset | attributes: %{changeset.attributes | chat_room_id: chat_room_id}}
|> IO.inspect(label: "changeset in create after action ###########")
end)

changeset |> IO.inspect(label: "changeset in create ###########")
end
update :update do
...
change fn changeset, _ ->
chat_room_id =
MyApp.Repo.get!(Post, changeset.data.post_id, load: [:chat_room_id]).chat_room_id

%{changeset | data: %{changeset.data | chat_room_id: chat_room_id}}
end
end

create :create do
...
change fn changeset, _ ->
Ash.Changeset.after_action(changeset, fn changeset, _result ->
chat_room_id =
MyApp.Repo.get!(Post, changeset.attributes.post_id,
load: [:chat_room_id]
).chat_room_id


%{changeset | attributes: %{changeset.attributes | chat_room_id: chat_room_id}}
|> IO.inspect(label: "changeset in create after action ###########")
end)

changeset |> IO.inspect(label: "changeset in create ###########")
end
In the :create action, I tried updating chat_room_id in changeset.data and changeset.attributes, but neither works. (ie. same error message mentioned above) The first IO.inspect message inside after_action` prints, so that makes me think the after_action hook isn't invoked.
ZachDaniel
ZachDaniel3y ago
for the create and update, you can just add change load(:chat_room_id) which will take care of the after action TBH your best bet here though is probably a custom notifier
notifiers: [YourApp.Notify]

# in YourApp.Notify
def notify(notification) do
Endpoint.broadcast(....)
end
notifiers: [YourApp.Notify]

# in YourApp.Notify
def notify(notification) do
Endpoint.broadcast(....)
end
Alternatively we can make some changes to teach the notifier how to load things when they are calculations/aggregates, which also sounds reasonable but is work that needs to be done
Jason
JasonOP3y ago
Thank you! Is the way I wrote after_action wrong? I would like to learn how to so it as it seems to be helpful elsewhere in my project. I noticed if I remove changeset |> IO.inspect(label: "changeset in create ###########") at the end, after_action does run and prints the output of IO.inspect(label: "changeset in create after action ###########") However, it then gives this error. (probably because change isn't returning a changeset?)
(CaseClauseError) no case clause matching: #Ash.Changeset<api:
(CaseClauseError) no case clause matching: #Ash.Changeset<api:
ZachDaniel
ZachDaniel3y ago
in your specific case, you aren't returning the change set with the after action hook i.e
Ash.Changeset.after_action(changeset, ....)

changeset
Ash.Changeset.after_action(changeset, ....)

changeset
that throws away the change set that has the hook attached the after_action function needs to return {:ok, result} (or {:error, error})
Jason
JasonOP3y ago
Thank you! load(:chat_room_id) worked.

Did you find this page helpful?