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
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require "graphql/execution/interpreter/runtime/graphql_result"
2
3
 
3
4
  module GraphQL
4
5
  module Execution
@@ -10,176 +11,19 @@ module GraphQL
10
11
  class Runtime
11
12
  class CurrentState
12
13
  def initialize
13
- @current_object = nil
14
14
  @current_field = nil
15
15
  @current_arguments = nil
16
16
  @current_result_name = nil
17
17
  @current_result = nil
18
+ @was_authorized_by_scope_items = nil
18
19
  end
19
20
 
20
- attr_accessor :current_result, :current_result_name,
21
- :current_arguments, :current_field, :current_object
22
- end
23
-
24
- module GraphQLResult
25
- def initialize(result_name, parent_result, is_non_null_in_parent)
26
- @graphql_parent = parent_result
27
- if parent_result && parent_result.graphql_dead
28
- @graphql_dead = true
29
- end
30
- @graphql_result_name = result_name
31
- @graphql_is_non_null_in_parent = is_non_null_in_parent
32
- # Jump through some hoops to avoid creating this duplicate storage if at all possible.
33
- @graphql_metadata = nil
34
- end
35
-
36
- def path
37
- @path ||= build_path([])
38
- end
39
-
40
- def build_path(path_array)
41
- graphql_result_name && path_array.unshift(graphql_result_name)
42
- @graphql_parent ? @graphql_parent.build_path(path_array) : path_array
43
- end
44
-
45
- attr_accessor :graphql_dead
46
- attr_reader :graphql_parent, :graphql_result_name, :graphql_is_non_null_in_parent
47
-
48
- # @return [Hash] Plain-Ruby result data (`@graphql_metadata` contains Result wrapper objects)
49
- attr_accessor :graphql_result_data
50
- end
51
-
52
- class GraphQLResultHash
53
- def initialize(_result_name, _parent_result, _is_non_null_in_parent)
54
- super
55
- @graphql_result_data = {}
21
+ def current_object
22
+ @current_result.graphql_application_value
56
23
  end
57
24
 
58
- include GraphQLResult
59
-
60
- attr_accessor :graphql_merged_into
61
-
62
- def set_leaf(key, value)
63
- # This is a hack.
64
- # Basically, this object is merged into the root-level result at some point.
65
- # But the problem is, some lazies are created whose closures retain reference to _this_
66
- # object. When those lazies are resolved, they cause an update to this object.
67
- #
68
- # In order to return a proper top-level result, we have to update that top-level result object.
69
- # In order to return a proper partial result (eg, for a directive), we have to update this object, too.
70
- # Yowza.
71
- if (t = @graphql_merged_into)
72
- t.set_leaf(key, value)
73
- end
74
-
75
- @graphql_result_data[key] = value
76
- # keep this up-to-date if it's been initialized
77
- @graphql_metadata && @graphql_metadata[key] = value
78
-
79
- value
80
- end
81
-
82
- def set_child_result(key, value)
83
- if (t = @graphql_merged_into)
84
- t.set_child_result(key, value)
85
- end
86
- @graphql_result_data[key] = value.graphql_result_data
87
- # If we encounter some part of this response that requires metadata tracking,
88
- # then create the metadata hash if necessary. It will be kept up-to-date after this.
89
- (@graphql_metadata ||= @graphql_result_data.dup)[key] = value
90
- value
91
- end
92
-
93
- def delete(key)
94
- @graphql_metadata && @graphql_metadata.delete(key)
95
- @graphql_result_data.delete(key)
96
- end
97
-
98
- def each
99
- (@graphql_metadata || @graphql_result_data).each { |k, v| yield(k, v) }
100
- end
101
-
102
- def values
103
- (@graphql_metadata || @graphql_result_data).values
104
- end
105
-
106
- def key?(k)
107
- @graphql_result_data.key?(k)
108
- end
109
-
110
- def [](k)
111
- (@graphql_metadata || @graphql_result_data)[k]
112
- end
113
-
114
- def merge_into(into_result)
115
- self.each do |key, value|
116
- case value
117
- when GraphQLResultHash
118
- next_into = into_result[key]
119
- if next_into
120
- value.merge_into(next_into)
121
- else
122
- into_result.set_child_result(key, value)
123
- end
124
- when GraphQLResultArray
125
- # There's no special handling of arrays because currently, there's no way to split the execution
126
- # of a list over several concurrent flows.
127
- into_result.set_child_result(key, value)
128
- else
129
- # We have to assume that, since this passed the `fields_will_merge` selection,
130
- # that the old and new values are the same.
131
- into_result.set_leaf(key, value)
132
- end
133
- end
134
- @graphql_merged_into = into_result
135
- end
136
- end
137
-
138
- class GraphQLResultArray
139
- include GraphQLResult
140
-
141
- def initialize(_result_name, _parent_result, _is_non_null_in_parent)
142
- super
143
- @graphql_result_data = []
144
- end
145
-
146
- def graphql_skip_at(index)
147
- # Mark this index as dead. It's tricky because some indices may already be storing
148
- # `Lazy`s. So the runtime is still holding indexes _before_ skipping,
149
- # this object has to coordinate incoming writes to account for any already-skipped indices.
150
- @skip_indices ||= []
151
- @skip_indices << index
152
- offset_by = @skip_indices.count { |skipped_idx| skipped_idx < index}
153
- delete_at_index = index - offset_by
154
- @graphql_metadata && @graphql_metadata.delete_at(delete_at_index)
155
- @graphql_result_data.delete_at(delete_at_index)
156
- end
157
-
158
- def set_leaf(idx, value)
159
- if @skip_indices
160
- offset_by = @skip_indices.count { |skipped_idx| skipped_idx < idx }
161
- idx -= offset_by
162
- end
163
- @graphql_result_data[idx] = value
164
- @graphql_metadata && @graphql_metadata[idx] = value
165
- value
166
- end
167
-
168
- def set_child_result(idx, value)
169
- if @skip_indices
170
- offset_by = @skip_indices.count { |skipped_idx| skipped_idx < idx }
171
- idx -= offset_by
172
- end
173
- @graphql_result_data[idx] = value.graphql_result_data
174
- # If we encounter some part of this response that requires metadata tracking,
175
- # then create the metadata hash if necessary. It will be kept up-to-date after this.
176
- (@graphql_metadata ||= @graphql_result_data.dup)[idx] = value
177
- value
178
- end
179
-
180
- def values
181
- (@graphql_metadata || @graphql_result_data)
182
- end
25
+ attr_accessor :current_result, :current_result_name,
26
+ :current_arguments, :current_field, :was_authorized_by_scope_items
183
27
  end
184
28
 
185
29
  # @return [GraphQL::Query]
@@ -191,14 +35,13 @@ module GraphQL
191
35
  # @return [GraphQL::Query::Context]
192
36
  attr_reader :context
193
37
 
194
- def initialize(query:, lazies_at_depth:)
38
+ def initialize(query:)
195
39
  @query = query
196
40
  @current_trace = query.current_trace
197
41
  @dataloader = query.multiplex.dataloader
198
- @lazies_at_depth = lazies_at_depth
199
42
  @schema = query.schema
200
43
  @context = query.context
201
- @response = GraphQLResultHash.new(nil, nil, false)
44
+ @response = nil
202
45
  # Identify runtime directives by checking which of this schema's directives have overridden `def self.resolve`
203
46
  @runtime_directive_names = []
204
47
  noop_resolve_owner = GraphQL::Schema::Directive.singleton_class
@@ -208,98 +51,165 @@ module GraphQL
208
51
  @runtime_directive_names << name
209
52
  end
210
53
  end
211
- # A cache of { Class => { String => Schema::Field } }
212
- # Which assumes that MyObject.get_field("myField") will return the same field
213
- # during the lifetime of a query
214
- @fields_cache = Hash.new { |h, k| h[k] = {} }
215
- # this can by by-identity since owners are the same object, but not the sub-hash, which uses strings.
216
- @fields_cache.compare_by_identity
217
54
  # { Class => Boolean }
218
- @lazy_cache = {}
219
- @lazy_cache.compare_by_identity
55
+ @lazy_cache = {}.compare_by_identity
220
56
  end
221
57
 
222
58
  def final_result
223
- @response && @response.graphql_result_data
59
+ @response.respond_to?(:graphql_result_data) ? @response.graphql_result_data : @response
224
60
  end
225
61
 
226
62
  def inspect
227
63
  "#<#{self.class.name} response=#{@response.inspect}>"
228
64
  end
229
65
 
230
- def tap_or_each(obj_or_array)
231
- if obj_or_array.is_a?(Array)
232
- obj_or_array.each do |item|
233
- yield(item, true)
234
- end
235
- else
236
- yield(obj_or_array, false)
237
- end
238
- end
239
-
240
- # This _begins_ the execution. Some deferred work
241
- # might be stored up in lazies.
242
66
  # @return [void]
243
67
  def run_eager
244
- root_operation = query.selected_operation
245
- root_op_type = root_operation.operation_type || "query"
246
- root_type = schema.root_type_for_operation(root_op_type)
247
- st = get_current_runtime_state
248
- st.current_object = query.root_value
249
- st.current_result = @response
250
- runtime_object = root_type.wrap(query.root_value, context)
251
- runtime_object = schema.sync_lazy(runtime_object)
252
-
253
- if runtime_object.nil?
254
- # Root .authorized? returned false.
255
- @response = nil
68
+ root_type = query.root_type
69
+ case query
70
+ when GraphQL::Query
71
+ ast_node = query.selected_operation
72
+ selections = ast_node.selections
73
+ object = query.root_value
74
+ is_eager = ast_node.operation_type == "mutation"
75
+ base_path = nil
76
+ when GraphQL::Query::Partial
77
+ ast_node = query.ast_nodes.first
78
+ selections = query.ast_nodes.map(&:selections).inject(&:+)
79
+ object = query.object
80
+ is_eager = false
81
+ base_path = query.path
256
82
  else
257
- call_method_on_directives(:resolve, runtime_object, root_operation.directives) do # execute query level directives
258
- gathered_selections = gather_selections(runtime_object, root_type, root_operation.selections)
259
- # This is kind of a hack -- `gathered_selections` is an Array if any of the selections
260
- # require isolation during execution (because of runtime directives). In that case,
261
- # make a new, isolated result hash for writing the result into. (That isolated response
262
- # is eventually merged back into the main response)
263
- #
264
- # Otherwise, `gathered_selections` is a hash of selections which can be
265
- # directly evaluated and the results can be written right into the main response hash.
266
- tap_or_each(gathered_selections) do |selections, is_selection_array|
267
- if is_selection_array
268
- selection_response = GraphQLResultHash.new(nil, nil, false)
269
- final_response = @response
270
- else
271
- selection_response = @response
272
- final_response = nil
273
- end
274
-
275
- @dataloader.append_job {
276
- st = get_current_runtime_state
277
- st.current_object = query.root_value
278
- st.current_result = selection_response
279
- # This is a less-frequent case; use a fast check since it's often not there.
280
- if (directives = selections[:graphql_directives])
281
- selections.delete(:graphql_directives)
83
+ raise ArgumentError, "Unexpected Runnable, can't execute: #{query.class} (#{query.inspect})"
84
+ end
85
+ object = schema.sync_lazy(object) # TODO test query partial with lazy root object
86
+ runtime_state = get_current_runtime_state
87
+ case root_type.kind.name
88
+ when "OBJECT"
89
+ object_proxy = root_type.wrap(object, context)
90
+ object_proxy = schema.sync_lazy(object_proxy)
91
+ if object_proxy.nil?
92
+ @response = nil
93
+ else
94
+ @response = GraphQLResultHash.new(nil, root_type, object_proxy, nil, false, selections, is_eager, ast_node, nil, nil)
95
+ @response.base_path = base_path
96
+ runtime_state.current_result = @response
97
+ call_method_on_directives(:resolve, object, ast_node.directives) do
98
+ each_gathered_selections(@response) do |selections, is_selection_array, ordered_result_keys|
99
+ @response.ordered_result_keys ||= ordered_result_keys
100
+ if is_selection_array
101
+ selection_response = GraphQLResultHash.new(nil, root_type, object_proxy, nil, false, selections, is_eager, ast_node, nil, nil)
102
+ selection_response.ordered_result_keys = ordered_result_keys
103
+ final_response = @response
104
+ else
105
+ selection_response = @response
106
+ final_response = nil
282
107
  end
283
- call_method_on_directives(:resolve, runtime_object, directives) do
108
+
109
+ @dataloader.append_job {
284
110
  evaluate_selections(
285
- runtime_object,
286
- root_type,
287
- root_op_type == "mutation",
288
111
  selections,
289
112
  selection_response,
290
113
  final_response,
291
114
  nil,
292
115
  )
293
- end
294
- }
116
+ }
117
+ end
118
+ end
119
+ end
120
+ when "LIST"
121
+ inner_type = root_type.unwrap
122
+ case inner_type.kind.name
123
+ when "SCALAR", "ENUM"
124
+ result_name = ast_node.alias || ast_node.name
125
+ field_defn = query.field_definition
126
+ owner_type = field_defn.owner
127
+ selection_result = GraphQLResultHash.new(nil, owner_type, nil, nil, false, EmptyObjects::EMPTY_ARRAY, false, ast_node, nil, nil)
128
+ selection_result.base_path = base_path
129
+ selection_result.ordered_result_keys = [result_name]
130
+ runtime_state = get_current_runtime_state
131
+ runtime_state.current_result = selection_result
132
+ runtime_state.current_result_name = result_name
133
+ continue_value = continue_value(object, field_defn, false, ast_node, result_name, selection_result)
134
+ if HALT != continue_value
135
+ continue_field(continue_value, owner_type, field_defn, root_type, ast_node, nil, false, nil, nil, result_name, selection_result, false, runtime_state) # rubocop:disable Metrics/ParameterLists
136
+ end
137
+ @response = selection_result[result_name]
138
+ else
139
+ @response = GraphQLResultArray.new(nil, root_type, nil, nil, false, selections, false, ast_node, nil, nil)
140
+ @response.base_path = base_path
141
+ idx = nil
142
+ object.each do |inner_value|
143
+ idx ||= 0
144
+ this_idx = idx
145
+ idx += 1
146
+ @dataloader.append_job do
147
+ runtime_state.current_result_name = this_idx
148
+ runtime_state.current_result = @response
149
+ continue_field(
150
+ inner_value, root_type, nil, inner_type, nil, @response.graphql_selections, false, object_proxy,
151
+ nil, this_idx, @response, false, runtime_state
152
+ )
153
+ end
295
154
  end
296
155
  end
156
+ when "SCALAR", "ENUM"
157
+ result_name = ast_node.alias || ast_node.name
158
+ field_defn = query.field_definition
159
+ owner_type = field_defn.owner
160
+ selection_result = GraphQLResultHash.new(nil, owner_type, nil, nil, false, EmptyObjects::EMPTY_ARRAY, false, ast_node, nil, nil)
161
+ selection_result.ordered_result_keys = [result_name]
162
+ selection_result.base_path = base_path
163
+ runtime_state = get_current_runtime_state
164
+ runtime_state.current_result = selection_result
165
+ runtime_state.current_result_name = result_name
166
+ continue_value = continue_value(object, field_defn, false, ast_node, result_name, selection_result)
167
+ if HALT != continue_value
168
+ continue_field(continue_value, owner_type, field_defn, query.root_type, ast_node, nil, false, nil, nil, result_name, selection_result, false, runtime_state) # rubocop:disable Metrics/ParameterLists
169
+ end
170
+ @response = selection_result[result_name]
171
+ when "UNION", "INTERFACE"
172
+ resolved_type, _resolved_obj = resolve_type(root_type, object)
173
+ resolved_type = schema.sync_lazy(resolved_type)
174
+ object_proxy = resolved_type.wrap(object, context)
175
+ object_proxy = schema.sync_lazy(object_proxy)
176
+ @response = GraphQLResultHash.new(nil, resolved_type, object_proxy, nil, false, selections, false, query.ast_nodes.first, nil, nil)
177
+ @response.base_path = base_path
178
+ each_gathered_selections(@response) do |selections, is_selection_array, ordered_result_keys|
179
+ @response.ordered_result_keys ||= ordered_result_keys
180
+ if is_selection_array == true
181
+ raise "This isn't supported yet"
182
+ end
183
+
184
+ @dataloader.append_job {
185
+ evaluate_selections(
186
+ selections,
187
+ @response,
188
+ nil,
189
+ runtime_state,
190
+ )
191
+ }
192
+ end
193
+ else
194
+ raise "Invariant: unsupported type kind for partial execution: #{root_type.kind.inspect} (#{root_type})"
297
195
  end
298
- delete_all_interpreter_context
299
196
  nil
300
197
  end
301
198
 
302
- def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = {})
199
+ def each_gathered_selections(response_hash)
200
+ ordered_result_keys = []
201
+ gathered_selections = gather_selections(response_hash.graphql_application_value, response_hash.graphql_result_type, response_hash.graphql_selections, nil, {}, ordered_result_keys)
202
+ ordered_result_keys.uniq!
203
+ if gathered_selections.is_a?(Array)
204
+ gathered_selections.each do |item|
205
+ yield(item, true, ordered_result_keys)
206
+ end
207
+ else
208
+ yield(gathered_selections, false, ordered_result_keys)
209
+ end
210
+ end
211
+
212
+ def gather_selections(owner_object, owner_type, selections, selections_to_run, selections_by_name, ordered_result_keys)
303
213
  selections.each do |node|
304
214
  # Skip gathering this if the directive says so
305
215
  if !directives_include?(node, owner_object, owner_type)
@@ -308,10 +218,11 @@ module GraphQL
308
218
 
309
219
  if node.is_a?(GraphQL::Language::Nodes::Field)
310
220
  response_key = node.alias || node.name
221
+ ordered_result_keys << response_key
311
222
  selections = selections_by_name[response_key]
312
223
  # if there was already a selection of this field,
313
224
  # use an array to hold all selections,
314
- # otherise, use the single node to represent the selection
225
+ # otherwise, use the single node to represent the selection
315
226
  if selections
316
227
  # This field was already selected at least once,
317
228
  # add this node to the list of selections
@@ -324,7 +235,7 @@ module GraphQL
324
235
  end
325
236
  else
326
237
  # This is an InlineFragment or a FragmentSpread
327
- if @runtime_directive_names.any? && node.directives.any? { |d| @runtime_directive_names.include?(d.name) }
238
+ if !@runtime_directive_names.empty? && node.directives.any? { |d| @runtime_directive_names.include?(d.name) }
328
239
  next_selections = {}
329
240
  next_selections[:graphql_directives] = node.directives
330
241
  if selections_to_run
@@ -341,26 +252,26 @@ module GraphQL
341
252
  case node
342
253
  when GraphQL::Language::Nodes::InlineFragment
343
254
  if node.type
344
- type_defn = schema.get_type(node.type.name, context)
255
+ type_defn = query.types.type(node.type.name)
345
256
 
346
- if query.warden.possible_types(type_defn).include?(owner_type)
347
- result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
257
+ if query.types.possible_types(type_defn).include?(owner_type)
258
+ result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections, ordered_result_keys)
348
259
  if !result.equal?(next_selections)
349
260
  selections_to_run = result
350
261
  end
351
262
  end
352
263
  else
353
264
  # it's an untyped fragment, definitely continue
354
- result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
265
+ result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections, ordered_result_keys)
355
266
  if !result.equal?(next_selections)
356
267
  selections_to_run = result
357
268
  end
358
269
  end
359
270
  when GraphQL::Language::Nodes::FragmentSpread
360
271
  fragment_def = query.fragments[node.name]
361
- type_defn = query.get_type(fragment_def.type.name)
362
- if query.warden.possible_types(type_defn).include?(owner_type)
363
- result = gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
272
+ type_defn = query.types.type(fragment_def.type.name)
273
+ if query.types.possible_types(type_defn).include?(owner_type)
274
+ result = gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections, ordered_result_keys)
364
275
  if !result.equal?(next_selections)
365
276
  selections_to_run = result
366
277
  end
@@ -376,38 +287,46 @@ module GraphQL
376
287
  NO_ARGS = GraphQL::EmptyObjects::EMPTY_HASH
377
288
 
378
289
  # @return [void]
379
- def evaluate_selections(owner_object, owner_type, is_eager_selection, gathered_selections, selections_result, target_result, parent_object) # rubocop:disable Metrics/ParameterLists
380
- st = get_current_runtime_state
381
- st.current_object = owner_object
382
- st.current_result_name = nil
383
- st.current_result = selections_result
384
-
385
- finished_jobs = 0
386
- enqueued_jobs = gathered_selections.size
387
- gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
388
- @dataloader.append_job {
389
- evaluate_selection(
390
- result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_selection, selections_result, parent_object
391
- )
392
- finished_jobs += 1
393
- if target_result && finished_jobs == enqueued_jobs
394
- selections_result.merge_into(target_result)
290
+ def evaluate_selections(gathered_selections, selections_result, target_result, runtime_state) # rubocop:disable Metrics/ParameterLists
291
+ runtime_state ||= get_current_runtime_state
292
+ runtime_state.current_result_name = nil
293
+ runtime_state.current_result = selections_result
294
+ # This is a less-frequent case; use a fast check since it's often not there.
295
+ if (directives = gathered_selections[:graphql_directives])
296
+ gathered_selections.delete(:graphql_directives)
297
+ end
298
+
299
+ call_method_on_directives(:resolve, selections_result.graphql_application_value, directives) do
300
+ gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
301
+ # Field resolution may pause the fiber,
302
+ # so it wouldn't get to the `Resolve` call that happens below.
303
+ # So instead trigger a run from this outer context.
304
+ if selections_result.graphql_is_eager
305
+ @dataloader.clear_cache
306
+ @dataloader.run_isolated {
307
+ evaluate_selection(
308
+ result_name, field_ast_nodes_or_ast_node, selections_result
309
+ )
310
+ @dataloader.clear_cache
311
+ }
312
+ else
313
+ @dataloader.append_job {
314
+ evaluate_selection(
315
+ result_name, field_ast_nodes_or_ast_node, selections_result
316
+ )
317
+ }
395
318
  end
396
- }
397
- # Field resolution may pause the fiber,
398
- # so it wouldn't get to the `Resolve` call that happens below.
399
- # So instead trigger a run from this outer context.
400
- if is_eager_selection
401
- @dataloader.run
402
319
  end
320
+ if target_result
321
+ selections_result.merge_into(target_result)
322
+ end
323
+ selections_result
403
324
  end
404
-
405
- selections_result
406
325
  end
407
326
 
408
327
  # @return [void]
409
- def evaluate_selection(result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_field, selections_result, parent_object) # rubocop:disable Metrics/ParameterLists
410
- return if dead_result?(selections_result)
328
+ def evaluate_selection(result_name, field_ast_nodes_or_ast_node, selections_result) # rubocop:disable Metrics/ParameterLists
329
+ return if selections_result.graphql_dead
411
330
  # As a performance optimization, the hash key will be a `Node` if
412
331
  # there's only one selection of the field. But if there are multiple
413
332
  # selections of the field, it will be an Array of nodes
@@ -419,59 +338,48 @@ module GraphQL
419
338
  ast_node = field_ast_nodes_or_ast_node
420
339
  end
421
340
  field_name = ast_node.name
422
- # This can't use `query.get_field` because it gets confused on introspection below if `field_defn` isn't `nil`,
423
- # because of how `is_introspection` is used to call `.authorized_new` later on.
424
- field_defn = @fields_cache[owner_type][field_name] ||= owner_type.get_field(field_name, @context)
425
- is_introspection = false
426
- if field_defn.nil?
427
- field_defn = if owner_type == schema.query && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
428
- is_introspection = true
429
- entry_point_field
430
- elsif (dynamic_field = schema.introspection_system.dynamic_field(name: field_name))
431
- is_introspection = true
432
- dynamic_field
433
- else
434
- raise "Invariant: no field for #{owner_type}.#{field_name}"
435
- end
436
- end
437
-
438
- return_type = field_defn.type
341
+ owner_type = selections_result.graphql_result_type
342
+ field_defn = query.types.field(owner_type, field_name)
439
343
 
440
- # This seems janky, but we need to know
441
- # the field's return type at this path in order
442
- # to propagate `null`
443
- return_type_non_null = return_type.non_null?
444
344
  # Set this before calling `run_with_directives`, so that the directive can have the latest path
445
- st = get_current_runtime_state
446
- st.current_field = field_defn
447
- st.current_result = selections_result
448
- st.current_result_name = result_name
345
+ runtime_state = get_current_runtime_state
346
+ runtime_state.current_field = field_defn
347
+ runtime_state.current_result = selections_result
348
+ runtime_state.current_result_name = result_name
449
349
 
450
- if is_introspection
350
+ owner_object = selections_result.graphql_application_value
351
+ if field_defn.dynamic_introspection
451
352
  owner_object = field_defn.owner.wrap(owner_object, context)
452
353
  end
453
354
 
454
- total_args_count = field_defn.arguments(context).size
455
- if total_args_count == 0
355
+ if !field_defn.any_arguments?
456
356
  resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
457
357
  if field_defn.extras.size == 0
458
358
  evaluate_selection_with_resolved_keyword_args(
459
- NO_ARGS, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, owner_object, is_eager_field, result_name, selections_result, parent_object, return_type, return_type_non_null
359
+ NO_ARGS, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, result_name, selections_result, runtime_state
460
360
  )
461
361
  else
462
- evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, owner_object, is_eager_field, result_name, selections_result, parent_object, return_type, return_type_non_null)
362
+ evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, result_name, selections_result, runtime_state)
463
363
  end
464
364
  else
465
365
  @query.arguments_cache.dataload_for(ast_node, field_defn, owner_object) do |resolved_arguments|
466
- evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, owner_object, is_eager_field, result_name, selections_result, parent_object, return_type, return_type_non_null)
366
+ runtime_state = get_current_runtime_state # This might be in a different fiber
367
+ runtime_state.current_field = field_defn
368
+ runtime_state.current_arguments = resolved_arguments
369
+ runtime_state.current_result_name = result_name
370
+ runtime_state.current_result = selections_result
371
+ evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, result_name, selections_result, runtime_state)
467
372
  end
468
373
  end
469
374
  end
470
375
 
471
- def evaluate_selection_with_args(arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null) # rubocop:disable Metrics/ParameterLists
472
- after_lazy(arguments, field: field_defn, ast_node: ast_node, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result) do |resolved_arguments|
376
+ def evaluate_selection_with_args(arguments, field_defn, ast_node, field_ast_nodes, object, result_name, selection_result, runtime_state) # rubocop:disable Metrics/ParameterLists
377
+ after_lazy(arguments, field: field_defn, ast_node: ast_node, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |resolved_arguments, runtime_state|
473
378
  if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
474
- continue_value(resolved_arguments, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
379
+ next if selection_result.collect_result(result_name, resolved_arguments)
380
+
381
+ return_type_non_null = field_defn.type.non_null?
382
+ continue_value(resolved_arguments, field_defn, return_type_non_null, ast_node, result_name, selection_result)
475
383
  next
476
384
  end
477
385
 
@@ -509,28 +417,27 @@ module GraphQL
509
417
  # to the keyword args hash _before_ freezing everything.
510
418
  extra_args[:argument_details] = :__arguments_add_self
511
419
  when :parent
512
- extra_args[:parent] = parent_object
420
+ parent_result = selection_result.graphql_parent
421
+ extra_args[:parent] = parent_result&.graphql_application_value&.object
513
422
  else
514
423
  extra_args[extra] = field_defn.fetch_extra(extra, context)
515
424
  end
516
425
  end
517
- if extra_args.any?
426
+ if !extra_args.empty?
518
427
  resolved_arguments = resolved_arguments.merge_extras(extra_args)
519
428
  end
520
429
  resolved_arguments.keyword_arguments
521
430
  end
522
431
 
523
- evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null)
432
+ evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, object, result_name, selection_result, runtime_state)
524
433
  end
525
434
  end
526
435
 
527
- def evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null) # rubocop:disable Metrics/ParameterLists
528
- st = get_current_runtime_state
529
- st.current_field = field_defn
530
- st.current_object = object
531
- st.current_arguments = resolved_arguments
532
- st.current_result_name = result_name
533
- st.current_result = selection_result
436
+ def evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, object, result_name, selection_result, runtime_state) # rubocop:disable Metrics/ParameterLists
437
+ runtime_state.current_field = field_defn
438
+ runtime_state.current_arguments = resolved_arguments
439
+ runtime_state.current_result_name = result_name
440
+ runtime_state.current_result = selection_result
534
441
  # Optimize for the case that field is selected only once
535
442
  if field_ast_nodes.nil? || field_ast_nodes.size == 1
536
443
  next_selections = ast_node.selections
@@ -544,9 +451,18 @@ module GraphQL
544
451
  }
545
452
  end
546
453
 
547
- field_result = call_method_on_directives(:resolve, object, directives) do
454
+ call_method_on_directives(:resolve, object, directives) do
455
+ if !directives.empty?
456
+ # This might be executed in a different context; reset this info
457
+ runtime_state = get_current_runtime_state
458
+ runtime_state.current_field = field_defn
459
+ runtime_state.current_arguments = resolved_arguments
460
+ runtime_state.current_result_name = result_name
461
+ runtime_state.current_result = selection_result
462
+ end
548
463
  # Actually call the field resolver and capture the result
549
464
  app_result = begin
465
+ @current_trace.begin_execute_field(field_defn, object, kwarg_arguments, query)
550
466
  @current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
551
467
  field_defn.resolve(object, kwarg_arguments, context)
552
468
  end
@@ -559,32 +475,32 @@ module GraphQL
559
475
  ex_err
560
476
  end
561
477
  end
562
- after_lazy(app_result, field: field_defn, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result) do |inner_result|
563
- continue_value = continue_value(inner_result, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
478
+ @current_trace.end_execute_field(field_defn, object, kwarg_arguments, query, app_result)
479
+ 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|
480
+ next if selection_result.collect_result(result_name, inner_result)
481
+
482
+ owner_type = selection_result.graphql_result_type
483
+ return_type = field_defn.type
484
+ continue_value = continue_value(inner_result, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
564
485
  if HALT != continue_value
565
- continue_field(continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result)
486
+ was_scoped = runtime_state.was_authorized_by_scope_items
487
+ runtime_state.was_authorized_by_scope_items = nil
488
+ 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)
489
+ else
490
+ nil
566
491
  end
567
492
  end
568
493
  end
569
-
570
494
  # If this field is a root mutation field, immediately resolve
571
495
  # all of its child fields before moving on to the next root mutation field.
572
496
  # (Subselections of this mutation will still be resolved level-by-level.)
573
- if is_eager_field
574
- Interpreter::Resolve.resolve_all([field_result], @dataloader)
575
- else
576
- # Return this from `after_lazy` because it might be another lazy that needs to be resolved
577
- field_result
497
+ if selection_result.graphql_is_eager
498
+ @dataloader.run
578
499
  end
579
500
  end
580
501
 
581
-
582
- def dead_result?(selection_result)
583
- selection_result.graphql_dead # || ((parent = selection_result.graphql_parent) && parent.graphql_dead)
584
- end
585
-
586
502
  def set_result(selection_result, result_name, value, is_child_result, is_non_null)
587
- if !dead_result?(selection_result)
503
+ if !selection_result.graphql_dead
588
504
  if value.nil? && is_non_null
589
505
  # This is an invalid nil that should be propagated
590
506
  # One caller of this method passes a block,
@@ -637,14 +553,17 @@ module GraphQL
637
553
  path
638
554
  end
639
555
 
640
- HALT = Object.new
641
- def continue_value(value, parent_type, field, is_non_null, ast_node, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
556
+ HALT = Object.new.freeze
557
+ def continue_value(value, field, is_non_null, ast_node, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
642
558
  case value
643
559
  when nil
644
560
  if is_non_null
645
561
  set_result(selection_result, result_name, nil, false, is_non_null) do
562
+ # When this comes from a list item, use the parent object:
563
+ is_from_array = selection_result.is_a?(GraphQLResultArray)
564
+ parent_type = is_from_array ? selection_result.graphql_parent.graphql_result_type : selection_result.graphql_result_type
646
565
  # This block is called if `result_name` is not dead. (Maybe a previous invalid nil caused it be marked dead.)
647
- err = parent_type::InvalidNullError.new(parent_type, field, value)
566
+ err = parent_type::InvalidNullError.new(parent_type, field, ast_node, is_from_array: is_from_array)
648
567
  schema.type_error(err, context)
649
568
  end
650
569
  else
@@ -656,7 +575,7 @@ module GraphQL
656
575
  # to avoid the overhead of checking three different classes
657
576
  # every time.
658
577
  if value.is_a?(GraphQL::ExecutionError)
659
- if selection_result.nil? || !dead_result?(selection_result)
578
+ if selection_result.nil? || !selection_result.graphql_dead
660
579
  value.path ||= current_path
661
580
  value.ast_node ||= ast_node
662
581
  context.errors << value
@@ -674,7 +593,7 @@ module GraphQL
674
593
  rescue GraphQL::ExecutionError => err
675
594
  err
676
595
  end
677
- continue_value(next_value, parent_type, field, is_non_null, ast_node, result_name, selection_result)
596
+ continue_value(next_value, field, is_non_null, ast_node, result_name, selection_result)
678
597
  elsif value.is_a?(GraphQL::UnauthorizedError)
679
598
  # this hook might raise & crash, or it might return
680
599
  # a replacement value
@@ -683,7 +602,7 @@ module GraphQL
683
602
  rescue GraphQL::ExecutionError => err
684
603
  err
685
604
  end
686
- continue_value(next_value, parent_type, field, is_non_null, ast_node, result_name, selection_result)
605
+ continue_value(next_value, field, is_non_null, ast_node, result_name, selection_result)
687
606
  elsif GraphQL::Execution::SKIP == value
688
607
  # It's possible a lazy was already written here
689
608
  case selection_result
@@ -704,9 +623,9 @@ module GraphQL
704
623
  end
705
624
  when Array
706
625
  # It's an array full of execution errors; add them all.
707
- if value.any? && value.all? { |v| v.is_a?(GraphQL::ExecutionError) }
626
+ if !value.empty? && value.all?(GraphQL::ExecutionError)
708
627
  list_type_at_all = (field && (field.type.list?))
709
- if selection_result.nil? || !dead_result?(selection_result)
628
+ if selection_result.nil? || !selection_result.graphql_dead
710
629
  value.each_with_index do |error, index|
711
630
  error.ast_node ||= ast_node
712
631
  error.path ||= current_path + (list_type_at_all ? [index] : [])
@@ -742,7 +661,7 @@ module GraphQL
742
661
  # Location information from `path` and `ast_node`.
743
662
  #
744
663
  # @return [Lazy, Array, Hash, Object] Lazy, Array, and Hash are all traversed to resolve lazy values later
745
- def continue_field(value, owner_type, field, current_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
664
+ def continue_field(value, owner_type, field, current_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result, was_scoped, runtime_state) # rubocop:disable Metrics/ParameterLists
746
665
  if current_type.non_null?
747
666
  current_type = current_type.of_type
748
667
  is_non_null = true
@@ -752,14 +671,30 @@ module GraphQL
752
671
  when "SCALAR", "ENUM"
753
672
  r = begin
754
673
  current_type.coerce_result(value, context)
674
+ rescue GraphQL::ExecutionError => ex_err
675
+ return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
755
676
  rescue StandardError => err
756
- schema.handle_or_reraise(context, err)
677
+ begin
678
+ query.handle_or_reraise(err)
679
+ rescue GraphQL::ExecutionError => ex_err
680
+ return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
681
+ end
757
682
  end
758
683
  set_result(selection_result, result_name, r, false, is_non_null)
759
684
  r
760
685
  when "UNION", "INTERFACE"
761
- resolved_type_or_lazy = resolve_type(current_type, value)
762
- after_lazy(resolved_type_or_lazy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |resolved_type_result|
686
+ resolved_type_or_lazy = begin
687
+ resolve_type(current_type, value)
688
+ rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
689
+ return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
690
+ rescue StandardError => err
691
+ begin
692
+ query.handle_or_reraise(err)
693
+ rescue GraphQL::ExecutionError => ex_err
694
+ return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
695
+ end
696
+ end
697
+ after_lazy(resolved_type_or_lazy, 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 |resolved_type_result, runtime_state|
763
698
  if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
764
699
  resolved_type, resolved_value = resolved_type_result
765
700
  else
@@ -767,7 +702,7 @@ module GraphQL
767
702
  resolved_value = value
768
703
  end
769
704
 
770
- possible_types = query.possible_types(current_type)
705
+ possible_types = query.types.possible_types(current_type)
771
706
  if !possible_types.include?(resolved_type)
772
707
  parent_type = field.owner_type
773
708
  err_class = current_type::UnresolvedTypeError
@@ -776,60 +711,37 @@ module GraphQL
776
711
  set_result(selection_result, result_name, nil, false, is_non_null)
777
712
  nil
778
713
  else
779
- continue_field(resolved_value, owner_type, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result)
714
+ continue_field(resolved_value, owner_type, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result, was_scoped, runtime_state)
780
715
  end
781
716
  end
782
717
  when "OBJECT"
783
718
  object_proxy = begin
784
- current_type.wrap(value, context)
719
+ was_scoped ? current_type.wrap_scoped(value, context) : current_type.wrap(value, context)
785
720
  rescue GraphQL::ExecutionError => err
786
721
  err
787
722
  end
788
- 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) do |inner_object|
789
- continue_value = continue_value(inner_object, owner_type, field, is_non_null, ast_node, result_name, selection_result)
723
+ 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|
724
+ continue_value = continue_value(inner_object, field, is_non_null, ast_node, result_name, selection_result)
790
725
  if HALT != continue_value
791
- response_hash = GraphQLResultHash.new(result_name, selection_result, is_non_null)
726
+ response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, next_selections, false, ast_node, arguments, field)
792
727
  set_result(selection_result, result_name, response_hash, true, is_non_null)
793
- gathered_selections = gather_selections(continue_value, current_type, next_selections)
794
- # There are two possibilities for `gathered_selections`:
795
- # 1. All selections of this object should be evaluated together (there are no runtime directives modifying execution).
796
- # This case is handled below, and the result can be written right into the main `response_hash` above.
797
- # In this case, `gathered_selections` is a hash of selections.
798
- # 2. Some selections of this object have runtime directives that may or may not modify execution.
799
- # That part of the selection is evaluated in an isolated way, writing into a sub-response object which is
800
- # eventually merged into the final response. In this case, `gathered_selections` is an array of things to run in isolation.
801
- # (Technically, it's possible that one of those entries _doesn't_ require isolation.)
802
- tap_or_each(gathered_selections) do |selections, is_selection_array|
728
+ each_gathered_selections(response_hash) do |selections, is_selection_array, ordered_result_keys|
729
+ response_hash.ordered_result_keys ||= ordered_result_keys
803
730
  if is_selection_array
804
- this_result = GraphQLResultHash.new(result_name, selection_result, is_non_null)
731
+ this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, selections, false, ast_node, arguments, field)
732
+ this_result.ordered_result_keys = ordered_result_keys
805
733
  final_result = response_hash
806
734
  else
807
735
  this_result = response_hash
808
736
  final_result = nil
809
737
  end
810
- # reset this mutable state
811
- # Unset `result_name` here because it's already included in the new response hash
812
- st = get_current_runtime_state
813
- st.current_object = continue_value
814
- st.current_result_name = nil
815
- st.current_result = this_result
816
738
 
817
- # This is a less-frequent case; use a fast check since it's often not there.
818
- if (directives = selections[:graphql_directives])
819
- selections.delete(:graphql_directives)
820
- end
821
- call_method_on_directives(:resolve, continue_value, directives) do
822
- evaluate_selections(
823
- continue_value,
824
- current_type,
825
- false,
826
- selections,
827
- this_result,
828
- final_result,
829
- owner_object.object,
830
- )
831
- this_result
832
- end
739
+ evaluate_selections(
740
+ selections,
741
+ this_result,
742
+ final_result,
743
+ runtime_state,
744
+ )
833
745
  end
834
746
  end
835
747
  end
@@ -838,35 +750,43 @@ module GraphQL
838
750
  # This is true for objects, unions, and interfaces
839
751
  use_dataloader_job = !inner_type.unwrap.kind.input?
840
752
  inner_type_non_null = inner_type.non_null?
841
- response_list = GraphQLResultArray.new(result_name, selection_result, is_non_null)
753
+ response_list = GraphQLResultArray.new(result_name, current_type, owner_object, selection_result, is_non_null, next_selections, false, ast_node, arguments, field)
842
754
  set_result(selection_result, result_name, response_list, true, is_non_null)
843
755
  idx = nil
844
756
  list_value = begin
845
- value.each do |inner_value|
846
- idx ||= 0
847
- this_idx = idx
848
- idx += 1
849
- if use_dataloader_job
850
- @dataloader.append_job do
851
- resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
757
+ begin
758
+ value.each do |inner_value|
759
+ idx ||= 0
760
+ this_idx = idx
761
+ idx += 1
762
+ if use_dataloader_job
763
+ @dataloader.append_job do
764
+ 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)
765
+ end
766
+ else
767
+ 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)
852
768
  end
853
- else
854
- resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
855
769
  end
856
- end
857
770
 
858
- response_list
859
- rescue NoMethodError => err
860
- # Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.)
861
- if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
862
- # This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
863
- raise ListResultFailedError.new(value: value, field: field, path: current_path)
864
- else
865
- # This was some other NoMethodError -- let it bubble to reveal the real error.
866
- raise
771
+ response_list
772
+ rescue NoMethodError => err
773
+ # Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.)
774
+ if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
775
+ # This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
776
+ raise ListResultFailedError.new(value: value, field: field, path: current_path)
777
+ else
778
+ # This was some other NoMethodError -- let it bubble to reveal the real error.
779
+ raise
780
+ end
781
+ rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
782
+ ex_err
783
+ rescue StandardError => err
784
+ begin
785
+ query.handle_or_reraise(err)
786
+ rescue GraphQL::ExecutionError => ex_err
787
+ ex_err
788
+ end
867
789
  end
868
- rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
869
- ex_err
870
790
  rescue StandardError => err
871
791
  begin
872
792
  query.handle_or_reraise(err)
@@ -876,22 +796,21 @@ module GraphQL
876
796
  end
877
797
  # Detect whether this error came while calling `.each` (before `idx` is set) or while running list *items* (after `idx` is set)
878
798
  error_is_non_null = idx.nil? ? is_non_null : inner_type.non_null?
879
- continue_value(list_value, owner_type, field, error_is_non_null, ast_node, result_name, selection_result)
799
+ continue_value(list_value, field, error_is_non_null, ast_node, result_name, selection_result)
880
800
  else
881
801
  raise "Invariant: Unhandled type kind #{current_type.kind} (#{current_type})"
882
802
  end
883
803
  end
884
804
 
885
- def resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type) # rubocop:disable Metrics/ParameterLists
886
- st = get_current_runtime_state
887
- st.current_result_name = this_idx
888
- st.current_result = response_list
805
+ def 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) # rubocop:disable Metrics/ParameterLists
806
+ runtime_state.current_result_name = this_idx
807
+ runtime_state.current_result = response_list
889
808
  call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
890
809
  # This will update `response_list` with the lazy
891
- after_lazy(inner_value, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list) do |inner_inner_value|
892
- continue_value = continue_value(inner_inner_value, owner_type, field, inner_type_non_null, ast_node, this_idx, response_list)
810
+ after_lazy(inner_value, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list, runtime_state: runtime_state) do |inner_inner_value, runtime_state|
811
+ continue_value = continue_value(inner_inner_value, field, inner_type_non_null, ast_node, this_idx, response_list)
893
812
  if HALT != continue_value
894
- continue_field(continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list)
813
+ continue_field(continue_value, owner_type, field, inner_type, ast_node, response_list.graphql_selections, false, owner_object, arguments, this_idx, response_list, was_scoped, runtime_state)
895
814
  end
896
815
  end
897
816
  end
@@ -909,9 +828,15 @@ module GraphQL
909
828
  else
910
829
  dir_defn = @schema_directives.fetch(dir_node.name)
911
830
  raw_dir_args = arguments(nil, dir_defn, dir_node)
831
+ if !raw_dir_args.is_a?(GraphQL::ExecutionError)
832
+ begin
833
+ dir_defn.validate!(raw_dir_args, context)
834
+ rescue GraphQL::ExecutionError => err
835
+ raw_dir_args = err
836
+ end
837
+ end
912
838
  dir_args = continue_value(
913
839
  raw_dir_args, # value
914
- dir_defn, # parent_type
915
840
  nil, # field
916
841
  false, # is_non_null
917
842
  dir_node, # ast_node
@@ -942,12 +867,7 @@ module GraphQL
942
867
  end
943
868
 
944
869
  def get_current_runtime_state
945
- current_state = Thread.current[:__graphql_runtime_info] ||= begin
946
- per_query_state = {}
947
- per_query_state.compare_by_identity
948
- per_query_state
949
- end
950
-
870
+ current_state = Fiber[:__graphql_runtime_info] ||= {}.compare_by_identity
951
871
  current_state[@query] ||= CurrentState.new
952
872
  end
953
873
 
@@ -968,20 +888,25 @@ module GraphQL
968
888
  # @param eager [Boolean] Set to `true` for mutation root fields only
969
889
  # @param trace [Boolean] If `false`, don't wrap this with field tracing
970
890
  # @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
971
- def after_lazy(lazy_obj, field:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, trace: true, &block)
891
+ def after_lazy(lazy_obj, field:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, runtime_state:, trace: true, &block)
972
892
  if lazy?(lazy_obj)
973
- orig_result = result
893
+ was_authorized_by_scope_items = runtime_state.was_authorized_by_scope_items
974
894
  lazy = GraphQL::Execution::Lazy.new(field: field) do
975
- st = get_current_runtime_state
976
- st.current_object = owner_object
977
- st.current_field = field
978
- st.current_arguments = arguments
979
- st.current_result_name = result_name
980
- st.current_result = orig_result
895
+ # This block might be called in a new fiber;
896
+ # In that case, this will initialize a new state
897
+ # to avoid conflicting with the parent fiber.
898
+ runtime_state = get_current_runtime_state
899
+ runtime_state.current_field = field
900
+ runtime_state.current_arguments = arguments
901
+ runtime_state.current_result_name = result_name
902
+ runtime_state.current_result = result
903
+ runtime_state.was_authorized_by_scope_items = was_authorized_by_scope_items
981
904
  # Wrap the execution of _this_ method with tracing,
982
905
  # but don't wrap the continuation below
906
+ sync_result = nil
983
907
  inner_obj = begin
984
- if trace
908
+ sync_result = if trace
909
+ @current_trace.begin_execute_field(field, owner_object, arguments, query)
985
910
  @current_trace.execute_field_lazy(field: field, query: query, object: owner_object, arguments: arguments, ast_node: ast_node) do
986
911
  schema.sync_lazy(lazy_obj)
987
912
  end
@@ -996,25 +921,24 @@ module GraphQL
996
921
  rescue GraphQL::ExecutionError => ex_err
997
922
  ex_err
998
923
  end
924
+ ensure
925
+ if trace
926
+ @current_trace.end_execute_field(field, owner_object, arguments, query, sync_result)
927
+ end
999
928
  end
1000
- yield(inner_obj)
929
+ yield(inner_obj, runtime_state)
1001
930
  end
1002
931
 
1003
932
  if eager
1004
933
  lazy.value
1005
934
  else
1006
935
  set_result(result, result_name, lazy, false, false) # is_non_null is irrelevant here
1007
- current_depth = 0
1008
- while result
1009
- current_depth += 1
1010
- result = result.graphql_parent
1011
- end
1012
- @lazies_at_depth[current_depth] << lazy
936
+ @dataloader.lazy_at_depth(result.depth, lazy)
1013
937
  lazy
1014
938
  end
1015
939
  else
1016
940
  # Don't need to reset state here because it _wasn't_ lazy.
1017
- yield(lazy_obj)
941
+ yield(lazy_obj, runtime_state)
1018
942
  end
1019
943
  end
1020
944
 
@@ -1028,25 +952,30 @@ module GraphQL
1028
952
  end
1029
953
 
1030
954
  def delete_all_interpreter_context
1031
- per_query_state = Thread.current[:__graphql_runtime_info]
955
+ per_query_state = Fiber[:__graphql_runtime_info]
1032
956
  if per_query_state
1033
957
  per_query_state.delete(@query)
1034
958
  if per_query_state.size == 0
1035
- Thread.current[:__graphql_runtime_info] = nil
959
+ Fiber[:__graphql_runtime_info] = nil
1036
960
  end
1037
961
  end
1038
962
  nil
1039
963
  end
1040
964
 
1041
965
  def resolve_type(type, value)
966
+ @current_trace.begin_resolve_type(type, value, context)
1042
967
  resolved_type, resolved_value = @current_trace.resolve_type(query: query, type: type, object: value) do
1043
968
  query.resolve_type(type, value)
1044
969
  end
970
+ @current_trace.end_resolve_type(type, value, context, resolved_type)
1045
971
 
1046
972
  if lazy?(resolved_type)
1047
973
  GraphQL::Execution::Lazy.new do
974
+ @current_trace.begin_resolve_type(type, value, context)
1048
975
  @current_trace.resolve_type_lazy(query: query, type: type, object: value) do
1049
- schema.sync_lazy(resolved_type)
976
+ rt = schema.sync_lazy(resolved_type)
977
+ @current_trace.end_resolve_type(type, value, context, rt)
978
+ rt
1050
979
  end
1051
980
  end
1052
981
  else