Is there a way to give an `Absinthe.Resolution` structure to `trigger/2` function's callback ?

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 !

2 Likes

This is a good question. Definitely the way you have it now is an option, but I think Absinthe would be better if we took a 2 arity function to trigger. There is a thing you could do with middleware too in the meantime, but I’ll need to loop back to that tomorrow.

3 Likes

Good to know, it would be a really useful change then, if it’s possible.
Thanks for your answer !

@benwilson512 I’m running into the same issue; it would be great to have a 2-arity trigger callback, the second argument of which would be the resolution object for the subscription field. Do you have a suggestion for how to get around this with middleware? Specifically, I’m trying to access the subscription field name to which the trigger callback corresponds.

1 Like