graphql 2.0.32 → 2.5.22

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 (308) 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 +95 -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/interpreter/argument_value.rb +5 -1
  83. data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
  84. data/lib/graphql/execution/interpreter/resolve.rb +23 -25
  85. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +228 -0
  86. data/lib/graphql/execution/interpreter/runtime.rb +363 -434
  87. data/lib/graphql/execution/interpreter.rb +91 -164
  88. data/lib/graphql/execution/lookahead.rb +105 -31
  89. data/lib/graphql/execution/multiplex.rb +7 -6
  90. data/lib/graphql/execution/next/field_resolve_step.rb +711 -0
  91. data/lib/graphql/execution/next/load_argument_step.rb +60 -0
  92. data/lib/graphql/execution/next/prepare_object_step.rb +129 -0
  93. data/lib/graphql/execution/next/runner.rb +389 -0
  94. data/lib/graphql/execution/next/selections_step.rb +37 -0
  95. data/lib/graphql/execution/next.rb +70 -0
  96. data/lib/graphql/execution.rb +1 -0
  97. data/lib/graphql/execution_error.rb +13 -10
  98. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  99. data/lib/graphql/introspection/directive_type.rb +7 -3
  100. data/lib/graphql/introspection/dynamic_fields.rb +5 -1
  101. data/lib/graphql/introspection/entry_points.rb +20 -6
  102. data/lib/graphql/introspection/enum_value_type.rb +5 -5
  103. data/lib/graphql/introspection/field_type.rb +13 -5
  104. data/lib/graphql/introspection/input_value_type.rb +21 -13
  105. data/lib/graphql/introspection/schema_type.rb +8 -11
  106. data/lib/graphql/introspection/type_type.rb +64 -28
  107. data/lib/graphql/invalid_name_error.rb +1 -1
  108. data/lib/graphql/invalid_null_error.rb +26 -17
  109. data/lib/graphql/language/block_string.rb +34 -18
  110. data/lib/graphql/language/cache.rb +13 -0
  111. data/lib/graphql/language/comment.rb +18 -0
  112. data/lib/graphql/language/definition_slice.rb +1 -1
  113. data/lib/graphql/language/document_from_schema_definition.rb +90 -61
  114. data/lib/graphql/language/lexer.rb +319 -193
  115. data/lib/graphql/language/nodes.rb +136 -77
  116. data/lib/graphql/language/parser.rb +807 -1985
  117. data/lib/graphql/language/printer.rb +324 -151
  118. data/lib/graphql/language/sanitized_printer.rb +21 -23
  119. data/lib/graphql/language/static_visitor.rb +171 -0
  120. data/lib/graphql/language/visitor.rb +23 -83
  121. data/lib/graphql/language.rb +71 -1
  122. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  123. data/lib/graphql/pagination/array_connection.rb +6 -6
  124. data/lib/graphql/pagination/connection.rb +30 -1
  125. data/lib/graphql/pagination/connections.rb +32 -0
  126. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  127. data/lib/graphql/query/context/scoped_context.rb +101 -0
  128. data/lib/graphql/query/context.rb +82 -144
  129. data/lib/graphql/query/null_context.rb +15 -18
  130. data/lib/graphql/query/partial.rb +179 -0
  131. data/lib/graphql/query/validation_pipeline.rb +4 -4
  132. data/lib/graphql/query/variable_validation_error.rb +1 -1
  133. data/lib/graphql/query/variables.rb +3 -3
  134. data/lib/graphql/query.rb +126 -81
  135. data/lib/graphql/railtie.rb +16 -6
  136. data/lib/graphql/rake_task.rb +3 -12
  137. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  138. data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
  139. data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
  140. data/lib/graphql/rubocop.rb +2 -0
  141. data/lib/graphql/schema/addition.rb +26 -13
  142. data/lib/graphql/schema/always_visible.rb +7 -2
  143. data/lib/graphql/schema/argument.rb +75 -9
  144. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  145. data/lib/graphql/schema/build_from_definition.rb +123 -60
  146. data/lib/graphql/schema/directive/flagged.rb +4 -2
  147. data/lib/graphql/schema/directive/one_of.rb +12 -0
  148. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  149. data/lib/graphql/schema/directive.rb +54 -2
  150. data/lib/graphql/schema/enum.rb +110 -27
  151. data/lib/graphql/schema/enum_value.rb +10 -2
  152. data/lib/graphql/schema/field/connection_extension.rb +15 -49
  153. data/lib/graphql/schema/field/scope_extension.rb +23 -7
  154. data/lib/graphql/schema/field.rb +245 -118
  155. data/lib/graphql/schema/field_extension.rb +34 -1
  156. data/lib/graphql/schema/has_single_input_argument.rb +160 -0
  157. data/lib/graphql/schema/input_object.rb +116 -60
  158. data/lib/graphql/schema/interface.rb +34 -16
  159. data/lib/graphql/schema/introspection_system.rb +8 -17
  160. data/lib/graphql/schema/late_bound_type.rb +4 -0
  161. data/lib/graphql/schema/list.rb +3 -3
  162. data/lib/graphql/schema/loader.rb +3 -4
  163. data/lib/graphql/schema/member/base_dsl_methods.rb +18 -2
  164. data/lib/graphql/schema/member/has_arguments.rb +132 -100
  165. data/lib/graphql/schema/member/has_authorization.rb +35 -0
  166. data/lib/graphql/schema/member/has_dataloader.rb +99 -0
  167. data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
  168. data/lib/graphql/schema/member/has_directives.rb +4 -4
  169. data/lib/graphql/schema/member/has_fields.rb +115 -15
  170. data/lib/graphql/schema/member/has_interfaces.rb +26 -12
  171. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  172. data/lib/graphql/schema/member/has_validators.rb +1 -1
  173. data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
  174. data/lib/graphql/schema/member/scoped.rb +19 -0
  175. data/lib/graphql/schema/member/type_system_helpers.rb +17 -4
  176. data/lib/graphql/schema/member/validates_input.rb +3 -3
  177. data/lib/graphql/schema/member.rb +6 -0
  178. data/lib/graphql/schema/mutation.rb +7 -0
  179. data/lib/graphql/schema/object.rb +34 -8
  180. data/lib/graphql/schema/printer.rb +9 -7
  181. data/lib/graphql/schema/ractor_shareable.rb +79 -0
  182. data/lib/graphql/schema/relay_classic_mutation.rb +6 -129
  183. data/lib/graphql/schema/resolver.rb +90 -32
  184. data/lib/graphql/schema/scalar.rb +4 -9
  185. data/lib/graphql/schema/subscription.rb +63 -10
  186. data/lib/graphql/schema/timeout.rb +19 -2
  187. data/lib/graphql/schema/type_expression.rb +2 -2
  188. data/lib/graphql/schema/union.rb +2 -2
  189. data/lib/graphql/schema/unique_within_type.rb +1 -1
  190. data/lib/graphql/schema/validator/all_validator.rb +62 -0
  191. data/lib/graphql/schema/validator/required_validator.rb +92 -11
  192. data/lib/graphql/schema/validator.rb +3 -1
  193. data/lib/graphql/schema/visibility/migration.rb +188 -0
  194. data/lib/graphql/schema/visibility/profile.rb +445 -0
  195. data/lib/graphql/schema/visibility/visit.rb +190 -0
  196. data/lib/graphql/schema/visibility.rb +311 -0
  197. data/lib/graphql/schema/warden.rb +275 -103
  198. data/lib/graphql/schema.rb +950 -210
  199. data/lib/graphql/static_validation/all_rules.rb +3 -3
  200. data/lib/graphql/static_validation/base_visitor.rb +7 -6
  201. data/lib/graphql/static_validation/literal_validator.rb +6 -7
  202. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  203. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  204. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -2
  205. data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
  206. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
  207. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +12 -2
  208. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
  209. data/lib/graphql/static_validation/rules/fields_will_merge.rb +88 -25
  210. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
  211. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  212. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +12 -2
  213. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  214. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  215. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  216. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
  217. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  218. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +5 -5
  219. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
  220. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
  221. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +7 -3
  222. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  223. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  224. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
  225. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +11 -2
  226. data/lib/graphql/static_validation/validation_context.rb +21 -5
  227. data/lib/graphql/static_validation/validator.rb +9 -1
  228. data/lib/graphql/static_validation.rb +0 -1
  229. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -5
  230. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  231. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
  232. data/lib/graphql/subscriptions/event.rb +21 -4
  233. data/lib/graphql/subscriptions/serialize.rb +3 -1
  234. data/lib/graphql/subscriptions.rb +21 -17
  235. data/lib/graphql/testing/helpers.rb +161 -0
  236. data/lib/graphql/testing/mock_action_cable.rb +111 -0
  237. data/lib/graphql/testing.rb +3 -0
  238. data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
  239. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  240. data/lib/graphql/tracing/appoptics_trace.rb +7 -3
  241. data/lib/graphql/tracing/appoptics_tracing.rb +9 -2
  242. data/lib/graphql/tracing/appsignal_trace.rb +32 -59
  243. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  244. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  245. data/lib/graphql/tracing/data_dog_trace.rb +46 -162
  246. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  247. data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +74 -0
  248. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  249. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  250. data/lib/graphql/tracing/detailed_trace.rb +156 -0
  251. data/lib/graphql/tracing/legacy_hooks_trace.rb +75 -0
  252. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  253. data/lib/graphql/tracing/monitor_trace.rb +283 -0
  254. data/lib/graphql/tracing/new_relic_trace.rb +47 -54
  255. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  256. data/lib/graphql/tracing/notifications_trace.rb +183 -37
  257. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  258. data/lib/graphql/tracing/null_trace.rb +9 -0
  259. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  260. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  261. data/lib/graphql/tracing/perfetto_trace.rb +864 -0
  262. data/lib/graphql/tracing/platform_tracing.rb +3 -1
  263. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +5 -1
  264. data/lib/graphql/tracing/prometheus_trace.rb +73 -73
  265. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  266. data/lib/graphql/tracing/scout_trace.rb +32 -58
  267. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  268. data/lib/graphql/tracing/sentry_trace.rb +82 -0
  269. data/lib/graphql/tracing/statsd_trace.rb +33 -45
  270. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  271. data/lib/graphql/tracing/trace.rb +112 -1
  272. data/lib/graphql/tracing.rb +31 -28
  273. data/lib/graphql/type_kinds.rb +2 -1
  274. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  275. data/lib/graphql/types/relay/connection_behaviors.rb +44 -2
  276. data/lib/graphql/types/relay/edge_behaviors.rb +18 -0
  277. data/lib/graphql/types/relay/has_node_field.rb +13 -8
  278. data/lib/graphql/types/relay/has_nodes_field.rb +13 -8
  279. data/lib/graphql/types/relay/node_behaviors.rb +13 -2
  280. data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
  281. data/lib/graphql/types.rb +18 -10
  282. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  283. data/lib/graphql/unauthorized_error.rb +5 -1
  284. data/lib/graphql/version.rb +1 -1
  285. data/lib/graphql.rb +71 -54
  286. data/readme.md +12 -2
  287. metadata +233 -37
  288. data/lib/graphql/analysis/ast/analyzer.rb +0 -84
  289. data/lib/graphql/analysis/ast/field_usage.rb +0 -57
  290. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  291. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  292. data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
  293. data/lib/graphql/analysis/ast/query_depth.rb +0 -55
  294. data/lib/graphql/analysis/ast/visitor.rb +0 -276
  295. data/lib/graphql/analysis/ast.rb +0 -81
  296. data/lib/graphql/backtrace/inspect_result.rb +0 -50
  297. data/lib/graphql/backtrace/trace.rb +0 -96
  298. data/lib/graphql/backtrace/tracer.rb +0 -80
  299. data/lib/graphql/deprecation.rb +0 -9
  300. data/lib/graphql/filter.rb +0 -59
  301. data/lib/graphql/language/parser.y +0 -560
  302. data/lib/graphql/language/token.rb +0 -34
  303. data/lib/graphql/schema/base_64_bp.rb +0 -26
  304. data/lib/graphql/schema/invalid_type_error.rb +0 -7
  305. data/lib/graphql/schema/null_mask.rb +0 -11
  306. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
  307. data/lib/graphql/static_validation/type_stack.rb +0 -216
  308. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
@@ -20,196 +20,123 @@ 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
73
-
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
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)
53
+
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
+ if query.subscription? && !query.subscription_update?
62
+ subs_namespace = query.context.namespace(:subscriptions)
63
+ subs_namespace[:events] = []
64
+ subs_namespace[:subscriptions] = {}
65
+ end
66
+ multiplex.dataloader.append_job {
67
+ operation = query.selected_operation
68
+ result = if operation.nil? || !query.valid? || !query.context.errors.empty?
69
+ NO_OPERATION
70
+ else
71
+ begin
72
+ # Although queries in a multiplex _share_ an Interpreter instance,
73
+ # they also have another item of state, which is private to that query
74
+ # in particular, assign it here:
75
+ runtime = Runtime.new(query: query)
76
+ query.context.namespace(:interpreter_runtime)[:runtime] = runtime
77
+
78
+ query.current_trace.execute_query(query: query) do
79
+ runtime.run_eager
81
80
  end
82
- results[idx] = result
83
- }
81
+ rescue GraphQL::ExecutionError => err
82
+ query.context.errors << err
83
+ end
84
84
  end
85
+ results[idx] = result
86
+ }
87
+ end
85
88
 
86
- multiplex.dataloader.run
89
+ multiplex.dataloader.run(trace_query_lazy: multiplex)
87
90
 
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
105
- end
106
- 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
- }
125
-
126
- if query.context.errors.any?
127
- error_result = query.context.errors.map(&:to_h)
128
- result["errors"] = error_result
129
- end
91
+ # Then, find all errors and assign the result to the query object
92
+ results.each_with_index do |data_result, idx|
93
+ query = queries[idx]
94
+ if (events = query.context.namespace(:subscriptions)[:events]) && !events.empty?
95
+ schema.subscriptions.write_subscription(query, events)
96
+ end
97
+ # Assign the result so that it can be accessed in instrumentation
98
+ query.result_values = if data_result.equal?(NO_OPERATION)
99
+ if !query.valid? || !query.context.errors.empty?
100
+ # A bit weird, but `Query#static_errors` _includes_ `query.context.errors`
101
+ { "errors" => query.static_errors.map(&:to_h) }
102
+ else
103
+ data_result
104
+ end
105
+ else
106
+ result = {}
130
107
 
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
108
+ if !query.context.errors.empty?
109
+ error_result = query.context.errors.map(&:to_h)
110
+ result["errors"] = error_result
138
111
  end
139
112
 
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
- }
113
+ result["data"] = query.context.namespace(:interpreter_runtime)[:runtime].final_result
114
+
115
+ result
116
+ end
117
+ if query.context.namespace?(:__query_result_extensions__)
118
+ query.result_values["extensions"] = query.context.namespace(:__query_result_extensions__)
153
119
  end
120
+ # Get the Query::Result, not the Hash
121
+ results[idx] = query.result
154
122
  end
155
- end
156
- end
157
- end
158
123
 
159
- private
160
-
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
124
+ results
125
+ rescue Exception
126
+ # TODO rescue at a higher level so it will catch errors in analysis, too
127
+ # Assign values here so that the query's `@executed` becomes true
128
+ queries.map { |q| q.result_values ||= {} }
129
+ raise
130
+ ensure
131
+ Fiber[:__graphql_current_multiplex] = nil
132
+ queries.map { |query|
133
+ runtime = query.context.namespace(:interpreter_runtime)[:runtime]
134
+ if runtime
135
+ runtime.delete_all_interpreter_context
136
+ end
172
137
  }
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
138
  end
211
139
  end
212
- ex
213
140
  end
214
141
  end
215
142
 
@@ -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