graphlient 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -14,4 +14,7 @@ Gem::Specification.new do |s|
14
14
  s.homepage = 'http://github.com/ashkan18/graphlient'
15
15
  s.licenses = ['MIT']
16
16
  s.summary = 'Ruby Gem for consuming GraphQL endpoints'
17
+ s.add_dependency 'graphql-client'
18
+ s.add_dependency 'faraday'
19
+ s.add_dependency 'faraday_middleware'
17
20
  end
@@ -1,9 +1,4 @@
1
- require 'net/http'
2
- require 'uri'
3
- require 'json'
4
-
5
1
  require 'graphlient/version'
6
- require 'graphlient/config.rb'
7
2
  require 'graphlient/extensions'
8
3
  require 'graphlient/errors'
9
4
  require 'graphlient/query'
@@ -0,0 +1,43 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+
4
+ module Graphlient
5
+ module Adapters
6
+ class FaradayAdapter
7
+ attr_accessor :url, :headers
8
+
9
+ def initialize(url, headers:, &_block)
10
+ @url = url
11
+ @headers = headers.dup if headers
12
+ yield self if block_given?
13
+ end
14
+
15
+ def execute(document:, operation_name:, variables:, context:)
16
+ response = connection.post do |req|
17
+ req.headers.merge!(context[:headers] || {})
18
+ req.body = {
19
+ query: document.to_query_string,
20
+ operationName: operation_name,
21
+ variables: variables.to_json
22
+ }.to_json
23
+ end
24
+ response.body
25
+ rescue Faraday::ClientError => e
26
+ raise Graphlient::Errors::Server.new(e.message, e)
27
+ end
28
+
29
+ def connection
30
+ @connection ||= Faraday.new(url: url, headers: headers) do |c|
31
+ c.use Faraday::Response::RaiseError
32
+ c.request :json
33
+ c.response :json
34
+ if block_given?
35
+ yield c
36
+ else
37
+ c.use Faraday::Adapter::NetHttp
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,37 +1,52 @@
1
+ require 'graphql/client'
2
+ require 'graphlient/adapters/faraday_adapter'
3
+
1
4
  module Graphlient
2
5
  class Client
3
- attr_reader :uri
4
- attr_reader :options
6
+ attr_accessor :uri, :options
5
7
 
6
- def initialize(url, options = {})
7
- @uri = URI(url)
8
+ def initialize(url, options = {}, &_block)
9
+ @url = url
8
10
  @options = options.dup
11
+ yield self if block_given?
9
12
  end
10
13
 
11
- def query(&block)
12
- query = Graphlient::Query.new do
14
+ def parse(&block)
15
+ query_str = Graphlient::Query.new do
13
16
  instance_eval(&block)
14
17
  end
15
- response = post(query)
16
- raise Graphlient::Errors::HTTP.new(response.message, response) unless response.is_a? Net::HTTPSuccess
17
- parse(response.body)
18
+ client.parse(query_str.to_s)
18
19
  end
19
20
 
20
- def connection
21
- @connection ||= Net::HTTP.new(uri.host, uri.port)
21
+ def execute(query, variables = nil)
22
+ query_params = {}
23
+ query_params[:context] = @options if @options
24
+ query_params[:variables] = variables if variables
25
+ client.query(query, query_params)
26
+ rescue GraphQL::Client::Error => e
27
+ raise Graphlient::Errors::Client.new(e.message, e)
22
28
  end
23
29
 
24
- def post(query)
25
- request = Net::HTTP::Post.new(uri.request_uri)
26
- request.body = { query: query.to_s }.to_json
27
- options[:headers].each do |k, v|
28
- request[k] = v
29
- end
30
- connection.request(request)
30
+ def query(variables = nil, &block)
31
+ execute(parse(&block), variables)
32
+ rescue GraphQL::Client::Error => e
33
+ raise Graphlient::Errors::Client.new(e.message, e)
34
+ end
35
+
36
+ def http(&block)
37
+ @http ||= Adapters::FaradayAdapter.new(@url, headers: @options[:headers], &block)
31
38
  end
32
39
 
33
- def parse(response)
34
- JSON.parse(response, symbolize_names: true)
40
+ def schema
41
+ @schema ||= GraphQL::Client.load_schema(http)
42
+ end
43
+
44
+ private
45
+
46
+ def client
47
+ @client ||= GraphQL::Client.new(schema: schema, execute: http).tap do |client|
48
+ client.allow_dynamic_queries = @options.key?(:allow_dynamic_queries) ? options[:allow_dynamic_queries] : true
49
+ end
35
50
  end
36
51
  end
37
52
  end
@@ -1,2 +1,3 @@
1
1
  require_relative 'errors/error'
2
- require_relative 'errors/http'
2
+ require_relative 'errors/client'
3
+ require_relative 'errors/server'
@@ -0,0 +1,6 @@
1
+ module Graphlient
2
+ module Errors
3
+ class Client < Error
4
+ end
5
+ end
6
+ end
@@ -1,6 +1,13 @@
1
1
  module Graphlient
2
2
  module Errors
3
3
  class Error < StandardError
4
+ attr_reader :inner_exception
5
+
6
+ def initialize(message, inner_exception = nil)
7
+ super(message)
8
+
9
+ @inner_exception = inner_exception
10
+ end
4
11
  end
5
12
  end
6
13
  end
@@ -0,0 +1,6 @@
1
+ module Graphlient
2
+ module Errors
3
+ class Server < Error
4
+ end
5
+ end
6
+ end
@@ -17,7 +17,7 @@ module Graphlient
17
17
  end
18
18
 
19
19
  def to_s
20
- "{ #{query_str} }"
20
+ query_str.strip
21
21
  end
22
22
 
23
23
  private
@@ -26,7 +26,7 @@ module Graphlient
26
26
  # add field
27
27
  @query_str << "\n#{indent}#{query_field}"
28
28
  # add filter
29
- @query_str << "(#{get_args_str(args)})" if args.any?
29
+ @query_str << "(#{get_args_str(args)})" if find_hash_arg(args)
30
30
 
31
31
  if block_given?
32
32
  @indents += 1
@@ -44,9 +44,15 @@ module Graphlient
44
44
  end
45
45
 
46
46
  def get_args_str(args)
47
- args.detect { |arg| arg.is_a? Hash }.map do |k, v|
48
- "#{k}: #{get_arg_value_str(v)}"
49
- end.join(', ')
47
+ hash_args_str(find_hash_arg(args))
48
+ end
49
+
50
+ def find_hash_arg(args)
51
+ args.detect { |arg| arg.is_a? Hash }
52
+ end
53
+
54
+ def hash_args_str(hash)
55
+ hash.map { |k, v| "#{k}: #{get_arg_value_str(v)}" }.join(', ')
50
56
  end
51
57
 
52
58
  def get_arg_value_str(value)
@@ -57,6 +63,8 @@ module Graphlient
57
63
  value.to_s
58
64
  when Array
59
65
  "[#{value.map { |v| get_arg_value_str(v) }.join(', ')}]"
66
+ when Hash
67
+ "{ #{hash_args_str(value)} }"
60
68
  else
61
69
  value
62
70
  end
@@ -1,3 +1,3 @@
1
1
  module Graphlient
2
- VERSION = '0.0.5'.freeze
2
+ VERSION = '0.0.6'.freeze
3
3
  end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe Graphlient::Adapters::FaradayAdapter do
4
+ let(:app) { Object.new }
5
+
6
+ context 'with a custom middleware' do
7
+ let(:client) do
8
+ Graphlient::Client.new('http://example.com/graphql') do |client|
9
+ client.http do |h|
10
+ h.connection do |c|
11
+ c.use Faraday::Adapter::Rack, app
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ it 'inserts a middleware into the connection' do
18
+ expect(client.http.connection.builder.handlers).to eq(
19
+ [
20
+ Faraday::Response::RaiseError,
21
+ FaradayMiddleware::EncodeJson,
22
+ FaradayMiddleware::ParseJson,
23
+ Faraday::Adapter::Rack
24
+ ]
25
+ )
26
+ end
27
+ end
28
+
29
+ context 'with custom url and headers' do
30
+ let(:url) { 'http://example.com/graphql' }
31
+ let(:headers) { { 'Foo' => 'bar' } }
32
+ let(:client) do
33
+ Graphlient::Client.new(url, headers: headers)
34
+ end
35
+
36
+ it 'sets url' do
37
+ expect(client.http.url).to eq url
38
+ end
39
+
40
+ it 'sets headers' do
41
+ expect(client.http.headers).to eq headers
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,158 @@
1
+ require 'spec_helper'
2
+
3
+ describe Graphlient::Client do
4
+ include_context 'Dummy Client'
5
+
6
+ describe 'parse and execute' do
7
+ context 'non-parameterized query' do
8
+ let(:query) do
9
+ client.parse do
10
+ query do
11
+ invoices(ids: [10]) do
12
+ id
13
+ fee_in_cents
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ it '#parse' do
20
+ expect(query).to be_a GraphQL::Client::OperationDefinition
21
+ end
22
+
23
+ it '#execute' do
24
+ response = client.execute(query)
25
+ invoices = response.data.invoices
26
+ expect(invoices.first.id).to eq 10
27
+ end
28
+ end
29
+
30
+ context 'parameterized query' do
31
+ let(:query) do
32
+ client.parse do
33
+ query(:$ids => :'[Int]') do
34
+ invoices(ids: :$ids) do
35
+ id
36
+ fee_in_cents
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ it '#parse' do
43
+ expect(query).to be_a GraphQL::Client::OperationDefinition
44
+ end
45
+
46
+ it '#execute' do
47
+ response = client.execute(query, ids: [42])
48
+ invoices = response.data.invoices
49
+ expect(invoices.first.id).to eq 42
50
+ expect(invoices.first.fee_in_cents).to eq 20_000
51
+ end
52
+
53
+ it '#execute without variables' do
54
+ response = client.execute(query)
55
+ invoices = response.data.invoices
56
+ expect(invoices).to eq([])
57
+ end
58
+ end
59
+ end
60
+
61
+ describe '#query' do
62
+ context 'non-parameterized query' do
63
+ it 'fails client-side on invalid schema' do
64
+ expect do
65
+ client.query do
66
+ query do
67
+ invoice(id: 10) do
68
+ id
69
+ fee_in_cents
70
+ end
71
+ end
72
+ end
73
+ end.to raise_error Graphlient::Errors::Client do |e|
74
+ expect(e.to_s).to eq "Field 'invoice' doesn't exist on type 'Query'"
75
+ end
76
+ end
77
+
78
+ it 'returns a response from a query' do
79
+ response = client.query do
80
+ query do
81
+ invoices(ids: [10]) do
82
+ id
83
+ fee_in_cents
84
+ end
85
+ end
86
+ end
87
+
88
+ invoices = response.data.invoices
89
+ expect(invoices.first.id).to eq 10
90
+ expect(invoices.first.fee_in_cents).to eq 20_000
91
+ end
92
+
93
+ it 'returns a response from a mutation' do
94
+ response = client.query do
95
+ mutation do
96
+ createInvoice(input: { fee_in_cents: 12_345 }) do
97
+ id
98
+ fee_in_cents
99
+ end
100
+ end
101
+ end
102
+
103
+ invoice = response.data.create_invoice.first
104
+ expect(invoice.id).to eq 1231
105
+ expect(invoice.fee_in_cents).to eq 12_345
106
+ end
107
+ end
108
+
109
+ context 'parameterized query' do
110
+ it 'fails when missing input' do
111
+ response = client.query do
112
+ mutation('$input' => :createInvoiceInput!) do
113
+ createInvoice(input: :$input) do
114
+ id
115
+ fee_in_cents
116
+ end
117
+ end
118
+ end
119
+
120
+ expect(response.errors.messages['data']).to eq(
121
+ [
122
+ 'Variable input of type createInvoiceInput! was provided invalid value'
123
+ ]
124
+ )
125
+ end
126
+
127
+ it 'returns a response from a query' do
128
+ response = client.query(ids: [42]) do
129
+ query(:$ids => :'[Int]') do
130
+ invoices(ids: :$ids) do
131
+ id
132
+ fee_in_cents
133
+ end
134
+ end
135
+ end
136
+
137
+ invoices = response.data.invoices
138
+ expect(invoices.first.id).to eq 42
139
+ expect(invoices.first.fee_in_cents).to eq 20_000
140
+ end
141
+
142
+ it 'executes the mutation' do
143
+ response = client.query(input: { fee_in_cents: 12_345 }) do
144
+ mutation(:$input => :createInvoiceInput!) do
145
+ createInvoice(input: :$input) do
146
+ id
147
+ fee_in_cents
148
+ end
149
+ end
150
+ end
151
+
152
+ invoice = response.data.create_invoice.first
153
+ expect(invoice.id).to eq 1231
154
+ expect(invoice.fee_in_cents).to eq 12_345
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe Graphlient::Client do
4
+ let(:client) { Graphlient::Client.new('http://graph.biz/graphql') }
5
+
6
+ describe '#schema' do
7
+ before do
8
+ stub_request(:post, 'http://graph.biz/graphql').to_return(status: 500)
9
+ end
10
+
11
+ it 'fails with an exception' do
12
+ expect do
13
+ client.schema
14
+ end.to raise_error Graphlient::Errors::Server do |e|
15
+ expect(e.to_s).to eq 'the server responded with status 500'
16
+ end
17
+ end
18
+ end
19
+ end