absinthe
absinthe copied to clipboard
Feature proposal: Subscription presence
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.
Right, so the main challenges to solve are: How are subscription topics mapped to presence topics?
So I think this can be approached in 2 ways.
- 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.
- 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?
👋 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?
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.
I support this feature and would like to help contribute too!
Just found this PR: https://github.com/absinthe-graphql/absinthe_phoenix/pull/87/files
Is there a plan to move forward with this?
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...