artemis 0.5.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +64 -0
- data/.gitignore +1 -0
- data/Appraisals +12 -20
- data/CHANGELOG.md +45 -0
- data/Gemfile +2 -0
- data/README.md +71 -6
- data/artemis.gemspec +0 -1
- data/gemfiles/rails_50.gemfile +3 -1
- data/gemfiles/rails_51.gemfile +3 -1
- data/gemfiles/rails_52.gemfile +3 -1
- data/gemfiles/rails_60.gemfile +3 -1
- data/gemfiles/{rails_40.gemfile → rails_61.gemfile} +5 -3
- data/gemfiles/{rails_41.gemfile → rails_70.gemfile} +5 -4
- data/gemfiles/rails_edge.gemfile +2 -0
- data/lib/artemis/adapters/abstract_adapter.rb +1 -1
- data/lib/artemis/adapters/curb_adapter.rb +17 -7
- data/lib/artemis/adapters/multi_domain_adapter.rb +58 -0
- data/lib/artemis/adapters/net_http_adapter.rb +25 -16
- data/lib/artemis/adapters/net_http_persistent_adapter.rb +7 -1
- data/lib/artemis/adapters/test_adapter.rb +22 -3
- data/lib/artemis/adapters.rb +1 -0
- data/lib/artemis/client.rb +42 -8
- data/lib/artemis/graphql_endpoint.rb +11 -5
- data/lib/artemis/railtie.rb +16 -8
- data/lib/artemis/test_helper.rb +2 -1
- data/lib/artemis/version.rb +1 -1
- data/lib/generators/artemis/mutation/templates/mutation.graphql +1 -1
- data/lib/generators/artemis/query/query_generator.rb +14 -2
- data/lib/generators/artemis/query/templates/fixture.yml +19 -0
- data/lib/generators/artemis/query/templates/query.graphql +2 -2
- data/spec/adapters_spec.rb +165 -10
- data/spec/autoloading_spec.rb +4 -4
- data/spec/client_spec.rb +38 -2
- data/spec/fixtures/metaphysics/{_artist_fragment.graphql → _artist_fields.graphql} +0 -0
- data/spec/fixtures/metaphysics/artists.graphql +1 -1
- data/spec/fixtures/responses/{spotify → spotify_client}/artist.yml +0 -0
- data/spec/test_helper_spec.rb +10 -1
- metadata +13 -26
- data/.travis.yml +0 -69
- data/gemfiles/rails_42.gemfile +0 -12
data/lib/artemis/client.rb
CHANGED
@@ -167,17 +167,19 @@ module Artemis
|
|
167
167
|
end
|
168
168
|
|
169
169
|
def resolve_graphql_file_path(filename, fragment: false)
|
170
|
-
|
171
|
-
filename = filename.to_s.underscore
|
170
|
+
filename = filename.to_s.underscore
|
172
171
|
|
173
172
|
graphql_file_paths.detect do |path|
|
174
|
-
path.end_with?("#{namespace}/#{filename}.graphql") ||
|
175
|
-
(fragment && filename.end_with?('fragment') && path.end_with?("#{namespace}/_#{filename}.graphql"))
|
173
|
+
path.end_with?("#{namespace}/#{filename}.graphql") || (fragment && path.end_with?("#{namespace}/_#{filename}.graphql"))
|
176
174
|
end
|
177
175
|
end
|
178
176
|
|
179
177
|
def graphql_file_paths
|
180
|
-
@graphql_file_paths ||= query_paths.flat_map {|path| Dir["#{path}/#{
|
178
|
+
@graphql_file_paths ||= query_paths.flat_map {|path| Dir["#{path}/#{namespace}/*.graphql"] }
|
179
|
+
end
|
180
|
+
|
181
|
+
def namespace
|
182
|
+
name.underscore
|
181
183
|
end
|
182
184
|
|
183
185
|
def preload!
|
@@ -218,6 +220,19 @@ module Artemis
|
|
218
220
|
new(default_context).execute(query, context: context, **arguments)
|
219
221
|
end
|
220
222
|
|
223
|
+
def multiplex(**context, &block)
|
224
|
+
queue = MultiplexQueue.new
|
225
|
+
wrapped_executor = Executor.new(queue, callbacks, default_context.deep_merge(context))
|
226
|
+
api_client = ::GraphQL::Client.new(schema: endpoint.schema, execute: wrapped_executor)
|
227
|
+
|
228
|
+
service_client = new
|
229
|
+
service_client.instance_variable_set(:@client, api_client)
|
230
|
+
|
231
|
+
block.call(service_client)
|
232
|
+
|
233
|
+
connection.multiplex(queue.queries, context: context)
|
234
|
+
end
|
235
|
+
|
221
236
|
private
|
222
237
|
|
223
238
|
# Looks up the GraphQL file that matches the given +const_name+ and sets it to a constant. If the files it not
|
@@ -248,9 +263,9 @@ module Artemis
|
|
248
263
|
# Github.user # => delegates to Github.new(default_context).user
|
249
264
|
#
|
250
265
|
# @api private
|
251
|
-
def method_missing(method_name,
|
266
|
+
def method_missing(method_name, **arguments, &block)
|
252
267
|
if resolve_graphql_file_path(method_name)
|
253
|
-
new(default_context).public_send(method_name,
|
268
|
+
new(default_context).public_send(method_name, **arguments, &block)
|
254
269
|
else
|
255
270
|
super
|
256
271
|
end
|
@@ -345,6 +360,25 @@ module Artemis
|
|
345
360
|
end
|
346
361
|
end
|
347
362
|
|
348
|
-
|
363
|
+
class MultiplexQueue
|
364
|
+
attr_reader :queries
|
365
|
+
|
366
|
+
def initialize
|
367
|
+
@queries = []
|
368
|
+
end
|
369
|
+
|
370
|
+
def execute(document:, operation_name: nil, variables: {}, context: {}) #:nodoc:
|
371
|
+
@queries << {
|
372
|
+
query: document.to_query_string,
|
373
|
+
variables: variables.presence || {},
|
374
|
+
operationName: operation_name,
|
375
|
+
context: context
|
376
|
+
}
|
377
|
+
|
378
|
+
{}
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
private_constant :Callbacks, :Executor, :MultiplexQueue
|
349
383
|
end
|
350
384
|
end
|
@@ -33,7 +33,7 @@ module Artemis
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def register!(service_name, configurations)
|
36
|
-
ENDPOINT_INSTANCES[service_name.to_s.underscore] = new(service_name.to_s, configurations.symbolize_keys)
|
36
|
+
ENDPOINT_INSTANCES[service_name.to_s.underscore] = new(service_name.to_s, **configurations.symbolize_keys)
|
37
37
|
end
|
38
38
|
|
39
39
|
##
|
@@ -44,10 +44,16 @@ module Artemis
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
attr_reader :name, :url, :adapter, :timeout, :schema_path, :pool_size
|
47
|
+
attr_reader :name, :url, :adapter, :timeout, :schema_path, :pool_size, :adapter_options
|
48
48
|
|
49
|
-
def initialize(name, url: nil, adapter: :net_http, timeout: 10, schema_path: nil, pool_size: 25)
|
50
|
-
@name
|
49
|
+
def initialize(name, url: nil, adapter: :net_http, timeout: 10, schema_path: nil, pool_size: 25, adapter_options: {})
|
50
|
+
@name = name.to_s
|
51
|
+
@url = url
|
52
|
+
@adapter = adapter
|
53
|
+
@timeout = timeout
|
54
|
+
@schema_path = schema_path
|
55
|
+
@pool_size = pool_size
|
56
|
+
@adapter_options = adapter_options
|
51
57
|
|
52
58
|
@mutex_for_schema = Mutex.new
|
53
59
|
@mutex_for_connection = Mutex.new
|
@@ -66,7 +72,7 @@ module Artemis
|
|
66
72
|
|
67
73
|
def connection
|
68
74
|
@connection || @mutex_for_connection.synchronize do
|
69
|
-
@connection ||= ::Artemis::Adapters.lookup(adapter).new(url, service_name: name, timeout: timeout, pool_size: pool_size)
|
75
|
+
@connection ||= ::Artemis::Adapters.lookup(adapter).new(url, service_name: name, timeout: timeout, pool_size: pool_size, adapter_options: adapter_options)
|
70
76
|
end
|
71
77
|
end
|
72
78
|
end
|
data/lib/artemis/railtie.rb
CHANGED
@@ -35,10 +35,9 @@ module Artemis
|
|
35
35
|
files_to_watch = Artemis::Client.query_paths.map {|path| [path, config.artemis.graphql_extentions] }.to_h
|
36
36
|
|
37
37
|
app.reloaders << ActiveSupport::FileUpdateChecker.new([], files_to_watch) do
|
38
|
-
|
39
|
-
endpoint_names.each do |endpoint_name|
|
38
|
+
Artemis.config_for_graphql(app).each_key do |endpoint_name|
|
40
39
|
Artemis::Client.query_paths.each do |path|
|
41
|
-
FileUtils.touch("#{path}/#{endpoint_name}.rb")
|
40
|
+
FileUtils.touch("#{path}/#{endpoint_name}.rb") if File.exist?("#{path}/#{endpoint_name}.rb")
|
42
41
|
end
|
43
42
|
end
|
44
43
|
end
|
@@ -48,17 +47,26 @@ module Artemis
|
|
48
47
|
initializer 'graphql.client.load_config' do |app|
|
49
48
|
if Pathname.new("#{app.paths["config"].existent.first}/graphql.yml").exist?
|
50
49
|
Artemis.config_for_graphql(app).each do |endpoint_name, options|
|
51
|
-
Artemis::GraphQLEndpoint
|
52
|
-
|
53
|
-
|
50
|
+
Artemis::GraphQLEndpoint
|
51
|
+
.register!(
|
52
|
+
endpoint_name,
|
53
|
+
schema_path: app.root.join(config.artemis.schema_path, "#{endpoint_name}.json").to_s,
|
54
|
+
**options.symbolize_keys
|
55
|
+
)
|
54
56
|
end
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
58
60
|
initializer 'graphql.client.preload', after: 'graphql.client.load_config' do |app|
|
59
61
|
if app.config.eager_load && app.config.cache_classes
|
60
|
-
Artemis.
|
61
|
-
|
62
|
+
Artemis::GraphQLEndpoint.registered_services.each do |endpoint_name|
|
63
|
+
begin
|
64
|
+
require endpoint_name # Rails 7.0 requires this.
|
65
|
+
rescue LoadError
|
66
|
+
# no-op...
|
67
|
+
end
|
68
|
+
|
69
|
+
endpoint_name.camelize.constantize.preload!
|
62
70
|
end
|
63
71
|
end
|
64
72
|
end
|
data/lib/artemis/test_helper.rb
CHANGED
@@ -4,6 +4,7 @@ require 'erb'
|
|
4
4
|
require 'yaml'
|
5
5
|
|
6
6
|
require 'active_support/core_ext/module/attribute_accessors'
|
7
|
+
require 'active_support/core_ext/string/inflections'
|
7
8
|
|
8
9
|
require 'artemis/exceptions'
|
9
10
|
|
@@ -110,7 +111,7 @@ module Artemis
|
|
110
111
|
|
111
112
|
def find_fixture_set
|
112
113
|
fixture_set = fixture_sets
|
113
|
-
.detect { |fixture| %r{#{service_name.
|
114
|
+
.detect { |fixture| %r{#{service_name.underscore}/#{query_name}\.(yml|json)\z} =~ fixture.path }
|
114
115
|
fixture_set ||= fixture_sets.detect { |fixture| fixture.name == query_name.to_s }
|
115
116
|
|
116
117
|
if fixture_set.nil?
|
data/lib/artemis/version.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
mutation<%= arguments.present? && "(#{ arguments.map {|name, type| "$#{name}: #{type.type}" }.join(", ") })" %> {
|
1
|
+
mutation<%= arguments.present? && "(#{ arguments.map {|name, type| "$#{name}: #{type.type.to_type_signature}" }.join(", ") })" %> {
|
2
2
|
<%= target_mutation.name %><%= arguments.present? && "(#{ arguments.map {|name, type| "#{name}: $#{name}" }.join(", ") })" %> {
|
3
3
|
# Add fields here...
|
4
4
|
}
|
@@ -14,6 +14,10 @@ class Artemis::QueryGenerator < Rails::Generators::Base
|
|
14
14
|
template "query.graphql", graphql_file_path
|
15
15
|
end
|
16
16
|
|
17
|
+
# def generate_text_fixture_file
|
18
|
+
# template "fixture.yml", text_fixture_path
|
19
|
+
# end
|
20
|
+
|
17
21
|
private
|
18
22
|
|
19
23
|
def query_name
|
@@ -21,7 +25,11 @@ class Artemis::QueryGenerator < Rails::Generators::Base
|
|
21
25
|
end
|
22
26
|
|
23
27
|
def graphql_file_path
|
24
|
-
"app/operations/#{service_name.underscore}/#{
|
28
|
+
"app/operations/#{service_name.underscore}/#{qualified_name}.graphql"
|
29
|
+
end
|
30
|
+
|
31
|
+
def text_fixture_path
|
32
|
+
File.join(Artemis::Railtie.config.artemis.fixture_path, service_name.underscore, "#{qualified_name}.yml")
|
25
33
|
end
|
26
34
|
|
27
35
|
def arguments
|
@@ -29,7 +37,7 @@ class Artemis::QueryGenerator < Rails::Generators::Base
|
|
29
37
|
end
|
30
38
|
|
31
39
|
def target_query
|
32
|
-
schema.
|
40
|
+
schema.query.fields[query_type] ||
|
33
41
|
raise(GraphQL::Schema::Finder::MemberNotFoundError, "Could not find type `#{query_type}` in schema.")
|
34
42
|
end
|
35
43
|
|
@@ -49,4 +57,8 @@ class Artemis::QueryGenerator < Rails::Generators::Base
|
|
49
57
|
end
|
50
58
|
end
|
51
59
|
end
|
60
|
+
|
61
|
+
def qualified_name
|
62
|
+
graphql_file_name.presence || query_name
|
63
|
+
end
|
52
64
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# You can stub GraphQL queries by calling the `stub_graphql' method in test:
|
2
|
+
#
|
3
|
+
# stub_graphql(<%= service_name.camelize %>, :<%= qualified_name.underscore %>).to_return(:<%= target_query.name %>_1)
|
4
|
+
#
|
5
|
+
# Or with a arguments matcher:
|
6
|
+
#
|
7
|
+
# stub_graphql(<%= service_name.camelize %>, :<%= qualified_name.underscore %>, <%= arguments.map {|name, _| "#{name}: \"...\"" }.join(", ") %>).to_return(:<%= target_query.name %>_2)
|
8
|
+
#
|
9
|
+
|
10
|
+
<%= target_query.name %>_1:
|
11
|
+
data:
|
12
|
+
<% target_query.type.fields.values.each do |field| -%>
|
13
|
+
<%= field.name %>: # type: <%= field.type.to_type_signature %>
|
14
|
+
<% end %>
|
15
|
+
<%= target_query.name %>_2:
|
16
|
+
data:
|
17
|
+
<% target_query.type.fields.values.each do |field| -%>
|
18
|
+
<%= field.name %>: # type: <%= field.type.to_type_signature %>
|
19
|
+
<% end %>
|
@@ -1,5 +1,5 @@
|
|
1
|
-
query<%= arguments.present?
|
2
|
-
<%= target_query.name %><%= arguments.present?
|
1
|
+
query<%= arguments.present? ? "(#{ arguments.map {|name, type| "$#{name}: #{type.type.to_type_signature}" }.join(", ") })" : "" %> {
|
2
|
+
<%= target_query.name %><%= arguments.present? ? "(#{ arguments.map {|name, type| "#{name}: $#{name}" }.join(", ") })" : "" %> {
|
3
3
|
# Add fields here...
|
4
4
|
}
|
5
5
|
}
|
data/spec/adapters_spec.rb
CHANGED
@@ -1,28 +1,59 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'rack'
|
3
|
+
require 'webrick'
|
3
4
|
|
4
5
|
describe 'Adapters' do
|
5
6
|
FakeServer = ->(env) {
|
6
7
|
case env['PATH_INFO']
|
7
8
|
when '/slow_server'
|
8
|
-
sleep
|
9
|
+
sleep 2.1
|
9
10
|
|
10
11
|
[200, {}, ['{}']]
|
11
12
|
when '/500'
|
12
13
|
[500, {}, ['Server error']]
|
13
|
-
|
14
|
+
when '/test_multi_domain'
|
14
15
|
body = {
|
15
16
|
data: {
|
16
|
-
body:
|
17
|
+
body: "Endpoint switched.",
|
17
18
|
headers: env.select {|key, val| key.match("^HTTP.*|^CONTENT.*|^AUTHORIZATION.*") }
|
18
|
-
|
19
|
-
|
19
|
+
.collect {|key, val| [key.gsub(/^HTTP_/, ''), val.downcase] }
|
20
|
+
.to_h,
|
20
21
|
},
|
21
22
|
errors: [],
|
22
23
|
extensions: {}
|
23
24
|
}.to_json
|
24
25
|
|
25
26
|
[200, {}, [body]]
|
27
|
+
else
|
28
|
+
request_body = JSON.parse(env['rack.input'].read)
|
29
|
+
|
30
|
+
response_body = if request_body['_json']
|
31
|
+
request_body['_json'].map do |query|
|
32
|
+
{
|
33
|
+
data: {
|
34
|
+
body: query,
|
35
|
+
headers: env.select {|key, val| key.match("^HTTP.*|^CONTENT.*|^AUTHORIZATION.*") }
|
36
|
+
.collect {|key, val| [key.gsub(/^HTTP_/, ''), val.downcase] }
|
37
|
+
.to_h,
|
38
|
+
},
|
39
|
+
errors: [],
|
40
|
+
extensions: {}
|
41
|
+
}
|
42
|
+
end.to_json
|
43
|
+
else
|
44
|
+
{
|
45
|
+
data: {
|
46
|
+
body: request_body,
|
47
|
+
headers: env.select {|key, val| key.match("^HTTP.*|^CONTENT.*|^AUTHORIZATION.*") }
|
48
|
+
.collect {|key, val| [key.gsub(/^HTTP_/, ''), val.downcase] }
|
49
|
+
.to_h,
|
50
|
+
},
|
51
|
+
errors: [],
|
52
|
+
extensions: {}
|
53
|
+
}.to_json
|
54
|
+
end
|
55
|
+
|
56
|
+
[200, {}, [response_body]]
|
26
57
|
end
|
27
58
|
}
|
28
59
|
|
@@ -91,6 +122,51 @@ describe 'Adapters' do
|
|
91
122
|
end.to raise_error(timeout_error)
|
92
123
|
end
|
93
124
|
end
|
125
|
+
|
126
|
+
describe '#multiplex' do
|
127
|
+
it 'makes an HTTP request with multiple queries' do
|
128
|
+
response = adapter.multiplex(
|
129
|
+
[
|
130
|
+
{
|
131
|
+
query: GraphQL::Client::IntrospectionDocument.to_query_string,
|
132
|
+
operationName: 'IntrospectionQuery',
|
133
|
+
variables: {
|
134
|
+
id: 'yayoi-kusama'
|
135
|
+
},
|
136
|
+
},
|
137
|
+
],
|
138
|
+
context: {
|
139
|
+
user_id: 1
|
140
|
+
}
|
141
|
+
)
|
142
|
+
|
143
|
+
introspection_query = response[0]
|
144
|
+
|
145
|
+
expect(introspection_query['data']['body']['query']).to eq(GraphQL::Client::IntrospectionDocument.to_query_string)
|
146
|
+
expect(introspection_query['data']['body']['variables']).to eq('id' => 'yayoi-kusama')
|
147
|
+
expect(introspection_query['data']['body']['operationName']).to eq('IntrospectionQuery')
|
148
|
+
expect(introspection_query['data']['headers']['CONTENT_TYPE']).to eq('application/json')
|
149
|
+
expect(introspection_query['data']['headers']['ACCEPT']).to eq('application/json')
|
150
|
+
expect(introspection_query['errors']).to eq([])
|
151
|
+
expect(introspection_query['extensions']).to eq({})
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'raises an error when it receives a server error' do
|
155
|
+
adapter.uri = URI.parse('http://localhost:8000/500')
|
156
|
+
|
157
|
+
expect do
|
158
|
+
adapter.multiplex([])
|
159
|
+
end.to raise_error(Artemis::GraphQLServerError, "Received server error status 500: Server error")
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'allows for overriding timeout' do
|
163
|
+
adapter.uri = URI.parse('http://localhost:8000/slow_server')
|
164
|
+
|
165
|
+
expect do
|
166
|
+
adapter.multiplex([])
|
167
|
+
end.to raise_error(timeout_error)
|
168
|
+
end
|
169
|
+
end
|
94
170
|
end
|
95
171
|
|
96
172
|
describe Artemis::Adapters::NetHttpAdapter do
|
@@ -102,15 +178,94 @@ describe 'Adapters' do
|
|
102
178
|
|
103
179
|
describe Artemis::Adapters::NetHttpPersistentAdapter do
|
104
180
|
let(:adapter) { Artemis::Adapters::NetHttpPersistentAdapter.new('http://localhost:8000', service_name: nil, timeout: 0.5, pool_size: 5) }
|
105
|
-
let(:timeout_error) { Net::
|
181
|
+
let(:timeout_error) { Net::ReadTimeout }
|
106
182
|
|
107
183
|
it_behaves_like 'an adapter'
|
108
184
|
end
|
109
185
|
|
110
|
-
describe Artemis::Adapters::
|
111
|
-
let(:adapter) { Artemis::Adapters::
|
112
|
-
let(:timeout_error) { Curl::Err::TimeoutError }
|
186
|
+
describe Artemis::Adapters::MultiDomainAdapter do
|
187
|
+
let(:adapter) { Artemis::Adapters::MultiDomainAdapter.new('ignored', service_name: nil, timeout: 0.5, pool_size: 5, adapter_options: { adapter: :net_http }) }
|
113
188
|
|
114
|
-
|
189
|
+
it 'makes an actual HTTP request' do
|
190
|
+
response = adapter.execute(document: GraphQL::Client::IntrospectionDocument, context: { url: 'http://localhost:8000/test_multi_domain' })
|
191
|
+
|
192
|
+
expect(response['data']['body']).to eq("Endpoint switched.")
|
193
|
+
expect(response['errors']).to eq([])
|
194
|
+
expect(response['extensions']).to eq({})
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'can make a multiplex (the graphql feature, not HTTP/2) request' do
|
198
|
+
response = adapter.multiplex(
|
199
|
+
[
|
200
|
+
{
|
201
|
+
query: GraphQL::Client::IntrospectionDocument.to_query_string,
|
202
|
+
operationName: 'IntrospectionQuery',
|
203
|
+
variables: {
|
204
|
+
id: 'yayoi-kusama'
|
205
|
+
},
|
206
|
+
},
|
207
|
+
],
|
208
|
+
context: {
|
209
|
+
url: 'http://localhost:8000/test_multi_domain'
|
210
|
+
}
|
211
|
+
)
|
212
|
+
|
213
|
+
expect(response['data']['body']).to eq("Endpoint switched.")
|
214
|
+
expect(response['errors']).to eq([])
|
215
|
+
expect(response['extensions']).to eq({})
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'can make a multiplex request with custom HTTP headers' do
|
219
|
+
response = adapter.multiplex(
|
220
|
+
[
|
221
|
+
{
|
222
|
+
query: GraphQL::Client::IntrospectionDocument.to_query_string,
|
223
|
+
operationName: 'IntrospectionQuery',
|
224
|
+
},
|
225
|
+
],
|
226
|
+
context: {
|
227
|
+
headers: {
|
228
|
+
Authorization: "Token token",
|
229
|
+
},
|
230
|
+
url: 'http://localhost:8000/test_multi_domain'
|
231
|
+
}
|
232
|
+
)
|
233
|
+
|
234
|
+
expect(response['data']['headers']['AUTHORIZATION']).to eq("token token")
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'raises an error when adapter_options.adapter is set to :multi domain' do
|
238
|
+
expect do
|
239
|
+
Artemis::Adapters::MultiDomainAdapter.new('ignored', service_name: nil, timeout: 0.5, pool_size: 5, adapter_options: { adapter: :multi_domain })
|
240
|
+
end.to raise_error(ArgumentError, 'You can not use the :multi_domain adapter with the :multi_domain adapter.')
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'raises an error when context.url is not specified' do
|
244
|
+
expect do
|
245
|
+
adapter.execute(document: GraphQL::Client::IntrospectionDocument)
|
246
|
+
end.to raise_error(ArgumentError, 'The MultiDomain adapter requires a url on every request. Please specify a ' \
|
247
|
+
'url with a context: Client.with_context(url: "https://awesomeshop.domain.conm")')
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'raises an error when it receives a server error' do
|
251
|
+
expect do
|
252
|
+
adapter.execute(document: GraphQL::Client::IntrospectionDocument, context: { url: 'http://localhost:8000/500' })
|
253
|
+
end.to raise_error(Artemis::GraphQLServerError, "Received server error status 500: Server error")
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'allows for overriding timeout' do
|
257
|
+
expect do
|
258
|
+
adapter.execute(document: GraphQL::Client::IntrospectionDocument, context: { url: 'http://localhost:8000/slow_server' })
|
259
|
+
end.to raise_error(Net::ReadTimeout)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
if RUBY_ENGINE == 'ruby'
|
264
|
+
describe Artemis::Adapters::CurbAdapter do
|
265
|
+
let(:adapter) { Artemis::Adapters::CurbAdapter.new('http://localhost:8000', service_name: nil, timeout: 2, pool_size: 5) }
|
266
|
+
let(:timeout_error) { Curl::Err::TimeoutError }
|
267
|
+
|
268
|
+
it_behaves_like 'an adapter'
|
269
|
+
end
|
115
270
|
end
|
116
271
|
end
|
data/spec/autoloading_spec.rb
CHANGED
@@ -15,7 +15,7 @@ describe "#{GraphQL::Client} Autoloading" do
|
|
15
15
|
|
16
16
|
describe ".preload!" do
|
17
17
|
it "preloads all the graphQL files in the query paths" do
|
18
|
-
%i(Artist Artwork
|
18
|
+
%i(Artist Artists Artwork ArtistFields)
|
19
19
|
.select {|const_name| Metaphysics.constants.include?(const_name) }
|
20
20
|
.each {|const_name| Metaphysics.send(:remove_const, const_name) }
|
21
21
|
|
@@ -43,12 +43,12 @@ describe "#{GraphQL::Client} Autoloading" do
|
|
43
43
|
end
|
44
44
|
|
45
45
|
it "dynamically loads the matching GraphQL fragment and sets it to a constant" do
|
46
|
-
Metaphysics.send(:remove_const, :
|
46
|
+
Metaphysics.send(:remove_const, :ArtistFields) if Metaphysics.constants.include?(:ArtistFields)
|
47
47
|
|
48
|
-
query = Metaphysics::
|
48
|
+
query = Metaphysics::ArtistFields
|
49
49
|
|
50
50
|
expect(query.document.to_query_string).to eq(<<~GRAPHQL.strip)
|
51
|
-
fragment
|
51
|
+
fragment Metaphysics__ArtistFields on Artist {
|
52
52
|
hometown
|
53
53
|
deathday
|
54
54
|
}
|
data/spec/client_spec.rb
CHANGED
@@ -86,11 +86,11 @@ describe GraphQL::Client do
|
|
86
86
|
name
|
87
87
|
bio
|
88
88
|
birthday
|
89
|
-
...
|
89
|
+
...Metaphysics__ArtistFields
|
90
90
|
}
|
91
91
|
}
|
92
92
|
|
93
|
-
fragment
|
93
|
+
fragment Metaphysics__ArtistFields on Artist {
|
94
94
|
hometown
|
95
95
|
deathday
|
96
96
|
}
|
@@ -171,6 +171,42 @@ describe GraphQL::Client do
|
|
171
171
|
end
|
172
172
|
end
|
173
173
|
|
174
|
+
it "can batch multiple requests using Multiplex" do
|
175
|
+
responses = Metaphysics.multiplex do |queue|
|
176
|
+
queue.artist(id: "yayoi-kusama", context: { headers: { Authorization: 'bearer ...' } })
|
177
|
+
queue.artwork
|
178
|
+
end
|
179
|
+
|
180
|
+
artist_query, artwork_query = requests[0].queries
|
181
|
+
|
182
|
+
expect(artist_query[:operationName]).to eq('Metaphysics__Artist')
|
183
|
+
expect(artist_query[:variables]).to eq('id' => 'yayoi-kusama')
|
184
|
+
expect(artist_query[:context]).to eq({ headers: { Authorization: 'bearer ...' } })
|
185
|
+
expect(artist_query[:query]).to eq(<<~GRAPHQL.strip)
|
186
|
+
query Metaphysics__Artist($id: String!) {
|
187
|
+
artist(id: $id) {
|
188
|
+
name
|
189
|
+
bio
|
190
|
+
birthday
|
191
|
+
}
|
192
|
+
}
|
193
|
+
GRAPHQL
|
194
|
+
|
195
|
+
expect(artwork_query[:operationName]).to eq('Metaphysics__Artwork')
|
196
|
+
expect(artwork_query[:variables]).to be_empty
|
197
|
+
expect(artwork_query[:context]).to eq({})
|
198
|
+
expect(artwork_query[:query]).to eq(<<~GRAPHQL.strip)
|
199
|
+
query Metaphysics__Artwork {
|
200
|
+
artwork(id: "yayoi-kusama-pumpkin-yellow-and-black") {
|
201
|
+
title
|
202
|
+
artist {
|
203
|
+
name
|
204
|
+
}
|
205
|
+
}
|
206
|
+
}
|
207
|
+
GRAPHQL
|
208
|
+
end
|
209
|
+
|
174
210
|
private
|
175
211
|
|
176
212
|
def requests
|
File without changes
|
File without changes
|
data/spec/test_helper_spec.rb
CHANGED
@@ -70,8 +70,17 @@ describe Artemis::TestHelper do
|
|
70
70
|
expect(yoshiki.data.artist.name).to eq("Artist Yoshiki")
|
71
71
|
end
|
72
72
|
|
73
|
+
it "can mock separate GraphQL queries with the same arguments" do
|
74
|
+
stub_graphql("SpotifyClient", :artist, id: "yoshiki").to_return(:yoshiki)
|
75
|
+
stub_graphql(Metaphysics, :artist, id: "yoshiki").to_return(:yoshiki)
|
76
|
+
|
77
|
+
yoshiki = Metaphysics.artist(id: "yoshiki")
|
78
|
+
|
79
|
+
expect(yoshiki.data.artist.name).to eq("Artist Yoshiki")
|
80
|
+
end
|
81
|
+
|
73
82
|
it "allows to get raw fixture data as a Hash" do
|
74
|
-
data = stub_graphql("
|
83
|
+
data = stub_graphql("SpotifyClient", :artist).get(:yoshiki)
|
75
84
|
|
76
85
|
expect(data).to eq({
|
77
86
|
"data" => {
|