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
Use
App.Endpoint.broadcast topic, event, payload
with the topic of your chat channel. It should work.Since you use
broadcast_from/4
from yourEndpoint
.You should usehandle_info/2
in yourchat_channel
:Or you can listen that event from your client:
Edit:
Let's explain a bit about how the
channel
andPubSub
system work.When you want to broadcast or push an event with payload.First it will send to the
PubSub
system and then thePubSub
system will send it to all the subscriber process (channel
) with the topic thatchannel
registered itself with thePubSub
system.And when you use
Endpoint.broadcast_from/4
to broadcast an event from your server.ThePubSub
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 yourchat_channel
you don't need tohandle_in
"new:recommendation" event.Your client just need to listen to that event.And let me rewrite your test:
By
subscribe
to the topic that you want to listen you can make sure that your channel is received the message withassert_receive
. That is the way to testbroadcast
to a different channel.Give it a try and tell me.The test will pass.