Skip to content

Commit f1448d2

Browse files
committed
feat(elixir:gsmlg): Add node channel in GSMLGWeb app.
1 parent 851ef29 commit f1448d2

File tree

6 files changed

+169
-1
lines changed

6 files changed

+169
-1
lines changed

elixir/gsmlg_umbrella/apps/gsmlg_web/assets/js/app.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import "../css/app.css"
44

55
// If you want to use Phoenix channels, run `mix help phx.gen.channel`
66
// to get started and then uncomment the line below.
7-
// import "./user_socket.js"
7+
import "./user_socket.js"
88

99
// You can include dependencies in two ways.
1010
//
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// NOTE: The contents of this file will only be executed if
2+
// you uncomment its entry in "assets/js/app.js".
3+
4+
// Bring in Phoenix channels client library:
5+
import {Socket} from "phoenix"
6+
7+
// And connect to the path in "lib/gsmlg_web/endpoint.ex". We pass the
8+
// token for authentication. Read below how it should be used.
9+
let socket = new Socket("/socket", {params: {token: window.userToken}})
10+
11+
// When you connect, you'll often need to authenticate the client.
12+
// For example, imagine you have an authentication plug, `MyAuth`,
13+
// which authenticates the session and assigns a `:current_user`.
14+
// If the current user exists you can assign the user's token in
15+
// the connection for use in the layout.
16+
//
17+
// In your "lib/gsmlg_web/router.ex":
18+
//
19+
// pipeline :browser do
20+
// ...
21+
// plug MyAuth
22+
// plug :put_user_token
23+
// end
24+
//
25+
// defp put_user_token(conn, _) do
26+
// if current_user = conn.assigns[:current_user] do
27+
// token = Phoenix.Token.sign(conn, "user socket", current_user.id)
28+
// assign(conn, :user_token, token)
29+
// else
30+
// conn
31+
// end
32+
// end
33+
//
34+
// Now you need to pass this token to JavaScript. You can do so
35+
// inside a script tag in "lib/gsmlg_web/templates/layout/app.html.heex":
36+
//
37+
// <script>window.userToken = "<%= assigns[:user_token] %>";</script>
38+
//
39+
// You will need to verify the user token in the "connect/3" function
40+
// in "lib/gsmlg_web/channels/user_socket.ex":
41+
//
42+
// def connect(%{"token" => token}, socket, _connect_info) do
43+
// # max_age: 1209600 is equivalent to two weeks in seconds
44+
// case Phoenix.Token.verify(socket, "user socket", token, max_age: 1_209_600) do
45+
// {:ok, user_id} ->
46+
// {:ok, assign(socket, :user, user_id)}
47+
//
48+
// {:error, reason} ->
49+
// :error
50+
// end
51+
// end
52+
//
53+
// Finally, connect to the socket:
54+
socket.connect()
55+
56+
// Now that you are connected, you can join channels with a topic.
57+
// Let's assume you have a channel with a topic named `room` and the
58+
// subtopic is its id - in this case 42:
59+
let channel = socket.channel("room:42", {})
60+
channel.join()
61+
.receive("ok", resp => { console.log("Joined successfully", resp) })
62+
.receive("error", resp => { console.log("Unable to join", resp) })
63+
64+
export default socket
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
defmodule GSMLGWeb.NodeChannel do
2+
use GSMLGWeb, :channel
3+
4+
@impl true
5+
def join("node:lobby", payload, socket) do
6+
if authorized?(payload) do
7+
{:ok, socket}
8+
else
9+
{:error, %{reason: "unauthorized"}}
10+
end
11+
end
12+
13+
# Channels can be used in a request/response fashion
14+
# by sending replies to requests from the client
15+
@impl true
16+
def handle_in("ping", payload, socket) do
17+
{:reply, {:ok, payload}, socket}
18+
end
19+
20+
# It is also common to receive messages from the client and
21+
# broadcast to everyone in the current topic (node:lobby).
22+
@impl true
23+
def handle_in("shout", payload, socket) do
24+
broadcast socket, "shout", payload
25+
{:noreply, socket}
26+
end
27+
28+
# Add authorization logic here as required.
29+
defp authorized?(_payload) do
30+
true
31+
end
32+
end
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
defmodule GSMLGWeb.UserSocket do
2+
use Phoenix.Socket
3+
4+
# A Socket handler
5+
#
6+
# It's possible to control the websocket connection and
7+
# assign values that can be accessed by your channel topics.
8+
9+
## Channels
10+
11+
channel "node:*", GSMLGWeb.NodeChannel
12+
13+
# Socket params are passed from the client and can
14+
# be used to verify and authenticate a user. After
15+
# verification, you can put default assigns into
16+
# the socket that will be set for all channels, ie
17+
#
18+
# {:ok, assign(socket, :user_id, verified_user_id)}
19+
#
20+
# To deny connection, return `:error`.
21+
#
22+
# See `Phoenix.Token` documentation for examples in
23+
# performing token verification on connect.
24+
@impl true
25+
def connect(_params, socket, _connect_info) do
26+
{:ok, socket}
27+
end
28+
29+
# Socket id's are topics that allow you to identify all sockets for a given user:
30+
#
31+
# def id(socket), do: "user_socket:#{socket.assigns.user_id}"
32+
#
33+
# Would allow you to broadcast a "disconnect" event and terminate
34+
# all active sockets and channels for a given user:
35+
#
36+
# Elixir.GSMLGWeb.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{})
37+
#
38+
# Returning `nil` makes this socket anonymous.
39+
@impl true
40+
def id(_socket), do: nil
41+
end

elixir/gsmlg_umbrella/apps/gsmlg_web/lib/gsmlg_web/endpoint.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ defmodule GSMLGWeb.Endpoint do
1212

1313
socket "/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: @session_options]]
1414

15+
socket "/socket", GSMLGWeb.UserSocket,
16+
websocket: true,
17+
longpoll: false
18+
1519
# Serve at "/" the static files from "priv/static" directory.
1620
#
1721
# You should set gzip to true if you are running phx.digest
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
defmodule GSMLGWeb.NodeChannelTest do
2+
use GSMLGWeb.ChannelCase
3+
4+
setup do
5+
{:ok, _, socket} =
6+
GSMLGWeb.UserSocket
7+
|> socket("user_id", %{some: :assign})
8+
|> subscribe_and_join(GSMLGWeb.NodeChannel, "node:lobby")
9+
10+
%{socket: socket}
11+
end
12+
13+
test "ping replies with status ok", %{socket: socket} do
14+
ref = push socket, "ping", %{"hello" => "there"}
15+
assert_reply ref, :ok, %{"hello" => "there"}
16+
end
17+
18+
test "shout broadcasts to node:lobby", %{socket: socket} do
19+
push socket, "shout", %{"hello" => "all"}
20+
assert_broadcast "shout", %{"hello" => "all"}
21+
end
22+
23+
test "broadcasts are pushed to the client", %{socket: socket} do
24+
broadcast_from! socket, "broadcast", %{"some" => "data"}
25+
assert_push "broadcast", %{"some" => "data"}
26+
end
27+
end

0 commit comments

Comments
 (0)