absinthe icon indicating copy to clipboard operation
absinthe copied to clipboard

Feature proposal: Subscription presence

Open lstrzebinczyk opened this issue 4 years ago • 8 comments

I would like to suggest a new feature, like me and @benwilson512 discussed in the slack.

Phoenix proper allows managing presence per-channel with Phoenix.Presence. Absinthe currently lacks such feature, and it would be very beneficial to have something similiar.

Currently the only way for any application to support presence is to add a non-absinthe channel to the backend and ask frontend to join it in addition to subscribing to absinth-based graphql subscription.

I imagine, since, channels allow defining join/3 and terminate/2 functions, it should be possible to do the same per-graphql-subscription.

I know little of absinthe internals, but I offered my help on the slack, so, I'm happy to help designing this feature in any capacity I'm able to.

lstrzebinczyk avatar Jun 22 '20 16:06 lstrzebinczyk

Right, so the main challenges to solve are: How are subscription topics mapped to presence topics?

benwilson512 avatar Jun 23 '20 01:06 benwilson512

So I think this can be approached in 2 ways.

  1. If we can map each absinthe subscription to it's own phoenix channel, we could do something like this: (based on https://hexdocs.pm/absinthe/subscriptions.html#schema)
field :comment_added, :comment do
  arg :repo_name, non_null(:string)

  config fn args, _ ->
    {:ok, topic: args.repo_name}
  end
  
  join fn %{topic: topic}, context, socket ->
    # user logic
  end
  
  terminate fn reason, context, socket -> 
    user logic
  end
end

This would require changing how subscriptions are done under the hood, but it would allow using of Phoenix.Presence, and join and terminate callbacks can be easily explained by saying they map to join and terminate in underlying phoenix channels. I guess that solution would allow for a nice integration with established phoenix features.

  1. As we've discussed earlier, currently absinthe maintians a single channel. It should be possible to do something like:
field :comment_added, :comment do
  arg :repo_name, non_null(:string)

  config fn args, _ ->
    {:ok, topic: args.repo_name}
  end
  
  join fn %{topic: topic}, context ->
    # user logic
  end
  
  terminate fn reason, context -> 
    user logic
  end
end

Exactly like above, except we don't expose the socket. join would be called whenever user is subscribing to a specific subscription. terminate could be mapped to terminate from underlying channel. Only problem I see here is that Absinthe would need to store a map of what subscriptions were used per socket, so that it can call terminate only on subscriptions that user joined.

And it wouldn't be possible to use Phoenix.Presence, users would have to roll their own.

@benwilson512 The first approach seem "nicer" in a way, what do you think?

lstrzebinczyk avatar Jun 23 '20 11:06 lstrzebinczyk

👋 Hi @lstrzebinczyk @benwilson512 I'm also facing the same problem, in which I need to track subscriptions presence. The second solution, without the socket, would be very convenient to use with Phoenix.Tracker, which does not need the socket. What do you think?

bigardone avatar Jul 29 '20 06:07 bigardone

I think all we need is a callback we can overwrite for when user is joining the websocket connection, If that was available, any tool can be used.

lstrzebinczyk avatar Jul 29 '20 11:07 lstrzebinczyk

I support this feature and would like to help contribute too!

marshallshen avatar Sep 25 '21 03:09 marshallshen

Just found this PR: https://github.com/absinthe-graphql/absinthe_phoenix/pull/87/files

marshallshen avatar Sep 25 '21 04:09 marshallshen

Is there a plan to move forward with this?

colman-hartinger avatar Jul 26 '22 23:07 colman-hartinger

This thing would come in handy to me about right now.

@benwilson512 is there anything we could do to make this happen? Is the PR above requiring tweaks/updates that nobody does, and the feature could land in master or is it otherwise unwanted?

EDIT: after looking at the PR I think the proposal by @lstrzebinczyk with the join/terminate callbacks is more useful and not for just this feature.

I also don't know the Absinthe internals, but for this very feature it could be just the join callback, and then the presence tracker would listen on the process being terminated when disconnect happens.

Alternatively, I wonder if this is possible to do so through current middleware? Maybe we can hook into a user joining a subscription this way already, and then implement a tracker based that + detection of process being terminated on disconnect...

hubertlepicki avatar Jan 05 '24 09:01 hubertlepicki