Ash FrameworkAF
Ash Framework8mo ago
7 replies
Joan Gavelán

How to manage notifications

I am trying to understand how to listen to notifications emitted from my resource actions and pass it down to a React client using Channels.

Currently this is what I've got:
# note.ex (resource)
...

pub_sub do
  module NotedWeb.Endpoint

  publish :create_note, "workspace"
end

# user_socket.ex
defmodule NotedWeb.UserSocket do
  use Phoenix.Socket
  
  channel "workspace", NotedWeb.WorkspaceChannel
  
  @impl true
  def connect(_params, socket, _connect_info) do
    {:ok, socket}
  end
end

# workspace_channel.ex
defmodule NotedWeb.WorkspaceChannel do
  use NotedWeb, :channel

  @impl true
  def join("workspace", _payload, socket) do
    {:ok, socket}
  end
end

I am able to successfully join to my channel from my React client. But I get this error when a new event is triggered from my resource:
[error] ** (Ash.Error.Unknown) 
Bread Crumbs:
  > Exception raised in: Noted.Workspace.Note.create_note

Unknown Error

* ** (Protocol.UndefinedError) protocol Jason.Encoder not implemented for type Ash.Notifier.Notification (a struct), Jason.Encoder protocol must always be explicitly implemented.

If you own the struct, you can derive the implementation specifying which fields should be encoded to JSON:

    @derive {Jason.Encoder, only: [....]}
    defstruct ...


The error is gone when I transform the notification

pub_sub do
  module NotedWeb.Endpoint

  transform fn notification ->
    serialized_note = NotedWeb.Serializers.serialize_listed_note(notification.data)

    %{note: serialized_note}
  end

  publish :create_note, "workspace"
end

But I am still unsure of what is going on in here. How is the notification being sent to the client? I tried to inspect on the handle_out/2 callback:
# workspace_channel.ex
@impl true
def handle_out("create_note" = event, payload, socket) do
  IO.inspect(event)
  IO.inspect(payload)

  {:noreply, socket}
end

But nothing seems to be happening there
Solution
Okay, figured it out thanks to the realworld-phoenix-inertia-react project.

Turns out I shouldn't have created a channel to connect from my client with the same topic the resources emit the events to. They should be different — that's what was causing the weird behavior and the "ghost" and uncontrolled sending of data through my channel.

Now my action is publishing to the topic notes instead of workspace
# note.ex
publish :create_note, "notes"

I am subscribing to that topic from within the join/3 channel callback
# workspace_channel.ex
@impl true
def join("workspace", _payload, socket) do
  NotedWeb.Endpoint.subscribe("notes")

  {:ok, socket}
end

And listening for events in handle_info/2 callbacks
# workspace_channel.ex
@impl true
def handle_info(%{topic: "notes", event: "create_note"} = message, socket) do
  push(socket, "note_created", message.payload.note)
  {:noreply, socket}
end


This works!
Was this page helpful?