graphql 1.9.6 → 1.9.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +5 -5
  2. data/lib/generators/graphql/install_generator.rb +2 -1
  3. data/lib/generators/graphql/templates/base_field.erb +7 -0
  4. data/lib/graphql/analysis/ast/visitor.rb +5 -2
  5. data/lib/graphql/execution/multiplex.rb +5 -1
  6. data/lib/graphql/query.rb +6 -1
  7. data/lib/graphql/relay/array_connection.rb +1 -1
  8. data/lib/graphql/schema.rb +15 -1
  9. data/lib/graphql/schema/argument.rb +5 -1
  10. data/lib/graphql/schema/input_object.rb +21 -12
  11. data/lib/graphql/schema/introspection_system.rb +6 -1
  12. data/lib/graphql/schema/member/has_arguments.rb +4 -2
  13. data/lib/graphql/schema/resolver.rb +8 -3
  14. data/lib/graphql/schema/subscription.rb +22 -0
  15. data/lib/graphql/schema/timeout.rb +109 -0
  16. data/lib/graphql/types/relay/base_edge.rb +0 -3
  17. data/lib/graphql/upgrader/member.rb +148 -111
  18. data/lib/graphql/version.rb +1 -1
  19. data/readme.md +1 -1
  20. data/spec/fixtures/upgrader/mutation.original.rb +28 -0
  21. data/spec/fixtures/upgrader/mutation.transformed.rb +28 -0
  22. data/spec/graphql/analysis/ast_spec.rb +27 -0
  23. data/spec/graphql/execution/instrumentation_spec.rb +34 -6
  24. data/spec/graphql/execution/multiplex_spec.rb +11 -0
  25. data/spec/graphql/internal_representation/rewrite_spec.rb +6 -1
  26. data/spec/graphql/schema/input_object_spec.rb +56 -7
  27. data/spec/graphql/schema/introspection_system_spec.rb +24 -0
  28. data/spec/graphql/schema/subscription_spec.rb +65 -0
  29. data/spec/graphql/schema/timeout_spec.rb +206 -0
  30. data/spec/integration/mongoid/star_trek/schema.rb +1 -2
  31. data/spec/integration/rails/graphql/input_object_spec.rb +19 -0
  32. data/spec/integration/rails/graphql/relay/array_connection_spec.rb +47 -28
  33. data/spec/integration/rails/graphql/schema_spec.rb +18 -0
  34. data/spec/integration/tmp/app/graphql/types/date_type.rb +14 -0
  35. data/spec/integration/tmp/dummy/Gemfile +50 -0
  36. data/spec/integration/tmp/dummy/README.md +24 -0
  37. data/spec/integration/tmp/dummy/Rakefile +6 -0
  38. data/spec/integration/tmp/dummy/app/assets/config/manifest.js +3 -0
  39. data/spec/integration/tmp/dummy/app/assets/javascripts/application.js +16 -0
  40. data/spec/integration/tmp/dummy/app/assets/javascripts/cable.js +13 -0
  41. data/spec/integration/tmp/dummy/app/assets/stylesheets/application.css +15 -0
  42. data/spec/integration/tmp/dummy/app/channels/application_cable/channel.rb +5 -0
  43. data/spec/integration/tmp/dummy/app/channels/application_cable/connection.rb +5 -0
  44. data/spec/integration/tmp/dummy/app/controllers/application_controller.rb +4 -0
  45. data/spec/integration/tmp/dummy/app/controllers/graphql_controller.rb +44 -0
  46. data/spec/integration/tmp/dummy/app/helpers/application_helper.rb +3 -0
  47. data/spec/integration/tmp/dummy/app/jobs/application_job.rb +3 -0
  48. data/spec/integration/tmp/dummy/app/mailers/application_mailer.rb +5 -0
  49. data/spec/integration/tmp/dummy/app/mydirectory/dummy_schema.rb +5 -0
  50. data/spec/integration/tmp/dummy/app/mydirectory/mutations/update_name.rb +15 -0
  51. data/spec/integration/tmp/dummy/app/mydirectory/types/base_enum.rb +5 -0
  52. data/spec/integration/tmp/dummy/app/mydirectory/types/base_input_object.rb +5 -0
  53. data/spec/integration/tmp/dummy/app/mydirectory/types/base_interface.rb +6 -0
  54. data/spec/integration/tmp/dummy/app/mydirectory/types/base_object.rb +5 -0
  55. data/spec/integration/tmp/dummy/app/mydirectory/types/base_scalar.rb +5 -0
  56. data/spec/integration/tmp/dummy/app/mydirectory/types/base_union.rb +5 -0
  57. data/spec/integration/tmp/dummy/app/mydirectory/types/mutation_type.rb +12 -0
  58. data/spec/integration/tmp/dummy/app/mydirectory/types/query_type.rb +14 -0
  59. data/spec/integration/tmp/dummy/app/views/layouts/application.html.erb +14 -0
  60. data/spec/integration/tmp/dummy/app/views/layouts/mailer.html.erb +13 -0
  61. data/spec/integration/tmp/dummy/app/views/layouts/mailer.text.erb +1 -0
  62. data/spec/integration/tmp/dummy/bin/bundle +3 -0
  63. data/spec/integration/tmp/dummy/bin/rails +4 -0
  64. data/spec/integration/tmp/dummy/bin/rake +4 -0
  65. data/spec/integration/tmp/dummy/bin/setup +34 -0
  66. data/spec/integration/tmp/dummy/bin/update +29 -0
  67. data/spec/integration/tmp/dummy/config.ru +5 -0
  68. data/spec/integration/tmp/dummy/config/application.rb +26 -0
  69. data/spec/integration/tmp/dummy/config/boot.rb +4 -0
  70. data/spec/integration/tmp/dummy/config/cable.yml +9 -0
  71. data/spec/integration/tmp/dummy/config/environment.rb +6 -0
  72. data/spec/integration/tmp/dummy/config/environments/development.rb +52 -0
  73. data/spec/integration/tmp/dummy/config/environments/production.rb +84 -0
  74. data/spec/integration/tmp/dummy/config/environments/test.rb +43 -0
  75. data/spec/integration/tmp/dummy/config/initializers/application_controller_renderer.rb +9 -0
  76. data/spec/integration/tmp/dummy/config/initializers/assets.rb +12 -0
  77. data/spec/integration/tmp/dummy/config/initializers/backtrace_silencers.rb +8 -0
  78. data/spec/integration/tmp/dummy/config/initializers/cookies_serializer.rb +6 -0
  79. data/spec/integration/tmp/dummy/config/initializers/filter_parameter_logging.rb +5 -0
  80. data/spec/integration/tmp/dummy/config/initializers/inflections.rb +17 -0
  81. data/spec/integration/tmp/dummy/config/initializers/mime_types.rb +5 -0
  82. data/spec/integration/tmp/dummy/config/initializers/new_framework_defaults.rb +24 -0
  83. data/spec/integration/tmp/dummy/config/initializers/session_store.rb +4 -0
  84. data/spec/integration/tmp/dummy/config/initializers/wrap_parameters.rb +10 -0
  85. data/spec/integration/tmp/dummy/config/locales/en.yml +23 -0
  86. data/spec/integration/tmp/dummy/config/puma.rb +48 -0
  87. data/spec/integration/tmp/dummy/config/routes.rb +9 -0
  88. data/spec/integration/tmp/dummy/config/secrets.yml +22 -0
  89. data/spec/integration/tmp/dummy/db/seeds.rb +8 -0
  90. data/spec/integration/tmp/dummy/log/test.log +0 -0
  91. data/spec/integration/tmp/dummy/public/404.html +67 -0
  92. data/spec/integration/tmp/dummy/public/422.html +67 -0
  93. data/spec/integration/tmp/dummy/public/500.html +66 -0
  94. data/spec/integration/tmp/dummy/public/apple-touch-icon-precomposed.png +0 -0
  95. data/spec/integration/tmp/dummy/public/apple-touch-icon.png +0 -0
  96. data/spec/integration/tmp/dummy/public/favicon.ico +0 -0
  97. data/spec/integration/tmp/dummy/public/robots.txt +5 -0
  98. data/spec/integration/tmp/dummy/test/test_helper.rb +8 -0
  99. data/spec/support/jazz.rb +6 -0
  100. data/spec/support/star_wars/schema.rb +1 -2
  101. metadata +171 -6
  102. data/spec/integration/tmp/app/graphql/types/bird_type.rb +0 -7
@@ -32,9 +32,6 @@ module GraphQL
32
32
  def node_type(node_type = nil, null: true)
33
33
  if node_type
34
34
  @node_type = node_type
35
- wrapped_type_name = node_type.graphql_name
36
- # Set this to be named like the node type, but suffixed with `Edge`
37
- graphql_name("#{wrapped_type_name}Edge")
38
35
  # Add a default `node` field
39
36
  field :node, node_type, null: null, description: "The item at the end of the edge."
40
37
  end
@@ -125,7 +125,7 @@ module GraphQL
125
125
  end
126
126
 
127
127
  def apply(input_text)
128
- input_text.sub(@find_pattern, @replace_pattern)
128
+ input_text.gsub(@find_pattern, @replace_pattern)
129
129
  end
130
130
  end
131
131
 
@@ -147,10 +147,10 @@ module GraphQL
147
147
  name = matches[:overridden_name]
148
148
  if type_name_without_the_type_part != name
149
149
  # If the overridden name is still required, use `graphql_name` for it
150
- transformable = transformable.sub(/ name (.*)/, ' graphql_name \1')
150
+ transformable = transformable.gsub(/ name (.*)/, ' graphql_name \1')
151
151
  else
152
152
  # Otherwise, remove it altogether
153
- transformable = transformable.sub(/\s+name ('|").*('|")/, '')
153
+ transformable = transformable.gsub(/\s+name ('|").*('|")/, '')
154
154
  end
155
155
  end
156
156
  end
@@ -285,43 +285,57 @@ module GraphQL
285
285
  def apply(input_text)
286
286
  if input_text =~ @proc_check_pattern
287
287
  processor = apply_processor(input_text, NamedProcProcessor.new(@proc_name))
288
- proc_body = input_text[processor.proc_body_start..processor.proc_body_end]
289
- method_defn_indent = " " * processor.proc_defn_indent
290
- method_defn = "def self.#{@proc_name}(#{processor.proc_arg_names.join(", ")})\n#{method_defn_indent} #{proc_body}\n#{method_defn_indent}end\n"
291
- method_defn = trim_lines(method_defn)
292
- # replace the proc with the new method
293
- input_text[processor.proc_defn_start..processor.proc_defn_end] = method_defn
288
+ processor.proc_to_method_sections.reverse.each do |proc_to_method_section|
289
+ proc_body = input_text[proc_to_method_section.proc_body_start..proc_to_method_section.proc_body_end]
290
+ method_defn_indent = " " * proc_to_method_section.proc_defn_indent
291
+ method_defn = "def self.#{@proc_name}(#{proc_to_method_section.proc_arg_names.join(", ")})\n#{method_defn_indent} #{proc_body}\n#{method_defn_indent}end\n"
292
+ method_defn = trim_lines(method_defn)
293
+ # replace the proc with the new method
294
+ input_text[proc_to_method_section.proc_defn_start..proc_to_method_section.proc_defn_end] = method_defn
295
+ end
294
296
  end
295
297
  input_text
296
298
  end
297
299
 
298
300
  class NamedProcProcessor < Parser::AST::Processor
299
- attr_reader :proc_arg_names, :proc_defn_start, :proc_defn_end, :proc_defn_indent, :proc_body_start, :proc_body_end
301
+ attr_reader :proc_to_method_sections
300
302
  def initialize(proc_name)
301
303
  @proc_name_sym = proc_name.to_sym
302
- @proc_arg_names = nil
303
- # Beginning of the `#{proc_name} -> {...}` call
304
- @proc_defn_start = nil
305
- # End of the last `end/}`
306
- @proc_defn_end = nil
307
- # Amount of whitespace to insert to the rewritten body
308
- @proc_defn_indent = nil
309
- # First statement of the proc
310
- @proc_body_start = nil
311
- # End of last statement in the proc
312
- @proc_body_end = nil
313
- # Used for identifying the proper block
314
- @inside_proc = false
304
+ @proc_to_method_sections = []
305
+ end
306
+
307
+ class ProcToMethodSection
308
+ attr_accessor :proc_arg_names, :proc_defn_start, :proc_defn_end, :proc_defn_indent, :proc_body_start, :proc_body_end, :inside_proc
309
+
310
+ def initialize
311
+ # @proc_name_sym = proc_name.to_sym
312
+ @proc_arg_names = nil
313
+ # Beginning of the `#{proc_name} -> {...}` call
314
+ @proc_defn_start = nil
315
+ # End of the last `end/}`
316
+ @proc_defn_end = nil
317
+ # Amount of whitespace to insert to the rewritten body
318
+ @proc_defn_indent = nil
319
+ # First statement of the proc
320
+ @proc_body_start = nil
321
+ # End of last statement in the proc
322
+ @proc_body_end = nil
323
+ # Used for identifying the proper block
324
+ @inside_proc = false
325
+ end
315
326
  end
316
327
 
317
328
  def on_send(node)
318
329
  receiver, method_name, _args = *node
319
330
  if method_name == @proc_name_sym && receiver.nil?
331
+ proc_section = ProcToMethodSection.new
320
332
  source_exp = node.loc.expression
321
- @proc_defn_start = source_exp.begin.begin_pos
322
- @proc_defn_end = source_exp.end.end_pos
323
- @proc_defn_indent = source_exp.column
324
- @inside_proc = true
333
+ proc_section.proc_defn_start = source_exp.begin.begin_pos
334
+ proc_section.proc_defn_end = source_exp.end.end_pos
335
+ proc_section.proc_defn_indent = source_exp.column
336
+ proc_section.inside_proc = true
337
+
338
+ @proc_to_method_sections << proc_section
325
339
  end
326
340
  res = super(node)
327
341
  @inside_proc = false
@@ -331,11 +345,14 @@ module GraphQL
331
345
  def on_block(node)
332
346
  send_node, args_node, body_node = node.children
333
347
  _receiver, method_name, _send_args_node = *send_node
334
- if method_name == :lambda && @inside_proc
348
+ if method_name == :lambda && !@proc_to_method_sections.empty? && @proc_to_method_sections[-1].inside_proc
349
+ proc_to_method_section = @proc_to_method_sections[-1]
350
+
335
351
  source_exp = body_node.loc.expression
336
- @proc_arg_names = args_node.children.map { |arg_node| arg_node.children[0].to_s }
337
- @proc_body_start = source_exp.begin.begin_pos
338
- @proc_body_end = source_exp.end.end_pos
352
+ proc_to_method_section.proc_arg_names = args_node.children.map { |arg_node| arg_node.children[0].to_s }
353
+ proc_to_method_section.proc_body_start = source_exp.begin.begin_pos
354
+ proc_to_method_section.proc_body_end = source_exp.end.end_pos
355
+ proc_to_method_section.inside_proc = false
339
356
  end
340
357
  super(node)
341
358
  end
@@ -353,30 +370,34 @@ module GraphQL
353
370
  if input_text =~ /GraphQL::Relay::Mutation\.define/
354
371
  named_proc_processor = apply_processor(input_text, ProcToClassMethodTransform::NamedProcProcessor.new(@proc_name))
355
372
  resolve_proc_processor = apply_processor(input_text, ResolveProcToMethodTransform::ResolveProcProcessor.new)
356
- proc_body = input_text[named_proc_processor.proc_body_start..named_proc_processor.proc_body_end]
357
- method_defn_indent = " " * named_proc_processor.proc_defn_indent
358
-
359
- obj_arg_name, args_arg_name, ctx_arg_name = resolve_proc_processor.proc_arg_names
360
- # This is not good, it will hit false positives
361
- # Should use AST to make this substitution
362
- if obj_arg_name != "_"
363
- proc_body.gsub!(/([^\w:.]|^)#{obj_arg_name}([^\w:]|$)/, '\1object\2')
364
- end
365
- if ctx_arg_name != "_"
366
- proc_body.gsub!(/([^\w:.]|^)#{ctx_arg_name}([^\w:]|$)/, '\1context\2')
367
- end
368
373
 
369
- method_defn = "def #{@proc_name}(**#{args_arg_name})\n#{method_defn_indent} #{proc_body}\n#{method_defn_indent}end\n"
370
- method_defn = trim_lines(method_defn)
371
- # Update usage of args keys
372
- method_defn = method_defn.gsub(/#{args_arg_name}(?<method_begin>\.key\?\(?|\[)["':](?<arg_name>[a-zA-Z0-9_]+)["']?(?<method_end>\]|\))?/) do
373
- method_begin = $~[:method_begin]
374
- arg_name = underscorize($~[:arg_name])
375
- method_end = $~[:method_end]
376
- "#{args_arg_name}#{method_begin}:#{arg_name}#{method_end}"
374
+ named_proc_processor.proc_to_method_sections.zip(resolve_proc_processor.resolve_proc_sections).reverse.each do |pair|
375
+ proc_to_method_section, resolve_proc_section = *pair
376
+ proc_body = input_text[proc_to_method_section.proc_body_start..proc_to_method_section.proc_body_end]
377
+ method_defn_indent = " " * proc_to_method_section.proc_defn_indent
378
+
379
+ obj_arg_name, args_arg_name, ctx_arg_name = resolve_proc_section.proc_arg_names
380
+ # This is not good, it will hit false positives
381
+ # Should use AST to make this substitution
382
+ if obj_arg_name != "_"
383
+ proc_body.gsub!(/([^\w:.]|^)#{obj_arg_name}([^\w:]|$)/, '\1object\2')
384
+ end
385
+ if ctx_arg_name != "_"
386
+ proc_body.gsub!(/([^\w:.]|^)#{ctx_arg_name}([^\w:]|$)/, '\1context\2')
387
+ end
388
+
389
+ method_defn = "def #{@proc_name}(**#{args_arg_name})\n#{method_defn_indent} #{proc_body}\n#{method_defn_indent}end\n"
390
+ method_defn = trim_lines(method_defn)
391
+ # Update usage of args keys
392
+ method_defn = method_defn.gsub(/#{args_arg_name}(?<method_begin>\.key\?\(?|\[)["':](?<arg_name>[a-zA-Z0-9_]+)["']?(?<method_end>\]|\))?/) do
393
+ method_begin = $~[:method_begin]
394
+ arg_name = underscorize($~[:arg_name])
395
+ method_end = $~[:method_end]
396
+ "#{args_arg_name}#{method_begin}:#{arg_name}#{method_end}"
397
+ end
398
+ # replace the proc with the new method
399
+ input_text[proc_to_method_section.proc_defn_start..proc_to_method_section.proc_defn_end] = method_defn
377
400
  end
378
- # replace the proc with the new method
379
- input_text[named_proc_processor.proc_defn_start..named_proc_processor.proc_defn_end] = method_defn
380
401
  end
381
402
  input_text
382
403
  end
@@ -512,74 +533,86 @@ module GraphQL
512
533
  input_text.match(/(?<field_type>input_field|field|connection|argument) :(?<name>[a-zA-Z_0-9_]*)/)
513
534
  field_name = $~[:name]
514
535
  processor = apply_processor(input_text, ResolveProcProcessor.new)
515
- proc_body = input_text[processor.proc_start..processor.proc_end]
516
- obj_arg_name, args_arg_name, ctx_arg_name = processor.proc_arg_names
517
- # This is not good, it will hit false positives
518
- # Should use AST to make this substitution
519
- if obj_arg_name != "_"
520
- proc_body.gsub!(/([^\w:.]|^)#{obj_arg_name}([^\w:]|$)/, '\1object\2')
521
- end
522
- if ctx_arg_name != "_"
523
- proc_body.gsub!(/([^\w:.]|^)#{ctx_arg_name}([^\w:]|$)/, '\1context\2')
524
- end
525
536
 
526
- method_def_indent = " " * (processor.resolve_indent - 2)
527
- # Turn the proc body into a method body
528
- method_body = reindent_lines(proc_body, from_indent: processor.resolve_indent + 2, to_indent: processor.resolve_indent)
529
- # Add `def... end`
530
- method_def = if input_text.include?("argument ")
531
- # This field has arguments
532
- "def #{field_name}(**#{args_arg_name})"
533
- else
534
- # No field arguments, so, no method arguments
535
- "def #{field_name}"
536
- end
537
- # Wrap the body in def ... end
538
- method_body = "\n#{method_def_indent}#{method_def}\n#{method_body}\n#{method_def_indent}end\n"
539
- # Update Argument access to be underscore and symbols
540
- # Update `args[...]` and `args.key?`
541
- method_body = method_body.gsub(/#{args_arg_name}(?<method_begin>\.key\?\(?|\[)["':](?<arg_name>[a-zA-Z0-9_]+)["']?(?<method_end>\]|\))?/) do
542
- method_begin = $~[:method_begin]
543
- arg_name = underscorize($~[:arg_name])
544
- method_end = $~[:method_end]
545
- "#{args_arg_name}#{method_begin}:#{arg_name}#{method_end}"
546
- end
537
+ processor.resolve_proc_sections.reverse.each do |resolve_proc_section|
538
+ proc_body = input_text[resolve_proc_section.proc_start..resolve_proc_section.proc_end]
539
+ obj_arg_name, args_arg_name, ctx_arg_name = resolve_proc_section.proc_arg_names
540
+ # This is not good, it will hit false positives
541
+ # Should use AST to make this substitution
542
+ if obj_arg_name != "_"
543
+ proc_body.gsub!(/([^\w:.]|^)#{obj_arg_name}([^\w:]|$)/, '\1object\2')
544
+ end
545
+ if ctx_arg_name != "_"
546
+ proc_body.gsub!(/([^\w:.]|^)#{ctx_arg_name}([^\w:]|$)/, '\1context\2')
547
+ end
548
+
549
+ method_def_indent = " " * (resolve_proc_section.resolve_indent - 2)
550
+ # Turn the proc body into a method body
551
+ method_body = reindent_lines(proc_body, from_indent: resolve_proc_section.resolve_indent + 2, to_indent: resolve_proc_section.resolve_indent)
552
+ # Add `def... end`
553
+ method_def = if input_text.include?("argument ")
554
+ # This field has arguments
555
+ "def #{field_name}(**#{args_arg_name})"
556
+ else
557
+ # No field arguments, so, no method arguments
558
+ "def #{field_name}"
559
+ end
560
+ # Wrap the body in def ... end
561
+ method_body = "\n#{method_def_indent}#{method_def}\n#{method_body}\n#{method_def_indent}end\n"
562
+ # Update Argument access to be underscore and symbols
563
+ # Update `args[...]` and `args.key?`
564
+ method_body = method_body.gsub(/#{args_arg_name}(?<method_begin>\.key\?\(?|\[)["':](?<arg_name>[a-zA-Z0-9_]+)["']?(?<method_end>\]|\))?/) do
565
+ method_begin = $~[:method_begin]
566
+ arg_name = underscorize($~[:arg_name])
567
+ method_end = $~[:method_end]
568
+ "#{args_arg_name}#{method_begin}:#{arg_name}#{method_end}"
569
+ end
547
570
 
548
- # Replace the resolve proc with the method
549
- input_text[processor.resolve_start..processor.resolve_end] = ""
550
- # The replacement above might have left some preceeding whitespace,
551
- # so remove it by deleting all whitespace chars before `resolve`:
552
- preceeding_whitespace = processor.resolve_start - 1
553
- while input_text[preceeding_whitespace] == " " && preceeding_whitespace > 0
554
- input_text[preceeding_whitespace] = ""
555
- preceeding_whitespace -= 1
571
+ # Replace the resolve proc with the method
572
+ input_text[resolve_proc_section.resolve_start..resolve_proc_section.resolve_end] = ""
573
+ # The replacement above might have left some preceeding whitespace,
574
+ # so remove it by deleting all whitespace chars before `resolve`:
575
+ preceeding_whitespace = resolve_proc_section.resolve_start - 1
576
+ while input_text[preceeding_whitespace] == " " && preceeding_whitespace > 0
577
+ input_text[preceeding_whitespace] = ""
578
+ preceeding_whitespace -= 1
579
+ end
580
+ input_text += method_body
581
+ input_text
556
582
  end
557
- input_text += method_body
558
- input_text
559
- else
560
- # No resolve proc
561
- input_text
562
583
  end
584
+
585
+ input_text
563
586
  end
564
587
 
565
588
  class ResolveProcProcessor < Parser::AST::Processor
566
- attr_reader :proc_start, :proc_end, :proc_arg_names, :resolve_start, :resolve_end, :resolve_indent
589
+ attr_reader :resolve_proc_sections
567
590
  def initialize
568
- @proc_arg_names = nil
569
- @resolve_start = nil
570
- @resolve_end = nil
571
- @resolve_indent = nil
572
- @proc_start = nil
573
- @proc_end = nil
591
+ @resolve_proc_sections = []
592
+ end
593
+
594
+ class ResolveProcSection
595
+ attr_accessor :proc_start, :proc_end, :proc_arg_names, :resolve_start, :resolve_end, :resolve_indent
596
+ def initialize
597
+ @proc_arg_names = nil
598
+ @resolve_start = nil
599
+ @resolve_end = nil
600
+ @resolve_indent = nil
601
+ @proc_start = nil
602
+ @proc_end = nil
603
+ end
574
604
  end
575
605
 
576
606
  def on_send(node)
577
607
  receiver, method_name, _args = *node
578
608
  if method_name == :resolve && receiver.nil?
609
+ resolve_proc_section = ResolveProcSection.new
579
610
  source_exp = node.loc.expression
580
- @resolve_start = source_exp.begin.begin_pos
581
- @resolve_end = source_exp.end.end_pos
582
- @resolve_indent = source_exp.column
611
+ resolve_proc_section.resolve_start = source_exp.begin.begin_pos
612
+ resolve_proc_section.resolve_end = source_exp.end.end_pos
613
+ resolve_proc_section.resolve_indent = source_exp.column
614
+
615
+ @resolve_proc_sections << resolve_proc_section
583
616
  end
584
617
  super(node)
585
618
  end
@@ -588,11 +621,15 @@ module GraphQL
588
621
  send_node, args_node, body_node = node.children
589
622
  _receiver, method_name, _send_args_node = *send_node
590
623
  # Assume that the first three-argument proc we enter is the resolve
591
- if method_name == :lambda && args_node.children.size == 3 && @proc_arg_names.nil?
624
+ if (
625
+ method_name == :lambda && args_node.children.size == 3 &&
626
+ !@resolve_proc_sections.empty? && @resolve_proc_sections[-1].proc_arg_names.nil?
627
+ )
628
+ resolve_proc_section = @resolve_proc_sections[-1]
592
629
  source_exp = body_node.loc.expression
593
- @proc_arg_names = args_node.children.map { |arg_node| arg_node.children[0].to_s }
594
- @proc_start = source_exp.begin.begin_pos
595
- @proc_end = source_exp.end.end_pos
630
+ resolve_proc_section.proc_arg_names = args_node.children.map { |arg_node| arg_node.children[0].to_s }
631
+ resolve_proc_section.proc_start = source_exp.begin.begin_pos
632
+ resolve_proc_section.proc_end = source_exp.end.end_pos
596
633
  end
597
634
  super(node)
598
635
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.9.6"
3
+ VERSION = "1.9.7"
4
4
  end
data/readme.md CHANGED
@@ -33,7 +33,7 @@ $ rails generate graphql:install
33
33
 
34
34
  After this, you may need to run `bundle install` again, as by default graphiql-rails is added on installation.
35
35
 
36
- Or, see ["Getting Started"](https://graphql-ruby.org/).
36
+ Or, see ["Getting Started"](https://graphql-ruby.org/getting_started.html).
37
37
 
38
38
  ## Upgrade
39
39
 
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+ module Platform
3
+ module Mutations
4
+ Echo = GraphQL::Relay::Mutation.define do
5
+ name 'EchoMutation'
6
+
7
+ input_field :message, types.String
8
+
9
+ field :data, types.String
10
+
11
+ resolve ->(_obj, inputs, _ctx) {
12
+ { data: inputs[:message] }
13
+ }
14
+ end
15
+
16
+ Repeat = GraphQL::Relay::Mutation.define do
17
+ name 'RepeatMutation'
18
+
19
+ input_field :message, types.String
20
+
21
+ field :data, types.String
22
+
23
+ resolve ->(_obj, inputs, _ctx) {
24
+ { data: inputs[:message] }
25
+ }
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+ module Platform
3
+ module Mutations
4
+ class Echo < Mutations::BaseMutation
5
+ graphql_name 'EchoMutation'
6
+
7
+ argument :message, String, required: false
8
+
9
+ field :data, String, null: true
10
+
11
+ def resolve(**inputs)
12
+ { data: inputs[:message] }
13
+ end
14
+ end
15
+
16
+ class Repeat < Mutations::BaseMutation
17
+ graphql_name 'RepeatMutation'
18
+
19
+ argument :message, String, required: false
20
+
21
+ field :data, String, null: true
22
+
23
+ def resolve(**inputs)
24
+ { data: inputs[:message] }
25
+ end
26
+ end
27
+ end
28
+ end
@@ -71,6 +71,17 @@ describe GraphQL::Analysis::AST do
71
71
  end
72
72
  end
73
73
 
74
+ class AstArguments < GraphQL::Analysis::AST::Analyzer
75
+ def on_enter_argument(node, parent, visitor)
76
+ @argument = visitor.argument_definition
77
+ @previous_argument = visitor.previous_argument_definition
78
+ end
79
+
80
+ def result
81
+ [@argument, @previous_argument]
82
+ end
83
+ end
84
+
74
85
  describe "using the AST analysis engine" do
75
86
  let(:schema) do
76
87
  query_type = Class.new(GraphQL::Schema::Object) do
@@ -179,6 +190,22 @@ describe GraphQL::Analysis::AST do
179
190
  assert_equal "__Schema.types", prev_field.metadata[:type_class].path
180
191
  end
181
192
  end
193
+
194
+ describe "Visitor#argument_definition" do
195
+ let(:analyzers) { [AstArguments] }
196
+ let(:query) do
197
+ GraphQL::Query.new(
198
+ Dummy::Schema,
199
+ '{ searchDairy(product: [{ source: "SHEEP" }]) { ... on Cheese { id } } }'
200
+ )
201
+ end
202
+
203
+ it "it runs the analyzer" do
204
+ argument, prev_argument = reduce_result.first
205
+ assert_equal "DairyProductInput.source", argument.metadata[:type_class].path
206
+ assert_equal "Query.searchDairy.product", prev_argument.metadata[:type_class].path
207
+ end
208
+ end
182
209
  end
183
210
 
184
211
  it "calls the defined analyzers" do