graphql 1.13.14 → 2.0.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (261) 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/tracer.rb +2 -3
  14. data/lib/graphql/backtrace.rb +2 -8
  15. data/lib/graphql/dataloader/null_dataloader.rb +3 -1
  16. data/lib/graphql/dataloader/source.rb +9 -0
  17. data/lib/graphql/dataloader.rb +4 -1
  18. data/lib/graphql/dig.rb +1 -1
  19. data/lib/graphql/execution/errors.rb +12 -82
  20. data/lib/graphql/execution/interpreter/resolve.rb +26 -0
  21. data/lib/graphql/execution/interpreter/runtime.rb +163 -120
  22. data/lib/graphql/execution/interpreter.rb +187 -78
  23. data/lib/graphql/execution/lazy.rb +7 -21
  24. data/lib/graphql/execution/lookahead.rb +44 -40
  25. data/lib/graphql/execution/multiplex.rb +3 -174
  26. data/lib/graphql/execution.rb +11 -4
  27. data/lib/graphql/introspection/directive_type.rb +2 -2
  28. data/lib/graphql/introspection/dynamic_fields.rb +3 -8
  29. data/lib/graphql/introspection/entry_points.rb +2 -15
  30. data/lib/graphql/introspection/field_type.rb +1 -1
  31. data/lib/graphql/introspection/schema_type.rb +2 -2
  32. data/lib/graphql/introspection/type_type.rb +13 -6
  33. data/lib/graphql/introspection.rb +4 -3
  34. data/lib/graphql/language/document_from_schema_definition.rb +18 -35
  35. data/lib/graphql/language/lexer.rb +216 -1488
  36. data/lib/graphql/language/lexer.ri +744 -0
  37. data/lib/graphql/language/nodes.rb +41 -33
  38. data/lib/graphql/language/parser.rb +375 -363
  39. data/lib/graphql/language/parser.y +48 -43
  40. data/lib/graphql/language/printer.rb +37 -21
  41. data/lib/graphql/language/visitor.rb +191 -83
  42. data/lib/graphql/pagination/active_record_relation_connection.rb +0 -8
  43. data/lib/graphql/pagination/array_connection.rb +4 -2
  44. data/lib/graphql/pagination/connection.rb +31 -4
  45. data/lib/graphql/pagination/connections.rb +3 -28
  46. data/lib/graphql/pagination/relation_connection.rb +2 -0
  47. data/lib/graphql/query/context.rb +155 -196
  48. data/lib/graphql/query/input_validation_result.rb +10 -1
  49. data/lib/graphql/query/null_context.rb +0 -3
  50. data/lib/graphql/query/validation_pipeline.rb +12 -37
  51. data/lib/graphql/query/variable_validation_error.rb +2 -2
  52. data/lib/graphql/query/variables.rb +35 -21
  53. data/lib/graphql/query.rb +32 -43
  54. data/lib/graphql/railtie.rb +0 -104
  55. data/lib/graphql/rake_task/validate.rb +1 -1
  56. data/lib/graphql/rake_task.rb +29 -1
  57. data/lib/graphql/relay/range_add.rb +9 -20
  58. data/lib/graphql/relay.rb +0 -15
  59. data/lib/graphql/schema/addition.rb +7 -9
  60. data/lib/graphql/schema/argument.rb +36 -43
  61. data/lib/graphql/schema/build_from_definition.rb +32 -18
  62. data/lib/graphql/schema/directive/one_of.rb +12 -0
  63. data/lib/graphql/schema/directive/transform.rb +1 -1
  64. data/lib/graphql/schema/directive.rb +12 -23
  65. data/lib/graphql/schema/enum.rb +29 -41
  66. data/lib/graphql/schema/enum_value.rb +5 -25
  67. data/lib/graphql/schema/field/connection_extension.rb +4 -0
  68. data/lib/graphql/schema/field.rb +245 -343
  69. data/lib/graphql/schema/input_object.rb +57 -69
  70. data/lib/graphql/schema/interface.rb +0 -35
  71. data/lib/graphql/schema/introspection_system.rb +3 -8
  72. data/lib/graphql/schema/late_bound_type.rb +8 -2
  73. data/lib/graphql/schema/list.rb +18 -9
  74. data/lib/graphql/schema/loader.rb +1 -2
  75. data/lib/graphql/schema/member/base_dsl_methods.rb +15 -19
  76. data/lib/graphql/schema/member/build_type.rb +5 -7
  77. data/lib/graphql/schema/member/has_arguments.rb +146 -55
  78. data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
  79. data/lib/graphql/schema/member/has_directives.rb +71 -56
  80. data/lib/graphql/schema/member/has_fields.rb +16 -4
  81. data/lib/graphql/schema/member/has_interfaces.rb +49 -10
  82. data/lib/graphql/schema/member/has_validators.rb +31 -5
  83. data/lib/graphql/schema/member/relay_shortcuts.rb +28 -2
  84. data/lib/graphql/schema/member/type_system_helpers.rb +17 -0
  85. data/lib/graphql/schema/member/validates_input.rb +3 -3
  86. data/lib/graphql/schema/member.rb +0 -6
  87. data/lib/graphql/schema/mutation.rb +0 -9
  88. data/lib/graphql/schema/non_null.rb +3 -9
  89. data/lib/graphql/schema/object.rb +15 -52
  90. data/lib/graphql/schema/relay_classic_mutation.rb +53 -42
  91. data/lib/graphql/schema/resolver/has_payload_type.rb +20 -10
  92. data/lib/graphql/schema/resolver.rb +41 -42
  93. data/lib/graphql/schema/scalar.rb +8 -23
  94. data/lib/graphql/schema/subscription.rb +0 -7
  95. data/lib/graphql/schema/timeout.rb +24 -28
  96. data/lib/graphql/schema/type_membership.rb +3 -0
  97. data/lib/graphql/schema/union.rb +10 -17
  98. data/lib/graphql/schema/warden.rb +34 -8
  99. data/lib/graphql/schema/wrapper.rb +0 -5
  100. data/lib/graphql/schema.rb +240 -968
  101. data/lib/graphql/static_validation/all_rules.rb +1 -0
  102. data/lib/graphql/static_validation/base_visitor.rb +4 -21
  103. data/lib/graphql/static_validation/definition_dependencies.rb +7 -1
  104. data/lib/graphql/static_validation/error.rb +2 -2
  105. data/lib/graphql/static_validation/literal_validator.rb +19 -1
  106. data/lib/graphql/static_validation/rules/directives_are_defined.rb +11 -5
  107. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +12 -12
  108. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +66 -0
  109. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -0
  110. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +12 -6
  111. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +1 -1
  112. data/lib/graphql/static_validation/validator.rb +3 -25
  113. data/lib/graphql/static_validation.rb +0 -2
  114. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +7 -1
  115. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +38 -1
  116. data/lib/graphql/subscriptions/event.rb +3 -8
  117. data/lib/graphql/subscriptions/instrumentation.rb +0 -51
  118. data/lib/graphql/subscriptions.rb +32 -20
  119. data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
  120. data/lib/graphql/tracing/appoptics_trace.rb +231 -0
  121. data/lib/graphql/tracing/appsignal_trace.rb +66 -0
  122. data/lib/graphql/tracing/data_dog_trace.rb +148 -0
  123. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  124. data/lib/graphql/tracing/new_relic_trace.rb +75 -0
  125. data/lib/graphql/tracing/notifications_trace.rb +41 -0
  126. data/lib/graphql/tracing/platform_trace.rb +107 -0
  127. data/lib/graphql/tracing/platform_tracing.rb +26 -40
  128. data/lib/graphql/tracing/prometheus_trace.rb +89 -0
  129. data/lib/graphql/tracing/prometheus_tracing.rb +3 -3
  130. data/lib/graphql/tracing/scout_trace.rb +72 -0
  131. data/lib/graphql/tracing/statsd_trace.rb +56 -0
  132. data/lib/graphql/tracing.rb +136 -41
  133. data/lib/graphql/type_kinds.rb +6 -3
  134. data/lib/graphql/types/iso_8601_date.rb +4 -1
  135. data/lib/graphql/types/iso_8601_date_time.rb +4 -0
  136. data/lib/graphql/types/relay/base_connection.rb +16 -6
  137. data/lib/graphql/types/relay/connection_behaviors.rb +5 -25
  138. data/lib/graphql/types/relay/default_relay.rb +5 -9
  139. data/lib/graphql/types/relay/edge_behaviors.rb +1 -4
  140. data/lib/graphql/types/relay/node_behaviors.rb +5 -1
  141. data/lib/graphql/types/relay.rb +0 -2
  142. data/lib/graphql/types/string.rb +1 -1
  143. data/lib/graphql/version.rb +1 -1
  144. data/lib/graphql.rb +11 -72
  145. metadata +31 -133
  146. data/lib/graphql/analysis/analyze_query.rb +0 -98
  147. data/lib/graphql/analysis/field_usage.rb +0 -45
  148. data/lib/graphql/analysis/max_query_complexity.rb +0 -26
  149. data/lib/graphql/analysis/max_query_depth.rb +0 -26
  150. data/lib/graphql/analysis/query_complexity.rb +0 -88
  151. data/lib/graphql/analysis/query_depth.rb +0 -43
  152. data/lib/graphql/analysis/reducer_state.rb +0 -48
  153. data/lib/graphql/argument.rb +0 -131
  154. data/lib/graphql/authorization.rb +0 -82
  155. data/lib/graphql/backtrace/legacy_tracer.rb +0 -56
  156. data/lib/graphql/backwards_compatibility.rb +0 -61
  157. data/lib/graphql/base_type.rb +0 -232
  158. data/lib/graphql/boolean_type.rb +0 -2
  159. data/lib/graphql/compatibility/execution_specification/counter_schema.rb +0 -53
  160. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +0 -200
  161. data/lib/graphql/compatibility/execution_specification.rb +0 -436
  162. data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +0 -111
  163. data/lib/graphql/compatibility/lazy_execution_specification.rb +0 -215
  164. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +0 -87
  165. data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +0 -79
  166. data/lib/graphql/compatibility/query_parser_specification.rb +0 -266
  167. data/lib/graphql/compatibility/schema_parser_specification.rb +0 -682
  168. data/lib/graphql/compatibility.rb +0 -5
  169. data/lib/graphql/define/assign_argument.rb +0 -12
  170. data/lib/graphql/define/assign_connection.rb +0 -13
  171. data/lib/graphql/define/assign_enum_value.rb +0 -18
  172. data/lib/graphql/define/assign_global_id_field.rb +0 -11
  173. data/lib/graphql/define/assign_mutation_function.rb +0 -34
  174. data/lib/graphql/define/assign_object_field.rb +0 -42
  175. data/lib/graphql/define/defined_object_proxy.rb +0 -53
  176. data/lib/graphql/define/instance_definable.rb +0 -255
  177. data/lib/graphql/define/no_definition_error.rb +0 -7
  178. data/lib/graphql/define/non_null_with_bang.rb +0 -16
  179. data/lib/graphql/define/type_definer.rb +0 -31
  180. data/lib/graphql/define.rb +0 -31
  181. data/lib/graphql/deprecated_dsl.rb +0 -55
  182. data/lib/graphql/directive/deprecated_directive.rb +0 -2
  183. data/lib/graphql/directive/include_directive.rb +0 -2
  184. data/lib/graphql/directive/skip_directive.rb +0 -2
  185. data/lib/graphql/directive.rb +0 -107
  186. data/lib/graphql/enum_type.rb +0 -133
  187. data/lib/graphql/execution/execute.rb +0 -333
  188. data/lib/graphql/execution/flatten.rb +0 -40
  189. data/lib/graphql/execution/instrumentation.rb +0 -92
  190. data/lib/graphql/execution/lazy/resolve.rb +0 -91
  191. data/lib/graphql/execution/typecast.rb +0 -50
  192. data/lib/graphql/field/resolve.rb +0 -59
  193. data/lib/graphql/field.rb +0 -226
  194. data/lib/graphql/float_type.rb +0 -2
  195. data/lib/graphql/function.rb +0 -128
  196. data/lib/graphql/id_type.rb +0 -2
  197. data/lib/graphql/input_object_type.rb +0 -138
  198. data/lib/graphql/int_type.rb +0 -2
  199. data/lib/graphql/interface_type.rb +0 -72
  200. data/lib/graphql/internal_representation/document.rb +0 -27
  201. data/lib/graphql/internal_representation/node.rb +0 -206
  202. data/lib/graphql/internal_representation/print.rb +0 -51
  203. data/lib/graphql/internal_representation/rewrite.rb +0 -184
  204. data/lib/graphql/internal_representation/scope.rb +0 -88
  205. data/lib/graphql/internal_representation/visit.rb +0 -36
  206. data/lib/graphql/internal_representation.rb +0 -7
  207. data/lib/graphql/language/lexer.rl +0 -260
  208. data/lib/graphql/list_type.rb +0 -80
  209. data/lib/graphql/non_null_type.rb +0 -71
  210. data/lib/graphql/object_type.rb +0 -130
  211. data/lib/graphql/query/arguments.rb +0 -189
  212. data/lib/graphql/query/arguments_cache.rb +0 -24
  213. data/lib/graphql/query/executor.rb +0 -52
  214. data/lib/graphql/query/literal_input.rb +0 -136
  215. data/lib/graphql/query/serial_execution/field_resolution.rb +0 -92
  216. data/lib/graphql/query/serial_execution/operation_resolution.rb +0 -19
  217. data/lib/graphql/query/serial_execution/selection_resolution.rb +0 -23
  218. data/lib/graphql/query/serial_execution/value_resolution.rb +0 -87
  219. data/lib/graphql/query/serial_execution.rb +0 -40
  220. data/lib/graphql/relay/array_connection.rb +0 -83
  221. data/lib/graphql/relay/base_connection.rb +0 -189
  222. data/lib/graphql/relay/connection_instrumentation.rb +0 -54
  223. data/lib/graphql/relay/connection_resolve.rb +0 -43
  224. data/lib/graphql/relay/connection_type.rb +0 -54
  225. data/lib/graphql/relay/edge.rb +0 -27
  226. data/lib/graphql/relay/edge_type.rb +0 -19
  227. data/lib/graphql/relay/edges_instrumentation.rb +0 -39
  228. data/lib/graphql/relay/global_id_resolve.rb +0 -17
  229. data/lib/graphql/relay/mongo_relation_connection.rb +0 -50
  230. data/lib/graphql/relay/mutation/instrumentation.rb +0 -23
  231. data/lib/graphql/relay/mutation/resolve.rb +0 -56
  232. data/lib/graphql/relay/mutation/result.rb +0 -38
  233. data/lib/graphql/relay/mutation.rb +0 -106
  234. data/lib/graphql/relay/node.rb +0 -39
  235. data/lib/graphql/relay/page_info.rb +0 -7
  236. data/lib/graphql/relay/relation_connection.rb +0 -188
  237. data/lib/graphql/relay/type_extensions.rb +0 -32
  238. data/lib/graphql/scalar_type.rb +0 -91
  239. data/lib/graphql/schema/catchall_middleware.rb +0 -35
  240. data/lib/graphql/schema/default_parse_error.rb +0 -10
  241. data/lib/graphql/schema/default_type_error.rb +0 -17
  242. data/lib/graphql/schema/member/accepts_definition.rb +0 -164
  243. data/lib/graphql/schema/member/cached_graphql_definition.rb +0 -58
  244. data/lib/graphql/schema/member/instrumentation.rb +0 -131
  245. data/lib/graphql/schema/middleware_chain.rb +0 -82
  246. data/lib/graphql/schema/possible_types.rb +0 -44
  247. data/lib/graphql/schema/rescue_middleware.rb +0 -60
  248. data/lib/graphql/schema/timeout_middleware.rb +0 -88
  249. data/lib/graphql/schema/traversal.rb +0 -228
  250. data/lib/graphql/schema/validation.rb +0 -313
  251. data/lib/graphql/static_validation/default_visitor.rb +0 -15
  252. data/lib/graphql/static_validation/no_validate_visitor.rb +0 -10
  253. data/lib/graphql/string_type.rb +0 -2
  254. data/lib/graphql/subscriptions/subscription_root.rb +0 -76
  255. data/lib/graphql/tracing/opentelemetry_tracing.rb +0 -101
  256. data/lib/graphql/tracing/skylight_tracing.rb +0 -70
  257. data/lib/graphql/types/relay/node_field.rb +0 -24
  258. data/lib/graphql/types/relay/nodes_field.rb +0 -43
  259. data/lib/graphql/union_type.rb +0 -115
  260. data/lib/graphql/upgrader/member.rb +0 -937
  261. data/lib/graphql/upgrader/schema.rb +0 -38
@@ -20,6 +20,15 @@ module GraphQL
20
20
  @graphql_metadata = nil
21
21
  end
22
22
 
23
+ def path
24
+ @path ||= build_path([])
25
+ end
26
+
27
+ def build_path(path_array)
28
+ graphql_result_name && path_array.unshift(graphql_result_name)
29
+ @graphql_parent ? @graphql_parent.build_path(path_array) : path_array
30
+ end
31
+
23
32
  attr_accessor :graphql_dead
24
33
  attr_reader :graphql_parent, :graphql_result_name
25
34
 
@@ -148,13 +157,24 @@ module GraphQL
148
157
  # @return [GraphQL::Query::Context]
149
158
  attr_reader :context
150
159
 
151
- def initialize(query:)
160
+ def thread_info
161
+ info = Thread.current[:__graphql_runtime_info]
162
+ if !info
163
+ new_ti = {}
164
+ info = Thread.current[:__graphql_runtime_info] = new_ti
165
+ end
166
+ info
167
+ end
168
+
169
+ def initialize(query:, lazies_at_depth:)
152
170
  @query = query
153
171
  @dataloader = query.multiplex.dataloader
172
+ @lazies_at_depth = lazies_at_depth
154
173
  @schema = query.schema
155
174
  @context = query.context
156
175
  @multiplex_context = query.multiplex.context
157
- @interpreter_context = @context.namespace(:interpreter)
176
+ # Start this off empty:
177
+ Thread.current[:__graphql_runtime_info] = nil
158
178
  @response = GraphQLResultHash.new(nil, nil)
159
179
  # Identify runtime directives by checking which of this schema's directives have overridden `def self.resolve`
160
180
  @runtime_directive_names = []
@@ -198,8 +218,7 @@ module GraphQL
198
218
  root_operation = query.selected_operation
199
219
  root_op_type = root_operation.operation_type || "query"
200
220
  root_type = schema.root_type_for_operation(root_op_type)
201
- path = []
202
- set_all_interpreter_context(query.root_value, nil, nil, path)
221
+ set_all_interpreter_context(query.root_value, nil, nil, nil, @response)
203
222
  object_proxy = authorized_new(root_type, query.root_value, context)
204
223
  object_proxy = schema.sync_lazy(object_proxy)
205
224
 
@@ -226,11 +245,9 @@ module GraphQL
226
245
  end
227
246
 
228
247
  @dataloader.append_job {
229
- set_all_interpreter_context(query.root_value, nil, nil, path)
248
+ set_all_interpreter_context(query.root_value, nil, nil, nil, selection_response)
230
249
  call_method_on_directives(:resolve, object_proxy, selections.graphql_directives) do
231
250
  evaluate_selections(
232
- path,
233
- context.scoped_context,
234
251
  object_proxy,
235
252
  root_type,
236
253
  root_op_type == "mutation",
@@ -244,10 +261,7 @@ module GraphQL
244
261
  end
245
262
  end
246
263
  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)
264
+ delete_all_interpreter_context
251
265
  nil
252
266
  end
253
267
 
@@ -349,15 +363,15 @@ module GraphQL
349
363
  NO_ARGS = {}.freeze
350
364
 
351
365
  # @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)
366
+ def evaluate_selections(owner_object, owner_type, is_eager_selection, gathered_selections, selections_result, target_result, parent_object) # rubocop:disable Metrics/ParameterLists
367
+ set_all_interpreter_context(owner_object, nil, nil, nil, selections_result)
354
368
 
355
369
  finished_jobs = 0
356
370
  enqueued_jobs = gathered_selections.size
357
371
  gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
358
372
  @dataloader.append_job {
359
373
  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
374
+ result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_selection, selections_result, parent_object
361
375
  )
362
376
  finished_jobs += 1
363
377
  if target_result && finished_jobs == enqueued_jobs
@@ -369,10 +383,8 @@ module GraphQL
369
383
  selections_result
370
384
  end
371
385
 
372
- attr_reader :progress_path
373
-
374
386
  # @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
387
+ 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
388
  return if dead_result?(selections_result)
377
389
  # As a performance optimization, the hash key will be a `Node` if
378
390
  # there's only one selection of the field. But if there are multiple
@@ -400,11 +412,8 @@ module GraphQL
400
412
  raise "Invariant: no field for #{owner_type}.#{field_name}"
401
413
  end
402
414
  end
403
- return_type = field_defn.type
404
415
 
405
- next_path = path.dup
406
- next_path << result_name
407
- next_path.freeze
416
+ return_type = field_defn.type
408
417
 
409
418
  # This seems janky, but we need to know
410
419
  # the field's return type at this path in order
@@ -413,9 +422,7 @@ module GraphQL
413
422
  (selections_result.graphql_non_null_field_names ||= []).push(result_name)
414
423
  end
415
424
  # 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)
417
-
418
- context.scoped_context = scoped_context
425
+ set_all_interpreter_context(nil, field_defn, nil, result_name, selections_result)
419
426
  object = owner_object
420
427
 
421
428
  if is_introspection
@@ -425,21 +432,19 @@ module GraphQL
425
432
  total_args_count = field_defn.arguments(context).size
426
433
  if total_args_count == 0
427
434
  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)
435
+ 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)
429
436
  else
430
437
  # TODO remove all arguments(...) usages?
431
438
  @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)
439
+ 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)
433
440
  end
434
441
  end
435
442
  end
436
443
 
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|
444
+ 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) # rubocop:disable Metrics/ParameterLists
445
+ 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
446
  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)
447
+ continue_value(resolved_arguments, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
443
448
  next
444
449
  end
445
450
 
@@ -455,9 +460,9 @@ module GraphQL
455
460
  when :ast_node
456
461
  extra_args[:ast_node] = ast_node
457
462
  when :execution_errors
458
- extra_args[:execution_errors] = ExecutionErrors.new(context, ast_node, next_path)
463
+ extra_args[:execution_errors] = ExecutionErrors.new(context, ast_node, current_path)
459
464
  when :path
460
- extra_args[:path] = next_path
465
+ extra_args[:path] = current_path
461
466
  when :lookahead
462
467
  if !field_ast_nodes
463
468
  field_ast_nodes = [ast_node]
@@ -472,10 +477,6 @@ module GraphQL
472
477
  # Use this flag to tell Interpreter::Arguments to add itself
473
478
  # to the keyword args hash _before_ freezing everything.
474
479
  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
480
  when :parent
480
481
  extra_args[:parent] = parent_object
481
482
  else
@@ -488,7 +489,7 @@ module GraphQL
488
489
  resolved_arguments.keyword_arguments
489
490
  end
490
491
 
491
- set_all_interpreter_context(nil, nil, resolved_arguments, nil)
492
+ set_all_interpreter_context(nil, nil, resolved_arguments, result_name, selection_result)
492
493
 
493
494
  # Optimize for the case that field is selected only once
494
495
  if field_ast_nodes.nil? || field_ast_nodes.size == 1
@@ -506,18 +507,22 @@ module GraphQL
506
507
  field_result = call_method_on_directives(:resolve, object, directives) do
507
508
  # Actually call the field resolver and capture the result
508
509
  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
510
+ query.current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
511
+ field_defn.resolve(object, kwarg_arguments, context)
513
512
  end
514
513
  rescue GraphQL::ExecutionError => err
515
514
  err
515
+ rescue StandardError => err
516
+ begin
517
+ query.handle_or_reraise(err)
518
+ rescue GraphQL::ExecutionError => ex_err
519
+ ex_err
520
+ end
516
521
  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)
522
+ 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|
523
+ continue_value = continue_value(inner_result, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
519
524
  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)
525
+ continue_field(continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result)
521
526
  end
522
527
  end
523
528
  end
@@ -582,8 +587,30 @@ module GraphQL
582
587
  end
583
588
  end
584
589
 
590
+ def current_path
591
+ ti = thread_info
592
+ path = ti &&
593
+ (result = ti[:current_result]) &&
594
+ (result.path)
595
+ if path && (rn = ti[:current_result_name])
596
+ path = path.dup
597
+ path.push(rn)
598
+ end
599
+ path
600
+ end
601
+
602
+ def current_depth
603
+ ti = thread_info
604
+ depth = 1
605
+ result = ti[:current_result]
606
+ while (result = result.graphql_parent)
607
+ depth += 1
608
+ end
609
+ depth
610
+ end
611
+
585
612
  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
613
+ def continue_value(value, parent_type, field, is_non_null, ast_node, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
587
614
  case value
588
615
  when nil
589
616
  if is_non_null
@@ -602,7 +629,7 @@ module GraphQL
602
629
  # every time.
603
630
  if value.is_a?(GraphQL::ExecutionError)
604
631
  if selection_result.nil? || !dead_result?(selection_result)
605
- value.path ||= path
632
+ value.path ||= current_path
606
633
  value.ast_node ||= ast_node
607
634
  context.errors << value
608
635
  if selection_result
@@ -610,6 +637,16 @@ module GraphQL
610
637
  end
611
638
  end
612
639
  HALT
640
+ elsif value.is_a?(GraphQL::UnauthorizedFieldError)
641
+ value.field ||= field
642
+ # this hook might raise & crash, or it might return
643
+ # a replacement value
644
+ next_value = begin
645
+ schema.unauthorized_field(value)
646
+ rescue GraphQL::ExecutionError => err
647
+ err
648
+ end
649
+ continue_value(next_value, parent_type, field, is_non_null, ast_node, result_name, selection_result)
613
650
  elsif value.is_a?(GraphQL::UnauthorizedError)
614
651
  # this hook might raise & crash, or it might return
615
652
  # a replacement value
@@ -618,8 +655,8 @@ module GraphQL
618
655
  rescue GraphQL::ExecutionError => err
619
656
  err
620
657
  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
658
+ continue_value(next_value, parent_type, field, is_non_null, ast_node, result_name, selection_result)
659
+ elsif GraphQL::Execution::SKIP == value
623
660
  # It's possible a lazy was already written here
624
661
  case selection_result
625
662
  when GraphQLResultHash
@@ -644,7 +681,7 @@ module GraphQL
644
681
  if selection_result.nil? || !dead_result?(selection_result)
645
682
  value.each_with_index do |error, index|
646
683
  error.ast_node ||= ast_node
647
- error.path ||= path + (list_type_at_all ? [index] : [])
684
+ error.path ||= current_path + (list_type_at_all ? [index] : [])
648
685
  context.errors << error
649
686
  end
650
687
  if selection_result
@@ -677,7 +714,7 @@ module GraphQL
677
714
  # Location information from `path` and `ast_node`.
678
715
  #
679
716
  # @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
717
+ 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
718
  if current_type.non_null?
682
719
  current_type = current_type.of_type
683
720
  is_non_null = true
@@ -685,16 +722,24 @@ module GraphQL
685
722
 
686
723
  case current_type.kind.name
687
724
  when "SCALAR", "ENUM"
688
- r = current_type.coerce_result(value, context)
725
+ r = begin
726
+ current_type.coerce_result(value, context)
727
+ rescue StandardError => err
728
+ schema.handle_or_reraise(context, err)
729
+ end
689
730
  set_result(selection_result, result_name, r)
690
731
  r
691
732
  when "UNION", "INTERFACE"
692
- resolved_type_or_lazy, resolved_value = resolve_type(current_type, value, path)
693
- resolved_value ||= value
733
+ resolved_type_or_lazy = resolve_type(current_type, value)
734
+ 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|
735
+ if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
736
+ resolved_type, resolved_value = resolved_type_result
737
+ else
738
+ resolved_type = resolved_type_result
739
+ resolved_value = value
740
+ end
694
741
 
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
742
  possible_types = query.possible_types(current_type)
697
-
698
743
  if !possible_types.include?(resolved_type)
699
744
  parent_type = field.owner_type
700
745
  err_class = current_type::UnresolvedTypeError
@@ -703,7 +748,7 @@ module GraphQL
703
748
  set_result(selection_result, result_name, nil)
704
749
  nil
705
750
  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)
751
+ continue_field(resolved_value, owner_type, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result)
707
752
  end
708
753
  end
709
754
  when "OBJECT"
@@ -712,8 +757,8 @@ module GraphQL
712
757
  rescue GraphQL::ExecutionError => err
713
758
  err
714
759
  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)
760
+ 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|
761
+ continue_value = continue_value(inner_object, owner_type, field, is_non_null, ast_node, result_name, selection_result)
717
762
  if HALT != continue_value
718
763
  response_hash = GraphQLResultHash.new(result_name, selection_result)
719
764
  set_result(selection_result, result_name, response_hash)
@@ -734,11 +779,10 @@ module GraphQL
734
779
  this_result = response_hash
735
780
  final_result = nil
736
781
  end
737
- set_all_interpreter_context(continue_value, nil, nil, path) # reset this mutable state
782
+ # Don't pass `result_name` here because it's already included in the new response hash
783
+ set_all_interpreter_context(continue_value, nil, nil, nil, this_result) # reset this mutable state
738
784
  call_method_on_directives(:resolve, continue_value, selections.graphql_directives) do
739
785
  evaluate_selections(
740
- path,
741
- context.scoped_context,
742
786
  continue_value,
743
787
  current_type,
744
788
  false,
@@ -759,50 +803,55 @@ module GraphQL
759
803
  response_list = GraphQLResultArray.new(result_name, selection_result)
760
804
  response_list.graphql_non_null_list_items = inner_type.non_null?
761
805
  set_result(selection_result, result_name, response_list)
762
-
763
806
  idx = 0
764
- scoped_context = context.scoped_context
765
- begin
807
+ list_value = begin
766
808
  value.each do |inner_value|
767
809
  break if dead_result?(response_list)
768
- next_path = path.dup
769
- next_path << idx
770
810
  this_idx = idx
771
- next_path.freeze
772
811
  idx += 1
773
812
  if use_dataloader_job
774
813
  @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)
814
+ resolve_list_item(inner_value, inner_type, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
776
815
  end
777
816
  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)
817
+ resolve_list_item(inner_value, inner_type, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
779
818
  end
780
819
  end
820
+
821
+ response_list
781
822
  rescue NoMethodError => err
782
823
  # Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.)
783
824
  if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
784
825
  # 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)
826
+ raise ListResultFailedError.new(value: value, field: field, path: current_path)
786
827
  else
787
828
  # This was some other NoMethodError -- let it bubble to reveal the real error.
788
829
  raise
789
830
  end
831
+ rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
832
+ ex_err
833
+ rescue StandardError => err
834
+ begin
835
+ query.handle_or_reraise(err)
836
+ rescue GraphQL::ExecutionError => ex_err
837
+ ex_err
838
+ end
790
839
  end
791
840
 
792
- response_list
841
+ continue_value(list_value, owner_type, field, inner_type.non_null?, ast_node, result_name, selection_result)
793
842
  else
794
843
  raise "Invariant: Unhandled type kind #{current_type.kind} (#{current_type})"
795
844
  end
796
845
  end
797
846
 
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)
847
+ def resolve_list_item(inner_value, inner_type, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type) # rubocop:disable Metrics/ParameterLists
848
+ set_all_interpreter_context(nil, nil, nil, this_idx, response_list)
800
849
  call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
801
850
  # 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)
851
+ 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|
852
+ continue_value = continue_value(inner_inner_value, owner_type, field, inner_type.non_null?, ast_node, this_idx, response_list)
804
853
  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)
854
+ continue_field(continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list)
806
855
  end
807
856
  end
808
857
  end
@@ -819,12 +868,8 @@ module GraphQL
819
868
  yield
820
869
  else
821
870
  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
871
  raw_dir_args = arguments(nil, dir_defn, dir_node)
826
872
  dir_args = continue_value(
827
- @context[:current_path], # path
828
873
  raw_dir_args, # value
829
874
  dir_defn, # parent_type
830
875
  nil, # field
@@ -847,7 +892,7 @@ module GraphQL
847
892
  # Check {Schema::Directive.include?} for each directive that's present
848
893
  def directives_include?(node, graphql_object, parent_type)
849
894
  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})")
895
+ dir_defn = @schema_directives.fetch(dir_node.name)
851
896
  args = arguments(graphql_object, dir_defn, dir_node)
852
897
  if !dir_defn.include?(graphql_object, args, context)
853
898
  return false
@@ -856,50 +901,50 @@ module GraphQL
856
901
  true
857
902
  end
858
903
 
859
- def set_all_interpreter_context(object, field, arguments, path)
904
+ def set_all_interpreter_context(object, field, arguments, result_name, result)
905
+ ti = thread_info
860
906
  if object
861
- @context[:current_object] = @interpreter_context[:current_object] = object
907
+ ti[:current_object] = object
862
908
  end
863
909
  if field
864
- @context[:current_field] = @interpreter_context[:current_field] = field
910
+ ti[:current_field] = field
865
911
  end
866
912
  if arguments
867
- @context[:current_arguments] = @interpreter_context[:current_arguments] = arguments
913
+ ti[:current_arguments] = arguments
868
914
  end
869
- if path
870
- @context[:current_path] = @interpreter_context[:current_path] = path
915
+ ti[:current_result_name] = result_name
916
+ if result
917
+ ti[:current_result] = result
871
918
  end
872
919
  end
873
920
 
874
921
  # @param obj [Object] Some user-returned value that may want to be batched
875
- # @param path [Array<String>]
876
922
  # @param field [GraphQL::Schema::Field]
877
923
  # @param eager [Boolean] Set to `true` for mutation root fields only
878
924
  # @param trace [Boolean] If `false`, don't wrap this with field tracing
879
925
  # @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)
926
+ def after_lazy(lazy_obj, owner:, field:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, trace: true, &block)
881
927
  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
928
+ lazy = GraphQL::Execution::Lazy.new(field: field) do
929
+ set_all_interpreter_context(owner_object, field, arguments, result_name, result)
885
930
  # Wrap the execution of _this_ method with tracing,
886
931
  # but don't wrap the continuation below
887
932
  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
933
+ if trace
934
+ query.current_trace.execute_field_lazy(field: field, query: query, object: owner_object, arguments: arguments, ast_node: ast_node) do
935
+ schema.sync_lazy(lazy_obj)
899
936
  end
937
+ else
938
+ schema.sync_lazy(lazy_obj)
900
939
  end
901
- rescue GraphQL::ExecutionError => ex_err
940
+ rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
902
941
  ex_err
942
+ rescue StandardError => err
943
+ begin
944
+ query.handle_or_reraise(err)
945
+ rescue GraphQL::ExecutionError => ex_err
946
+ ex_err
947
+ end
903
948
  end
904
949
  yield(inner_obj)
905
950
  end
@@ -908,10 +953,11 @@ module GraphQL
908
953
  lazy.value
909
954
  else
910
955
  set_result(result, result_name, lazy)
956
+ @lazies_at_depth[current_depth] << lazy
911
957
  lazy
912
958
  end
913
959
  else
914
- set_all_interpreter_context(owner_object, field, arguments, path)
960
+ set_all_interpreter_context(owner_object, field, arguments, result_name, result)
915
961
  yield(lazy_obj)
916
962
  end
917
963
  end
@@ -925,27 +971,24 @@ module GraphQL
925
971
  end
926
972
  end
927
973
 
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)
974
+ def delete_all_interpreter_context
975
+ if (ti = thread_info)
976
+ ti.delete(:current_result)
977
+ ti.delete(:current_result_name)
978
+ ti.delete(:current_field)
979
+ ti.delete(:current_object)
980
+ ti.delete(:current_arguments)
981
+ end
938
982
  end
939
983
 
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
984
+ def resolve_type(type, value)
985
+ resolved_type, resolved_value = query.current_trace.resolve_type(query: query, type: type, object: value) do
943
986
  query.resolve_type(type, value)
944
987
  end
945
988
 
946
989
  if lazy?(resolved_type)
947
990
  GraphQL::Execution::Lazy.new do
948
- query.trace("resolve_type_lazy", trace_payload) do
991
+ query.current_trace.resolve_type_lazy(query: query, type: type, object: value) do
949
992
  schema.sync_lazy(resolved_type)
950
993
  end
951
994
  end