artemis 0.5.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +64 -0
  3. data/.gitignore +1 -0
  4. data/Appraisals +12 -20
  5. data/CHANGELOG.md +45 -0
  6. data/Gemfile +2 -0
  7. data/README.md +71 -6
  8. data/artemis.gemspec +0 -1
  9. data/gemfiles/rails_50.gemfile +3 -1
  10. data/gemfiles/rails_51.gemfile +3 -1
  11. data/gemfiles/rails_52.gemfile +3 -1
  12. data/gemfiles/rails_60.gemfile +3 -1
  13. data/gemfiles/{rails_40.gemfile → rails_61.gemfile} +5 -3
  14. data/gemfiles/{rails_41.gemfile → rails_70.gemfile} +5 -4
  15. data/gemfiles/rails_edge.gemfile +2 -0
  16. data/lib/artemis/adapters/abstract_adapter.rb +1 -1
  17. data/lib/artemis/adapters/curb_adapter.rb +17 -7
  18. data/lib/artemis/adapters/multi_domain_adapter.rb +58 -0
  19. data/lib/artemis/adapters/net_http_adapter.rb +25 -16
  20. data/lib/artemis/adapters/net_http_persistent_adapter.rb +7 -1
  21. data/lib/artemis/adapters/test_adapter.rb +22 -3
  22. data/lib/artemis/adapters.rb +1 -0
  23. data/lib/artemis/client.rb +42 -8
  24. data/lib/artemis/graphql_endpoint.rb +11 -5
  25. data/lib/artemis/railtie.rb +16 -8
  26. data/lib/artemis/test_helper.rb +2 -1
  27. data/lib/artemis/version.rb +1 -1
  28. data/lib/generators/artemis/mutation/templates/mutation.graphql +1 -1
  29. data/lib/generators/artemis/query/query_generator.rb +14 -2
  30. data/lib/generators/artemis/query/templates/fixture.yml +19 -0
  31. data/lib/generators/artemis/query/templates/query.graphql +2 -2
  32. data/spec/adapters_spec.rb +165 -10
  33. data/spec/autoloading_spec.rb +4 -4
  34. data/spec/client_spec.rb +38 -2
  35. data/spec/fixtures/metaphysics/{_artist_fragment.graphql → _artist_fields.graphql} +0 -0
  36. data/spec/fixtures/metaphysics/artists.graphql +1 -1
  37. data/spec/fixtures/responses/{spotify → spotify_client}/artist.yml +0 -0
  38. data/spec/test_helper_spec.rb +10 -1
  39. metadata +13 -26
  40. data/.travis.yml +0 -69
  41. data/gemfiles/rails_42.gemfile +0 -12
@@ -167,17 +167,19 @@ module Artemis
167
167
  end
168
168
 
169
169
  def resolve_graphql_file_path(filename, fragment: false)
170
- namespace = name.underscore
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}/#{name.underscore}/*.graphql"] }
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, *arguments, &block)
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, *arguments, &block)
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
- private_constant :Callbacks, :Executor
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, @url, @adapter, @timeout, @schema_path, @pool_size = name.to_s, url, adapter, timeout, schema_path, pool_size
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
@@ -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
- endpoint_names = Artemis.config_for_graphql(app).keys
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.register!(endpoint_name, {
52
- schema_path: app.root.join(config.artemis.schema_path, "#{endpoint_name}.json").to_s
53
- }.merge(options.symbolize_keys))
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.config_for_graphql(app).keys.each do |endpoint_name|
61
- endpoint_name.to_s.camelize.constantize.preload!
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
@@ -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.downcase}/#{query_name}\.(yml|json)\z} =~ fixture.path }
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?
@@ -1,3 +1,3 @@
1
1
  module Artemis
2
- VERSION = "0.5.1"
2
+ VERSION = "0.7.0"
3
3
  end
@@ -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}/#{graphql_file_name.presence || query_name}.graphql"
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.find("Query").fields[query_type] ||
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? && "(#{ arguments.map {|name, type| "$#{name}: #{type.type}" }.join(", ") })" %> {
2
- <%= target_query.name %><%= arguments.present? && "(#{ arguments.map {|name, type| "#{name}: $#{name}" }.join(", ") })" %> {
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
  }
@@ -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 1.1
9
+ sleep 2.1
9
10
 
10
11
  [200, {}, ['{}']]
11
12
  when '/500'
12
13
  [500, {}, ['Server error']]
13
- else
14
+ when '/test_multi_domain'
14
15
  body = {
15
16
  data: {
16
- body: JSON.parse(env['rack.input'].read),
17
+ body: "Endpoint switched.",
17
18
  headers: env.select {|key, val| key.match("^HTTP.*|^CONTENT.*|^AUTHORIZATION.*") }
18
- .collect {|key, val| [key.gsub(/^HTTP_/, ''), val.downcase] }
19
- .to_h,
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::HTTP::Persistent::Error }
181
+ let(:timeout_error) { Net::ReadTimeout }
106
182
 
107
183
  it_behaves_like 'an adapter'
108
184
  end
109
185
 
110
- describe Artemis::Adapters::CurbAdapter do
111
- let(:adapter) { Artemis::Adapters::CurbAdapter.new('http://localhost:8000', service_name: nil, timeout: 2, pool_size: 5) }
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
- it_behaves_like 'an adapter'
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
@@ -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 ArtistFragment)
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, :ArtistFragment) if Metaphysics.constants.include?(:ArtistFragment)
46
+ Metaphysics.send(:remove_const, :ArtistFields) if Metaphysics.constants.include?(:ArtistFields)
47
47
 
48
- query = Metaphysics::ArtistFragment
48
+ query = Metaphysics::ArtistFields
49
49
 
50
50
  expect(query.document.to_query_string).to eq(<<~GRAPHQL.strip)
51
- fragment Metaphysics__ArtistFragment on Artist {
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
- ...Metaphysics__ArtistFragment
89
+ ...Metaphysics__ArtistFields
90
90
  }
91
91
  }
92
92
 
93
- fragment Metaphysics__ArtistFragment on Artist {
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
@@ -3,6 +3,6 @@ query($size: Int!) {
3
3
  name
4
4
  bio
5
5
  birthday
6
- ...Metaphysics::ArtistFragment
6
+ ...Metaphysics::ArtistFields
7
7
  }
8
8
  }
@@ -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("Spotify", :artist).get(:yoshiki)
83
+ data = stub_graphql("SpotifyClient", :artist).get(:yoshiki)
75
84
 
76
85
  expect(data).to eq({
77
86
  "data" => {