graphlient 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/.rubocop_todo.yml +32 -17
- data/.travis.yml +8 -2
- data/CHANGELOG.md +11 -0
- data/Dangerfile +1 -0
- data/Gemfile +5 -0
- data/README.md +200 -26
- data/RELEASING.md +1 -1
- data/Rakefile +1 -1
- data/graphlient.gemspec +3 -0
- data/lib/graphlient.rb +0 -5
- data/lib/graphlient/adapters/faraday_adapter.rb +43 -0
- data/lib/graphlient/client.rb +35 -20
- data/lib/graphlient/errors.rb +2 -1
- data/lib/graphlient/errors/client.rb +6 -0
- data/lib/graphlient/errors/error.rb +7 -0
- data/lib/graphlient/errors/server.rb +6 -0
- data/lib/graphlient/query.rb +13 -5
- data/lib/graphlient/version.rb +1 -1
- data/spec/graphlient/adapters/faraday_adapter_spec.rb +44 -0
- data/spec/graphlient/client_query_spec.rb +158 -0
- data/spec/graphlient/client_schema_spec.rb +19 -0
- data/spec/graphlient/extensions/query_spec.rb +6 -4
- data/spec/graphlient/query_spec.rb +72 -25
- data/spec/graphlient/static_client_query_spec.rb +39 -0
- data/spec/spec_helper.rb +3 -1
- data/spec/support/context/dummy_client.rb +26 -0
- data/spec/support/dummy_app.rb +25 -0
- data/spec/support/dummy_schema.rb +9 -0
- data/spec/support/mutations/create_invoice_mutation.rb +16 -0
- data/spec/support/mutations/mutation.rb +7 -0
- data/spec/support/queries/query.rb +16 -0
- data/spec/support/types/invoice_type.rb +6 -0
- metadata +61 -8
- data/.byebug_history +0 -23
- data/lib/graphlient/config.rb +0 -21
- data/lib/graphlient/errors/http.rb +0 -12
- data/spec/graphlient/client_spec.rb +0 -42
data/graphlient.gemspec
CHANGED
@@ -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
|
data/lib/graphlient.rb
CHANGED
@@ -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
|
data/lib/graphlient/client.rb
CHANGED
@@ -1,37 +1,52 @@
|
|
1
|
+
require 'graphql/client'
|
2
|
+
require 'graphlient/adapters/faraday_adapter'
|
3
|
+
|
1
4
|
module Graphlient
|
2
5
|
class Client
|
3
|
-
|
4
|
-
attr_reader :options
|
6
|
+
attr_accessor :uri, :options
|
5
7
|
|
6
|
-
def initialize(url, options = {})
|
7
|
-
@
|
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
|
12
|
-
|
14
|
+
def parse(&block)
|
15
|
+
query_str = Graphlient::Query.new do
|
13
16
|
instance_eval(&block)
|
14
17
|
end
|
15
|
-
|
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
|
21
|
-
|
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
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
34
|
-
|
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
|
data/lib/graphlient/errors.rb
CHANGED
data/lib/graphlient/query.rb
CHANGED
@@ -17,7 +17,7 @@ module Graphlient
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def to_s
|
20
|
-
|
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
|
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
|
48
|
-
|
49
|
-
|
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
|
data/lib/graphlient/version.rb
CHANGED
@@ -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
|