I need to perform some logic when client unsubscribes from absinthe channel or if socket is severed (like browser windows closed). In the example below, the app allows multiple users to work on an object graph, and each node being edited is locked. When user switches to another UI or closes browser window the lock needs to be released.
So far I’ve been handling this by providing a custom channel and socket decorators, and pattern-matching handle_in()
callbacks.
In the absinthe schema I, of course, have my subscriptions defined, along with channel names that I expect the user to subscribe to. To handle unsubscribes, I have custom pattern matching for each type of subscription with handle_in
. But the arguments that come in make it incredibly difficult to figure out what channel I’m unsubscribing from because all I have is a subscriptionId and a query, so I have to pattern-match on substring to know which subscription it is.
This is extremely ugly and error-prone! Why couldn’t Absinthe just provide a configurable callback for channel unsubscribes that I could plug in the configuration somewhere? Like on_unsubscribe
:
object :lock_subscriptions do
field :lock_changed, non_null(:lock_result) do
arg(:lock_input, :lock_input)
config(fn
%{lock_input: %{id: target_id, target_type: target_type}}, _ ->
{:ok, topic: "lock-changed:#{target_type}:#{target_id}", on_unsubscribe: &my_callback/3}
_, _ ->
{:ok, topic: "lock-changed:*"}
end)
trigger([:acquire_lock, :release_lock],
topic: fn
%{success: true, id: target_id, target_type: target_type} ->
[
"lock-changed:#{target_type}:#{target_id}",
"lock-changed:*"
]
_ ->
"lock-changed-do-not-publish"
end
)
end
field :lock_freed, non_null(:lock_result) do
arg(:lock_input, non_null(:lock_input))
config(fn
%{lock_input: %{id: target_id, target_type: target_type}}, _ ->
{:ok, topic: "lock-freed:#{target_type}:#{target_id}", on_unsubscribe: &my_callback/3}
end)
trigger(:release_lock,
topic: fn
%{success: true, id: target_id, target_type: target_type} ->
"lock-freed:#{target_type}:#{target_id}"
_ ->
"not-freed_do-not-publish"
end
)
end
end
If all I have is subscriptionId which looks like subscriptionId: "__absinthe__:doc:-576460752303423225:09741AC4B0D1FF92645F8C67BA51FB3656E35965E66A2A42FCEA62D4B9BB6754"
then I have to build a registry that associates initial arguments to subscriptionId so I can retrieve them on unsubscribe
and perform handle_unsub
.
I have a suspicion that absinthe disregards the channel name I’m providing in the schema in config
and instead uses this long subscriptionId as the topic name. Life would be easier if it was passing the channel name that I could parse for arguments, "#{@lock_changed_prefix}:#{target_type}:#{target_id}"
. But it doesn’t.
So either I’m not configuring the subscription properly or nobody is using absinthe unsubscribe events, because otherwise they’d be pulling their hair out too and we’d have a lot of bald people.
Appreciate some help. How have you done it before, when you needed to, say, release resources on unsubscription, based on the arguments provided during subscribe
?