Absinthe.plug.put_options for context overwrites previous context

Hello!

I have recently discovered that calling Absinthe.Plug.put_options , in my case to add some information to the context, actually overwrites whatever might have been set as the context before. I have two plugs for my router, one that auths and adds the user to the context, and another that adds a workspace ID to the context. I know I could just combine these plugs but that kind of defeats the point of plugs (modular parts of a pipeline).

Absinthe.Plug.put_options(conn, context: %{authenticated: true, current_user: user})
Absinthe.Plug.put_options(conn, context: %{workspace: current_workspace})

So the question is, is there any way that I could modify the context object instead of overwriting it? I’d be happy to just get the current context, merge the new options, and call put_options with the merged object but I can’t see a great way to get the current context. Only way I can see is to grab it from conn.private.absinthe but that doesn’t feel right.

1 Like

I had the same issue and just ended up merging the context.

e.g. to resolve some subfields in our queries, we need the language passed down to their resolvers like this:

  object :search_queries do
    @desc "Find products using search terms."
    field :search, non_null(:search_result) do
      # Store the language on the context for field level resolution
      middleware fn res, _ ->
        %{res | context: Map.merge(res.context, %{args: res.arguments})}
      end

      @desc "Search keywords."
      arg :keywords, :string, default_value: ""

      @desc "Country code."
      arg :country, :string, default_value: "US"

      @desc "Currency."
      arg :currency, :string, default_value: "USD"

      @desc "Language."
      arg :language, :string, default_value: "EN"

      @desc "Page number to return."
      arg :page, :integer, default_value: 1

      @desc "Number of items per page."
      arg :page_size, :integer, default_value: 20

      @desc "The width of images (in pixels) to suit the user's phone"
      arg :image_width, :integer, default_value: 500

      resolve &SearchResolver.search/2
    end
  end

That makes the language value available at the field level where we need to translate strings.

We do something slightly similar in our global middleware(s). e.g.

    %{
      res
      | middleware:
          res.middleware ++
            [
              {{__MODULE__, :complete},
               [
                 start_time_mono: start_time_mono
               ]}
            ]
    }

@Francesco PR welcome for an update_options function. In the short term feel free to grab it from conn.private.

2 Likes

@Catharz thanks for the help :slight_smile:

@benwilson512 I’m definitely keen to try my hand at the PR as soon as I can spare a few hours! But yep just using conn.private is working fine in the meantime