graphql 2.0.31 → 2.6.1

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 (316) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/detailed_trace_generator.rb +77 -0
  3. data/lib/generators/graphql/install/mutation_root_generator.rb +2 -2
  4. data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
  5. data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
  6. data/lib/generators/graphql/install_generator.rb +49 -0
  7. data/lib/generators/graphql/orm_mutations_base.rb +1 -1
  8. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  9. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  10. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  11. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  12. data/lib/generators/graphql/templates/base_field.erb +2 -0
  13. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  14. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  15. data/lib/generators/graphql/templates/base_object.erb +2 -0
  16. data/lib/generators/graphql/templates/base_resolver.erb +8 -0
  17. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  18. data/lib/generators/graphql/templates/base_union.erb +2 -0
  19. data/lib/generators/graphql/templates/create_graphql_detailed_traces.erb +10 -0
  20. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  21. data/lib/generators/graphql/templates/loader.erb +2 -0
  22. data/lib/generators/graphql/templates/mutation.erb +2 -0
  23. data/lib/generators/graphql/templates/node_type.erb +2 -0
  24. data/lib/generators/graphql/templates/query_type.erb +2 -0
  25. data/lib/generators/graphql/templates/schema.erb +5 -0
  26. data/lib/generators/graphql/type_generator.rb +1 -1
  27. data/lib/graphql/analysis/analyzer.rb +90 -0
  28. data/lib/graphql/analysis/field_usage.rb +82 -0
  29. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  30. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  31. data/lib/graphql/analysis/query_complexity.rb +263 -0
  32. data/lib/graphql/analysis/query_depth.rb +58 -0
  33. data/lib/graphql/analysis/visitor.rb +280 -0
  34. data/lib/graphql/analysis.rb +102 -1
  35. data/lib/graphql/autoload.rb +38 -0
  36. data/lib/graphql/backtrace/table.rb +118 -55
  37. data/lib/graphql/backtrace.rb +1 -19
  38. data/lib/graphql/coercion_error.rb +1 -9
  39. data/lib/graphql/current.rb +57 -0
  40. data/lib/graphql/dashboard/application_controller.rb +41 -0
  41. data/lib/graphql/dashboard/detailed_traces.rb +47 -0
  42. data/lib/graphql/dashboard/installable.rb +22 -0
  43. data/lib/graphql/dashboard/landings_controller.rb +9 -0
  44. data/lib/graphql/dashboard/limiters.rb +93 -0
  45. data/lib/graphql/dashboard/operation_store.rb +199 -0
  46. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
  47. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
  48. data/lib/graphql/dashboard/statics/charts.min.css +1 -0
  49. data/lib/graphql/dashboard/statics/dashboard.css +30 -0
  50. data/lib/graphql/dashboard/statics/dashboard.js +143 -0
  51. data/lib/graphql/dashboard/statics/header-icon.png +0 -0
  52. data/lib/graphql/dashboard/statics/icon.png +0 -0
  53. data/lib/graphql/dashboard/statics_controller.rb +31 -0
  54. data/lib/graphql/dashboard/subscriptions.rb +97 -0
  55. data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
  56. data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
  57. data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
  58. data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
  59. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +24 -0
  60. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
  61. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
  62. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
  63. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
  64. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
  65. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
  66. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
  67. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
  68. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
  69. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
  70. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
  71. data/lib/graphql/dashboard.rb +96 -0
  72. data/lib/graphql/dataloader/active_record_association_source.rb +84 -0
  73. data/lib/graphql/dataloader/active_record_source.rb +47 -0
  74. data/lib/graphql/dataloader/async_dataloader.rb +112 -0
  75. data/lib/graphql/dataloader/null_dataloader.rb +55 -10
  76. data/lib/graphql/dataloader/request.rb +5 -0
  77. data/lib/graphql/dataloader/source.rb +35 -12
  78. data/lib/graphql/dataloader.rb +224 -149
  79. data/lib/graphql/date_encoding_error.rb +1 -1
  80. data/lib/graphql/dig.rb +2 -1
  81. data/lib/graphql/duration_encoding_error.rb +16 -0
  82. data/lib/graphql/execution/field_resolve_step.rb +631 -0
  83. data/lib/graphql/execution/finalize.rb +217 -0
  84. data/lib/graphql/execution/input_values.rb +261 -0
  85. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  86. data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
  87. data/lib/graphql/execution/interpreter/handles_raw_value.rb +6 -0
  88. data/lib/graphql/execution/interpreter/resolve.rb +23 -25
  89. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +228 -0
  90. data/lib/graphql/execution/interpreter/runtime.rb +365 -435
  91. data/lib/graphql/execution/interpreter.rb +87 -163
  92. data/lib/graphql/execution/lazy.rb +1 -1
  93. data/lib/graphql/execution/load_argument_step.rb +64 -0
  94. data/lib/graphql/execution/lookahead.rb +105 -31
  95. data/lib/graphql/execution/multiplex.rb +7 -6
  96. data/lib/graphql/execution/next.rb +90 -0
  97. data/lib/graphql/execution/prepare_object_step.rb +128 -0
  98. data/lib/graphql/execution/runner.rb +410 -0
  99. data/lib/graphql/execution/selections_step.rb +91 -0
  100. data/lib/graphql/execution.rb +8 -4
  101. data/lib/graphql/execution_error.rb +17 -10
  102. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  103. data/lib/graphql/introspection/directive_type.rb +7 -3
  104. data/lib/graphql/introspection/dynamic_fields.rb +5 -1
  105. data/lib/graphql/introspection/entry_points.rb +20 -6
  106. data/lib/graphql/introspection/enum_value_type.rb +5 -5
  107. data/lib/graphql/introspection/field_type.rb +13 -5
  108. data/lib/graphql/introspection/input_value_type.rb +21 -13
  109. data/lib/graphql/introspection/schema_type.rb +8 -11
  110. data/lib/graphql/introspection/type_type.rb +64 -28
  111. data/lib/graphql/invalid_name_error.rb +1 -1
  112. data/lib/graphql/invalid_null_error.rb +26 -17
  113. data/lib/graphql/language/block_string.rb +34 -18
  114. data/lib/graphql/language/cache.rb +13 -0
  115. data/lib/graphql/language/comment.rb +18 -0
  116. data/lib/graphql/language/definition_slice.rb +1 -1
  117. data/lib/graphql/language/document_from_schema_definition.rb +90 -61
  118. data/lib/graphql/language/lexer.rb +323 -193
  119. data/lib/graphql/language/nodes.rb +139 -77
  120. data/lib/graphql/language/parser.rb +807 -1985
  121. data/lib/graphql/language/printer.rb +324 -151
  122. data/lib/graphql/language/sanitized_printer.rb +21 -23
  123. data/lib/graphql/language/static_visitor.rb +171 -0
  124. data/lib/graphql/language/visitor.rb +62 -119
  125. data/lib/graphql/language.rb +71 -1
  126. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  127. data/lib/graphql/pagination/array_connection.rb +6 -6
  128. data/lib/graphql/pagination/connection.rb +30 -1
  129. data/lib/graphql/pagination/connections.rb +32 -0
  130. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  131. data/lib/graphql/query/context/scoped_context.rb +101 -0
  132. data/lib/graphql/query/context.rb +88 -144
  133. data/lib/graphql/query/null_context.rb +15 -18
  134. data/lib/graphql/query/partial.rb +194 -0
  135. data/lib/graphql/query/validation_pipeline.rb +4 -4
  136. data/lib/graphql/query/variable_validation_error.rb +1 -1
  137. data/lib/graphql/query/variables.rb +3 -3
  138. data/lib/graphql/query.rb +135 -81
  139. data/lib/graphql/railtie.rb +16 -6
  140. data/lib/graphql/rake_task.rb +3 -12
  141. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  142. data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
  143. data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
  144. data/lib/graphql/rubocop.rb +2 -0
  145. data/lib/graphql/runtime_error.rb +6 -0
  146. data/lib/graphql/schema/addition.rb +26 -13
  147. data/lib/graphql/schema/always_visible.rb +7 -2
  148. data/lib/graphql/schema/argument.rb +78 -14
  149. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  150. data/lib/graphql/schema/build_from_definition.rb +140 -66
  151. data/lib/graphql/schema/directive/flagged.rb +4 -2
  152. data/lib/graphql/schema/directive/one_of.rb +12 -0
  153. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  154. data/lib/graphql/schema/directive.rb +78 -12
  155. data/lib/graphql/schema/enum.rb +110 -27
  156. data/lib/graphql/schema/enum_value.rb +11 -3
  157. data/lib/graphql/schema/field/connection_extension.rb +4 -51
  158. data/lib/graphql/schema/field/scope_extension.rb +19 -7
  159. data/lib/graphql/schema/field.rb +245 -119
  160. data/lib/graphql/schema/field_extension.rb +12 -9
  161. data/lib/graphql/schema/has_single_input_argument.rb +160 -0
  162. data/lib/graphql/schema/input_object.rb +123 -65
  163. data/lib/graphql/schema/interface.rb +60 -16
  164. data/lib/graphql/schema/introspection_system.rb +8 -17
  165. data/lib/graphql/schema/late_bound_type.rb +4 -0
  166. data/lib/graphql/schema/list.rb +8 -4
  167. data/lib/graphql/schema/loader.rb +3 -4
  168. data/lib/graphql/schema/member/base_dsl_methods.rb +18 -12
  169. data/lib/graphql/schema/member/has_arguments.rb +132 -100
  170. data/lib/graphql/schema/member/has_authorization.rb +35 -0
  171. data/lib/graphql/schema/member/has_dataloader.rb +99 -0
  172. data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
  173. data/lib/graphql/schema/member/has_directives.rb +5 -5
  174. data/lib/graphql/schema/member/has_fields.rb +121 -17
  175. data/lib/graphql/schema/member/has_interfaces.rb +27 -13
  176. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  177. data/lib/graphql/schema/member/has_validators.rb +1 -1
  178. data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
  179. data/lib/graphql/schema/member/scoped.rb +19 -0
  180. data/lib/graphql/schema/member/type_system_helpers.rb +18 -5
  181. data/lib/graphql/schema/member/validates_input.rb +3 -3
  182. data/lib/graphql/schema/member.rb +6 -0
  183. data/lib/graphql/schema/mutation.rb +7 -0
  184. data/lib/graphql/schema/non_null.rb +1 -1
  185. data/lib/graphql/schema/object.rb +34 -8
  186. data/lib/graphql/schema/printer.rb +9 -7
  187. data/lib/graphql/schema/ractor_shareable.rb +79 -0
  188. data/lib/graphql/schema/relay_classic_mutation.rb +6 -129
  189. data/lib/graphql/schema/resolver.rb +128 -32
  190. data/lib/graphql/schema/scalar.rb +4 -9
  191. data/lib/graphql/schema/subscription.rb +63 -12
  192. data/lib/graphql/schema/timeout.rb +19 -2
  193. data/lib/graphql/schema/type_expression.rb +2 -2
  194. data/lib/graphql/schema/union.rb +2 -2
  195. data/lib/graphql/schema/unique_within_type.rb +1 -1
  196. data/lib/graphql/schema/validator/all_validator.rb +62 -0
  197. data/lib/graphql/schema/validator/required_validator.rb +92 -11
  198. data/lib/graphql/schema/validator.rb +3 -1
  199. data/lib/graphql/schema/visibility/migration.rb +188 -0
  200. data/lib/graphql/schema/visibility/profile.rb +464 -0
  201. data/lib/graphql/schema/visibility/visit.rb +190 -0
  202. data/lib/graphql/schema/visibility.rb +311 -0
  203. data/lib/graphql/schema/warden.rb +275 -103
  204. data/lib/graphql/schema/wrapper.rb +7 -1
  205. data/lib/graphql/schema.rb +954 -212
  206. data/lib/graphql/static_validation/all_rules.rb +3 -3
  207. data/lib/graphql/static_validation/base_visitor.rb +96 -71
  208. data/lib/graphql/static_validation/literal_validator.rb +6 -7
  209. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +2 -2
  210. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +18 -6
  211. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +6 -2
  212. data/lib/graphql/static_validation/rules/directives_are_defined.rb +6 -3
  213. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
  214. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +14 -3
  215. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +59 -15
  216. data/lib/graphql/static_validation/rules/fields_will_merge.rb +391 -262
  217. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
  218. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +6 -6
  219. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +15 -2
  220. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  221. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  222. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  223. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
  224. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  225. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +28 -8
  226. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
  227. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
  228. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +7 -3
  229. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  230. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  231. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
  232. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +14 -2
  233. data/lib/graphql/static_validation/validation_context.rb +22 -6
  234. data/lib/graphql/static_validation/validator.rb +9 -1
  235. data/lib/graphql/static_validation.rb +0 -1
  236. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -5
  237. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  238. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +45 -19
  239. data/lib/graphql/subscriptions/event.rb +22 -4
  240. data/lib/graphql/subscriptions/serialize.rb +3 -1
  241. data/lib/graphql/subscriptions.rb +56 -17
  242. data/lib/graphql/testing/helpers.rb +161 -0
  243. data/lib/graphql/testing/mock_action_cable.rb +111 -0
  244. data/lib/graphql/testing.rb +3 -0
  245. data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
  246. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  247. data/lib/graphql/tracing/appoptics_trace.rb +11 -3
  248. data/lib/graphql/tracing/appoptics_tracing.rb +9 -2
  249. data/lib/graphql/tracing/appsignal_trace.rb +32 -55
  250. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  251. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  252. data/lib/graphql/tracing/data_dog_trace.rb +46 -158
  253. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  254. data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +74 -0
  255. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  256. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  257. data/lib/graphql/tracing/detailed_trace.rb +156 -0
  258. data/lib/graphql/tracing/legacy_hooks_trace.rb +75 -0
  259. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  260. data/lib/graphql/tracing/monitor_trace.rb +283 -0
  261. data/lib/graphql/tracing/new_relic_trace.rb +47 -54
  262. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  263. data/lib/graphql/tracing/notifications_trace.rb +184 -34
  264. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  265. data/lib/graphql/tracing/null_trace.rb +9 -0
  266. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  267. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  268. data/lib/graphql/tracing/perfetto_trace.rb +864 -0
  269. data/lib/graphql/tracing/platform_trace.rb +5 -0
  270. data/lib/graphql/tracing/platform_tracing.rb +3 -1
  271. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +5 -1
  272. data/lib/graphql/tracing/prometheus_trace.rb +72 -68
  273. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  274. data/lib/graphql/tracing/scout_trace.rb +32 -55
  275. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  276. data/lib/graphql/tracing/sentry_trace.rb +82 -0
  277. data/lib/graphql/tracing/statsd_trace.rb +33 -41
  278. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  279. data/lib/graphql/tracing/trace.rb +118 -1
  280. data/lib/graphql/tracing.rb +31 -28
  281. data/lib/graphql/type_kinds.rb +2 -1
  282. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  283. data/lib/graphql/types/relay/connection_behaviors.rb +45 -3
  284. data/lib/graphql/types/relay/edge_behaviors.rb +19 -1
  285. data/lib/graphql/types/relay/has_node_field.rb +13 -8
  286. data/lib/graphql/types/relay/has_nodes_field.rb +13 -8
  287. data/lib/graphql/types/relay/node_behaviors.rb +13 -2
  288. data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
  289. data/lib/graphql/types.rb +18 -10
  290. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  291. data/lib/graphql/unauthorized_error.rb +9 -1
  292. data/lib/graphql/version.rb +1 -1
  293. data/lib/graphql.rb +69 -54
  294. data/readme.md +12 -2
  295. metadata +236 -40
  296. data/lib/graphql/analysis/ast/analyzer.rb +0 -84
  297. data/lib/graphql/analysis/ast/field_usage.rb +0 -57
  298. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  299. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  300. data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
  301. data/lib/graphql/analysis/ast/query_depth.rb +0 -55
  302. data/lib/graphql/analysis/ast/visitor.rb +0 -276
  303. data/lib/graphql/analysis/ast.rb +0 -81
  304. data/lib/graphql/backtrace/inspect_result.rb +0 -50
  305. data/lib/graphql/backtrace/trace.rb +0 -96
  306. data/lib/graphql/backtrace/tracer.rb +0 -80
  307. data/lib/graphql/deprecation.rb +0 -9
  308. data/lib/graphql/filter.rb +0 -59
  309. data/lib/graphql/language/parser.y +0 -560
  310. data/lib/graphql/language/token.rb +0 -34
  311. data/lib/graphql/schema/base_64_bp.rb +0 -26
  312. data/lib/graphql/schema/invalid_type_error.rb +0 -7
  313. data/lib/graphql/schema/null_mask.rb +0 -11
  314. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
  315. data/lib/graphql/static_validation/type_stack.rb +0 -216
  316. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
@@ -20,196 +20,120 @@ module GraphQL
20
20
  # @param queries [Array<GraphQL::Query, Hash>]
21
21
  # @param context [Hash]
22
22
  # @param max_complexity [Integer, nil]
23
- # @return [Array<Hash>] One result per query
23
+ # @return [Array<GraphQL::Query::Result>] One result per query
24
24
  def run_all(schema, query_options, context: {}, max_complexity: schema.max_complexity)
25
25
  queries = query_options.map do |opts|
26
- case opts
26
+ query = case opts
27
27
  when Hash
28
- GraphQL::Query.new(schema, nil, **opts)
29
- when GraphQL::Query
28
+ schema.query_class.new(schema, nil, **opts)
29
+ when GraphQL::Query, GraphQL::Query::Partial
30
30
  opts
31
31
  else
32
32
  raise "Expected Hash or GraphQL::Query, not #{opts.class} (#{opts.inspect})"
33
33
  end
34
+ query
34
35
  end
35
36
 
37
+ return GraphQL::EmptyObjects::EMPTY_ARRAY if queries.empty?
38
+
36
39
  multiplex = Execution::Multiplex.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
37
- multiplex.current_trace.execute_multiplex(multiplex: multiplex) do
40
+ trace = multiplex.current_trace
41
+ Fiber[:__graphql_current_multiplex] = multiplex
42
+ trace.execute_multiplex(multiplex: multiplex) do
38
43
  schema = multiplex.schema
39
44
  queries = multiplex.queries
40
- query_instrumenters = schema.instrumenters[:query]
41
- multiplex_instrumenters = schema.instrumenters[:multiplex]
42
- lazies_at_depth = Hash.new { |h, k| h[k] = [] }
43
-
44
- # First, run multiplex instrumentation, then query instrumentation for each query
45
- call_hooks(multiplex_instrumenters, multiplex, :before_multiplex, :after_multiplex) do
46
- each_query_call_hooks(query_instrumenters, queries) do
47
- schema = multiplex.schema
48
- multiplex_analyzers = schema.multiplex_analyzers
49
- queries = multiplex.queries
50
- if multiplex.max_complexity
51
- multiplex_analyzers += [GraphQL::Analysis::AST::MaxQueryComplexity]
52
- end
45
+ multiplex_analyzers = schema.multiplex_analyzers
46
+ if multiplex.max_complexity
47
+ multiplex_analyzers += [GraphQL::Analysis::MaxQueryComplexity]
48
+ end
53
49
 
54
- schema.analysis_engine.analyze_multiplex(multiplex, multiplex_analyzers)
55
- begin
56
- # Since this is basically the batching context,
57
- # share it for a whole multiplex
58
- multiplex.context[:interpreter_instance] ||= multiplex.schema.query_execution_strategy.new
59
- # Do as much eager evaluation of the query as possible
60
- results = []
61
- queries.each_with_index do |query, idx|
62
- multiplex.dataloader.append_job {
63
- operation = query.selected_operation
64
- result = if operation.nil? || !query.valid? || query.context.errors.any?
65
- NO_OPERATION
66
- else
67
- begin
68
- # Although queries in a multiplex _share_ an Interpreter instance,
69
- # they also have another item of state, which is private to that query
70
- # in particular, assign it here:
71
- runtime = Runtime.new(query: query, lazies_at_depth: lazies_at_depth)
72
- query.context.namespace(:interpreter_runtime)[:runtime] = runtime
50
+ trace.begin_analyze_multiplex(multiplex, multiplex_analyzers)
51
+ schema.analysis_engine.analyze_multiplex(multiplex, multiplex_analyzers)
52
+ trace.end_analyze_multiplex(multiplex, multiplex_analyzers)
73
53
 
74
- query.current_trace.execute_query(query: query) do
75
- runtime.run_eager
76
- end
77
- rescue GraphQL::ExecutionError => err
78
- query.context.errors << err
79
- NO_OPERATION
80
- end
54
+ begin
55
+ # Since this is basically the batching context,
56
+ # share it for a whole multiplex
57
+ multiplex.context[:interpreter_instance] ||= multiplex.schema.query_execution_strategy(deprecation_warning: false).new
58
+ # Do as much eager evaluation of the query as possible
59
+ results = []
60
+ queries.each_with_index do |query, idx|
61
+ multiplex.dataloader.append_job {
62
+ operation = query.selected_operation
63
+ result = if operation.nil? || !query.valid? || !query.context.errors.empty?
64
+ NO_OPERATION
65
+ else
66
+ begin
67
+ # Although queries in a multiplex _share_ an Interpreter instance,
68
+ # they also have another item of state, which is private to that query
69
+ # in particular, assign it here:
70
+ runtime = Runtime.new(query: query)
71
+ query.context.namespace(:interpreter_runtime)[:runtime] = runtime
72
+ if query.subscription? && !query.subscription_update?
73
+ schema.subscriptions.initialize_subscriptions(query)
81
74
  end
82
- results[idx] = result
83
- }
84
- end
85
-
86
- multiplex.dataloader.run
87
-
88
- # Then, work through lazy results in a breadth-first way
89
- multiplex.dataloader.append_job {
90
- query = multiplex.queries.length == 1 ? multiplex.queries[0] : nil
91
- queries = multiplex ? multiplex.queries : [query]
92
- final_values = queries.map do |query|
93
- runtime = query.context.namespace(:interpreter_runtime)[:runtime]
94
- # it might not be present if the query has an error
95
- runtime ? runtime.final_result : nil
96
- end
97
- final_values.compact!
98
- multiplex.current_trace.execute_query_lazy(multiplex: multiplex, query: query) do
99
- Interpreter::Resolve.resolve_each_depth(lazies_at_depth, multiplex.dataloader)
100
- end
101
- queries.each do |query|
102
- runtime = query.context.namespace(:interpreter_runtime)[:runtime]
103
- if runtime
104
- runtime.delete_all_interpreter_context
75
+ query.current_trace.execute_query(query: query) do
76
+ runtime.run_eager
105
77
  end
78
+ rescue GraphQL::ExecutionError => err
79
+ query.context.errors << err
106
80
  end
107
- }
108
- multiplex.dataloader.run
109
-
110
- # Then, find all errors and assign the result to the query object
111
- results.each_with_index do |data_result, idx|
112
- query = queries[idx]
113
- # Assign the result so that it can be accessed in instrumentation
114
- query.result_values = if data_result.equal?(NO_OPERATION)
115
- if !query.valid? || query.context.errors.any?
116
- # A bit weird, but `Query#static_errors` _includes_ `query.context.errors`
117
- { "errors" => query.static_errors.map(&:to_h) }
118
- else
119
- data_result
120
- end
121
- else
122
- result = {
123
- "data" => query.context.namespace(:interpreter_runtime)[:runtime].final_result
124
- }
81
+ end
82
+ results[idx] = result
83
+ }
84
+ end
125
85
 
126
- if query.context.errors.any?
127
- error_result = query.context.errors.map(&:to_h)
128
- result["errors"] = error_result
129
- end
86
+ multiplex.dataloader.run(trace_query_lazy: multiplex)
87
+
88
+ # Then, find all errors and assign the result to the query object
89
+ results.each_with_index do |data_result, idx|
90
+ query = queries[idx]
91
+ # Assign the result so that it can be accessed in instrumentation
92
+ query.result_values = if data_result.equal?(NO_OPERATION)
93
+ if !query.valid? || !query.context.errors.empty?
94
+ # A bit weird, but `Query#static_errors` _includes_ `query.context.errors`
95
+ { "errors" => query.static_errors.map(&:to_h) }
96
+ else
97
+ data_result
98
+ end
99
+ else
100
+ if query.subscription?
101
+ schema.subscriptions.finish_subscriptions(query)
102
+ end
103
+ result = {}
130
104
 
131
- result
132
- end
133
- if query.context.namespace?(:__query_result_extensions__)
134
- query.result_values["extensions"] = query.context.namespace(:__query_result_extensions__)
135
- end
136
- # Get the Query::Result, not the Hash
137
- results[idx] = query.result
105
+ if !query.context.errors.empty?
106
+ error_result = query.context.errors.map(&:to_h)
107
+ result["errors"] = error_result
138
108
  end
139
109
 
140
- results
141
- rescue Exception
142
- # TODO rescue at a higher level so it will catch errors in analysis, too
143
- # Assign values here so that the query's `@executed` becomes true
144
- queries.map { |q| q.result_values ||= {} }
145
- raise
146
- ensure
147
- queries.map { |query|
148
- runtime = query.context.namespace(:interpreter_runtime)[:runtime]
149
- if runtime
150
- runtime.delete_all_interpreter_context
151
- end
152
- }
110
+ result["data"] = query.context.namespace(:interpreter_runtime)[:runtime].final_result
111
+
112
+ result
113
+ end
114
+ if query.context.namespace?(:__query_result_extensions__)
115
+ query.result_values["extensions"] = query.context.namespace(:__query_result_extensions__)
153
116
  end
117
+ # Get the Query::Result, not the Hash
118
+ results[idx] = query.result
154
119
  end
155
- end
156
- end
157
- end
158
-
159
- private
160
120
 
161
- # Call the before_ hooks of each query,
162
- # Then yield if no errors.
163
- # `call_hooks` takes care of appropriate cleanup.
164
- def each_query_call_hooks(instrumenters, queries, i = 0)
165
- if i >= queries.length
166
- yield
167
- else
168
- query = queries[i]
169
- call_hooks(instrumenters, query, :before_query, :after_query) {
170
- each_query_call_hooks(instrumenters, queries, i + 1) {
171
- yield
121
+ results
122
+ rescue Exception
123
+ # TODO rescue at a higher level so it will catch errors in analysis, too
124
+ # Assign values here so that the query's `@executed` becomes true
125
+ queries.map { |q| q.result_values ||= {} }
126
+ raise
127
+ ensure
128
+ Fiber[:__graphql_current_multiplex] = nil
129
+ queries.map { |query|
130
+ runtime = query.context.namespace(:interpreter_runtime)[:runtime]
131
+ if runtime
132
+ runtime.delete_all_interpreter_context
133
+ end
172
134
  }
173
- }
174
- end
175
- end
176
-
177
- # Call each before hook, and if they all succeed, yield.
178
- # If they don't all succeed, call after_ for each one that succeeded.
179
- def call_hooks(instrumenters, object, before_hook_name, after_hook_name)
180
- begin
181
- successful = []
182
- instrumenters.each do |instrumenter|
183
- instrumenter.public_send(before_hook_name, object)
184
- successful << instrumenter
185
- end
186
-
187
- # if any before hooks raise an exception, quit calling before hooks,
188
- # but call the after hooks on anything that succeeded but also
189
- # raise the exception that came from the before hook.
190
- rescue GraphQL::ExecutionError => err
191
- object.context.errors << err
192
- rescue => e
193
- raise call_after_hooks(successful, object, after_hook_name, e)
194
- end
195
-
196
- begin
197
- yield # Call the user code
198
- ensure
199
- ex = call_after_hooks(successful, object, after_hook_name, nil)
200
- raise ex if ex
201
- end
202
- end
203
-
204
- def call_after_hooks(instrumenters, object, after_hook_name, ex)
205
- instrumenters.reverse_each do |instrumenter|
206
- begin
207
- instrumenter.public_send(after_hook_name, object)
208
- rescue => e
209
- ex = e
210
135
  end
211
136
  end
212
- ex
213
137
  end
214
138
  end
215
139
 
@@ -38,7 +38,7 @@ module GraphQL
38
38
  # (fewer clauses in a hot `case` block), but now it requires special handling here.
39
39
  # I think it's still worth it for the performance win, but if the number of special
40
40
  # cases grows, then maybe it's worth rethinking somehow.
41
- if @value.is_a?(StandardError) && @value != GraphQL::Execution::SKIP
41
+ if @value.is_a?(StandardError) && !@value.is_a?(GraphQL::Execution::Skip)
42
42
  raise @value
43
43
  else
44
44
  @value
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Execution
4
+ class LoadArgumentStep
5
+ def initialize(field_resolve_step:, arguments:, load_receiver:, argument_value:, argument_definition:, argument_key:)
6
+ @field_resolve_step = field_resolve_step
7
+ @load_receiver = load_receiver
8
+ @arguments = arguments
9
+ @argument_value = argument_value
10
+ @argument_definition = argument_definition
11
+ @argument_key = argument_key
12
+ @loaded_value = nil
13
+ end
14
+
15
+ def value
16
+ @loaded_value = @field_resolve_step.sync(@loaded_value)
17
+ assign_value
18
+ end
19
+
20
+ def call
21
+ context = @field_resolve_step.selections_step.query.context
22
+ @loaded_value = begin
23
+ @load_receiver.load_and_authorize_application_object(@argument_definition, @argument_value, context)
24
+ rescue GraphQL::UnauthorizedError => auth_err
25
+ context.schema.unauthorized_object(auth_err)
26
+ end
27
+ if (runner = @field_resolve_step.runner).resolves_lazies && runner.lazy?(@loaded_value)
28
+ runner.dataloader.lazy_at_depth(@field_resolve_step.path.size, self)
29
+ else
30
+ assign_value
31
+ end
32
+ rescue GraphQL::RuntimeError => err
33
+ @loaded_value = err
34
+ assign_value
35
+ rescue StandardError => stderr
36
+ @loaded_value = begin
37
+ context.query.handle_or_reraise(stderr)
38
+ rescue GraphQL::ExecutionError => ex_err
39
+ ex_err
40
+ end
41
+ assign_value
42
+ end
43
+
44
+ private
45
+
46
+ def assign_value
47
+ if @loaded_value.is_a?(GraphQL::RuntimeError)
48
+ @loaded_value.path = @field_resolve_step.path
49
+ @field_resolve_step.arguments = @loaded_value
50
+ else
51
+ query = @field_resolve_step.selections_step.query
52
+ query.current_trace.object_loaded(@argument_definition, @loaded_value, query.context)
53
+ @arguments[@argument_key] = @loaded_value
54
+ end
55
+
56
+ field_pending_steps = @field_resolve_step.pending_steps
57
+ field_pending_steps.delete(self)
58
+ if @field_resolve_step.arguments && field_pending_steps.size == 0 # rubocop:disable Development/ContextIsPassedCop
59
+ @field_resolve_step.runner.add_step(@field_resolve_step)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -56,7 +56,14 @@ module GraphQL
56
56
  else
57
57
  @arguments = if @field
58
58
  @query.after_lazy(@query.arguments_for(@ast_nodes.first, @field)) do |args|
59
- args.is_a?(Execution::Interpreter::Arguments) ? args.keyword_arguments : args
59
+ case args
60
+ when Execution::Interpreter::Arguments
61
+ args.keyword_arguments
62
+ when GraphQL::ExecutionError
63
+ EmptyObjects::EMPTY_HASH
64
+ else
65
+ args
66
+ end
60
67
  end
61
68
  else
62
69
  nil
@@ -80,6 +87,22 @@ module GraphQL
80
87
  selection(field_name, selected_type: selected_type, arguments: arguments).selected?
81
88
  end
82
89
 
90
+ # True if this node has a selection with alias matching `alias_name`.
91
+ # If `alias_name` is a String, it is treated as a GraphQL-style (camelized)
92
+ # field name and used verbatim. If `alias_name` is a Symbol, it is
93
+ # treated as a Ruby-style (underscored) name and camelized before comparing.
94
+ #
95
+ # If `arguments:` is provided, each provided key/value will be matched
96
+ # against the arguments in the next selection. This method will return false
97
+ # if any of the given `arguments:` are not present and matching in the next selection.
98
+ # (But, the next selection may contain _more_ than the given arguments.)
99
+ # @param alias_name [String, Symbol]
100
+ # @param arguments [Hash] Arguments which must match in the selection
101
+ # @return [Boolean]
102
+ def selects_alias?(alias_name, arguments: nil)
103
+ alias_selection(alias_name, arguments: arguments).selected?
104
+ end
105
+
83
106
  # @return [Boolean] True if this lookahead represents a field that was requested
84
107
  def selected?
85
108
  true
@@ -92,45 +115,52 @@ module GraphQL
92
115
  def selection(field_name, selected_type: @selected_type, arguments: nil)
93
116
  next_field_defn = case field_name
94
117
  when String
95
- @query.get_field(selected_type, field_name)
118
+ @query.types.field(selected_type, field_name)
96
119
  when Symbol
97
120
  # Try to avoid the `.to_s` below, if possible
98
121
  all_fields = if selected_type.kind.fields?
99
- @query.warden.fields(selected_type)
122
+ @query.types.fields(selected_type)
100
123
  else
101
124
  # Handle unions by checking possible
102
- @query.warden
125
+ @query.types
103
126
  .possible_types(selected_type)
104
- .map { |t| @query.warden.fields(t) }
105
- .flatten
127
+ .map { |t| @query.types.fields(t) }
128
+ .tap(&:flatten!)
106
129
  end
107
130
 
131
+
108
132
  if (match_by_orig_name = all_fields.find { |f| f.original_name == field_name })
109
133
  match_by_orig_name
110
134
  else
111
135
  # Symbol#name is only present on 3.0+
112
136
  sym_s = field_name.respond_to?(:name) ? field_name.name : field_name.to_s
113
137
  guessed_name = Schema::Member::BuildType.camelize(sym_s)
114
- @query.get_field(selected_type, guessed_name)
138
+ @query.types.field(selected_type, guessed_name)
115
139
  end
116
140
  end
141
+ lookahead_for_selection(next_field_defn, selected_type, arguments)
142
+ end
117
143
 
118
- if next_field_defn
119
- next_nodes = []
120
- @ast_nodes.each do |ast_node|
121
- ast_node.selections.each do |selection|
122
- find_selected_nodes(selection, next_field_defn, arguments: arguments, matches: next_nodes)
123
- end
124
- end
144
+ # Like {#selection}, but for aliases.
145
+ # It returns a null object (check with {#selected?})
146
+ # @return [GraphQL::Execution::Lookahead]
147
+ def alias_selection(alias_name, selected_type: @selected_type, arguments: nil)
148
+ alias_cache_key = [alias_name, arguments]
149
+ return alias_selections[key] if alias_selections.key?(alias_name)
125
150
 
126
- if next_nodes.any?
127
- Lookahead.new(query: @query, ast_nodes: next_nodes, field: next_field_defn, owner_type: selected_type)
128
- else
129
- NULL_LOOKAHEAD
130
- end
131
- else
132
- NULL_LOOKAHEAD
151
+ alias_node = lookup_alias_node(ast_nodes, alias_name)
152
+ return NULL_LOOKAHEAD unless alias_node
153
+
154
+ next_field_defn = @query.types.field(selected_type, alias_node.name)
155
+
156
+ alias_arguments = @query.arguments_for(alias_node, next_field_defn)
157
+ if alias_arguments.is_a?(::GraphQL::Execution::Interpreter::Arguments)
158
+ alias_arguments = alias_arguments.keyword_arguments
133
159
  end
160
+
161
+ return NULL_LOOKAHEAD if arguments && arguments != alias_arguments
162
+
163
+ alias_selections[alias_cache_key] = lookahead_for_selection(next_field_defn, selected_type, alias_arguments, alias_name)
134
164
  end
135
165
 
136
166
  # Like {#selection}, but for all nodes.
@@ -160,7 +190,7 @@ module GraphQL
160
190
 
161
191
  subselections_by_type.each do |type, ast_nodes_by_response_key|
162
192
  ast_nodes_by_response_key.each do |response_key, ast_nodes|
163
- field_defn = @query.get_field(type, ast_nodes.first.name)
193
+ field_defn = @query.types.field(type, ast_nodes.first.name)
164
194
  lookahead = Lookahead.new(query: @query, ast_nodes: ast_nodes, field: field_defn, owner_type: type)
165
195
  subselections.push(lookahead)
166
196
  end
@@ -243,7 +273,7 @@ module GraphQL
243
273
  elsif arguments.nil? || arguments.empty?
244
274
  selections_on_type[response_key] = [ast_selection]
245
275
  else
246
- field_defn = @query.get_field(selected_type, ast_selection.name)
276
+ field_defn = @query.types.field(selected_type, ast_selection.name)
247
277
  if arguments_match?(arguments, field_defn, ast_selection)
248
278
  selections_on_type[response_key] = [ast_selection]
249
279
  end
@@ -253,14 +283,14 @@ module GraphQL
253
283
  subselections_on_type = selections_on_type
254
284
  if (t = ast_selection.type)
255
285
  # Assuming this is valid, that `t` will be found.
256
- on_type = @query.get_type(t.name)
286
+ on_type = @query.types.type(t.name)
257
287
  subselections_on_type = subselections_by_type[on_type] ||= {}
258
288
  end
259
289
  find_selections(subselections_by_type, subselections_on_type, on_type, ast_selection.selections, arguments)
260
290
  when GraphQL::Language::Nodes::FragmentSpread
261
- frag_defn = @query.fragments[ast_selection.name] || raise("Invariant: Can't look ahead to nonexistent fragment #{ast_selection.name} (found: #{@query.fragments.keys})")
291
+ frag_defn = lookup_fragment(ast_selection)
262
292
  # Again, assuming a valid AST
263
- on_type = @query.get_type(frag_defn.type.name)
293
+ on_type = @query.types.type(frag_defn.type.name)
264
294
  subselections_on_type = subselections_by_type[on_type] ||= {}
265
295
  find_selections(subselections_by_type, subselections_on_type, on_type, frag_defn.selections, arguments)
266
296
  else
@@ -271,11 +301,11 @@ module GraphQL
271
301
 
272
302
  # If a selection on `node` matches `field_name` (which is backed by `field_defn`)
273
303
  # and matches the `arguments:` constraints, then add that node to `matches`
274
- def find_selected_nodes(node, field_defn, arguments:, matches:)
304
+ def find_selected_nodes(node, field_name, field_defn, arguments:, matches:, alias_name: NOT_CONFIGURED)
275
305
  return if skipped_by_directive?(node)
276
306
  case node
277
307
  when GraphQL::Language::Nodes::Field
278
- if node.name == field_defn.graphql_name
308
+ if node.name == field_name && (NOT_CONFIGURED.equal?(alias_name) || node.alias == alias_name)
279
309
  if arguments.nil? || arguments.empty?
280
310
  # No constraint applied
281
311
  matches << node
@@ -284,10 +314,10 @@ module GraphQL
284
314
  end
285
315
  end
286
316
  when GraphQL::Language::Nodes::InlineFragment
287
- node.selections.each { |s| find_selected_nodes(s, field_defn, arguments: arguments, matches: matches) }
317
+ node.selections.each { |s| find_selected_nodes(s, field_name, field_defn, arguments: arguments, matches: matches, alias_name: alias_name) }
288
318
  when GraphQL::Language::Nodes::FragmentSpread
289
- frag_defn = @query.fragments[node.name] || raise("Invariant: Can't look ahead to nonexistent fragment #{node.name} (found: #{@query.fragments.keys})")
290
- frag_defn.selections.each { |s| find_selected_nodes(s, field_defn, arguments: arguments, matches: matches) }
319
+ frag_defn = lookup_fragment(node)
320
+ frag_defn.selections.each { |s| find_selected_nodes(s, field_name, field_defn, arguments: arguments, matches: matches, alias_name: alias_name) }
291
321
  else
292
322
  raise "Unexpected selection comparison on #{node.class.name} (#{node})"
293
323
  end
@@ -306,6 +336,50 @@ module GraphQL
306
336
  query_kwargs.key?(arg_name_sym) && query_kwargs[arg_name_sym] == arg_value
307
337
  end
308
338
  end
339
+
340
+ def lookahead_for_selection(field_defn, selected_type, arguments, alias_name = NOT_CONFIGURED)
341
+ return NULL_LOOKAHEAD unless field_defn
342
+
343
+ next_nodes = []
344
+ field_name = field_defn.name
345
+ @ast_nodes.each do |ast_node|
346
+ ast_node.selections.each do |selection|
347
+ find_selected_nodes(selection, field_name, field_defn, arguments: arguments, matches: next_nodes, alias_name: alias_name)
348
+ end
349
+ end
350
+
351
+ return NULL_LOOKAHEAD if next_nodes.empty?
352
+
353
+ Lookahead.new(query: @query, ast_nodes: next_nodes, field: field_defn, owner_type: selected_type)
354
+ end
355
+
356
+ def alias_selections
357
+ return @alias_selections if defined?(@alias_selections)
358
+ @alias_selections ||= {}
359
+ end
360
+
361
+ def lookup_alias_node(nodes, name)
362
+ return if nodes.empty?
363
+
364
+ nodes.flat_map(&:children)
365
+ .flat_map { |child| unwrap_fragments(child) }
366
+ .find { |child| child.is_a?(GraphQL::Language::Nodes::Field) && child.alias == name }
367
+ end
368
+
369
+ def unwrap_fragments(node)
370
+ case node
371
+ when GraphQL::Language::Nodes::InlineFragment
372
+ node.children
373
+ when GraphQL::Language::Nodes::FragmentSpread
374
+ lookup_fragment(node).children
375
+ else
376
+ [node]
377
+ end
378
+ end
379
+
380
+ def lookup_fragment(ast_selection)
381
+ @query.fragments[ast_selection.name] || raise("Invariant: Can't look ahead to nonexistent fragment #{ast_selection.name} (found: #{@query.fragments.keys})")
382
+ end
309
383
  end
310
384
  end
311
385
  end
@@ -32,14 +32,15 @@ module GraphQL
32
32
  @queries = queries
33
33
  @queries.each { |q| q.multiplex = self }
34
34
  @context = context
35
- @current_trace = @context[:trace] || schema.new_trace(multiplex: self)
36
35
  @dataloader = @context[:dataloader] ||= @schema.dataloader_class.new
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
36
+ @tracers = schema.tracers + (context[:tracers] || EmptyObjects::EMPTY_ARRAY)
42
37
  @max_complexity = max_complexity
38
+ @current_trace = context[:trace] ||= schema.new_trace(multiplex: self)
39
+ @logger = nil
40
+ end
41
+
42
+ def logger
43
+ @logger ||= @schema.logger_for(context)
43
44
  end
44
45
  end
45
46
  end