graphql 2.4.3 → 2.4.13

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.
Files changed (141) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/analyzer.rb +2 -1
  3. data/lib/graphql/analysis/visitor.rb +38 -41
  4. data/lib/graphql/analysis.rb +15 -12
  5. data/lib/graphql/autoload.rb +38 -0
  6. data/lib/graphql/backtrace/table.rb +95 -55
  7. data/lib/graphql/backtrace.rb +1 -19
  8. data/lib/graphql/current.rb +6 -1
  9. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
  10. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
  11. data/lib/graphql/dashboard/statics/dashboard.css +3 -0
  12. data/lib/graphql/dashboard/statics/dashboard.js +78 -0
  13. data/lib/graphql/dashboard/statics/header-icon.png +0 -0
  14. data/lib/graphql/dashboard/statics/icon.png +0 -0
  15. data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
  16. data/lib/graphql/dashboard/views/graphql/dashboard/traces/index.html.erb +63 -0
  17. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +60 -0
  18. data/lib/graphql/dashboard.rb +142 -0
  19. data/lib/graphql/dataloader/active_record_association_source.rb +64 -0
  20. data/lib/graphql/dataloader/active_record_source.rb +26 -0
  21. data/lib/graphql/dataloader/async_dataloader.rb +21 -9
  22. data/lib/graphql/dataloader/null_dataloader.rb +1 -1
  23. data/lib/graphql/dataloader/source.rb +3 -3
  24. data/lib/graphql/dataloader.rb +43 -14
  25. data/lib/graphql/execution/interpreter/resolve.rb +3 -3
  26. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +11 -4
  27. data/lib/graphql/execution/interpreter/runtime.rb +67 -40
  28. data/lib/graphql/execution/interpreter.rb +16 -6
  29. data/lib/graphql/execution/multiplex.rb +0 -4
  30. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  31. data/lib/graphql/invalid_name_error.rb +1 -1
  32. data/lib/graphql/invalid_null_error.rb +5 -15
  33. data/lib/graphql/language/cache.rb +13 -0
  34. data/lib/graphql/language/document_from_schema_definition.rb +8 -7
  35. data/lib/graphql/language/lexer.rb +11 -4
  36. data/lib/graphql/language/nodes.rb +3 -0
  37. data/lib/graphql/language/parser.rb +2 -2
  38. data/lib/graphql/language/printer.rb +8 -8
  39. data/lib/graphql/language/static_visitor.rb +37 -33
  40. data/lib/graphql/language/visitor.rb +59 -55
  41. data/lib/graphql/pagination/connection.rb +1 -1
  42. data/lib/graphql/query/context/scoped_context.rb +1 -1
  43. data/lib/graphql/query/context.rb +6 -5
  44. data/lib/graphql/query/variable_validation_error.rb +1 -1
  45. data/lib/graphql/query.rb +20 -22
  46. data/lib/graphql/railtie.rb +7 -0
  47. data/lib/graphql/schema/addition.rb +1 -1
  48. data/lib/graphql/schema/argument.rb +3 -5
  49. data/lib/graphql/schema/build_from_definition.rb +8 -7
  50. data/lib/graphql/schema/directive/flagged.rb +1 -1
  51. data/lib/graphql/schema/directive.rb +2 -2
  52. data/lib/graphql/schema/enum.rb +36 -1
  53. data/lib/graphql/schema/enum_value.rb +1 -1
  54. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  55. data/lib/graphql/schema/field.rb +12 -12
  56. data/lib/graphql/schema/field_extension.rb +1 -1
  57. data/lib/graphql/schema/has_single_input_argument.rb +3 -1
  58. data/lib/graphql/schema/input_object.rb +70 -34
  59. data/lib/graphql/schema/interface.rb +3 -2
  60. data/lib/graphql/schema/loader.rb +1 -1
  61. data/lib/graphql/schema/member/has_arguments.rb +25 -17
  62. data/lib/graphql/schema/member/has_dataloader.rb +60 -0
  63. data/lib/graphql/schema/member/has_directives.rb +4 -4
  64. data/lib/graphql/schema/member/has_fields.rb +19 -1
  65. data/lib/graphql/schema/member/has_interfaces.rb +5 -5
  66. data/lib/graphql/schema/member/has_validators.rb +1 -1
  67. data/lib/graphql/schema/member/scoped.rb +1 -1
  68. data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
  69. data/lib/graphql/schema/member.rb +1 -0
  70. data/lib/graphql/schema/object.rb +25 -8
  71. data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
  72. data/lib/graphql/schema/resolver.rb +11 -10
  73. data/lib/graphql/schema/subscription.rb +52 -6
  74. data/lib/graphql/schema/union.rb +1 -1
  75. data/lib/graphql/schema/validator/required_validator.rb +23 -6
  76. data/lib/graphql/schema/validator.rb +1 -1
  77. data/lib/graphql/schema/visibility/migration.rb +1 -0
  78. data/lib/graphql/schema/visibility/profile.rb +69 -237
  79. data/lib/graphql/schema/visibility/visit.rb +190 -0
  80. data/lib/graphql/schema/visibility.rb +169 -28
  81. data/lib/graphql/schema/warden.rb +18 -5
  82. data/lib/graphql/schema.rb +90 -43
  83. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  84. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +1 -1
  85. data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
  86. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  87. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  88. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
  89. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  90. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
  91. data/lib/graphql/static_validation/validation_context.rb +1 -0
  92. data/lib/graphql/static_validation/validator.rb +6 -1
  93. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
  94. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
  95. data/lib/graphql/subscriptions/event.rb +12 -1
  96. data/lib/graphql/subscriptions/serialize.rb +1 -1
  97. data/lib/graphql/subscriptions.rb +1 -1
  98. data/lib/graphql/testing/helpers.rb +2 -2
  99. data/lib/graphql/tracing/active_support_notifications_trace.rb +7 -3
  100. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  101. data/lib/graphql/tracing/appoptics_trace.rb +9 -1
  102. data/lib/graphql/tracing/appoptics_tracing.rb +2 -0
  103. data/lib/graphql/tracing/appsignal_trace.rb +12 -0
  104. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  105. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  106. data/lib/graphql/tracing/data_dog_trace.rb +11 -0
  107. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  108. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  109. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  110. data/lib/graphql/tracing/detailed_trace.rb +93 -0
  111. data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
  112. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  113. data/lib/graphql/tracing/new_relic_trace.rb +164 -41
  114. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  115. data/lib/graphql/tracing/notifications_trace.rb +4 -0
  116. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  117. data/lib/graphql/tracing/null_trace.rb +9 -0
  118. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  119. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  120. data/lib/graphql/tracing/perfetto_trace.rb +737 -0
  121. data/lib/graphql/tracing/platform_trace.rb +5 -0
  122. data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
  123. data/lib/graphql/tracing/prometheus_trace.rb +31 -0
  124. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  125. data/lib/graphql/tracing/scout_trace.rb +11 -0
  126. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  127. data/lib/graphql/tracing/sentry_trace.rb +11 -0
  128. data/lib/graphql/tracing/statsd_trace.rb +15 -0
  129. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  130. data/lib/graphql/tracing/trace.rb +128 -1
  131. data/lib/graphql/tracing.rb +30 -30
  132. data/lib/graphql/types/relay/connection_behaviors.rb +3 -3
  133. data/lib/graphql/types/relay/edge_behaviors.rb +2 -2
  134. data/lib/graphql/types.rb +18 -11
  135. data/lib/graphql/version.rb +1 -1
  136. data/lib/graphql.rb +55 -47
  137. metadata +152 -10
  138. data/lib/graphql/backtrace/inspect_result.rb +0 -38
  139. data/lib/graphql/backtrace/trace.rb +0 -93
  140. data/lib/graphql/backtrace/tracer.rb +0 -80
  141. data/lib/graphql/schema/null_mask.rb +0 -11
@@ -74,7 +74,7 @@ module GraphQL
74
74
  runtime_object = root_type.wrap(query.root_value, context)
75
75
  runtime_object = schema.sync_lazy(runtime_object)
76
76
  is_eager = root_op_type == "mutation"
77
- @response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, root_operation.selections, is_eager)
77
+ @response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, root_operation.selections, is_eager, root_operation, nil, nil)
78
78
  st = get_current_runtime_state
79
79
  st.current_result = @response
80
80
 
@@ -85,7 +85,7 @@ module GraphQL
85
85
  call_method_on_directives(:resolve, runtime_object, root_operation.directives) do # execute query level directives
86
86
  each_gathered_selections(@response) do |selections, is_selection_array|
87
87
  if is_selection_array
88
- selection_response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, selections, is_eager)
88
+ selection_response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, selections, is_eager, root_operation, nil, nil)
89
89
  final_response = @response
90
90
  else
91
91
  selection_response = @response
@@ -142,7 +142,7 @@ module GraphQL
142
142
  end
143
143
  else
144
144
  # This is an InlineFragment or a FragmentSpread
145
- if @runtime_directive_names.any? && node.directives.any? { |d| @runtime_directive_names.include?(d.name) }
145
+ if !@runtime_directive_names.empty? && node.directives.any? { |d| @runtime_directive_names.include?(d.name) }
146
146
  next_selections = {}
147
147
  next_selections[:graphql_directives] = node.directives
148
148
  if selections_to_run
@@ -218,8 +218,10 @@ module GraphQL
218
218
  result_name, field_ast_nodes_or_ast_node, selections_result
219
219
  )
220
220
  finished_jobs += 1
221
- if target_result && finished_jobs == enqueued_jobs
222
- selections_result.merge_into(target_result)
221
+ if finished_jobs == enqueued_jobs
222
+ if target_result
223
+ selections_result.merge_into(target_result)
224
+ end
223
225
  end
224
226
  @dataloader.clear_cache
225
227
  }
@@ -229,8 +231,10 @@ module GraphQL
229
231
  result_name, field_ast_nodes_or_ast_node, selections_result
230
232
  )
231
233
  finished_jobs += 1
232
- if target_result && finished_jobs == enqueued_jobs
233
- selections_result.merge_into(target_result)
234
+ if finished_jobs == enqueued_jobs
235
+ if target_result
236
+ selections_result.merge_into(target_result)
237
+ end
234
238
  end
235
239
  }
236
240
  end
@@ -332,7 +336,7 @@ module GraphQL
332
336
  extra_args[extra] = field_defn.fetch_extra(extra, context)
333
337
  end
334
338
  end
335
- if extra_args.any?
339
+ if !extra_args.empty?
336
340
  resolved_arguments = resolved_arguments.merge_extras(extra_args)
337
341
  end
338
342
  resolved_arguments.keyword_arguments
@@ -361,7 +365,7 @@ module GraphQL
361
365
  end
362
366
 
363
367
  field_result = call_method_on_directives(:resolve, object, directives) do
364
- if directives.any?
368
+ if !directives.empty?
365
369
  # This might be executed in a different context; reset this info
366
370
  runtime_state = get_current_runtime_state
367
371
  runtime_state.current_field = field_defn
@@ -371,6 +375,7 @@ module GraphQL
371
375
  end
372
376
  # Actually call the field resolver and capture the result
373
377
  app_result = begin
378
+ @current_trace.begin_execute_field(field_defn, object, kwarg_arguments, query)
374
379
  @current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
375
380
  field_defn.resolve(object, kwarg_arguments, context)
376
381
  end
@@ -383,6 +388,7 @@ module GraphQL
383
388
  ex_err
384
389
  end
385
390
  end
391
+ @current_trace.end_execute_field(field_defn, object, kwarg_arguments, query, app_result)
386
392
  after_lazy(app_result, field: field_defn, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |inner_result, runtime_state|
387
393
  owner_type = selection_result.graphql_result_type
388
394
  return_type = field_defn.type
@@ -391,6 +397,8 @@ module GraphQL
391
397
  was_scoped = runtime_state.was_authorized_by_scope_items
392
398
  runtime_state.was_authorized_by_scope_items = nil
393
399
  continue_field(continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result, was_scoped, runtime_state)
400
+ else
401
+ nil
394
402
  end
395
403
  end
396
404
  end
@@ -465,7 +473,7 @@ module GraphQL
465
473
  # When this comes from a list item, use the parent object:
466
474
  parent_type = selection_result.is_a?(GraphQLResultArray) ? selection_result.graphql_parent.graphql_result_type : selection_result.graphql_result_type
467
475
  # This block is called if `result_name` is not dead. (Maybe a previous invalid nil caused it be marked dead.)
468
- err = parent_type::InvalidNullError.new(parent_type, field, value)
476
+ err = parent_type::InvalidNullError.new(parent_type, field, ast_node)
469
477
  schema.type_error(err, context)
470
478
  end
471
479
  else
@@ -525,7 +533,7 @@ module GraphQL
525
533
  end
526
534
  when Array
527
535
  # It's an array full of execution errors; add them all.
528
- if value.any? && value.all?(GraphQL::ExecutionError)
536
+ if !value.empty? && value.all?(GraphQL::ExecutionError)
529
537
  list_type_at_all = (field && (field.type.list?))
530
538
  if selection_result.nil? || !selection_result.graphql_dead
531
539
  value.each_with_index do |error, index|
@@ -574,7 +582,7 @@ module GraphQL
574
582
  r = begin
575
583
  current_type.coerce_result(value, context)
576
584
  rescue StandardError => err
577
- schema.handle_or_reraise(context, err)
585
+ query.handle_or_reraise(err)
578
586
  end
579
587
  set_result(selection_result, result_name, r, false, is_non_null)
580
588
  r
@@ -609,11 +617,11 @@ module GraphQL
609
617
  after_lazy(object_proxy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |inner_object, runtime_state|
610
618
  continue_value = continue_value(inner_object, field, is_non_null, ast_node, result_name, selection_result)
611
619
  if HALT != continue_value
612
- response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, next_selections, false)
620
+ response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, next_selections, false, ast_node, arguments, field)
613
621
  set_result(selection_result, result_name, response_hash, true, is_non_null)
614
622
  each_gathered_selections(response_hash) do |selections, is_selection_array|
615
623
  if is_selection_array
616
- this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, selections, false)
624
+ this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, selections, false, ast_node, arguments, field)
617
625
  final_result = response_hash
618
626
  else
619
627
  this_result = response_hash
@@ -634,35 +642,43 @@ module GraphQL
634
642
  # This is true for objects, unions, and interfaces
635
643
  use_dataloader_job = !inner_type.unwrap.kind.input?
636
644
  inner_type_non_null = inner_type.non_null?
637
- response_list = GraphQLResultArray.new(result_name, current_type, owner_object, selection_result, is_non_null, next_selections, false)
645
+ response_list = GraphQLResultArray.new(result_name, current_type, owner_object, selection_result, is_non_null, next_selections, false, ast_node, arguments, field)
638
646
  set_result(selection_result, result_name, response_list, true, is_non_null)
639
647
  idx = nil
640
648
  list_value = begin
641
- value.each do |inner_value|
642
- idx ||= 0
643
- this_idx = idx
644
- idx += 1
645
- if use_dataloader_job
646
- @dataloader.append_job do
649
+ begin
650
+ value.each do |inner_value|
651
+ idx ||= 0
652
+ this_idx = idx
653
+ idx += 1
654
+ if use_dataloader_job
655
+ @dataloader.append_job do
656
+ resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state)
657
+ end
658
+ else
647
659
  resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state)
648
660
  end
649
- else
650
- resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state)
651
661
  end
652
- end
653
662
 
654
- response_list
655
- rescue NoMethodError => err
656
- # Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.)
657
- if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
658
- # This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
659
- raise ListResultFailedError.new(value: value, field: field, path: current_path)
660
- else
661
- # This was some other NoMethodError -- let it bubble to reveal the real error.
662
- raise
663
+ response_list
664
+ rescue NoMethodError => err
665
+ # Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.)
666
+ if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
667
+ # This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
668
+ raise ListResultFailedError.new(value: value, field: field, path: current_path)
669
+ else
670
+ # This was some other NoMethodError -- let it bubble to reveal the real error.
671
+ raise
672
+ end
673
+ rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
674
+ ex_err
675
+ rescue StandardError => err
676
+ begin
677
+ query.handle_or_reraise(err)
678
+ rescue GraphQL::ExecutionError => ex_err
679
+ ex_err
680
+ end
663
681
  end
664
- rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
665
- ex_err
666
682
  rescue StandardError => err
667
683
  begin
668
684
  query.handle_or_reraise(err)
@@ -736,7 +752,7 @@ module GraphQL
736
752
  end
737
753
 
738
754
  def get_current_runtime_state
739
- current_state = Thread.current[:__graphql_runtime_info] ||= {}.compare_by_identity
755
+ current_state = Fiber[:__graphql_runtime_info] ||= {}.compare_by_identity
740
756
  current_state[@query] ||= CurrentState.new
741
757
  end
742
758
 
@@ -773,8 +789,10 @@ module GraphQL
773
789
  runtime_state.was_authorized_by_scope_items = was_authorized_by_scope_items
774
790
  # Wrap the execution of _this_ method with tracing,
775
791
  # but don't wrap the continuation below
792
+ result = nil
776
793
  inner_obj = begin
777
- if trace
794
+ result = if trace
795
+ @current_trace.begin_execute_field(field, owner_object, arguments, query)
778
796
  @current_trace.execute_field_lazy(field: field, query: query, object: owner_object, arguments: arguments, ast_node: ast_node) do
779
797
  schema.sync_lazy(lazy_obj)
780
798
  end
@@ -789,6 +807,10 @@ module GraphQL
789
807
  rescue GraphQL::ExecutionError => ex_err
790
808
  ex_err
791
809
  end
810
+ ensure
811
+ if trace
812
+ @current_trace.end_execute_field(field, owner_object, arguments, query, result)
813
+ end
792
814
  end
793
815
  yield(inner_obj, runtime_state)
794
816
  end
@@ -821,25 +843,30 @@ module GraphQL
821
843
  end
822
844
 
823
845
  def delete_all_interpreter_context
824
- per_query_state = Thread.current[:__graphql_runtime_info]
846
+ per_query_state = Fiber[:__graphql_runtime_info]
825
847
  if per_query_state
826
848
  per_query_state.delete(@query)
827
849
  if per_query_state.size == 0
828
- Thread.current[:__graphql_runtime_info] = nil
850
+ Fiber[:__graphql_runtime_info] = nil
829
851
  end
830
852
  end
831
853
  nil
832
854
  end
833
855
 
834
856
  def resolve_type(type, value)
857
+ @current_trace.begin_resolve_type(type, value, context)
835
858
  resolved_type, resolved_value = @current_trace.resolve_type(query: query, type: type, object: value) do
836
859
  query.resolve_type(type, value)
837
860
  end
861
+ @current_trace.end_resolve_type(type, value, context, resolved_type)
838
862
 
839
863
  if lazy?(resolved_type)
840
864
  GraphQL::Execution::Lazy.new do
865
+ @current_trace.begin_resolve_type(type, value, context)
841
866
  @current_trace.resolve_type_lazy(query: query, type: type, object: value) do
842
- schema.sync_lazy(resolved_type)
867
+ rt = schema.sync_lazy(resolved_type)
868
+ @current_trace.end_resolve_type(type, value, context, rt)
869
+ rt
843
870
  end
844
871
  end
845
872
  else
@@ -33,9 +33,12 @@ module GraphQL
33
33
  end
34
34
  end
35
35
 
36
+
36
37
  multiplex = Execution::Multiplex.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
37
38
  Fiber[:__graphql_current_multiplex] = multiplex
38
- multiplex.current_trace.execute_multiplex(multiplex: multiplex) do
39
+ trace = multiplex.current_trace
40
+ trace.begin_execute_multiplex(multiplex)
41
+ trace.execute_multiplex(multiplex: multiplex) do
39
42
  schema = multiplex.schema
40
43
  queries = multiplex.queries
41
44
  lazies_at_depth = Hash.new { |h, k| h[k] = [] }
@@ -44,7 +47,10 @@ module GraphQL
44
47
  multiplex_analyzers += [GraphQL::Analysis::MaxQueryComplexity]
45
48
  end
46
49
 
50
+ trace.begin_analyze_multiplex(multiplex, multiplex_analyzers)
47
51
  schema.analysis_engine.analyze_multiplex(multiplex, multiplex_analyzers)
52
+ trace.end_analyze_multiplex(multiplex, multiplex_analyzers)
53
+
48
54
  begin
49
55
  # Since this is basically the batching context,
50
56
  # share it for a whole multiplex
@@ -53,11 +59,13 @@ module GraphQL
53
59
  results = []
54
60
  queries.each_with_index do |query, idx|
55
61
  if query.subscription? && !query.subscription_update?
56
- query.context.namespace(:subscriptions)[:events] = []
62
+ subs_namespace = query.context.namespace(:subscriptions)
63
+ subs_namespace[:events] = []
64
+ subs_namespace[:subscriptions] = {}
57
65
  end
58
66
  multiplex.dataloader.append_job {
59
67
  operation = query.selected_operation
60
- result = if operation.nil? || !query.valid? || query.context.errors.any?
68
+ result = if operation.nil? || !query.valid? || !query.context.errors.empty?
61
69
  NO_OPERATION
62
70
  else
63
71
  begin
@@ -100,12 +108,12 @@ module GraphQL
100
108
  # Then, find all errors and assign the result to the query object
101
109
  results.each_with_index do |data_result, idx|
102
110
  query = queries[idx]
103
- if (events = query.context.namespace(:subscriptions)[:events]) && events.any?
111
+ if (events = query.context.namespace(:subscriptions)[:events]) && !events.empty?
104
112
  schema.subscriptions.write_subscription(query, events)
105
113
  end
106
114
  # Assign the result so that it can be accessed in instrumentation
107
115
  query.result_values = if data_result.equal?(NO_OPERATION)
108
- if !query.valid? || query.context.errors.any?
116
+ if !query.valid? || !query.context.errors.empty?
109
117
  # A bit weird, but `Query#static_errors` _includes_ `query.context.errors`
110
118
  { "errors" => query.static_errors.map(&:to_h) }
111
119
  else
@@ -114,7 +122,7 @@ module GraphQL
114
122
  else
115
123
  result = {}
116
124
 
117
- if query.context.errors.any?
125
+ if !query.context.errors.empty?
118
126
  error_result = query.context.errors.map(&:to_h)
119
127
  result["errors"] = error_result
120
128
  end
@@ -146,6 +154,8 @@ module GraphQL
146
154
  }
147
155
  end
148
156
  end
157
+ ensure
158
+ trace&.end_execute_multiplex(multiplex)
149
159
  end
150
160
  end
151
161
 
@@ -35,10 +35,6 @@ module GraphQL
35
35
  @current_trace = @context[:trace] || schema.new_trace(multiplex: self)
36
36
  @dataloader = @context[:dataloader] ||= @schema.dataloader_class.new
37
37
  @tracers = schema.tracers + (context[:tracers] || [])
38
- # Support `context: {backtrace: true}`
39
- if context[:backtrace] && !@tracers.include?(GraphQL::Backtrace::Tracer)
40
- @tracers << GraphQL::Backtrace::Tracer
41
- end
42
38
  @max_complexity = max_complexity
43
39
  end
44
40
  end
@@ -7,7 +7,7 @@ module GraphQL
7
7
  "a __DirectiveLocation describes one such possible adjacencies."
8
8
 
9
9
  GraphQL::Schema::Directive::LOCATIONS.each do |location|
10
- value(location.to_s, GraphQL::Schema::Directive::LOCATION_DESCRIPTIONS[location], value: location)
10
+ value(location.to_s, GraphQL::Schema::Directive::LOCATION_DESCRIPTIONS[location], value: location, value_method: false)
11
11
  end
12
12
  introspection true
13
13
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- class InvalidNameError < GraphQL::ExecutionError
3
+ class InvalidNameError < GraphQL::Error
4
4
  attr_reader :name, :valid_regex
5
5
  def initialize(name, valid_regex)
6
6
  @name = name
@@ -2,33 +2,23 @@
2
2
  module GraphQL
3
3
  # Raised automatically when a field's resolve function returns `nil`
4
4
  # for a non-null field.
5
- class InvalidNullError < GraphQL::RuntimeTypeError
5
+ class InvalidNullError < GraphQL::Error
6
6
  # @return [GraphQL::BaseType] The owner of {#field}
7
7
  attr_reader :parent_type
8
8
 
9
9
  # @return [GraphQL::Field] The field which failed to return a value
10
10
  attr_reader :field
11
11
 
12
- # @return [nil, GraphQL::ExecutionError] The invalid value for this field
13
- attr_reader :value
12
+ # @return [GraphQL::Language::Nodes::Field] the field where the error occurred
13
+ attr_reader :ast_node
14
14
 
15
- def initialize(parent_type, field, value)
15
+ def initialize(parent_type, field, ast_node)
16
16
  @parent_type = parent_type
17
17
  @field = field
18
- @value = value
18
+ @ast_node = ast_node
19
19
  super("Cannot return null for non-nullable field #{@parent_type.graphql_name}.#{@field.graphql_name}")
20
20
  end
21
21
 
22
- # @return [Hash] An entry for the response's "errors" key
23
- def to_h
24
- { "message" => message }
25
- end
26
-
27
- # @deprecated always false
28
- def parent_error?
29
- false
30
- end
31
-
32
22
  class << self
33
23
  attr_accessor :parent_class
34
24
 
@@ -5,12 +5,25 @@ require 'digest/sha2'
5
5
 
6
6
  module GraphQL
7
7
  module Language
8
+ # This cache is used by {GraphQL::Language::Parser.parse_file} when it's enabled.
9
+ #
10
+ # With Rails, parser caching may enabled by setting `config.graphql.parser_cache = true` in your Rails application.
11
+ #
12
+ # The cache may be manually built by assigning `GraphQL::Language::Parser.cache = GraphQL::Language::Cache.new("some_dir")`.
13
+ # This will create a directory (`tmp/cache/graphql` by default) that stores a cache of parsed files.
14
+ #
15
+ # Much like [bootsnap](https://github.com/Shopify/bootsnap), the parser cache needs to be cleaned up manually.
16
+ # You will need to clear the cache directory for each new deployment of your application.
17
+ # Also note that the parser cache will grow as your schema is loaded, so the cache directory must be writable.
18
+ #
19
+ # @see GraphQL::Railtie for simple Rails integration
8
20
  class Cache
9
21
  def initialize(path)
10
22
  @path = path
11
23
  end
12
24
 
13
25
  DIGEST = Digest::SHA256.new << GraphQL::VERSION
26
+
14
27
  def fetch(filename)
15
28
  hash = DIGEST.dup << filename
16
29
  begin
@@ -52,7 +52,7 @@ module GraphQL
52
52
 
53
53
  def build_object_type_node(object_type)
54
54
  ints = @types.interfaces(object_type)
55
- if ints.any?
55
+ if !ints.empty?
56
56
  ints.sort_by!(&:graphql_name)
57
57
  ints.map! { |iface| build_type_name_node(iface) }
58
58
  end
@@ -247,7 +247,7 @@ module GraphQL
247
247
  end
248
248
 
249
249
  def build_argument_nodes(arguments)
250
- if arguments.any?
250
+ if !arguments.empty?
251
251
  nodes = arguments.map { |arg| build_argument_node(arg) }
252
252
  nodes.sort_by!(&:name)
253
253
  nodes
@@ -271,7 +271,7 @@ module GraphQL
271
271
  all_types = @types.all_types
272
272
  type_nodes = build_type_definition_nodes(all_types)
273
273
 
274
- if (ex_t = schema.extra_types).any?
274
+ if !(ex_t = schema.extra_types).empty?
275
275
  dummy_query = Class.new(GraphQL::Schema::Object) do
276
276
  graphql_name "DummyQuery"
277
277
  (all_types + ex_t).each_with_index do |type, idx|
@@ -346,10 +346,11 @@ module GraphQL
346
346
  end
347
347
 
348
348
  def definition_directives(member, directives_method)
349
- dirs = if !member.respond_to?(directives_method) || member.directives.empty?
349
+ if !member.respond_to?(directives_method) || member.directives.empty?
350
350
  EmptyObjects::EMPTY_ARRAY
351
351
  else
352
- member.public_send(directives_method).map do |dir|
352
+ visible_directives = member.public_send(directives_method).select { |dir| @types.directive_exists?(dir.graphql_name) }
353
+ visible_directives.map! do |dir|
353
354
  args = []
354
355
  dir.arguments.argument_values.each_value do |arg_value| # rubocop:disable Development/ContextIsPassedCop -- directive instance method
355
356
  arg_defn = arg_value.definition
@@ -373,9 +374,9 @@ module GraphQL
373
374
  arguments: args
374
375
  )
375
376
  end
376
- end
377
377
 
378
- dirs
378
+ visible_directives
379
+ end
379
380
  end
380
381
 
381
382
  attr_reader :schema, :always_include_schema,
@@ -13,17 +13,21 @@ module GraphQL
13
13
  @pos = nil
14
14
  @max_tokens = max_tokens || Float::INFINITY
15
15
  @tokens_count = 0
16
+ @finished = false
16
17
  end
17
18
 
18
- def eos?
19
- @scanner.eos?
19
+ def finished?
20
+ @finished
20
21
  end
21
22
 
22
23
  attr_reader :pos, :tokens_count
23
24
 
24
25
  def advance
25
26
  @scanner.skip(IGNORE_REGEXP)
26
- return false if @scanner.eos?
27
+ if @scanner.eos?
28
+ @finished = true
29
+ return false
30
+ end
27
31
  @tokens_count += 1
28
32
  if @tokens_count > @max_tokens
29
33
  raise_parse_error("This query is too large to execute.")
@@ -72,7 +76,10 @@ module GraphQL
72
76
  # Check for a matched decimal:
73
77
  @scanner[1] ? :FLOAT : :INT
74
78
  else
75
- raise_parse_error("Expected a number, but it was malformed (#{@string[@pos].inspect})")
79
+ # Attempt to find the part after the `-`
80
+ value = @scanner.scan(/-\s?[a-z0-9]*/i)
81
+ invalid_byte_for_number_error_message = "Expected type 'number', but it was malformed#{value.nil? ? "" : ": #{value.inspect}"}."
82
+ raise_parse_error(invalid_byte_for_number_error_message)
76
83
  end
77
84
  when ByteFor::ELLIPSIS
78
85
  if @string.getbyte(@pos + 1) != 46 || @string.getbyte(@pos + 2) != 46
@@ -141,6 +141,8 @@ module GraphQL
141
141
  end
142
142
 
143
143
  class << self
144
+ # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
145
+
144
146
  # Add a default `#visit_method` and `#children_method_name` using the class name
145
147
  def inherited(child_class)
146
148
  super
@@ -343,6 +345,7 @@ module GraphQL
343
345
  RUBY
344
346
  end
345
347
  end
348
+ # rubocop:enable Development/NoEvalCop
346
349
  end
347
350
  end
348
351
 
@@ -110,7 +110,7 @@ module GraphQL
110
110
  # Only ignored characters is not a valid document
111
111
  raise GraphQL::ParseError.new("Unexpected end of document", nil, nil, @graphql_str)
112
112
  end
113
- while !@lexer.eos?
113
+ while !@lexer.finished?
114
114
  defns << definition
115
115
  end
116
116
  Document.new(pos: 0, definitions: defns, filename: @filename, source: self)
@@ -161,7 +161,7 @@ module GraphQL
161
161
  expect_token(:VAR_SIGN)
162
162
  var_name = parse_name
163
163
  expect_token(:COLON)
164
- var_type = self.type
164
+ var_type = self.type || raise_parse_error("Missing type definition for variable: $#{var_name}")
165
165
  default_value = if at?(:EQUALS)
166
166
  advance_token
167
167
  value
@@ -92,7 +92,7 @@ module GraphQL
92
92
  print_string("@")
93
93
  print_string(directive.name)
94
94
 
95
- if directive.arguments.any?
95
+ if !directive.arguments.empty?
96
96
  print_string("(")
97
97
  directive.arguments.each_with_index do |a, i|
98
98
  print_argument(a)
@@ -117,7 +117,7 @@ module GraphQL
117
117
  print_string(": ")
118
118
  end
119
119
  print_string(field.name)
120
- if field.arguments.any?
120
+ if !field.arguments.empty?
121
121
  print_string("(")
122
122
  field.arguments.each_with_index do |a, i|
123
123
  print_argument(a)
@@ -182,7 +182,7 @@ module GraphQL
182
182
  print_string(operation_definition.name)
183
183
  end
184
184
 
185
- if operation_definition.variables.any?
185
+ if !operation_definition.variables.empty?
186
186
  print_string("(")
187
187
  operation_definition.variables.each_with_index do |v, i|
188
188
  print_variable_definition(v)
@@ -230,7 +230,7 @@ module GraphQL
230
230
 
231
231
  extension ? print_string("extend schema") : print_string("schema")
232
232
 
233
- if schema.directives.any?
233
+ if !schema.directives.empty?
234
234
  schema.directives.each do |dir|
235
235
  print_string("\n ")
236
236
  print_node(dir)
@@ -332,7 +332,7 @@ module GraphQL
332
332
  extension ? print_string("extend ") : print_description_and_comment(interface_type)
333
333
  print_string("interface ")
334
334
  print_string(interface_type.name)
335
- print_implements(interface_type) if interface_type.interfaces.any?
335
+ print_implements(interface_type) if !interface_type.interfaces.empty?
336
336
  print_directives(interface_type.directives)
337
337
  print_field_definitions(interface_type.fields)
338
338
  end
@@ -342,7 +342,7 @@ module GraphQL
342
342
  print_string("union ")
343
343
  print_string(union_type.name)
344
344
  print_directives(union_type.directives)
345
- if union_type.types.any?
345
+ if !union_type.types.empty?
346
346
  print_string(" = ")
347
347
  i = 0
348
348
  union_type.types.each do |t|
@@ -360,7 +360,7 @@ module GraphQL
360
360
  print_string("enum ")
361
361
  print_string(enum_type.name)
362
362
  print_directives(enum_type.directives)
363
- if enum_type.values.any?
363
+ if !enum_type.values.empty?
364
364
  print_string(" {\n")
365
365
  enum_type.values.each.with_index do |value, i|
366
366
  print_description(value, indent: " ", first_in_block: i == 0)
@@ -401,7 +401,7 @@ module GraphQL
401
401
  print_string("directive @")
402
402
  print_string(directive.name)
403
403
 
404
- if directive.arguments.any?
404
+ if !directive.arguments.empty?
405
405
  print_arguments(directive.arguments)
406
406
  end
407
407