yuki24 / artemis

Ruby GraphQL client on Rails that actually makes you more productive
MIT License
207 stars 14 forks source link

Support for adding middleware in adapters #57

Open JanStevens opened 5 years ago

JanStevens commented 5 years ago

Hello,

Thanks for this project, after struggling a day with various other GraphQL ruby clients I finally got recommended this one. The only part I'm fighting with is how I can add middleware concepts right before the request is send (like in Faraday).

What I need to add is HMAC authentication so I need the raw json body and be able to set new headers (while also accepting some global config)

Any advise on how to tackle this? Right now I'm going for the before_execute but that seems a bit to early since I have to structure the body again to take the MD5 for example

Regards

JanStevens commented 5 years ago

For now I've added my own adapter, copy pasting Net HTTP adapter and changing the following lines

# Only changes are in here
signed_request = ApiAuth.sign!(request, context[:access_id], context[:hmac_api_key], digest: 'sha256')
response = connection.request(signed_request)

It would be cool if right before the request was made you would have a hook that receives the request object, context and you return it a request object which is then used to make the request

yuki24 commented 5 years ago

Right now there isn't a great way to capture raw HTTP request/response, but I'm happy to add a hook that looks like:

class YourClient < Artemis::Client
  before_request do |headers, body, context|
    headers[:Authorization] = generate_signature(body, context[:access_id], context[:hmac_api_key])
  end

  after_request do |status, headers, body, context|
    ...
  end

  # Or with different names:
  intercept_request do |...|
    ...
  end

  received_response do |...|
    ...
  end
end

Right now, a more structured way to customize the adapter is to add your own adapter to the Artemis::Adapters namespace so Artemis will be able to look up the adapter:

# lib/artemis/adapters/signed_net_http_adapter.rb
module Artemis::Adapters
  class SignedNetHttpAdapter < NetHttpAdapter
    def execute(document:, operation_name: nil, variables: {}, context: {})
      ...

      signed_request = ApiAuth.sign!(request, context[:access_id], context[:hmac_api_key], digest: 'sha256')
      response = connection.request(signed_request)

      ...
    end
  end
end

# config/initializers/artemis.rb
require 'artemis/adapters/signed_net_http_adapter'

# config/graphql.yml
development:
  your_service:
    adapter: :signed_net_http

This seems a little over-engineering since all you want to add is basically just a single line of code you already use. We could make it simpler if this is more common. Let me know what you think.

JanStevens commented 5 years ago

Hello,

Yes the before request / after request seem very helpful and might indeed solve almost all typical cases. Another idea that comes to mind is using Faraday (https://github.com/lostisland/faraday). It has support for all the adapters you currently are supporting (except for test / curb but faraday-curb exists) and allows to easily plugin middleware.

Our authentication right now is a Faraday Middleware.

I'll try to take a stab at that before_request and after_request hooks

Regards,