Skip to content

Commit

Permalink
Allow swapping the HTTP stack.
Browse files Browse the repository at this point in the history
  • Loading branch information
dblock committed Oct 26, 2017
1 parent 7052ae6 commit 316bb6f
Show file tree
Hide file tree
Showing 15 changed files with 221 additions and 81 deletions.
15 changes: 15 additions & 0 deletions .rubocop.yml
@@ -1,4 +1,19 @@
Style/FrozenStringLiteralComment:
Enabled: false

Style/Documentation:
Enabled: false

Metrics/LineLength:
Enabled: false

Metrics/MethodLength:
Enabled: false

Metrics/BlockLength:
Enabled: false

Metrics/AbcSize:
Enabled: false

inherit_from: .rubocop_todo.yml
36 changes: 5 additions & 31 deletions .rubocop_todo.yml
@@ -1,31 +1,17 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2017-10-26 10:54:26 -0400 using RuboCop version 0.47.1.
# on 2017-10-26 13:24:56 -0400 using RuboCop version 0.47.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.

# Offense count: 1
Lint/UselessAssignment:
# Cop supports --auto-correct.
# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods.
Lint/UnusedMethodArgument:
Exclude:
- 'spec/graphlient/client_query_spec.rb'

# Offense count: 11
# Configuration parameters: CountComments, ExcludedMethods.
Metrics/BlockLength:
Max: 185

# Offense count: 20
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
# URISchemes: http, https
Metrics/LineLength:
Max: 177

# Offense count: 2
# Configuration parameters: CountComments.
Metrics/MethodLength:
Max: 12
- 'lib/graphlient/adapters/http/http_adapter.rb'

# Offense count: 1
# Configuration parameters: EnforcedStyle, SupportedStyles.
Expand All @@ -34,18 +20,6 @@ Style/ClassAndModuleChildren:
Exclude:
- 'spec/graphlient/static_client_query_spec.rb'

# Offense count: 6
Style/Documentation:
Exclude:
- 'spec/**/*'
- 'test/**/*'
- 'lib/graphlient/adapters/faraday_adapter.rb'
- 'lib/graphlient/client.rb'
- 'lib/graphlient/errors/error.rb'
- 'lib/graphlient/errors/graphql.rb'
- 'lib/graphlient/extensions/query.rb'
- 'lib/graphlient/query.rb'

# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
@@ -1,6 +1,7 @@
### 0.0.9 (Next)

* [#28](https://github.com/ashkan18/graphlient/pull/28): Raise errors in `execute`, not only `query` - [@dblock](https://github.com/dblock).
* [#29](https://github.com/ashkan18/graphlient/pull/29): Added `Graphlient::Adapters::HTTP::HTTPAdapter` that replaces Faraday with `Net::HTTP` - [@dblock](https://github.com/dblock).
* Your contribution here.

### 0.0.8 (10/26/2017)
Expand Down
32 changes: 31 additions & 1 deletion README.md
Expand Up @@ -265,9 +265,19 @@ query.to_s
# "\nquery{\n invoice(id: 10){\n line_items\n }\n }\n"
```

### Swapping the HTTP Stack

You can swap the default Faraday adapter for `Net::HTTP`.

```ruby
client = Graphlient::Client.new('https://test-graphql.biz/graphql',
http: Graphlient::Adapters::HTTP::HTTPAdapter
)
```

### Testing with Graphlient and RSpec

Use Graphlient inside your RSpec tests in a Rails application or with `Rack::Test`, no more messy HTTP POSTs.
Use Graphlient inside your RSpec tests in a Rails application or with `Rack::Test` against your actual application.

```ruby
require 'spec_helper'
Expand Down Expand Up @@ -307,6 +317,26 @@ describe App do
end
```

Alternately you can `stub_request` with Webmock.

```ruby
describe App do
let(:url) { 'http://example.com/graphql' }
let(:client) { Graphlient::Client.new(url) }

before do
stub_request(:post, url).to_return(
status: 200,
body: DummySchema.execute(GraphQL::Introspection::INTROSPECTION_QUERY).to_json
)
end

it 'retrieves schema' do
expect(client.schema).to be_a GraphQL::Schema
end
end
```

## License

MIT License, see [LICENSE](LICENSE)
Expand Down
2 changes: 2 additions & 0 deletions lib/graphlient.rb
@@ -1,5 +1,7 @@
require 'graphql/client'
require 'graphlient/version'
require 'graphlient/extensions'
require 'graphlient/errors'
require 'graphlient/query'
require 'graphlient/adapters'
require 'graphlient/client'
1 change: 1 addition & 0 deletions lib/graphlient/adapters.rb
@@ -0,0 +1 @@
require_relative 'adapters/http'
43 changes: 0 additions & 43 deletions lib/graphlient/adapters/faraday_adapter.rb

This file was deleted.

3 changes: 3 additions & 0 deletions lib/graphlient/adapters/http.rb
@@ -0,0 +1,3 @@
require_relative 'http/adapter'
require_relative 'http/faraday_adapter'
require_relative 'http/http_adapter'
23 changes: 23 additions & 0 deletions lib/graphlient/adapters/http/adapter.rb
@@ -0,0 +1,23 @@
module Graphlient
module Adapters
module HTTP
class Adapter
attr_accessor :url, :options

def initialize(url, options = {}, &_block)
@url = url
@options = options.dup if options
yield self if block_given?
end

def headers
options[:headers] if options
end

def execute(*)
raise NotImplementedError
end
end
end
end
end
37 changes: 37 additions & 0 deletions lib/graphlient/adapters/http/faraday_adapter.rb
@@ -0,0 +1,37 @@
require 'faraday'
require 'faraday_middleware'

module Graphlient
module Adapters
module HTTP
class FaradayAdapter < Adapter
def execute(document:, operation_name:, variables:, context:)
response = connection.post do |req|
req.headers.merge!(context[:headers] || {})
req.body = {
query: document.to_query_string,
operationName: operation_name,
variables: variables.to_json
}.to_json
end
response.body
rescue Faraday::ClientError => e
raise Graphlient::Errors::Server.new(e.message, e)
end

def connection
@connection ||= Faraday.new(url: url, headers: headers) do |c|
c.use Faraday::Response::RaiseError
c.request :json
c.response :json
if block_given?
yield c
else
c.use Faraday::Adapter::NetHttp
end
end
end
end
end
end
end
39 changes: 39 additions & 0 deletions lib/graphlient/adapters/http/http_adapter.rb
@@ -0,0 +1,39 @@
require 'graphql/client/http'

module Graphlient
module Adapters
module HTTP
class HTTPAdapter < Adapter
attr_reader :uri

def execute(document:, operation_name: nil, variables: {}, context: {})
request = Net::HTTP::Post.new(url)

request['Accept'] = 'application/json'
request['Content-Type'] = 'application/json'
headers&.each { |name, value| request[name] = value }

body = {}
body['query'] = document.to_query_string
body['variables'] = variables if variables.any?
body['operationName'] = operation_name if operation_name
request.body = JSON.generate(body)

response = connection.request(request)
raise Graphlient::Errors::Server.new("the server responded with status #{response.code}", response) unless response.is_a?(Net::HTTPOK)
JSON.parse(response.body)
end

def uri
@uri ||= URI(url)
end

def connection
Net::HTTP.new(uri.host, uri.port).tap do |client|
client.use_ssl = uri.scheme == 'https'
end
end
end
end
end
end
9 changes: 5 additions & 4 deletions lib/graphlient/client.rb
@@ -1,6 +1,3 @@
require 'graphql/client'
require 'graphlient/adapters/faraday_adapter'

module Graphlient
class Client
attr_accessor :uri, :options
Expand Down Expand Up @@ -40,8 +37,12 @@ def query(query_or_variables = nil, variables = nil, &block)
end
end

def http_adapter_class
options[:http] || Adapters::HTTP::FaradayAdapter
end

def http(&block)
@http ||= Adapters::FaradayAdapter.new(@url, headers: @options[:headers], &block)
@http ||= http_adapter_class.new(@url, headers: @options[:headers], &block)
end

def schema
Expand Down
@@ -1,6 +1,6 @@
require 'spec_helper'

describe Graphlient::Adapters::FaradayAdapter do
describe Graphlient::Adapters::HTTP::FaradayAdapter do
let(:app) { Object.new }

context 'with a custom middleware' do
Expand Down Expand Up @@ -41,4 +41,20 @@
expect(client.http.headers).to eq headers
end
end

context 'default' do
let(:url) { 'http://example.com/graphql' }
let(:client) { Graphlient::Client.new(url) }

before do
stub_request(:post, url).to_return(
status: 200,
body: DummySchema.execute(GraphQL::Introspection::INTROSPECTION_QUERY).to_json
)
end

it 'retrieves schema' do
expect(client.schema).to be_a GraphQL::Schema
end
end
end
41 changes: 41 additions & 0 deletions spec/graphlient/adapters/http/http_adapter_spec.rb
@@ -0,0 +1,41 @@
require 'spec_helper'

describe Graphlient::Adapters::HTTP::HTTPAdapter do
let(:app) { Object.new }

context 'with custom url and headers' do
let(:url) { 'http://example.com/graphql' }
let(:headers) { { 'Foo' => 'bar' } }
let(:client) do
Graphlient::Client.new(url, headers: headers, http: Graphlient::Adapters::HTTP::HTTPAdapter)
end

it 'sets adapter' do
expect(client.http).to be_a Graphlient::Adapters::HTTP::HTTPAdapter
end

it 'sets url' do
expect(client.http.url).to eq url
end

it 'sets headers' do
expect(client.http.headers).to eq headers
end
end

context 'default' do
let(:url) { 'http://example.com/graphql' }
let(:client) { Graphlient::Client.new(url, http: Graphlient::Adapters::HTTP::HTTPAdapter) }

before do
stub_request(:post, url).to_return(
status: 200,
body: DummySchema.execute(GraphQL::Introspection::INTROSPECTION_QUERY).to_json
)
end

it 'retrieves schema' do
expect(client.schema).to be_a GraphQL::Schema
end
end
end
2 changes: 1 addition & 1 deletion spec/graphlient/client_query_spec.rb
Expand Up @@ -78,7 +78,7 @@

it 'fails when wrong input type' do
expect do
rc = client.execute(query, ids: ['42'])
client.execute(query, ids: ['42'])
end.to raise_error Graphlient::Errors::GraphQL do |e|
expect(e.to_s).to eq "Variable ids of type [Int] was provided invalid value\n 0: Could not coerce value \"42\" to Int"
end
Expand Down

0 comments on commit 316bb6f

Please sign in to comment.