Ok I have a few channel/websocket questions I hope I can get cleared up.
How do I put a wildcard in join function. I currently have it hard coded to “user:8” for testing and it works fine, but I want that integer value to be dynamic and I will need access to it inside the function for a lookup.
def join("user:8", _params, socket) do
Logger.info ("join called..")
send(self(), :after_join)
{:ok, socket}
end
When I receive a message, I am broadcasting it and this works fine.
def handle_in("message:new", message, socket) do
broadcast! socket, "message:new", %{
user: socket.assigns.user,
body: message,
timestamp: :os.system_time(:milli_seconds)
}
{:noreply, socket}
end
So I am going to create a API endpoint where my Rails application can post to this endpoint, and then I want this to broadcast to my channel. How would I handle this?
So my 2nd question is basically, from another api controller I want to publish a message to go on my channel controller so it broadcasts to all the active socket clients. Is there a way to hook into my channel from a controller?
I tried this, it doesn’t crash but I don’t see a message in my UI:
defmodule RealtimeWeb.MessageController do
use RealtimeWeb, :controller
require Logger
def create(conn, _params) do
Logger.info "api#create called"
RealtimeWeb.Endpoint.broadcast!("board:8", "messages:new", %{
user: "asdf",
body: "some message",
timestamp: :os.system_time(:milli_seconds)
})
json(conn, %{id: 123})
end
end
My logs show that I am joined to the topic:
JOIN “board:8” to RealtimeWeb.BoardChannel
I’m not sure if you meant for me to use RealtimeWeb.Endpoint.broadcast! or RealtimeWeb.BoardChannel.broadcast! it with my real endpoint. When I put BoardChannel I got an error:
** (UndefinedFunctionError) function RealtimeWeb.BoardChannel.broadcast!/3 is undefined or private
(realtime) RealtimeWeb.BoardChannel.broadcast!(“board:8”, “messages:new”, %{body: “some message”, timestamp: 1543411781378, user: “asdf”})
I cannot really tell why it’s not working for You, but here is some code I use to notify by websocket (with Phoenix 1.3) …
defmodule GameWeb.Notifier do
@moduledoc false
require Logger
# Notification Hub for application
# Used by GameEngine to notify worker's exit
def notify(%{payload: payload, type: :game_created}) do
GameWeb.Endpoint.broadcast!("lobby", "game_added", %{game: payload})
end
def notify(%{payload: payload, type: :game_stopped}) do
GameWeb.Endpoint.broadcast!("lobby", "game_removed", %{uuid: payload})
# Notify remaining guest the game has ended!
GameWeb.Endpoint.broadcast!("game:#{payload}", "game_force_quit", %{uuid: payload})
end
def notify(%{type: :request_created} = message) do
# Do nothing, notification is done in lobby channel
Logger.debug(fn -> "No op #{inspect(message)}" end)
end
def notify(%{payload: payload, type: :request_stopped}) do
GameWeb.Endpoint.broadcast!("lobby", "request_cancelled", %{uuid: payload})
end
def notify(message) do
Logger.debug(fn -> "Unknown notification #{inspect(message)}" end)
end
end
I have to say it’s kind of easier by enabling logs in your client code… I do it like this.
Thanks for that debugging tip. I basically changed the way I was setting up my channel.
I had this:
let channel = socket.channel(channelId, {params: {token: authToken}})
I changed it to this and it works:
let channel = socket.channel(channelId)
I’m not sure why I was passing in token when creating a token, but since I have a socket I am already authenticated so passing it very a channel subscription doesn’t make sense.
Given that the socket has already a token, it’s not useful to pass it in the channel, as You mentionned. But I do have some access control in the join function of my channels.