Hello world!
I’m currently working around Absinthe subscriptions feature, and I come with a question on trigger/2. And I was wondering if :
Is there a way to give an Absinthe.Resolution
structure to the topic callback function which is given as second parameter of the trigger/2
function ?
I might be missing something, but in my opinion, as the config/2 function callback receives the resolution structure as second parameter, which allows us to create subscription topic based on some context content; I would have expected the trigger topic callback to do the same.
I will expose the problem which leds me to this question, and the solution I found to overcome it.
Problem
I would like to create a subscription on a rule
type and trigger it when a rule
is created.
I want this subscription to be triggered on an tenant_id
topic which is obtained from the context in the resolution structure passed in parameter of the config/2
function’s callback.
Instead of using publish/3 in my resolver, I would like to use the trigger/2 function to centralize all my trigger definitions.
Therefore, I have this mutation:
mutation do
field :create_rule, non_null(:rule) do
arg(:rule, non_null(:create_rule_input))
resolve(&RuleResolver.create_rule/3)
end
end
And this subscription configured :
subscription do
field :rule, :rule do
config(fn
_args, %{context: %{current_tenant: %{id: tenant_id}}} ->
{:ok, topic: tenant_id}
_, _ ->
ErrorResolver.resolve(:forbidden)
end)
trigger(:create_rule,
topic: fn rule ->
rule.tenant_id
end
)
end
end
Here, i’m using the tenant_id
contained in the rule
structure I return to determine the topic and to make it work.
But, tomorrow, if I have another structure which doesn’t contains this tenant_id
, I could not use the same logic as above. More over, I cannot retrieve it from the context in the resolution since it’s not accessible in the trigger.
Potential Solution
To make sure any trigger can determine a topic based on the tenant_id
without worrying about the fact that this id is contained or not in the result structure, I modified the code above like the following to retrieve the tenant_id
from the context in the resolution structure:
mutation do
field :create_rule, non_null(:rule) do
arg(:rule, non_null(:create_rule_input))
resolve(&RuleResolver.create_rule/3)
middleware(SubscriptionTriggerContextMiddleware)
end
end
subscription do
field :rule, :rule do
config(fn
_args, %{context: %{current_tenant: %{id: tenant_id}}} ->
{:ok, topic: tenant_id}
_, _ ->
ErrorResolver.resolve(:forbidden)
end)
trigger(:create_rule,
topic: fn %{context: %{current_tenant: %{id: tenant_id}}} ->
tenant_id
end
)
end
end
With the help of a middleware, I’m adding the resolution context to the result value of my mutation, which allows me in the end to retrieve my id:
@behaviour Absinthe.Middleware
def call(resolution, _config) do
case resolution do
%{context: context, value: resolver_result} when is_map(resolver_result) ->
new_value = Map.put(resolver_result, :context, context)
Map.put(resolution, :value, new_value)
_ ->
resolution
end
end
This solution is not really elegant since i’m modifying the return value of my resolver, and it also only works for resolvers results values which are structures.
Does anyone faced this problem before ?
I’m really not happy with my solution, and I’m open to any advise to improve it, or a better solution to solve my problem.
Can’t wait to have your thoughts on this !