graphql 1.13.12 → 2.0.21

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.

Potentially problematic release.


This version of graphql might be problematic. Click here for more details.

Files changed (273) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install_generator.rb +1 -1
  3. data/lib/generators/graphql/relay.rb +3 -17
  4. data/lib/generators/graphql/templates/schema.erb +3 -0
  5. data/lib/graphql/analysis/ast/field_usage.rb +3 -1
  6. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -1
  7. data/lib/graphql/analysis/ast/query_complexity.rb +1 -1
  8. data/lib/graphql/analysis/ast/query_depth.rb +0 -1
  9. data/lib/graphql/analysis/ast/visitor.rb +43 -36
  10. data/lib/graphql/analysis/ast.rb +2 -12
  11. data/lib/graphql/analysis.rb +0 -7
  12. data/lib/graphql/backtrace/table.rb +2 -20
  13. data/lib/graphql/backtrace/trace.rb +96 -0
  14. data/lib/graphql/backtrace/tracer.rb +2 -3
  15. data/lib/graphql/backtrace.rb +7 -8
  16. data/lib/graphql/dataloader/null_dataloader.rb +3 -1
  17. data/lib/graphql/dataloader/source.rb +9 -0
  18. data/lib/graphql/dataloader.rb +4 -1
  19. data/lib/graphql/dig.rb +1 -1
  20. data/lib/graphql/execution/errors.rb +12 -82
  21. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  22. data/lib/graphql/execution/interpreter/arguments_cache.rb +2 -3
  23. data/lib/graphql/execution/interpreter/resolve.rb +26 -0
  24. data/lib/graphql/execution/interpreter/runtime.rb +300 -222
  25. data/lib/graphql/execution/interpreter.rb +187 -78
  26. data/lib/graphql/execution/lazy.rb +7 -21
  27. data/lib/graphql/execution/lookahead.rb +44 -40
  28. data/lib/graphql/execution/multiplex.rb +3 -174
  29. data/lib/graphql/execution.rb +11 -4
  30. data/lib/graphql/filter.rb +7 -2
  31. data/lib/graphql/introspection/directive_type.rb +2 -2
  32. data/lib/graphql/introspection/dynamic_fields.rb +3 -8
  33. data/lib/graphql/introspection/entry_points.rb +2 -15
  34. data/lib/graphql/introspection/field_type.rb +1 -1
  35. data/lib/graphql/introspection/schema_type.rb +2 -2
  36. data/lib/graphql/introspection/type_type.rb +13 -6
  37. data/lib/graphql/introspection.rb +4 -3
  38. data/lib/graphql/language/document_from_schema_definition.rb +43 -44
  39. data/lib/graphql/language/lexer.rb +216 -1488
  40. data/lib/graphql/language/nodes.rb +66 -40
  41. data/lib/graphql/language/parser.rb +539 -510
  42. data/lib/graphql/language/parser.y +53 -44
  43. data/lib/graphql/language/printer.rb +37 -21
  44. data/lib/graphql/language/visitor.rb +191 -83
  45. data/lib/graphql/pagination/active_record_relation_connection.rb +0 -8
  46. data/lib/graphql/pagination/array_connection.rb +4 -2
  47. data/lib/graphql/pagination/connection.rb +33 -6
  48. data/lib/graphql/pagination/connections.rb +3 -28
  49. data/lib/graphql/pagination/relation_connection.rb +2 -0
  50. data/lib/graphql/query/context.rb +156 -196
  51. data/lib/graphql/query/input_validation_result.rb +10 -1
  52. data/lib/graphql/query/null_context.rb +1 -4
  53. data/lib/graphql/query/validation_pipeline.rb +12 -37
  54. data/lib/graphql/query/variable_validation_error.rb +2 -2
  55. data/lib/graphql/query/variables.rb +35 -21
  56. data/lib/graphql/query.rb +39 -46
  57. data/lib/graphql/railtie.rb +0 -104
  58. data/lib/graphql/rake_task/validate.rb +1 -1
  59. data/lib/graphql/rake_task.rb +29 -1
  60. data/lib/graphql/relay/range_add.rb +9 -20
  61. data/lib/graphql/relay.rb +0 -15
  62. data/lib/graphql/schema/addition.rb +7 -9
  63. data/lib/graphql/schema/argument.rb +38 -47
  64. data/lib/graphql/schema/build_from_definition.rb +47 -21
  65. data/lib/graphql/schema/directive/one_of.rb +12 -0
  66. data/lib/graphql/schema/directive/transform.rb +1 -1
  67. data/lib/graphql/schema/directive.rb +12 -23
  68. data/lib/graphql/schema/enum.rb +29 -41
  69. data/lib/graphql/schema/enum_value.rb +2 -25
  70. data/lib/graphql/schema/field/connection_extension.rb +4 -0
  71. data/lib/graphql/schema/field.rb +256 -349
  72. data/lib/graphql/schema/field_extension.rb +1 -4
  73. data/lib/graphql/schema/find_inherited_value.rb +2 -7
  74. data/lib/graphql/schema/input_object.rb +57 -69
  75. data/lib/graphql/schema/interface.rb +0 -35
  76. data/lib/graphql/schema/introspection_system.rb +3 -8
  77. data/lib/graphql/schema/late_bound_type.rb +8 -2
  78. data/lib/graphql/schema/list.rb +18 -9
  79. data/lib/graphql/schema/loader.rb +1 -2
  80. data/lib/graphql/schema/member/base_dsl_methods.rb +17 -19
  81. data/lib/graphql/schema/member/build_type.rb +5 -7
  82. data/lib/graphql/schema/member/has_arguments.rb +147 -56
  83. data/lib/graphql/schema/member/has_ast_node.rb +12 -0
  84. data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
  85. data/lib/graphql/schema/member/has_directives.rb +81 -61
  86. data/lib/graphql/schema/member/has_fields.rb +97 -40
  87. data/lib/graphql/schema/member/has_interfaces.rb +49 -10
  88. data/lib/graphql/schema/member/has_validators.rb +32 -6
  89. data/lib/graphql/schema/member/relay_shortcuts.rb +47 -2
  90. data/lib/graphql/schema/member/type_system_helpers.rb +17 -0
  91. data/lib/graphql/schema/member/validates_input.rb +3 -3
  92. data/lib/graphql/schema/member.rb +0 -6
  93. data/lib/graphql/schema/mutation.rb +0 -9
  94. data/lib/graphql/schema/non_null.rb +3 -9
  95. data/lib/graphql/schema/object.rb +15 -52
  96. data/lib/graphql/schema/relay_classic_mutation.rb +53 -42
  97. data/lib/graphql/schema/resolver/has_payload_type.rb +20 -10
  98. data/lib/graphql/schema/resolver.rb +43 -44
  99. data/lib/graphql/schema/scalar.rb +8 -23
  100. data/lib/graphql/schema/subscription.rb +0 -7
  101. data/lib/graphql/schema/timeout.rb +24 -28
  102. data/lib/graphql/schema/type_membership.rb +3 -0
  103. data/lib/graphql/schema/union.rb +10 -17
  104. data/lib/graphql/schema/validator.rb +1 -1
  105. data/lib/graphql/schema/warden.rb +37 -9
  106. data/lib/graphql/schema/wrapper.rb +0 -5
  107. data/lib/graphql/schema.rb +265 -968
  108. data/lib/graphql/static_validation/all_rules.rb +1 -0
  109. data/lib/graphql/static_validation/base_visitor.rb +4 -21
  110. data/lib/graphql/static_validation/definition_dependencies.rb +7 -1
  111. data/lib/graphql/static_validation/error.rb +2 -2
  112. data/lib/graphql/static_validation/literal_validator.rb +19 -1
  113. data/lib/graphql/static_validation/rules/directives_are_defined.rb +11 -5
  114. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +12 -12
  115. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -4
  116. data/lib/graphql/static_validation/rules/fields_will_merge.rb +2 -2
  117. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +66 -0
  118. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -0
  119. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +12 -6
  120. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +1 -1
  121. data/lib/graphql/static_validation/validator.rb +3 -25
  122. data/lib/graphql/static_validation.rb +0 -2
  123. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +7 -1
  124. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +38 -1
  125. data/lib/graphql/subscriptions/event.rb +3 -8
  126. data/lib/graphql/subscriptions/instrumentation.rb +0 -51
  127. data/lib/graphql/subscriptions.rb +32 -20
  128. data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
  129. data/lib/graphql/tracing/appoptics_trace.rb +231 -0
  130. data/lib/graphql/tracing/appsignal_trace.rb +77 -0
  131. data/lib/graphql/tracing/data_dog_trace.rb +148 -0
  132. data/lib/graphql/tracing/data_dog_tracing.rb +21 -2
  133. data/lib/graphql/tracing/legacy_trace.rb +65 -0
  134. data/lib/graphql/tracing/new_relic_trace.rb +75 -0
  135. data/lib/graphql/tracing/notifications_trace.rb +42 -0
  136. data/lib/graphql/tracing/platform_trace.rb +109 -0
  137. data/lib/graphql/tracing/platform_tracing.rb +33 -43
  138. data/lib/graphql/tracing/prometheus_trace.rb +89 -0
  139. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +1 -1
  140. data/lib/graphql/tracing/prometheus_tracing.rb +3 -3
  141. data/lib/graphql/tracing/scout_trace.rb +72 -0
  142. data/lib/graphql/tracing/statsd_trace.rb +56 -0
  143. data/lib/graphql/tracing/trace.rb +75 -0
  144. data/lib/graphql/tracing.rb +16 -40
  145. data/lib/graphql/type_kinds.rb +6 -3
  146. data/lib/graphql/types/iso_8601_date.rb +4 -1
  147. data/lib/graphql/types/iso_8601_date_time.rb +4 -0
  148. data/lib/graphql/types/relay/base_connection.rb +16 -6
  149. data/lib/graphql/types/relay/connection_behaviors.rb +29 -27
  150. data/lib/graphql/types/relay/edge_behaviors.rb +16 -5
  151. data/lib/graphql/types/relay/node_behaviors.rb +12 -2
  152. data/lib/graphql/types/relay/page_info_behaviors.rb +7 -2
  153. data/lib/graphql/types/relay.rb +0 -3
  154. data/lib/graphql/types/string.rb +1 -1
  155. data/lib/graphql/version.rb +1 -1
  156. data/lib/graphql.rb +17 -74
  157. metadata +33 -133
  158. data/lib/graphql/analysis/analyze_query.rb +0 -98
  159. data/lib/graphql/analysis/field_usage.rb +0 -45
  160. data/lib/graphql/analysis/max_query_complexity.rb +0 -26
  161. data/lib/graphql/analysis/max_query_depth.rb +0 -26
  162. data/lib/graphql/analysis/query_complexity.rb +0 -88
  163. data/lib/graphql/analysis/query_depth.rb +0 -43
  164. data/lib/graphql/analysis/reducer_state.rb +0 -48
  165. data/lib/graphql/argument.rb +0 -131
  166. data/lib/graphql/authorization.rb +0 -82
  167. data/lib/graphql/backtrace/legacy_tracer.rb +0 -56
  168. data/lib/graphql/backwards_compatibility.rb +0 -61
  169. data/lib/graphql/base_type.rb +0 -232
  170. data/lib/graphql/boolean_type.rb +0 -2
  171. data/lib/graphql/compatibility/execution_specification/counter_schema.rb +0 -53
  172. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +0 -200
  173. data/lib/graphql/compatibility/execution_specification.rb +0 -436
  174. data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +0 -111
  175. data/lib/graphql/compatibility/lazy_execution_specification.rb +0 -215
  176. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +0 -87
  177. data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +0 -79
  178. data/lib/graphql/compatibility/query_parser_specification.rb +0 -266
  179. data/lib/graphql/compatibility/schema_parser_specification.rb +0 -682
  180. data/lib/graphql/compatibility.rb +0 -5
  181. data/lib/graphql/define/assign_argument.rb +0 -12
  182. data/lib/graphql/define/assign_connection.rb +0 -13
  183. data/lib/graphql/define/assign_enum_value.rb +0 -18
  184. data/lib/graphql/define/assign_global_id_field.rb +0 -11
  185. data/lib/graphql/define/assign_mutation_function.rb +0 -34
  186. data/lib/graphql/define/assign_object_field.rb +0 -42
  187. data/lib/graphql/define/defined_object_proxy.rb +0 -53
  188. data/lib/graphql/define/instance_definable.rb +0 -255
  189. data/lib/graphql/define/no_definition_error.rb +0 -7
  190. data/lib/graphql/define/non_null_with_bang.rb +0 -16
  191. data/lib/graphql/define/type_definer.rb +0 -31
  192. data/lib/graphql/define.rb +0 -31
  193. data/lib/graphql/deprecated_dsl.rb +0 -55
  194. data/lib/graphql/directive/deprecated_directive.rb +0 -2
  195. data/lib/graphql/directive/include_directive.rb +0 -2
  196. data/lib/graphql/directive/skip_directive.rb +0 -2
  197. data/lib/graphql/directive.rb +0 -107
  198. data/lib/graphql/enum_type.rb +0 -133
  199. data/lib/graphql/execution/execute.rb +0 -333
  200. data/lib/graphql/execution/flatten.rb +0 -40
  201. data/lib/graphql/execution/instrumentation.rb +0 -92
  202. data/lib/graphql/execution/lazy/resolve.rb +0 -91
  203. data/lib/graphql/execution/typecast.rb +0 -50
  204. data/lib/graphql/field/resolve.rb +0 -59
  205. data/lib/graphql/field.rb +0 -226
  206. data/lib/graphql/float_type.rb +0 -2
  207. data/lib/graphql/function.rb +0 -128
  208. data/lib/graphql/id_type.rb +0 -2
  209. data/lib/graphql/input_object_type.rb +0 -138
  210. data/lib/graphql/int_type.rb +0 -2
  211. data/lib/graphql/interface_type.rb +0 -72
  212. data/lib/graphql/internal_representation/document.rb +0 -27
  213. data/lib/graphql/internal_representation/node.rb +0 -206
  214. data/lib/graphql/internal_representation/print.rb +0 -51
  215. data/lib/graphql/internal_representation/rewrite.rb +0 -184
  216. data/lib/graphql/internal_representation/scope.rb +0 -88
  217. data/lib/graphql/internal_representation/visit.rb +0 -36
  218. data/lib/graphql/internal_representation.rb +0 -7
  219. data/lib/graphql/language/lexer.rl +0 -260
  220. data/lib/graphql/list_type.rb +0 -80
  221. data/lib/graphql/non_null_type.rb +0 -71
  222. data/lib/graphql/object_type.rb +0 -130
  223. data/lib/graphql/query/arguments.rb +0 -189
  224. data/lib/graphql/query/arguments_cache.rb +0 -24
  225. data/lib/graphql/query/executor.rb +0 -52
  226. data/lib/graphql/query/literal_input.rb +0 -136
  227. data/lib/graphql/query/serial_execution/field_resolution.rb +0 -92
  228. data/lib/graphql/query/serial_execution/operation_resolution.rb +0 -19
  229. data/lib/graphql/query/serial_execution/selection_resolution.rb +0 -23
  230. data/lib/graphql/query/serial_execution/value_resolution.rb +0 -87
  231. data/lib/graphql/query/serial_execution.rb +0 -40
  232. data/lib/graphql/relay/array_connection.rb +0 -83
  233. data/lib/graphql/relay/base_connection.rb +0 -189
  234. data/lib/graphql/relay/connection_instrumentation.rb +0 -54
  235. data/lib/graphql/relay/connection_resolve.rb +0 -43
  236. data/lib/graphql/relay/connection_type.rb +0 -54
  237. data/lib/graphql/relay/edge.rb +0 -27
  238. data/lib/graphql/relay/edge_type.rb +0 -19
  239. data/lib/graphql/relay/edges_instrumentation.rb +0 -39
  240. data/lib/graphql/relay/global_id_resolve.rb +0 -17
  241. data/lib/graphql/relay/mongo_relation_connection.rb +0 -50
  242. data/lib/graphql/relay/mutation/instrumentation.rb +0 -23
  243. data/lib/graphql/relay/mutation/resolve.rb +0 -56
  244. data/lib/graphql/relay/mutation/result.rb +0 -38
  245. data/lib/graphql/relay/mutation.rb +0 -106
  246. data/lib/graphql/relay/node.rb +0 -39
  247. data/lib/graphql/relay/page_info.rb +0 -7
  248. data/lib/graphql/relay/relation_connection.rb +0 -188
  249. data/lib/graphql/relay/type_extensions.rb +0 -32
  250. data/lib/graphql/scalar_type.rb +0 -91
  251. data/lib/graphql/schema/catchall_middleware.rb +0 -35
  252. data/lib/graphql/schema/default_parse_error.rb +0 -10
  253. data/lib/graphql/schema/default_type_error.rb +0 -17
  254. data/lib/graphql/schema/member/accepts_definition.rb +0 -164
  255. data/lib/graphql/schema/member/cached_graphql_definition.rb +0 -58
  256. data/lib/graphql/schema/member/instrumentation.rb +0 -131
  257. data/lib/graphql/schema/middleware_chain.rb +0 -82
  258. data/lib/graphql/schema/possible_types.rb +0 -44
  259. data/lib/graphql/schema/rescue_middleware.rb +0 -60
  260. data/lib/graphql/schema/timeout_middleware.rb +0 -88
  261. data/lib/graphql/schema/traversal.rb +0 -228
  262. data/lib/graphql/schema/validation.rb +0 -313
  263. data/lib/graphql/static_validation/default_visitor.rb +0 -15
  264. data/lib/graphql/static_validation/no_validate_visitor.rb +0 -10
  265. data/lib/graphql/string_type.rb +0 -2
  266. data/lib/graphql/subscriptions/subscription_root.rb +0 -76
  267. data/lib/graphql/tracing/skylight_tracing.rb +0 -70
  268. data/lib/graphql/types/relay/default_relay.rb +0 -31
  269. data/lib/graphql/types/relay/node_field.rb +0 -24
  270. data/lib/graphql/types/relay/nodes_field.rb +0 -43
  271. data/lib/graphql/union_type.rb +0 -115
  272. data/lib/graphql/upgrader/member.rb +0 -937
  273. data/lib/graphql/upgrader/schema.rb +0 -38
@@ -8,6 +8,18 @@ module GraphQL
8
8
  #
9
9
  # @api private
10
10
  class Runtime
11
+ class CurrentState
12
+ def initialize
13
+ @current_object = nil
14
+ @current_field = nil
15
+ @current_arguments = nil
16
+ @current_result_name = nil
17
+ @current_result = nil
18
+ end
19
+
20
+ attr_accessor :current_result, :current_result_name,
21
+ :current_arguments, :current_field, :current_object
22
+ end
11
23
 
12
24
  module GraphQLResult
13
25
  def initialize(result_name, parent_result)
@@ -20,6 +32,15 @@ module GraphQL
20
32
  @graphql_metadata = nil
21
33
  end
22
34
 
35
+ def path
36
+ @path ||= build_path([])
37
+ end
38
+
39
+ def build_path(path_array)
40
+ graphql_result_name && path_array.unshift(graphql_result_name)
41
+ @graphql_parent ? @graphql_parent.build_path(path_array) : path_array
42
+ end
43
+
23
44
  attr_accessor :graphql_dead
24
45
  attr_reader :graphql_parent, :graphql_result_name
25
46
 
@@ -45,7 +66,7 @@ module GraphQL
45
66
 
46
67
  attr_accessor :graphql_merged_into
47
68
 
48
- def []=(key, value)
69
+ def set_leaf(key, value)
49
70
  # This is a hack.
50
71
  # Basically, this object is merged into the root-level result at some point.
51
72
  # But the problem is, some lazies are created whose closures retain reference to _this_
@@ -55,23 +76,27 @@ module GraphQL
55
76
  # In order to return a proper partial result (eg, for a directive), we have to update this object, too.
56
77
  # Yowza.
57
78
  if (t = @graphql_merged_into)
58
- t[key] = value
79
+ t.set_leaf(key, value)
59
80
  end
60
81
 
61
- if value.respond_to?(:graphql_result_data)
62
- @graphql_result_data[key] = value.graphql_result_data
63
- # If we encounter some part of this response that requires metadata tracking,
64
- # then create the metadata hash if necessary. It will be kept up-to-date after this.
65
- (@graphql_metadata ||= @graphql_result_data.dup)[key] = value
66
- else
67
- @graphql_result_data[key] = value
68
- # keep this up-to-date if it's been initialized
69
- @graphql_metadata && @graphql_metadata[key] = value
70
- end
82
+ @graphql_result_data[key] = value
83
+ # keep this up-to-date if it's been initialized
84
+ @graphql_metadata && @graphql_metadata[key] = value
71
85
 
72
86
  value
73
87
  end
74
88
 
89
+ def set_child_result(key, value)
90
+ if (t = @graphql_merged_into)
91
+ t.set_child_result(key, value)
92
+ end
93
+ @graphql_result_data[key] = value.graphql_result_data
94
+ # If we encounter some part of this response that requires metadata tracking,
95
+ # then create the metadata hash if necessary. It will be kept up-to-date after this.
96
+ (@graphql_metadata ||= @graphql_result_data.dup)[key] = value
97
+ value
98
+ end
99
+
75
100
  def delete(key)
76
101
  @graphql_metadata && @graphql_metadata.delete(key)
77
102
  @graphql_result_data.delete(key)
@@ -92,6 +117,29 @@ module GraphQL
92
117
  def [](k)
93
118
  (@graphql_metadata || @graphql_result_data)[k]
94
119
  end
120
+
121
+ def merge_into(into_result)
122
+ self.each do |key, value|
123
+ case value
124
+ when GraphQLResultHash
125
+ next_into = into_result[key]
126
+ if next_into
127
+ value.merge_into(next_into)
128
+ else
129
+ into_result.set_child_result(key, value)
130
+ end
131
+ when GraphQLResultArray
132
+ # There's no special handling of arrays because currently, there's no way to split the execution
133
+ # of a list over several concurrent flows.
134
+ next_result.set_child_result(key, value)
135
+ else
136
+ # We have to assume that, since this passed the `fields_will_merge` selection,
137
+ # that the old and new values are the same.
138
+ into_result.set_leaf(key, value)
139
+ end
140
+ end
141
+ @graphql_merged_into = into_result
142
+ end
95
143
  end
96
144
 
97
145
  class GraphQLResultArray
@@ -114,19 +162,25 @@ module GraphQL
114
162
  @graphql_result_data.delete_at(delete_at_index)
115
163
  end
116
164
 
117
- def []=(idx, value)
165
+ def set_leaf(idx, value)
118
166
  if @skip_indices
119
167
  offset_by = @skip_indices.count { |skipped_idx| skipped_idx < idx }
120
168
  idx -= offset_by
121
169
  end
122
- if value.respond_to?(:graphql_result_data)
123
- @graphql_result_data[idx] = value.graphql_result_data
124
- (@graphql_metadata ||= @graphql_result_data.dup)[idx] = value
125
- else
126
- @graphql_result_data[idx] = value
127
- @graphql_metadata && @graphql_metadata[idx] = value
128
- end
170
+ @graphql_result_data[idx] = value
171
+ @graphql_metadata && @graphql_metadata[idx] = value
172
+ value
173
+ end
129
174
 
175
+ def set_child_result(idx, value)
176
+ if @skip_indices
177
+ offset_by = @skip_indices.count { |skipped_idx| skipped_idx < idx }
178
+ idx -= offset_by
179
+ end
180
+ @graphql_result_data[idx] = value.graphql_result_data
181
+ # If we encounter some part of this response that requires metadata tracking,
182
+ # then create the metadata hash if necessary. It will be kept up-to-date after this.
183
+ (@graphql_metadata ||= @graphql_result_data.dup)[idx] = value
130
184
  value
131
185
  end
132
186
 
@@ -148,13 +202,15 @@ module GraphQL
148
202
  # @return [GraphQL::Query::Context]
149
203
  attr_reader :context
150
204
 
151
- def initialize(query:)
205
+ def initialize(query:, lazies_at_depth:)
152
206
  @query = query
153
207
  @dataloader = query.multiplex.dataloader
208
+ @lazies_at_depth = lazies_at_depth
154
209
  @schema = query.schema
155
210
  @context = query.context
156
211
  @multiplex_context = query.multiplex.context
157
- @interpreter_context = @context.namespace(:interpreter)
212
+ # Start this off empty:
213
+ Thread.current[:__graphql_runtime_info] = nil
158
214
  @response = GraphQLResultHash.new(nil, nil)
159
215
  # Identify runtime directives by checking which of this schema's directives have overridden `def self.resolve`
160
216
  @runtime_directive_names = []
@@ -198,8 +254,9 @@ module GraphQL
198
254
  root_operation = query.selected_operation
199
255
  root_op_type = root_operation.operation_type || "query"
200
256
  root_type = schema.root_type_for_operation(root_op_type)
201
- path = []
202
- set_all_interpreter_context(query.root_value, nil, nil, path)
257
+ st = get_current_runtime_state
258
+ st.current_object = query.root_value
259
+ st.current_result = @response
203
260
  object_proxy = authorized_new(root_type, query.root_value, context)
204
261
  object_proxy = schema.sync_lazy(object_proxy)
205
262
 
@@ -226,11 +283,12 @@ module GraphQL
226
283
  end
227
284
 
228
285
  @dataloader.append_job {
229
- set_all_interpreter_context(query.root_value, nil, nil, path)
286
+ st = get_current_runtime_state
287
+ st.current_object = query.root_value
288
+ st.current_result = selection_response
289
+
230
290
  call_method_on_directives(:resolve, object_proxy, selections.graphql_directives) do
231
291
  evaluate_selections(
232
- path,
233
- context.scoped_context,
234
292
  object_proxy,
235
293
  root_type,
236
294
  root_op_type == "mutation",
@@ -244,32 +302,7 @@ module GraphQL
244
302
  end
245
303
  end
246
304
  end
247
- delete_interpreter_context(:current_path)
248
- delete_interpreter_context(:current_field)
249
- delete_interpreter_context(:current_object)
250
- delete_interpreter_context(:current_arguments)
251
- nil
252
- end
253
-
254
- # @return [void]
255
- def deep_merge_selection_result(from_result, into_result)
256
- from_result.each do |key, value|
257
- if !into_result.key?(key)
258
- into_result[key] = value
259
- else
260
- case value
261
- when GraphQLResultHash
262
- deep_merge_selection_result(value, into_result[key])
263
- else
264
- # We have to assume that, since this passed the `fields_will_merge` selection,
265
- # that the old and new values are the same.
266
- # There's no special handling of arrays because currently, there's no way to split the execution
267
- # of a list over several concurrent flows.
268
- into_result[key] = value
269
- end
270
- end
271
- end
272
- from_result.graphql_merged_into = into_result
305
+ delete_all_interpreter_context
273
306
  nil
274
307
  end
275
308
 
@@ -346,22 +379,25 @@ module GraphQL
346
379
  selections_to_run || selections_by_name
347
380
  end
348
381
 
349
- NO_ARGS = {}.freeze
382
+ NO_ARGS = GraphQL::EmptyObjects::EMPTY_HASH
350
383
 
351
384
  # @return [void]
352
- def evaluate_selections(path, scoped_context, owner_object, owner_type, is_eager_selection, gathered_selections, selections_result, target_result, parent_object) # rubocop:disable Metrics/ParameterLists
353
- set_all_interpreter_context(owner_object, nil, nil, path)
385
+ def evaluate_selections(owner_object, owner_type, is_eager_selection, gathered_selections, selections_result, target_result, parent_object) # rubocop:disable Metrics/ParameterLists
386
+ st = get_current_runtime_state
387
+ st.current_object = owner_object
388
+ st.current_result_name = nil
389
+ st.current_result = selections_result
354
390
 
355
391
  finished_jobs = 0
356
392
  enqueued_jobs = gathered_selections.size
357
393
  gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
358
394
  @dataloader.append_job {
359
395
  evaluate_selection(
360
- path, result_name, field_ast_nodes_or_ast_node, scoped_context, owner_object, owner_type, is_eager_selection, selections_result, parent_object
396
+ result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_selection, selections_result, parent_object
361
397
  )
362
398
  finished_jobs += 1
363
399
  if target_result && finished_jobs == enqueued_jobs
364
- deep_merge_selection_result(selections_result, target_result)
400
+ selections_result.merge_into(target_result)
365
401
  end
366
402
  }
367
403
  end
@@ -369,10 +405,8 @@ module GraphQL
369
405
  selections_result
370
406
  end
371
407
 
372
- attr_reader :progress_path
373
-
374
408
  # @return [void]
375
- def evaluate_selection(path, result_name, field_ast_nodes_or_ast_node, scoped_context, owner_object, owner_type, is_eager_field, selections_result, parent_object) # rubocop:disable Metrics/ParameterLists
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
376
410
  return if dead_result?(selections_result)
377
411
  # As a performance optimization, the hash key will be a `Node` if
378
412
  # there's only one selection of the field. But if there are multiple
@@ -400,22 +434,22 @@ module GraphQL
400
434
  raise "Invariant: no field for #{owner_type}.#{field_name}"
401
435
  end
402
436
  end
403
- return_type = field_defn.type
404
437
 
405
- next_path = path.dup
406
- next_path << result_name
407
- next_path.freeze
438
+ return_type = field_defn.type
408
439
 
409
440
  # This seems janky, but we need to know
410
441
  # the field's return type at this path in order
411
442
  # to propagate `null`
412
- if return_type.non_null?
443
+ return_type_non_null = return_type.non_null?
444
+ if return_type_non_null
413
445
  (selections_result.graphql_non_null_field_names ||= []).push(result_name)
414
446
  end
415
447
  # Set this before calling `run_with_directives`, so that the directive can have the latest path
416
- set_all_interpreter_context(nil, field_defn, nil, next_path)
448
+ st = get_current_runtime_state
449
+ st.current_field = field_defn
450
+ st.current_result = selections_result
451
+ st.current_result_name = result_name
417
452
 
418
- context.scoped_context = scoped_context
419
453
  object = owner_object
420
454
 
421
455
  if is_introspection
@@ -425,27 +459,34 @@ module GraphQL
425
459
  total_args_count = field_defn.arguments(context).size
426
460
  if total_args_count == 0
427
461
  resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
428
- evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selections_result, parent_object)
462
+ if field_defn.extras.size == 0
463
+ evaluate_selection_with_resolved_keyword_args(
464
+ NO_ARGS, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object, return_type, return_type_non_null
465
+ )
466
+ else
467
+ evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object, return_type, return_type_non_null)
468
+ end
429
469
  else
430
- # TODO remove all arguments(...) usages?
431
470
  @query.arguments_cache.dataload_for(ast_node, field_defn, object) do |resolved_arguments|
432
- evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selections_result, parent_object)
471
+ evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object, return_type, return_type_non_null)
433
472
  end
434
473
  end
435
474
  end
436
475
 
437
- def evaluate_selection_with_args(arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selection_result, parent_object) # rubocop:disable Metrics/ParameterLists
438
- context.scoped_context = scoped_context
439
- return_type = field_defn.type
440
- after_lazy(arguments, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, scoped_context: context.scoped_context, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result) do |resolved_arguments|
476
+ 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
477
+ after_lazy(arguments, owner: owner_type, field: field_defn, ast_node: ast_node, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result) do |resolved_arguments|
441
478
  if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
442
- continue_value(next_path, resolved_arguments, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
479
+ continue_value(resolved_arguments, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
443
480
  next
444
481
  end
445
482
 
446
- kwarg_arguments = if resolved_arguments.empty? && field_defn.extras.empty?
447
- # We can avoid allocating the `{ Symbol => Object }` hash in this case
448
- NO_ARGS
483
+ kwarg_arguments = if field_defn.extras.empty?
484
+ if resolved_arguments.empty?
485
+ # We can avoid allocating the `{ Symbol => Object }` hash in this case
486
+ NO_ARGS
487
+ else
488
+ resolved_arguments.keyword_arguments
489
+ end
449
490
  else
450
491
  # Bundle up the extras, then make a new arguments instance
451
492
  # that includes the extras, too.
@@ -455,9 +496,9 @@ module GraphQL
455
496
  when :ast_node
456
497
  extra_args[:ast_node] = ast_node
457
498
  when :execution_errors
458
- extra_args[:execution_errors] = ExecutionErrors.new(context, ast_node, next_path)
499
+ extra_args[:execution_errors] = ExecutionErrors.new(context, ast_node, current_path)
459
500
  when :path
460
- extra_args[:path] = next_path
501
+ extra_args[:path] = current_path
461
502
  when :lookahead
462
503
  if !field_ast_nodes
463
504
  field_ast_nodes = [ast_node]
@@ -472,10 +513,6 @@ module GraphQL
472
513
  # Use this flag to tell Interpreter::Arguments to add itself
473
514
  # to the keyword args hash _before_ freezing everything.
474
515
  extra_args[:argument_details] = :__arguments_add_self
475
- when :irep_node
476
- # This is used by `__typename` in order to support the legacy runtime,
477
- # but it has no use here (and it's always `nil`).
478
- # Stop adding it here to avoid the overhead of `.merge_extras` below.
479
516
  when :parent
480
517
  extra_args[:parent] = parent_object
481
518
  else
@@ -488,57 +525,70 @@ module GraphQL
488
525
  resolved_arguments.keyword_arguments
489
526
  end
490
527
 
491
- set_all_interpreter_context(nil, nil, resolved_arguments, nil)
528
+ 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)
529
+ end
530
+ end
492
531
 
493
- # Optimize for the case that field is selected only once
494
- if field_ast_nodes.nil? || field_ast_nodes.size == 1
495
- next_selections = ast_node.selections
496
- directives = ast_node.directives
497
- else
498
- next_selections = []
499
- directives = []
500
- field_ast_nodes.each { |f|
501
- next_selections.concat(f.selections)
502
- directives.concat(f.directives)
503
- }
504
- end
505
-
506
- field_result = call_method_on_directives(:resolve, object, directives) do
507
- # Actually call the field resolver and capture the result
508
- app_result = begin
509
- query.with_error_handling do
510
- query.trace("execute_field", {owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments}) do
511
- field_defn.resolve(object, kwarg_arguments, context)
512
- end
513
- end
514
- rescue GraphQL::ExecutionError => err
515
- err
532
+ 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
533
+ st = get_current_runtime_state
534
+ st.current_field = field_defn
535
+ st.current_object = object
536
+ st.current_arguments = resolved_arguments
537
+ st.current_result_name = result_name
538
+ st.current_result = selection_result
539
+ # Optimize for the case that field is selected only once
540
+ if field_ast_nodes.nil? || field_ast_nodes.size == 1
541
+ next_selections = ast_node.selections
542
+ directives = ast_node.directives
543
+ else
544
+ next_selections = []
545
+ directives = []
546
+ field_ast_nodes.each { |f|
547
+ next_selections.concat(f.selections)
548
+ directives.concat(f.directives)
549
+ }
550
+ end
551
+
552
+ field_result = call_method_on_directives(:resolve, object, directives) do
553
+ # Actually call the field resolver and capture the result
554
+ app_result = begin
555
+ query.current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
556
+ field_defn.resolve(object, kwarg_arguments, context)
516
557
  end
517
- after_lazy(app_result, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, scoped_context: context.scoped_context, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result) do |inner_result|
518
- continue_value = continue_value(next_path, inner_result, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
519
- if HALT != continue_value
520
- continue_field(next_path, continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result)
521
- end
558
+ rescue GraphQL::ExecutionError => err
559
+ err
560
+ rescue StandardError => err
561
+ begin
562
+ query.handle_or_reraise(err)
563
+ rescue GraphQL::ExecutionError => ex_err
564
+ ex_err
522
565
  end
523
566
  end
524
-
525
- # If this field is a root mutation field, immediately resolve
526
- # all of its child fields before moving on to the next root mutation field.
527
- # (Subselections of this mutation will still be resolved level-by-level.)
528
- if is_eager_field
529
- Interpreter::Resolve.resolve_all([field_result], @dataloader)
530
- else
531
- # Return this from `after_lazy` because it might be another lazy that needs to be resolved
532
- field_result
567
+ after_lazy(app_result, owner: owner_type, field: field_defn, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result) do |inner_result|
568
+ continue_value = continue_value(inner_result, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
569
+ if HALT != continue_value
570
+ continue_field(continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result)
571
+ end
533
572
  end
534
573
  end
574
+
575
+ # If this field is a root mutation field, immediately resolve
576
+ # all of its child fields before moving on to the next root mutation field.
577
+ # (Subselections of this mutation will still be resolved level-by-level.)
578
+ if is_eager_field
579
+ Interpreter::Resolve.resolve_all([field_result], @dataloader)
580
+ else
581
+ # Return this from `after_lazy` because it might be another lazy that needs to be resolved
582
+ field_result
583
+ end
535
584
  end
536
585
 
586
+
537
587
  def dead_result?(selection_result)
538
- selection_result.graphql_dead || ((parent = selection_result.graphql_parent) && parent.graphql_dead)
588
+ selection_result.graphql_dead # || ((parent = selection_result.graphql_parent) && parent.graphql_dead)
539
589
  end
540
590
 
541
- def set_result(selection_result, result_name, value)
591
+ def set_result(selection_result, result_name, value, is_child_result)
542
592
  if !dead_result?(selection_result)
543
593
  if value.nil? &&
544
594
  ( # there are two conditions under which `nil` is not allowed in the response:
@@ -558,11 +608,13 @@ module GraphQL
558
608
  if parent.nil? # This is a top-level result hash
559
609
  @response = nil
560
610
  else
561
- set_result(parent, name_in_parent, nil)
611
+ set_result(parent, name_in_parent, nil, false)
562
612
  set_graphql_dead(selection_result)
563
613
  end
614
+ elsif is_child_result
615
+ selection_result.set_child_result(result_name, value)
564
616
  else
565
- selection_result[result_name] = value
617
+ selection_result.set_leaf(result_name, value)
566
618
  end
567
619
  end
568
620
  end
@@ -582,18 +634,29 @@ module GraphQL
582
634
  end
583
635
  end
584
636
 
637
+ def current_path
638
+ st = get_current_runtime_state
639
+ result = st.current_result
640
+ path = result && result.path
641
+ if path && (rn = st.current_result_name)
642
+ path = path.dup
643
+ path.push(rn)
644
+ end
645
+ path
646
+ end
647
+
585
648
  HALT = Object.new
586
- def continue_value(path, value, parent_type, field, is_non_null, ast_node, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
649
+ def continue_value(value, parent_type, field, is_non_null, ast_node, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
587
650
  case value
588
651
  when nil
589
652
  if is_non_null
590
- set_result(selection_result, result_name, nil) do
653
+ set_result(selection_result, result_name, nil, false) do
591
654
  # This block is called if `result_name` is not dead. (Maybe a previous invalid nil caused it be marked dead.)
592
655
  err = parent_type::InvalidNullError.new(parent_type, field, value)
593
656
  schema.type_error(err, context)
594
657
  end
595
658
  else
596
- set_result(selection_result, result_name, nil)
659
+ set_result(selection_result, result_name, nil, false)
597
660
  end
598
661
  HALT
599
662
  when GraphQL::Error
@@ -602,14 +665,24 @@ module GraphQL
602
665
  # every time.
603
666
  if value.is_a?(GraphQL::ExecutionError)
604
667
  if selection_result.nil? || !dead_result?(selection_result)
605
- value.path ||= path
668
+ value.path ||= current_path
606
669
  value.ast_node ||= ast_node
607
670
  context.errors << value
608
671
  if selection_result
609
- set_result(selection_result, result_name, nil)
672
+ set_result(selection_result, result_name, nil, false)
610
673
  end
611
674
  end
612
675
  HALT
676
+ elsif value.is_a?(GraphQL::UnauthorizedFieldError)
677
+ value.field ||= field
678
+ # this hook might raise & crash, or it might return
679
+ # a replacement value
680
+ next_value = begin
681
+ schema.unauthorized_field(value)
682
+ rescue GraphQL::ExecutionError => err
683
+ err
684
+ end
685
+ continue_value(next_value, parent_type, field, is_non_null, ast_node, result_name, selection_result)
613
686
  elsif value.is_a?(GraphQL::UnauthorizedError)
614
687
  # this hook might raise & crash, or it might return
615
688
  # a replacement value
@@ -618,8 +691,8 @@ module GraphQL
618
691
  rescue GraphQL::ExecutionError => err
619
692
  err
620
693
  end
621
- continue_value(path, next_value, parent_type, field, is_non_null, ast_node, result_name, selection_result)
622
- elsif GraphQL::Execution::Execute::SKIP == value
694
+ continue_value(next_value, parent_type, field, is_non_null, ast_node, result_name, selection_result)
695
+ elsif GraphQL::Execution::SKIP == value
623
696
  # It's possible a lazy was already written here
624
697
  case selection_result
625
698
  when GraphQLResultHash
@@ -644,15 +717,15 @@ module GraphQL
644
717
  if selection_result.nil? || !dead_result?(selection_result)
645
718
  value.each_with_index do |error, index|
646
719
  error.ast_node ||= ast_node
647
- error.path ||= path + (list_type_at_all ? [index] : [])
720
+ error.path ||= current_path + (list_type_at_all ? [index] : [])
648
721
  context.errors << error
649
722
  end
650
723
  if selection_result
651
724
  if list_type_at_all
652
725
  result_without_errors = value.map { |v| v.is_a?(GraphQL::ExecutionError) ? nil : v }
653
- set_result(selection_result, result_name, result_without_errors)
726
+ set_result(selection_result, result_name, result_without_errors, false)
654
727
  else
655
- set_result(selection_result, result_name, nil)
728
+ set_result(selection_result, result_name, nil, false)
656
729
  end
657
730
  end
658
731
  end
@@ -662,7 +735,7 @@ module GraphQL
662
735
  end
663
736
  when GraphQL::Execution::Interpreter::RawValue
664
737
  # Write raw value directly to the response without resolving nested objects
665
- set_result(selection_result, result_name, value.resolve)
738
+ set_result(selection_result, result_name, value.resolve, false)
666
739
  HALT
667
740
  else
668
741
  value
@@ -677,7 +750,7 @@ module GraphQL
677
750
  # Location information from `path` and `ast_node`.
678
751
  #
679
752
  # @return [Lazy, Array, Hash, Object] Lazy, Array, and Hash are all traversed to resolve lazy values later
680
- def continue_field(path, value, owner_type, field, current_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
753
+ 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
681
754
  if current_type.non_null?
682
755
  current_type = current_type.of_type
683
756
  is_non_null = true
@@ -685,25 +758,33 @@ module GraphQL
685
758
 
686
759
  case current_type.kind.name
687
760
  when "SCALAR", "ENUM"
688
- r = current_type.coerce_result(value, context)
689
- set_result(selection_result, result_name, r)
761
+ r = begin
762
+ current_type.coerce_result(value, context)
763
+ rescue StandardError => err
764
+ schema.handle_or_reraise(context, err)
765
+ end
766
+ set_result(selection_result, result_name, r, false)
690
767
  r
691
768
  when "UNION", "INTERFACE"
692
- resolved_type_or_lazy, resolved_value = resolve_type(current_type, value, path)
693
- resolved_value ||= value
769
+ resolved_type_or_lazy = resolve_type(current_type, value)
770
+ after_lazy(resolved_type_or_lazy, owner: current_type, 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|
771
+ if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
772
+ resolved_type, resolved_value = resolved_type_result
773
+ else
774
+ resolved_type = resolved_type_result
775
+ resolved_value = value
776
+ end
694
777
 
695
- after_lazy(resolved_type_or_lazy, owner: current_type, path: path, ast_node: ast_node, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |resolved_type|
696
778
  possible_types = query.possible_types(current_type)
697
-
698
779
  if !possible_types.include?(resolved_type)
699
780
  parent_type = field.owner_type
700
781
  err_class = current_type::UnresolvedTypeError
701
782
  type_error = err_class.new(resolved_value, field, parent_type, resolved_type, possible_types)
702
783
  schema.type_error(type_error, context)
703
- set_result(selection_result, result_name, nil)
784
+ set_result(selection_result, result_name, nil, false)
704
785
  nil
705
786
  else
706
- continue_field(path, resolved_value, owner_type, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result)
787
+ continue_field(resolved_value, owner_type, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result)
707
788
  end
708
789
  end
709
790
  when "OBJECT"
@@ -712,11 +793,11 @@ module GraphQL
712
793
  rescue GraphQL::ExecutionError => err
713
794
  err
714
795
  end
715
- after_lazy(object_proxy, owner: current_type, path: path, ast_node: ast_node, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |inner_object|
716
- continue_value = continue_value(path, inner_object, owner_type, field, is_non_null, ast_node, result_name, selection_result)
796
+ after_lazy(object_proxy, owner: current_type, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |inner_object|
797
+ continue_value = continue_value(inner_object, owner_type, field, is_non_null, ast_node, result_name, selection_result)
717
798
  if HALT != continue_value
718
799
  response_hash = GraphQLResultHash.new(result_name, selection_result)
719
- set_result(selection_result, result_name, response_hash)
800
+ set_result(selection_result, result_name, response_hash, true)
720
801
  gathered_selections = gather_selections(continue_value, current_type, next_selections)
721
802
  # There are two possibilities for `gathered_selections`:
722
803
  # 1. All selections of this object should be evaluated together (there are no runtime directives modifying execution).
@@ -734,11 +815,15 @@ module GraphQL
734
815
  this_result = response_hash
735
816
  final_result = nil
736
817
  end
737
- set_all_interpreter_context(continue_value, nil, nil, path) # reset this mutable state
818
+ # reset this mutable state
819
+ # Unset `result_name` here because it's already included in the new response hash
820
+ st = get_current_runtime_state
821
+ st.current_object = continue_value
822
+ st.current_result_name = nil
823
+ st.current_result = this_result
824
+
738
825
  call_method_on_directives(:resolve, continue_value, selections.graphql_directives) do
739
826
  evaluate_selections(
740
- path,
741
- context.scoped_context,
742
827
  continue_value,
743
828
  current_type,
744
829
  false,
@@ -756,53 +841,60 @@ module GraphQL
756
841
  inner_type = current_type.of_type
757
842
  # This is true for objects, unions, and interfaces
758
843
  use_dataloader_job = !inner_type.unwrap.kind.input?
844
+ inner_type_non_null = inner_type.non_null?
759
845
  response_list = GraphQLResultArray.new(result_name, selection_result)
760
- response_list.graphql_non_null_list_items = inner_type.non_null?
761
- set_result(selection_result, result_name, response_list)
762
-
846
+ response_list.graphql_non_null_list_items = inner_type_non_null
847
+ set_result(selection_result, result_name, response_list, true)
763
848
  idx = 0
764
- scoped_context = context.scoped_context
765
- begin
849
+ list_value = begin
766
850
  value.each do |inner_value|
767
- break if dead_result?(response_list)
768
- next_path = path.dup
769
- next_path << idx
770
851
  this_idx = idx
771
- next_path.freeze
772
852
  idx += 1
773
853
  if use_dataloader_job
774
854
  @dataloader.append_job do
775
- resolve_list_item(inner_value, inner_type, next_path, ast_node, scoped_context, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
855
+ 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)
776
856
  end
777
857
  else
778
- resolve_list_item(inner_value, inner_type, next_path, ast_node, scoped_context, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
858
+ 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)
779
859
  end
780
860
  end
861
+
862
+ response_list
781
863
  rescue NoMethodError => err
782
864
  # Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.)
783
865
  if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
784
866
  # This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
785
- raise ListResultFailedError.new(value: value, field: field, path: path)
867
+ raise ListResultFailedError.new(value: value, field: field, path: current_path)
786
868
  else
787
869
  # This was some other NoMethodError -- let it bubble to reveal the real error.
788
870
  raise
789
871
  end
872
+ rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
873
+ ex_err
874
+ rescue StandardError => err
875
+ begin
876
+ query.handle_or_reraise(err)
877
+ rescue GraphQL::ExecutionError => ex_err
878
+ ex_err
879
+ end
790
880
  end
791
881
 
792
- response_list
882
+ continue_value(list_value, owner_type, field, inner_type.non_null?, ast_node, result_name, selection_result)
793
883
  else
794
884
  raise "Invariant: Unhandled type kind #{current_type.kind} (#{current_type})"
795
885
  end
796
886
  end
797
887
 
798
- def resolve_list_item(inner_value, inner_type, next_path, ast_node, scoped_context, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type) # rubocop:disable Metrics/ParameterLists
799
- set_all_interpreter_context(nil, nil, nil, next_path)
888
+ 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
889
+ st = get_current_runtime_state
890
+ st.current_result_name = this_idx
891
+ st.current_result = response_list
800
892
  call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
801
893
  # This will update `response_list` with the lazy
802
- after_lazy(inner_value, owner: inner_type, path: next_path, ast_node: ast_node, scoped_context: scoped_context, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list) do |inner_inner_value|
803
- continue_value = continue_value(next_path, inner_inner_value, owner_type, field, inner_type.non_null?, ast_node, this_idx, response_list)
894
+ after_lazy(inner_value, owner: inner_type, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list) do |inner_inner_value|
895
+ continue_value = continue_value(inner_inner_value, owner_type, field, inner_type_non_null, ast_node, this_idx, response_list)
804
896
  if HALT != continue_value
805
- continue_field(next_path, continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list)
897
+ continue_field(continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list)
806
898
  end
807
899
  end
808
900
  end
@@ -819,12 +911,8 @@ module GraphQL
819
911
  yield
820
912
  else
821
913
  dir_defn = @schema_directives.fetch(dir_node.name)
822
- if !dir_defn.is_a?(Class)
823
- dir_defn = dir_defn.type_class || raise("Only class-based directives are supported (not `@#{dir_node.name}`)")
824
- end
825
914
  raw_dir_args = arguments(nil, dir_defn, dir_node)
826
915
  dir_args = continue_value(
827
- @context[:current_path], # path
828
916
  raw_dir_args, # value
829
917
  dir_defn, # parent_type
830
918
  nil, # field
@@ -847,7 +935,7 @@ module GraphQL
847
935
  # Check {Schema::Directive.include?} for each directive that's present
848
936
  def directives_include?(node, graphql_object, parent_type)
849
937
  node.directives.each do |dir_node|
850
- dir_defn = @schema_directives.fetch(dir_node.name).type_class || raise("Only class-based directives are supported (not #{dir_node.name.inspect})")
938
+ dir_defn = @schema_directives.fetch(dir_node.name)
851
939
  args = arguments(graphql_object, dir_defn, dir_node)
852
940
  if !dir_defn.include?(graphql_object, args, context)
853
941
  return false
@@ -856,50 +944,43 @@ module GraphQL
856
944
  true
857
945
  end
858
946
 
859
- def set_all_interpreter_context(object, field, arguments, path)
860
- if object
861
- @context[:current_object] = @interpreter_context[:current_object] = object
862
- end
863
- if field
864
- @context[:current_field] = @interpreter_context[:current_field] = field
865
- end
866
- if arguments
867
- @context[:current_arguments] = @interpreter_context[:current_arguments] = arguments
868
- end
869
- if path
870
- @context[:current_path] = @interpreter_context[:current_path] = path
871
- end
947
+ def get_current_runtime_state
948
+ Thread.current[:__graphql_runtime_info] ||= CurrentState.new
872
949
  end
873
950
 
874
951
  # @param obj [Object] Some user-returned value that may want to be batched
875
- # @param path [Array<String>]
876
952
  # @param field [GraphQL::Schema::Field]
877
953
  # @param eager [Boolean] Set to `true` for mutation root fields only
878
954
  # @param trace [Boolean] If `false`, don't wrap this with field tracing
879
955
  # @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
880
- def after_lazy(lazy_obj, owner:, field:, path:, scoped_context:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, trace: true, &block)
956
+ def after_lazy(lazy_obj, owner:, field:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, trace: true, &block)
881
957
  if lazy?(lazy_obj)
882
- lazy = GraphQL::Execution::Lazy.new(path: path, field: field) do
883
- set_all_interpreter_context(owner_object, field, arguments, path)
884
- context.scoped_context = scoped_context
958
+ orig_result = result
959
+ lazy = GraphQL::Execution::Lazy.new(field: field) do
960
+ st = get_current_runtime_state
961
+ st.current_object = owner_object
962
+ st.current_field = field
963
+ st.current_arguments = arguments
964
+ st.current_result_name = result_name
965
+ st.current_result = orig_result
885
966
  # Wrap the execution of _this_ method with tracing,
886
967
  # but don't wrap the continuation below
887
968
  inner_obj = begin
888
- query.with_error_handling do
889
- begin
890
- if trace
891
- query.trace("execute_field_lazy", {owner: owner, field: field, path: path, query: query, object: owner_object, arguments: arguments, ast_node: ast_node}) do
892
- schema.sync_lazy(lazy_obj)
893
- end
894
- else
895
- schema.sync_lazy(lazy_obj)
896
- end
897
- rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => err
898
- err
969
+ if trace
970
+ query.current_trace.execute_field_lazy(field: field, query: query, object: owner_object, arguments: arguments, ast_node: ast_node) do
971
+ schema.sync_lazy(lazy_obj)
899
972
  end
973
+ else
974
+ schema.sync_lazy(lazy_obj)
900
975
  end
901
- rescue GraphQL::ExecutionError => ex_err
976
+ rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
902
977
  ex_err
978
+ rescue StandardError => err
979
+ begin
980
+ query.handle_or_reraise(err)
981
+ rescue GraphQL::ExecutionError => ex_err
982
+ ex_err
983
+ end
903
984
  end
904
985
  yield(inner_obj)
905
986
  end
@@ -907,11 +988,17 @@ module GraphQL
907
988
  if eager
908
989
  lazy.value
909
990
  else
910
- set_result(result, result_name, lazy)
991
+ set_result(result, result_name, lazy, false)
992
+ current_depth = 0
993
+ while result
994
+ current_depth += 1
995
+ result = result.graphql_parent
996
+ end
997
+ @lazies_at_depth[current_depth] << lazy
911
998
  lazy
912
999
  end
913
1000
  else
914
- set_all_interpreter_context(owner_object, field, arguments, path)
1001
+ # Don't need to reset state here because it _wasn't_ lazy.
915
1002
  yield(lazy_obj)
916
1003
  end
917
1004
  end
@@ -925,27 +1012,18 @@ module GraphQL
925
1012
  end
926
1013
  end
927
1014
 
928
- # Set this pair in the Query context, but also in the interpeter namespace,
929
- # for compatibility.
930
- def set_interpreter_context(key, value)
931
- @interpreter_context[key] = value
932
- @context[key] = value
933
- end
934
-
935
- def delete_interpreter_context(key)
936
- @interpreter_context.delete(key)
937
- @context.delete(key)
1015
+ def delete_all_interpreter_context
1016
+ Thread.current[:__graphql_runtime_info] = nil
938
1017
  end
939
1018
 
940
- def resolve_type(type, value, path)
941
- trace_payload = { context: context, type: type, object: value, path: path }
942
- resolved_type, resolved_value = query.trace("resolve_type", trace_payload) do
1019
+ def resolve_type(type, value)
1020
+ resolved_type, resolved_value = query.current_trace.resolve_type(query: query, type: type, object: value) do
943
1021
  query.resolve_type(type, value)
944
1022
  end
945
1023
 
946
1024
  if lazy?(resolved_type)
947
1025
  GraphQL::Execution::Lazy.new do
948
- query.trace("resolve_type_lazy", trace_payload) do
1026
+ query.current_trace.resolve_type_lazy(query: query, type: type, object: value) do
949
1027
  schema.sync_lazy(resolved_type)
950
1028
  end
951
1029
  end