graphlient 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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