I'm trying to broadcast to a different channel in my app but I can not get it to work. Also I trying to write a test but I'm not sure how.
From what I can gather I succeed in broadcasting the message from the notification_channel but it's not received in chat_channel.
Notification should send to chat.
notification_channel.ex
def handle_in("new:group:recommendation", msg, socket) do
payload = %{
message: msg["message"],
url: msg["url"],
title: msg["title"],
user_name: get_name_of_user(socket.assigns.user_grapqhl_id),
user_grapqhl_id: socket.assigns.user_grapqhl_id
}
IO.puts "incomming"
IO.inspect msg
Enum.map(msg["groups"], fn(x) ->
App.Endpoint.broadcast_from! self(), "chat:"<>x,
"new:recommendation", payload
end)
{:reply, :ok, socket}
end
chat_channel.ex
def handle_in("new:recommendation", msg, socket) do
IO.puts "i am a recommendation !"
IO.inspect msg
chat_msg = %{
"creator_id" => msg["user_grapqhl_id"],
"text" => msg["message"],
"creator_name" => msg["user_name"]
}
broadcast! socket, "new:msg", create_chat_msg(chat_msg,socket)
{:reply, :ok, socket}
end
test
test "do we send a new:recommendation to chat ?", %{guardian_token: guardian_token} do
nils_base_64 = Base.encode64("user:nils")
{:ok, socket} = connect(UserSocket, %{})
{:ok, _, socket1} = subscribe_and_join(socket, "notifications:"<>nils_base_64, %{"guardian_token" => guardian_token})
{:ok, _, socket} = subscribe_and_join(socket1, "chat:Y2hhdDpjaGF0Mw==", %{"guardian_token" => guardian_token})
payload = %{
"message" => "look at this cool thing!",
"url" => "link to stuff",
"title" => "AWESOME EVENT",
"groups" => ["Y2hhdDpjaGF0Mw==", "Y2hhdDpwdWJsaWM="]
}
reply = %{message: "look at this cool thing!", title: "AWESOME EVENT", url: "link to stuff", user_grapqhl_id: nils_base_64, user_name: "Nils Eriksson"}
ref = push socket1, "new:group:recommendation", payload
assert_reply ref, :ok
assert_broadcast "new:recommendation", ^reply
end
This test passes and i can get it to fail by changing the reply
or commenting out the broadcast. I can not get it to fail by changing the handle_in
to receive fail:please
in chat_channel
.
That is something that it would complain on if i would send change this
ref = push socket1, "new:group:recommendation", payload
to ref = push socket, "new:group:recommendation", payload
not supprising in that case.
This is what is on the wire.
Process mailbox:
%Phoenix.Socket.Message{event: "init:msgs", payload: %{messages: []}, ref: nil, topic: "chat:Y2hhdDpjaGF0Mw=="}
%Phoenix.Socket.Broadcast{event: "new:recommendation", payload: %{message: "look at this cool thing!", title: "AWESOME EVENTs", url: "link to stuff", user_grapqhl_id: "dXNlcjpuaWxz", user_name: "Nils Eriksson"}, topic: "chat:Y2hhdDpjaGF0Mw=="}
%Phoenix.Socket.Message{event: "new:recommendation", payload: %{message: "look at this cool thing!", title: "AWESOME EVENTs", url: "link to stuff", user_grapqhl_id: "dXNlcjpuaWxz", user_name: "Nils Eriksson"}, ref: nil, topic: "chat:Y2hhdDpjaGF0Mw=="}
I use channel authentication since the elm package i use does not support authentication on socket level yet. so this is what it looks like in chat
def join("chat:" <> chat_id, %{"guardian_token" => token}, socket) do
IO.puts chat_id
case sign_in(socket, token) do
{:ok, authed_socket, _guardian_params} ->
Process.flag(:trap_exit, true)
send(self, {:after_join})
[_type, node_chat_id] = Node.from_global_id(chat_id)
{:ok, assign(authed_socket, :chat_id, node_chat_id)}
{:error, reason} ->
IO.puts "Can't join channel cuz: " <> reason
# handle error TODO
end
end
Since you use broadcast_from/4
from your Endpoint
.You should use handle_info/2
in your chat_channel
:
alias Phoenix.Socket.Broadcast
...
def handle_info(%Broadcast{topic: _, event: ev, payload: payload}, socket) do
IO.puts ev
IO.inspect payload
# do something with ev and payload( push or broadcast)
{:noreply, socket}
end
Or you can listen that event from your client:
chatChannel.on("new:recommendation", resp => {
// doSomething with response
}
Edit:
Let's explain a bit about how the channel
and PubSub
system work.
When you want to broadcast or push an event with payload.First it will send to the PubSub
system and then the PubSub
system will send it to all the subscriber process (channel
) with the topic that channel
registered itself with the PubSub
system.
And when you use Endpoint.broadcast_from/4
to broadcast an event from your server.The PubSub
system will receive an event with payload and broadcast that event to the topic that channel registered.
The channel will trigger handle_out
callback and push the message to the client.
So in your chat_channel
you don't need to handle_in
"new:recommendation" event.Your client just need to listen to that event.
chatChannel.on("new:recommendation", resp => {
// do something with response
}
And let me rewrite your test:
setup do
nils_base_64 = Base.encode64("user:nils")
{:ok, socket} = connect(UserSocket, %{})
{:ok, _, socket} = subscribe_and_join(socket, "notifications:"<>nils_base_64, %{"guardian_token" => guardian_token})
{:ok, socket: socket}
end
test "do we send a new:recommendation to chat ?", %{socket: socket} do
MyApp.Endpoint.subscribe("chat:Y2hhdDpjaGF0Mw==")
payload = %{
"message" => "look at this cool thing!",
"url" => "link to stuff",
"title" => "AWESOME EVENT",
"groups" => ["Y2hhdDpjaGF0Mw==", "Y2hhdDpwdWJsaWM="]
}
reply = %Phoenix.Socket.Broadcast{message: "look at this cool thing!",
title: "AWESOME EVENT",
url: "link to stuff",
user_grapqhl_id: nils_base_64,
user_name: "Nils Eriksson"}
ref = push socket, "new:group:recommendation", payload
assert_reply ref, :ok
assert_receive ^reply
end
By subscribe
to the topic that you want to listen you can make sure that your channel is received the message with assert_receive
.
That is the way to test broadcast
to a different channel.
Give it a try and tell me.The test will pass.
Use App.Endpoint.broadcast topic, event, payload
with the topic of your chat channel. It should work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With