graphql 1.12.12 → 2.4.8

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 (428) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +3 -8
  3. data/lib/generators/graphql/enum_generator.rb +4 -10
  4. data/lib/generators/graphql/field_extractor.rb +31 -0
  5. data/lib/generators/graphql/input_generator.rb +50 -0
  6. data/lib/generators/graphql/install/mutation_root_generator.rb +34 -0
  7. data/lib/generators/graphql/{templates → install/templates}/base_mutation.erb +2 -0
  8. data/lib/generators/graphql/{templates → install/templates}/mutation_type.erb +2 -0
  9. data/lib/generators/graphql/install_generator.rb +60 -4
  10. data/lib/generators/graphql/interface_generator.rb +7 -7
  11. data/lib/generators/graphql/mutation_create_generator.rb +22 -0
  12. data/lib/generators/graphql/mutation_delete_generator.rb +22 -0
  13. data/lib/generators/graphql/mutation_generator.rb +5 -30
  14. data/lib/generators/graphql/mutation_update_generator.rb +22 -0
  15. data/lib/generators/graphql/object_generator.rb +10 -38
  16. data/lib/generators/graphql/orm_mutations_base.rb +40 -0
  17. data/lib/generators/graphql/relay.rb +23 -12
  18. data/lib/generators/graphql/scalar_generator.rb +4 -2
  19. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  20. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  21. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  22. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  23. data/lib/generators/graphql/templates/base_field.erb +2 -0
  24. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  25. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  26. data/lib/generators/graphql/templates/base_object.erb +2 -0
  27. data/lib/generators/graphql/templates/base_resolver.erb +8 -0
  28. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  29. data/lib/generators/graphql/templates/base_union.erb +2 -0
  30. data/lib/generators/graphql/templates/enum.erb +5 -1
  31. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  32. data/lib/generators/graphql/templates/input.erb +9 -0
  33. data/lib/generators/graphql/templates/interface.erb +4 -2
  34. data/lib/generators/graphql/templates/loader.erb +2 -0
  35. data/lib/generators/graphql/templates/mutation.erb +3 -1
  36. data/lib/generators/graphql/templates/mutation_create.erb +20 -0
  37. data/lib/generators/graphql/templates/mutation_delete.erb +20 -0
  38. data/lib/generators/graphql/templates/mutation_update.erb +21 -0
  39. data/lib/generators/graphql/templates/node_type.erb +2 -0
  40. data/lib/generators/graphql/templates/object.erb +4 -2
  41. data/lib/generators/graphql/templates/query_type.erb +2 -0
  42. data/lib/generators/graphql/templates/scalar.erb +3 -1
  43. data/lib/generators/graphql/templates/schema.erb +22 -2
  44. data/lib/generators/graphql/templates/union.erb +4 -2
  45. data/lib/generators/graphql/type_generator.rb +46 -10
  46. data/lib/generators/graphql/union_generator.rb +5 -5
  47. data/lib/graphql/analysis/analyzer.rb +89 -0
  48. data/lib/graphql/analysis/field_usage.rb +65 -28
  49. data/lib/graphql/analysis/max_query_complexity.rb +11 -17
  50. data/lib/graphql/analysis/max_query_depth.rb +13 -19
  51. data/lib/graphql/analysis/query_complexity.rb +156 -61
  52. data/lib/graphql/analysis/query_depth.rb +38 -23
  53. data/lib/graphql/analysis/visitor.rb +283 -0
  54. data/lib/graphql/analysis.rb +90 -6
  55. data/lib/graphql/autoload.rb +38 -0
  56. data/lib/graphql/backtrace/inspect_result.rb +0 -12
  57. data/lib/graphql/backtrace/table.rb +4 -22
  58. data/lib/graphql/backtrace/trace.rb +93 -0
  59. data/lib/graphql/backtrace/tracer.rb +8 -6
  60. data/lib/graphql/backtrace.rb +3 -8
  61. data/lib/graphql/coercion_error.rb +1 -9
  62. data/lib/graphql/current.rb +52 -0
  63. data/lib/graphql/dataloader/async_dataloader.rb +89 -0
  64. data/lib/graphql/dataloader/null_dataloader.rb +4 -2
  65. data/lib/graphql/dataloader/request.rb +5 -0
  66. data/lib/graphql/dataloader/source.rb +125 -33
  67. data/lib/graphql/dataloader.rb +193 -143
  68. data/lib/graphql/date_encoding_error.rb +16 -0
  69. data/lib/graphql/dig.rb +1 -1
  70. data/lib/graphql/duration_encoding_error.rb +16 -0
  71. data/lib/graphql/execution/errors.rb +12 -81
  72. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  73. data/lib/graphql/execution/interpreter/arguments.rb +2 -2
  74. data/lib/graphql/execution/interpreter/arguments_cache.rb +33 -36
  75. data/lib/graphql/execution/interpreter/resolve.rb +38 -4
  76. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +175 -0
  77. data/lib/graphql/execution/interpreter/runtime.rb +447 -403
  78. data/lib/graphql/execution/interpreter.rb +126 -80
  79. data/lib/graphql/execution/lazy.rb +11 -21
  80. data/lib/graphql/execution/lookahead.rb +133 -55
  81. data/lib/graphql/execution/multiplex.rb +4 -172
  82. data/lib/graphql/execution.rb +11 -4
  83. data/lib/graphql/integer_encoding_error.rb +18 -2
  84. data/lib/graphql/introspection/directive_location_enum.rb +2 -2
  85. data/lib/graphql/introspection/directive_type.rb +6 -4
  86. data/lib/graphql/introspection/dynamic_fields.rb +3 -8
  87. data/lib/graphql/introspection/entry_points.rb +11 -18
  88. data/lib/graphql/introspection/enum_value_type.rb +2 -2
  89. data/lib/graphql/introspection/field_type.rb +4 -4
  90. data/lib/graphql/introspection/input_value_type.rb +10 -4
  91. data/lib/graphql/introspection/schema_type.rb +17 -15
  92. data/lib/graphql/introspection/type_type.rb +29 -16
  93. data/lib/graphql/introspection.rb +6 -2
  94. data/lib/graphql/invalid_null_error.rb +1 -1
  95. data/lib/graphql/language/block_string.rb +37 -25
  96. data/lib/graphql/language/cache.rb +13 -0
  97. data/lib/graphql/language/comment.rb +18 -0
  98. data/lib/graphql/language/definition_slice.rb +1 -1
  99. data/lib/graphql/language/document_from_schema_definition.rb +122 -81
  100. data/lib/graphql/language/lexer.rb +364 -1467
  101. data/lib/graphql/language/nodes.rb +197 -106
  102. data/lib/graphql/language/parser.rb +799 -1920
  103. data/lib/graphql/language/printer.rb +372 -160
  104. data/lib/graphql/language/sanitized_printer.rb +25 -27
  105. data/lib/graphql/language/static_visitor.rb +167 -0
  106. data/lib/graphql/language/visitor.rb +188 -141
  107. data/lib/graphql/language.rb +62 -1
  108. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  109. data/lib/graphql/name_validator.rb +0 -4
  110. data/lib/graphql/pagination/active_record_relation_connection.rb +37 -8
  111. data/lib/graphql/pagination/array_connection.rb +8 -6
  112. data/lib/graphql/pagination/connection.rb +61 -7
  113. data/lib/graphql/pagination/connections.rb +22 -23
  114. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  115. data/lib/graphql/pagination/relation_connection.rb +60 -28
  116. data/lib/graphql/query/context/scoped_context.rb +101 -0
  117. data/lib/graphql/query/context.rb +146 -222
  118. data/lib/graphql/query/input_validation_result.rb +10 -1
  119. data/lib/graphql/query/null_context.rb +15 -32
  120. data/lib/graphql/query/validation_pipeline.rb +15 -39
  121. data/lib/graphql/query/variable_validation_error.rb +3 -3
  122. data/lib/graphql/query/variables.rb +35 -17
  123. data/lib/graphql/query.rb +149 -82
  124. data/lib/graphql/railtie.rb +15 -109
  125. data/lib/graphql/rake_task/validate.rb +1 -1
  126. data/lib/graphql/rake_task.rb +30 -11
  127. data/lib/graphql/relay/range_add.rb +9 -16
  128. data/lib/graphql/relay.rb +0 -15
  129. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  130. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  131. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  132. data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
  133. data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
  134. data/lib/graphql/rubocop.rb +6 -0
  135. data/lib/graphql/schema/addition.rb +98 -54
  136. data/lib/graphql/schema/always_visible.rb +14 -0
  137. data/lib/graphql/schema/argument.rb +179 -82
  138. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  139. data/lib/graphql/schema/build_from_definition.rb +77 -39
  140. data/lib/graphql/schema/directive/feature.rb +1 -1
  141. data/lib/graphql/schema/directive/flagged.rb +4 -4
  142. data/lib/graphql/schema/directive/include.rb +1 -1
  143. data/lib/graphql/schema/directive/one_of.rb +24 -0
  144. data/lib/graphql/schema/directive/skip.rb +1 -1
  145. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  146. data/lib/graphql/schema/directive/transform.rb +2 -2
  147. data/lib/graphql/schema/directive.rb +36 -22
  148. data/lib/graphql/schema/enum.rb +158 -63
  149. data/lib/graphql/schema/enum_value.rb +12 -21
  150. data/lib/graphql/schema/field/connection_extension.rb +7 -17
  151. data/lib/graphql/schema/field/scope_extension.rb +8 -1
  152. data/lib/graphql/schema/field.rb +521 -359
  153. data/lib/graphql/schema/field_extension.rb +86 -2
  154. data/lib/graphql/schema/find_inherited_value.rb +3 -7
  155. data/lib/graphql/schema/finder.rb +5 -5
  156. data/lib/graphql/schema/has_single_input_argument.rb +160 -0
  157. data/lib/graphql/schema/input_object.rb +148 -99
  158. data/lib/graphql/schema/interface.rb +41 -64
  159. data/lib/graphql/schema/introspection_system.rb +12 -26
  160. data/lib/graphql/schema/late_bound_type.rb +12 -2
  161. data/lib/graphql/schema/list.rb +18 -7
  162. data/lib/graphql/schema/loader.rb +6 -5
  163. data/lib/graphql/schema/member/base_dsl_methods.rb +32 -18
  164. data/lib/graphql/schema/member/build_type.rb +16 -13
  165. data/lib/graphql/schema/member/has_arguments.rb +270 -86
  166. data/lib/graphql/schema/member/has_ast_node.rb +12 -0
  167. data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
  168. data/lib/graphql/schema/member/has_directives.rb +81 -61
  169. data/lib/graphql/schema/member/has_fields.rb +169 -31
  170. data/lib/graphql/schema/member/has_interfaces.rb +143 -0
  171. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  172. data/lib/graphql/schema/member/has_validators.rb +32 -6
  173. data/lib/graphql/schema/member/relay_shortcuts.rb +47 -2
  174. data/lib/graphql/schema/member/scoped.rb +19 -0
  175. data/lib/graphql/schema/member/type_system_helpers.rb +16 -0
  176. data/lib/graphql/schema/member/validates_input.rb +6 -6
  177. data/lib/graphql/schema/member.rb +1 -6
  178. data/lib/graphql/schema/mutation.rb +7 -9
  179. data/lib/graphql/schema/non_null.rb +7 -7
  180. data/lib/graphql/schema/object.rb +38 -119
  181. data/lib/graphql/schema/printer.rb +24 -25
  182. data/lib/graphql/schema/relay_classic_mutation.rb +13 -91
  183. data/lib/graphql/schema/resolver/has_payload_type.rb +46 -11
  184. data/lib/graphql/schema/resolver.rb +118 -115
  185. data/lib/graphql/schema/scalar.rb +20 -21
  186. data/lib/graphql/schema/subscription.rb +95 -21
  187. data/lib/graphql/schema/timeout.rb +25 -29
  188. data/lib/graphql/schema/type_expression.rb +2 -2
  189. data/lib/graphql/schema/type_membership.rb +21 -4
  190. data/lib/graphql/schema/union.rb +16 -16
  191. data/lib/graphql/schema/unique_within_type.rb +1 -1
  192. data/lib/graphql/schema/validator/all_validator.rb +62 -0
  193. data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
  194. data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
  195. data/lib/graphql/schema/validator/exclusion_validator.rb +3 -1
  196. data/lib/graphql/schema/validator/format_validator.rb +4 -5
  197. data/lib/graphql/schema/validator/inclusion_validator.rb +3 -1
  198. data/lib/graphql/schema/validator/length_validator.rb +5 -3
  199. data/lib/graphql/schema/validator/numericality_validator.rb +13 -2
  200. data/lib/graphql/schema/validator/required_validator.rb +56 -18
  201. data/lib/graphql/schema/validator.rb +38 -28
  202. data/lib/graphql/schema/visibility/migration.rb +188 -0
  203. data/lib/graphql/schema/visibility/profile.rb +359 -0
  204. data/lib/graphql/schema/visibility/visit.rb +190 -0
  205. data/lib/graphql/schema/visibility.rb +294 -0
  206. data/lib/graphql/schema/warden.rb +423 -134
  207. data/lib/graphql/schema/wrapper.rb +0 -5
  208. data/lib/graphql/schema.rb +1015 -1057
  209. data/lib/graphql/static_validation/all_rules.rb +3 -1
  210. data/lib/graphql/static_validation/base_visitor.rb +15 -28
  211. data/lib/graphql/static_validation/definition_dependencies.rb +7 -2
  212. data/lib/graphql/static_validation/error.rb +3 -1
  213. data/lib/graphql/static_validation/literal_validator.rb +24 -7
  214. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  215. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  216. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +4 -3
  217. data/lib/graphql/static_validation/rules/directives_are_defined.rb +13 -7
  218. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +15 -13
  219. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +12 -2
  220. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +13 -5
  221. data/lib/graphql/static_validation/rules/fields_will_merge.rb +62 -35
  222. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
  223. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  224. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +12 -2
  225. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
  226. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  227. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  228. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  229. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +66 -0
  230. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -0
  231. data/lib/graphql/static_validation/rules/query_root_exists.rb +17 -0
  232. data/lib/graphql/static_validation/rules/query_root_exists_error.rb +26 -0
  233. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +7 -5
  234. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
  235. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
  236. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +14 -8
  237. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  238. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  239. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +14 -8
  240. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +11 -2
  241. data/lib/graphql/static_validation/validation_context.rb +32 -6
  242. data/lib/graphql/static_validation/validator.rb +11 -27
  243. data/lib/graphql/static_validation.rb +0 -3
  244. data/lib/graphql/string_encoding_error.rb +13 -3
  245. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +49 -11
  246. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  247. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +40 -1
  248. data/lib/graphql/subscriptions/event.rb +87 -38
  249. data/lib/graphql/subscriptions/serialize.rb +27 -3
  250. data/lib/graphql/subscriptions.rb +63 -49
  251. data/lib/graphql/testing/helpers.rb +155 -0
  252. data/lib/graphql/testing.rb +2 -0
  253. data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
  254. data/lib/graphql/tracing/active_support_notifications_tracing.rb +6 -20
  255. data/lib/graphql/tracing/appoptics_trace.rb +253 -0
  256. data/lib/graphql/tracing/appoptics_tracing.rb +4 -2
  257. data/lib/graphql/tracing/appsignal_trace.rb +79 -0
  258. data/lib/graphql/tracing/appsignal_tracing.rb +17 -0
  259. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  260. data/lib/graphql/tracing/data_dog_trace.rb +185 -0
  261. data/lib/graphql/tracing/data_dog_tracing.rb +27 -15
  262. data/lib/graphql/{execution/instrumentation.rb → tracing/legacy_hooks_trace.rb} +11 -28
  263. data/lib/graphql/tracing/legacy_trace.rb +12 -0
  264. data/lib/graphql/tracing/new_relic_trace.rb +77 -0
  265. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  266. data/lib/graphql/tracing/notifications_trace.rb +45 -0
  267. data/lib/graphql/tracing/notifications_tracing.rb +61 -0
  268. data/lib/graphql/tracing/null_trace.rb +9 -0
  269. data/lib/graphql/tracing/platform_trace.rb +118 -0
  270. data/lib/graphql/tracing/platform_tracing.rb +46 -49
  271. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +6 -2
  272. data/lib/graphql/tracing/prometheus_trace.rb +94 -0
  273. data/lib/graphql/tracing/prometheus_tracing.rb +5 -3
  274. data/lib/graphql/tracing/scout_trace.rb +74 -0
  275. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  276. data/lib/graphql/tracing/sentry_trace.rb +114 -0
  277. data/lib/graphql/tracing/statsd_trace.rb +58 -0
  278. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  279. data/lib/graphql/tracing/trace.rb +79 -0
  280. data/lib/graphql/tracing.rb +29 -52
  281. data/lib/graphql/type_kinds.rb +7 -4
  282. data/lib/graphql/types/big_int.rb +5 -1
  283. data/lib/graphql/types/int.rb +1 -1
  284. data/lib/graphql/types/iso_8601_date.rb +17 -6
  285. data/lib/graphql/types/iso_8601_date_time.rb +12 -1
  286. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  287. data/lib/graphql/types/relay/base_connection.rb +16 -6
  288. data/lib/graphql/types/relay/connection_behaviors.rb +92 -32
  289. data/lib/graphql/types/relay/edge_behaviors.rb +46 -7
  290. data/lib/graphql/types/relay/has_node_field.rb +2 -2
  291. data/lib/graphql/types/relay/has_nodes_field.rb +2 -2
  292. data/lib/graphql/types/relay/node_behaviors.rb +12 -2
  293. data/lib/graphql/types/relay/page_info_behaviors.rb +11 -2
  294. data/lib/graphql/types/relay.rb +0 -3
  295. data/lib/graphql/types/string.rb +2 -2
  296. data/lib/graphql/types.rb +18 -10
  297. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  298. data/lib/graphql/unauthorized_error.rb +1 -1
  299. data/lib/graphql/version.rb +1 -1
  300. data/lib/graphql.rb +82 -137
  301. data/readme.md +13 -6
  302. metadata +127 -186
  303. data/lib/graphql/analysis/analyze_query.rb +0 -98
  304. data/lib/graphql/analysis/ast/analyzer.rb +0 -84
  305. data/lib/graphql/analysis/ast/field_usage.rb +0 -28
  306. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -23
  307. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  308. data/lib/graphql/analysis/ast/query_complexity.rb +0 -234
  309. data/lib/graphql/analysis/ast/query_depth.rb +0 -56
  310. data/lib/graphql/analysis/ast/visitor.rb +0 -268
  311. data/lib/graphql/analysis/ast.rb +0 -91
  312. data/lib/graphql/analysis/reducer_state.rb +0 -48
  313. data/lib/graphql/argument.rb +0 -131
  314. data/lib/graphql/authorization.rb +0 -82
  315. data/lib/graphql/backtrace/legacy_tracer.rb +0 -56
  316. data/lib/graphql/backwards_compatibility.rb +0 -61
  317. data/lib/graphql/base_type.rb +0 -230
  318. data/lib/graphql/boolean_type.rb +0 -2
  319. data/lib/graphql/compatibility/execution_specification/counter_schema.rb +0 -53
  320. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +0 -200
  321. data/lib/graphql/compatibility/execution_specification.rb +0 -436
  322. data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +0 -111
  323. data/lib/graphql/compatibility/lazy_execution_specification.rb +0 -215
  324. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +0 -87
  325. data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +0 -79
  326. data/lib/graphql/compatibility/query_parser_specification.rb +0 -266
  327. data/lib/graphql/compatibility/schema_parser_specification.rb +0 -682
  328. data/lib/graphql/compatibility.rb +0 -5
  329. data/lib/graphql/define/assign_argument.rb +0 -12
  330. data/lib/graphql/define/assign_connection.rb +0 -13
  331. data/lib/graphql/define/assign_enum_value.rb +0 -18
  332. data/lib/graphql/define/assign_global_id_field.rb +0 -11
  333. data/lib/graphql/define/assign_mutation_function.rb +0 -34
  334. data/lib/graphql/define/assign_object_field.rb +0 -42
  335. data/lib/graphql/define/defined_object_proxy.rb +0 -53
  336. data/lib/graphql/define/instance_definable.rb +0 -240
  337. data/lib/graphql/define/no_definition_error.rb +0 -7
  338. data/lib/graphql/define/non_null_with_bang.rb +0 -16
  339. data/lib/graphql/define/type_definer.rb +0 -31
  340. data/lib/graphql/define.rb +0 -31
  341. data/lib/graphql/deprecated_dsl.rb +0 -47
  342. data/lib/graphql/deprecation.rb +0 -13
  343. data/lib/graphql/directive/deprecated_directive.rb +0 -2
  344. data/lib/graphql/directive/include_directive.rb +0 -2
  345. data/lib/graphql/directive/skip_directive.rb +0 -2
  346. data/lib/graphql/directive.rb +0 -111
  347. data/lib/graphql/enum_type.rb +0 -129
  348. data/lib/graphql/execution/execute.rb +0 -333
  349. data/lib/graphql/execution/flatten.rb +0 -40
  350. data/lib/graphql/execution/lazy/resolve.rb +0 -91
  351. data/lib/graphql/execution/typecast.rb +0 -50
  352. data/lib/graphql/field/resolve.rb +0 -59
  353. data/lib/graphql/field.rb +0 -226
  354. data/lib/graphql/filter.rb +0 -53
  355. data/lib/graphql/float_type.rb +0 -2
  356. data/lib/graphql/function.rb +0 -128
  357. data/lib/graphql/id_type.rb +0 -2
  358. data/lib/graphql/input_object_type.rb +0 -138
  359. data/lib/graphql/int_type.rb +0 -2
  360. data/lib/graphql/interface_type.rb +0 -72
  361. data/lib/graphql/internal_representation/document.rb +0 -27
  362. data/lib/graphql/internal_representation/node.rb +0 -206
  363. data/lib/graphql/internal_representation/print.rb +0 -51
  364. data/lib/graphql/internal_representation/rewrite.rb +0 -184
  365. data/lib/graphql/internal_representation/scope.rb +0 -88
  366. data/lib/graphql/internal_representation/visit.rb +0 -36
  367. data/lib/graphql/internal_representation.rb +0 -7
  368. data/lib/graphql/language/lexer.rl +0 -262
  369. data/lib/graphql/language/parser.y +0 -543
  370. data/lib/graphql/language/token.rb +0 -38
  371. data/lib/graphql/list_type.rb +0 -80
  372. data/lib/graphql/non_null_type.rb +0 -71
  373. data/lib/graphql/object_type.rb +0 -130
  374. data/lib/graphql/query/arguments.rb +0 -189
  375. data/lib/graphql/query/arguments_cache.rb +0 -24
  376. data/lib/graphql/query/executor.rb +0 -52
  377. data/lib/graphql/query/literal_input.rb +0 -136
  378. data/lib/graphql/query/serial_execution/field_resolution.rb +0 -92
  379. data/lib/graphql/query/serial_execution/operation_resolution.rb +0 -19
  380. data/lib/graphql/query/serial_execution/selection_resolution.rb +0 -23
  381. data/lib/graphql/query/serial_execution/value_resolution.rb +0 -87
  382. data/lib/graphql/query/serial_execution.rb +0 -40
  383. data/lib/graphql/relay/array_connection.rb +0 -83
  384. data/lib/graphql/relay/base_connection.rb +0 -189
  385. data/lib/graphql/relay/connection_instrumentation.rb +0 -54
  386. data/lib/graphql/relay/connection_resolve.rb +0 -43
  387. data/lib/graphql/relay/connection_type.rb +0 -41
  388. data/lib/graphql/relay/edge.rb +0 -27
  389. data/lib/graphql/relay/edge_type.rb +0 -19
  390. data/lib/graphql/relay/edges_instrumentation.rb +0 -40
  391. data/lib/graphql/relay/global_id_resolve.rb +0 -18
  392. data/lib/graphql/relay/mongo_relation_connection.rb +0 -50
  393. data/lib/graphql/relay/mutation/instrumentation.rb +0 -23
  394. data/lib/graphql/relay/mutation/resolve.rb +0 -56
  395. data/lib/graphql/relay/mutation/result.rb +0 -38
  396. data/lib/graphql/relay/mutation.rb +0 -106
  397. data/lib/graphql/relay/node.rb +0 -39
  398. data/lib/graphql/relay/page_info.rb +0 -7
  399. data/lib/graphql/relay/relation_connection.rb +0 -188
  400. data/lib/graphql/relay/type_extensions.rb +0 -32
  401. data/lib/graphql/scalar_type.rb +0 -91
  402. data/lib/graphql/schema/base_64_bp.rb +0 -26
  403. data/lib/graphql/schema/catchall_middleware.rb +0 -35
  404. data/lib/graphql/schema/default_parse_error.rb +0 -10
  405. data/lib/graphql/schema/default_type_error.rb +0 -17
  406. data/lib/graphql/schema/invalid_type_error.rb +0 -7
  407. data/lib/graphql/schema/member/accepts_definition.rb +0 -152
  408. data/lib/graphql/schema/member/cached_graphql_definition.rb +0 -31
  409. data/lib/graphql/schema/member/instrumentation.rb +0 -131
  410. data/lib/graphql/schema/middleware_chain.rb +0 -82
  411. data/lib/graphql/schema/possible_types.rb +0 -44
  412. data/lib/graphql/schema/rescue_middleware.rb +0 -60
  413. data/lib/graphql/schema/timeout_middleware.rb +0 -88
  414. data/lib/graphql/schema/traversal.rb +0 -228
  415. data/lib/graphql/schema/validation.rb +0 -313
  416. data/lib/graphql/static_validation/default_visitor.rb +0 -15
  417. data/lib/graphql/static_validation/no_validate_visitor.rb +0 -10
  418. data/lib/graphql/static_validation/type_stack.rb +0 -216
  419. data/lib/graphql/string_type.rb +0 -2
  420. data/lib/graphql/subscriptions/instrumentation.rb +0 -79
  421. data/lib/graphql/subscriptions/subscription_root.rb +0 -76
  422. data/lib/graphql/tracing/skylight_tracing.rb +0 -70
  423. data/lib/graphql/types/relay/default_relay.rb +0 -27
  424. data/lib/graphql/types/relay/node_field.rb +0 -25
  425. data/lib/graphql/types/relay/nodes_field.rb +0 -27
  426. data/lib/graphql/union_type.rb +0 -115
  427. data/lib/graphql/upgrader/member.rb +0 -937
  428. data/lib/graphql/upgrader/schema.rb +0 -38
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require "graphql/execution/interpreter/runtime/graphql_result"
2
3
 
3
4
  module GraphQL
4
5
  module Execution
@@ -8,47 +9,21 @@ module GraphQL
8
9
  #
9
10
  # @api private
10
11
  class Runtime
11
-
12
- module GraphQLResult
13
- # These methods are private concerns of GraphQL-Ruby,
14
- # they aren't guaranteed to continue working in the future.
15
- attr_accessor :graphql_dead, :graphql_parent, :graphql_result_name
16
- # Although these are used by only one of the Result classes,
17
- # it's handy to have the methods implemented on both (even though they just return `nil`)
18
- # because it makes it easy to check if anything is assigned.
19
- # @return [nil, Array<String>]
20
- attr_accessor :graphql_non_null_field_names
21
- # @return [nil, true]
22
- attr_accessor :graphql_non_null_list_items
23
- end
24
-
25
- class GraphQLResultHash < Hash
26
- include GraphQLResult
27
-
28
- attr_accessor :graphql_merged_into
29
-
30
- def []=(key, value)
31
- # This is a hack.
32
- # Basically, this object is merged into the root-level result at some point.
33
- # But the problem is, some lazies are created whose closures retain reference to _this_
34
- # object. When those lazies are resolved, they cause an update to this object.
35
- #
36
- # In order to return a proper top-level result, we have to update that top-level result object.
37
- # In order to return a proper partial result (eg, for a directive), we have to update this object, too.
38
- # Yowza.
39
- if (t = @graphql_merged_into)
40
- t[key] = value
41
- end
42
- super
12
+ class CurrentState
13
+ def initialize
14
+ @current_field = nil
15
+ @current_arguments = nil
16
+ @current_result_name = nil
17
+ @current_result = nil
18
+ @was_authorized_by_scope_items = nil
43
19
  end
44
- end
45
20
 
46
- class GraphQLResultArray < Array
47
- include GraphQLResult
48
- end
21
+ def current_object
22
+ @current_result.graphql_application_value
23
+ end
49
24
 
50
- class GraphQLSelectionSet < Hash
51
- attr_accessor :graphql_directives
25
+ attr_accessor :current_result, :current_result_name,
26
+ :current_arguments, :current_field, :was_authorized_by_scope_items
52
27
  end
53
28
 
54
29
  # @return [GraphQL::Query]
@@ -60,45 +35,33 @@ module GraphQL
60
35
  # @return [GraphQL::Query::Context]
61
36
  attr_reader :context
62
37
 
63
- # @return [Hash]
64
- attr_reader :response
65
-
66
- def initialize(query:)
38
+ def initialize(query:, lazies_at_depth:)
67
39
  @query = query
40
+ @current_trace = query.current_trace
68
41
  @dataloader = query.multiplex.dataloader
42
+ @lazies_at_depth = lazies_at_depth
69
43
  @schema = query.schema
70
44
  @context = query.context
71
- @multiplex_context = query.multiplex.context
72
- @interpreter_context = @context.namespace(:interpreter)
73
- @response = GraphQLResultHash.new
45
+ @response = nil
74
46
  # Identify runtime directives by checking which of this schema's directives have overridden `def self.resolve`
75
47
  @runtime_directive_names = []
76
48
  noop_resolve_owner = GraphQL::Schema::Directive.singleton_class
77
- schema.directives.each do |name, dir_defn|
49
+ @schema_directives = schema.directives
50
+ @schema_directives.each do |name, dir_defn|
78
51
  if dir_defn.method(:resolve).owner != noop_resolve_owner
79
52
  @runtime_directive_names << name
80
53
  end
81
54
  end
82
- # A cache of { Class => { String => Schema::Field } }
83
- # Which assumes that MyObject.get_field("myField") will return the same field
84
- # during the lifetime of a query
85
- @fields_cache = Hash.new { |h, k| h[k] = {} }
86
55
  # { Class => Boolean }
87
- @lazy_cache = {}
56
+ @lazy_cache = {}.compare_by_identity
88
57
  end
89
58
 
90
- def inspect
91
- "#<#{self.class.name} response=#{@response.inspect}>"
59
+ def final_result
60
+ @response && @response.graphql_result_data
92
61
  end
93
62
 
94
- def tap_or_each(obj_or_array)
95
- if obj_or_array.is_a?(Array)
96
- obj_or_array.each do |item|
97
- yield(item, true)
98
- end
99
- else
100
- yield(obj_or_array, false)
101
- end
63
+ def inspect
64
+ "#<#{self.class.name} response=#{@response.inspect}>"
102
65
  end
103
66
 
104
67
  # This _begins_ the execution. Some deferred work
@@ -108,27 +71,21 @@ module GraphQL
108
71
  root_operation = query.selected_operation
109
72
  root_op_type = root_operation.operation_type || "query"
110
73
  root_type = schema.root_type_for_operation(root_op_type)
111
- path = []
112
- set_all_interpreter_context(query.root_value, nil, nil, path)
113
- object_proxy = authorized_new(root_type, query.root_value, context)
114
- object_proxy = schema.sync_lazy(object_proxy)
115
-
116
- if object_proxy.nil?
74
+ runtime_object = root_type.wrap(query.root_value, context)
75
+ runtime_object = schema.sync_lazy(runtime_object)
76
+ is_eager = root_op_type == "mutation"
77
+ @response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, root_operation.selections, is_eager)
78
+ st = get_current_runtime_state
79
+ st.current_result = @response
80
+
81
+ if runtime_object.nil?
117
82
  # Root .authorized? returned false.
118
83
  @response = nil
119
84
  else
120
- resolve_with_directives(object_proxy, root_operation.directives) do # execute query level directives
121
- gathered_selections = gather_selections(object_proxy, root_type, root_operation.selections)
122
- # This is kind of a hack -- `gathered_selections` is an Array if any of the selections
123
- # require isolation during execution (because of runtime directives). In that case,
124
- # make a new, isolated result hash for writing the result into. (That isolated response
125
- # is eventually merged back into the main response)
126
- #
127
- # Otherwise, `gathered_selections` is a hash of selections which can be
128
- # directly evaluated and the results can be written right into the main response hash.
129
- tap_or_each(gathered_selections) do |selections, is_selection_array|
85
+ call_method_on_directives(:resolve, runtime_object, root_operation.directives) do # execute query level directives
86
+ each_gathered_selections(@response) do |selections, is_selection_array|
130
87
  if is_selection_array
131
- selection_response = GraphQLResultHash.new
88
+ selection_response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, selections, is_eager)
132
89
  final_response = @response
133
90
  else
134
91
  selection_response = @response
@@ -136,53 +93,31 @@ module GraphQL
136
93
  end
137
94
 
138
95
  @dataloader.append_job {
139
- set_all_interpreter_context(query.root_value, nil, nil, path)
140
- resolve_with_directives(object_proxy, selections.graphql_directives) do
141
- evaluate_selections(
142
- path,
143
- context.scoped_context,
144
- object_proxy,
145
- root_type,
146
- root_op_type == "mutation",
147
- selections,
148
- selection_response,
149
- final_response,
150
- )
151
- end
96
+ evaluate_selections(
97
+ selections,
98
+ selection_response,
99
+ final_response,
100
+ nil,
101
+ )
152
102
  }
153
103
  end
154
104
  end
155
105
  end
156
- delete_interpreter_context(:current_path)
157
- delete_interpreter_context(:current_field)
158
- delete_interpreter_context(:current_object)
159
- delete_interpreter_context(:current_arguments)
160
106
  nil
161
107
  end
162
108
 
163
- # @return [void]
164
- def deep_merge_selection_result(from_result, into_result)
165
- from_result.each do |key, value|
166
- if !into_result.key?(key)
167
- into_result[key] = value
168
- else
169
- case value
170
- when Hash
171
- deep_merge_selection_result(value, into_result[key])
172
- else
173
- # We have to assume that, since this passed the `fields_will_merge` selection,
174
- # that the old and new values are the same.
175
- # There's no special handling of arrays because currently, there's no way to split the execution
176
- # of a list over several concurrent flows.
177
- into_result[key] = value
178
- end
109
+ def each_gathered_selections(response_hash)
110
+ gathered_selections = gather_selections(response_hash.graphql_application_value, response_hash.graphql_result_type, response_hash.graphql_selections)
111
+ if gathered_selections.is_a?(Array)
112
+ gathered_selections.each do |item|
113
+ yield(item, true)
179
114
  end
115
+ else
116
+ yield(gathered_selections, false)
180
117
  end
181
- from_result.graphql_merged_into = into_result
182
- nil
183
118
  end
184
119
 
185
- def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = GraphQLSelectionSet.new)
120
+ def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = {})
186
121
  selections.each do |node|
187
122
  # Skip gathering this if the directive says so
188
123
  if !directives_include?(node, owner_object, owner_type)
@@ -194,7 +129,7 @@ module GraphQL
194
129
  selections = selections_by_name[response_key]
195
130
  # if there was already a selection of this field,
196
131
  # use an array to hold all selections,
197
- # otherise, use the single node to represent the selection
132
+ # otherwise, use the single node to represent the selection
198
133
  if selections
199
134
  # This field was already selected at least once,
200
135
  # add this node to the list of selections
@@ -207,9 +142,9 @@ module GraphQL
207
142
  end
208
143
  else
209
144
  # This is an InlineFragment or a FragmentSpread
210
- if @runtime_directive_names.any? && node.directives.any? { |d| @runtime_directive_names.include?(d.name) }
211
- next_selections = GraphQLSelectionSet.new
212
- next_selections.graphql_directives = node.directives
145
+ if !@runtime_directive_names.empty? && node.directives.any? { |d| @runtime_directive_names.include?(d.name) }
146
+ next_selections = {}
147
+ next_selections[:graphql_directives] = node.directives
213
148
  if selections_to_run
214
149
  selections_to_run << next_selections
215
150
  else
@@ -224,27 +159,28 @@ module GraphQL
224
159
  case node
225
160
  when GraphQL::Language::Nodes::InlineFragment
226
161
  if node.type
227
- type_defn = schema.get_type(node.type.name)
162
+ type_defn = query.types.type(node.type.name)
228
163
 
229
- # Faster than .map{}.include?()
230
- query.warden.possible_types(type_defn).each do |t|
231
- if t == owner_type
232
- gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
233
- break
164
+ if query.types.possible_types(type_defn).include?(owner_type)
165
+ result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
166
+ if !result.equal?(next_selections)
167
+ selections_to_run = result
234
168
  end
235
169
  end
236
170
  else
237
171
  # it's an untyped fragment, definitely continue
238
- gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
172
+ result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
173
+ if !result.equal?(next_selections)
174
+ selections_to_run = result
175
+ end
239
176
  end
240
177
  when GraphQL::Language::Nodes::FragmentSpread
241
178
  fragment_def = query.fragments[node.name]
242
- type_defn = schema.get_type(fragment_def.type.name)
243
- possible_types = query.warden.possible_types(type_defn)
244
- possible_types.each do |t|
245
- if t == owner_type
246
- gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
247
- break
179
+ type_defn = query.types.type(fragment_def.type.name)
180
+ if query.types.possible_types(type_defn).include?(owner_type)
181
+ result = gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
182
+ if !result.equal?(next_selections)
183
+ selections_to_run = result
248
184
  end
249
185
  end
250
186
  else
@@ -255,33 +191,57 @@ module GraphQL
255
191
  selections_to_run || selections_by_name
256
192
  end
257
193
 
258
- NO_ARGS = {}.freeze
194
+ NO_ARGS = GraphQL::EmptyObjects::EMPTY_HASH
259
195
 
260
196
  # @return [void]
261
- def evaluate_selections(path, scoped_context, owner_object, owner_type, is_eager_selection, gathered_selections, selections_result, target_result) # rubocop:disable Metrics/ParameterLists
262
- set_all_interpreter_context(owner_object, nil, nil, path)
263
-
264
- finished_jobs = 0
265
- enqueued_jobs = gathered_selections.size
266
- gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
267
- @dataloader.append_job {
268
- evaluate_selection(
269
- path, result_name, field_ast_nodes_or_ast_node, scoped_context, owner_object, owner_type, is_eager_selection, selections_result
270
- )
271
- finished_jobs += 1
272
- if target_result && finished_jobs == enqueued_jobs
273
- deep_merge_selection_result(selections_result, target_result)
274
- end
275
- }
197
+ def evaluate_selections(gathered_selections, selections_result, target_result, runtime_state) # rubocop:disable Metrics/ParameterLists
198
+ runtime_state ||= get_current_runtime_state
199
+ runtime_state.current_result_name = nil
200
+ runtime_state.current_result = selections_result
201
+ # This is a less-frequent case; use a fast check since it's often not there.
202
+ if (directives = gathered_selections[:graphql_directives])
203
+ gathered_selections.delete(:graphql_directives)
276
204
  end
277
205
 
278
- selections_result
206
+ call_method_on_directives(:resolve, selections_result.graphql_application_value, directives) do
207
+ finished_jobs = 0
208
+ enqueued_jobs = gathered_selections.size
209
+ gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
210
+
211
+ # Field resolution may pause the fiber,
212
+ # so it wouldn't get to the `Resolve` call that happens below.
213
+ # So instead trigger a run from this outer context.
214
+ if selections_result.graphql_is_eager
215
+ @dataloader.clear_cache
216
+ @dataloader.run_isolated {
217
+ evaluate_selection(
218
+ result_name, field_ast_nodes_or_ast_node, selections_result
219
+ )
220
+ finished_jobs += 1
221
+ if target_result && finished_jobs == enqueued_jobs
222
+ selections_result.merge_into(target_result)
223
+ end
224
+ @dataloader.clear_cache
225
+ }
226
+ else
227
+ @dataloader.append_job {
228
+ evaluate_selection(
229
+ result_name, field_ast_nodes_or_ast_node, selections_result
230
+ )
231
+ finished_jobs += 1
232
+ if target_result && finished_jobs == enqueued_jobs
233
+ selections_result.merge_into(target_result)
234
+ end
235
+ }
236
+ end
237
+ end
238
+ selections_result
239
+ end
279
240
  end
280
241
 
281
- attr_reader :progress_path
282
-
283
242
  # @return [void]
284
- def evaluate_selection(path, result_name, field_ast_nodes_or_ast_node, scoped_context, owner_object, owner_type, is_eager_field, selections_result) # rubocop:disable Metrics/ParameterLists
243
+ def evaluate_selection(result_name, field_ast_nodes_or_ast_node, selections_result) # rubocop:disable Metrics/ParameterLists
244
+ return if selections_result.graphql_dead
285
245
  # As a performance optimization, the hash key will be a `Node` if
286
246
  # there's only one selection of the field. But if there are multiple
287
247
  # selections of the field, it will be an Array of nodes
@@ -293,65 +253,52 @@ module GraphQL
293
253
  ast_node = field_ast_nodes_or_ast_node
294
254
  end
295
255
  field_name = ast_node.name
296
- field_defn = @fields_cache[owner_type][field_name] ||= owner_type.get_field(field_name)
297
- is_introspection = false
298
- if field_defn.nil?
299
- field_defn = if owner_type == schema.query && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
300
- is_introspection = true
301
- entry_point_field
302
- elsif (dynamic_field = schema.introspection_system.dynamic_field(name: field_name))
303
- is_introspection = true
304
- dynamic_field
305
- else
306
- raise "Invariant: no field for #{owner_type}.#{field_name}"
307
- end
308
- end
309
- return_type = field_defn.type
310
-
311
- next_path = path.dup
312
- next_path << result_name
313
- next_path.freeze
256
+ owner_type = selections_result.graphql_result_type
257
+ field_defn = query.types.field(owner_type, field_name)
314
258
 
315
- # This seems janky, but we need to know
316
- # the field's return type at this path in order
317
- # to propagate `null`
318
- if return_type.non_null?
319
- (selections_result.graphql_non_null_field_names ||= []).push(result_name)
320
- end
321
259
  # Set this before calling `run_with_directives`, so that the directive can have the latest path
322
- set_all_interpreter_context(nil, field_defn, nil, next_path)
323
-
324
- context.scoped_context = scoped_context
325
- object = owner_object
326
-
327
- if is_introspection
328
- object = authorized_new(field_defn.owner, object, context)
260
+ runtime_state = get_current_runtime_state
261
+ runtime_state.current_field = field_defn
262
+ runtime_state.current_result = selections_result
263
+ runtime_state.current_result_name = result_name
264
+
265
+ owner_object = selections_result.graphql_application_value
266
+ if field_defn.dynamic_introspection
267
+ owner_object = field_defn.owner.wrap(owner_object, context)
329
268
  end
330
269
 
331
- total_args_count = field_defn.arguments.size
332
- if total_args_count == 0
333
- kwarg_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
334
- evaluate_selection_with_args(kwarg_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selections_result)
270
+ if !field_defn.any_arguments?
271
+ resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
272
+ if field_defn.extras.size == 0
273
+ evaluate_selection_with_resolved_keyword_args(
274
+ NO_ARGS, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, result_name, selections_result, runtime_state
275
+ )
276
+ else
277
+ evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, result_name, selections_result, runtime_state)
278
+ end
335
279
  else
336
- # TODO remove all arguments(...) usages?
337
- @query.arguments_cache.dataload_for(ast_node, field_defn, object) do |resolved_arguments|
338
- 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)
280
+ @query.arguments_cache.dataload_for(ast_node, field_defn, owner_object) do |resolved_arguments|
281
+ runtime_state = get_current_runtime_state # This might be in a different fiber
282
+ evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, result_name, selections_result, runtime_state)
339
283
  end
340
284
  end
341
285
  end
342
286
 
343
- def evaluate_selection_with_args(kwarg_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
344
- context.scoped_context = scoped_context
345
- return_type = field_defn.type
346
- after_lazy(kwarg_arguments, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, scoped_context: context.scoped_context, owner_object: object, arguments: kwarg_arguments, result_name: result_name, result: selection_result) do |resolved_arguments|
287
+ def evaluate_selection_with_args(arguments, field_defn, ast_node, field_ast_nodes, object, result_name, selection_result, runtime_state) # rubocop:disable Metrics/ParameterLists
288
+ after_lazy(arguments, field: field_defn, ast_node: ast_node, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |resolved_arguments, runtime_state|
347
289
  if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
348
- continue_value(next_path, resolved_arguments, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
290
+ return_type_non_null = field_defn.type.non_null?
291
+ continue_value(resolved_arguments, field_defn, return_type_non_null, ast_node, result_name, selection_result)
349
292
  next
350
293
  end
351
294
 
352
- kwarg_arguments = if resolved_arguments.empty? && field_defn.extras.empty?
353
- # We can avoid allocating the `{ Symbol => Object }` hash in this case
354
- NO_ARGS
295
+ kwarg_arguments = if field_defn.extras.empty?
296
+ if resolved_arguments.empty?
297
+ # We can avoid allocating the `{ Symbol => Object }` hash in this case
298
+ NO_ARGS
299
+ else
300
+ resolved_arguments.keyword_arguments
301
+ end
355
302
  else
356
303
  # Bundle up the extras, then make a new arguments instance
357
304
  # that includes the extras, too.
@@ -361,9 +308,9 @@ module GraphQL
361
308
  when :ast_node
362
309
  extra_args[:ast_node] = ast_node
363
310
  when :execution_errors
364
- extra_args[:execution_errors] = ExecutionErrors.new(context, ast_node, next_path)
311
+ extra_args[:execution_errors] = ExecutionErrors.new(context, ast_node, current_path)
365
312
  when :path
366
- extra_args[:path] = next_path
313
+ extra_args[:path] = current_path
367
314
  when :lookahead
368
315
  if !field_ast_nodes
369
316
  field_ast_nodes = [ast_node]
@@ -378,85 +325,86 @@ module GraphQL
378
325
  # Use this flag to tell Interpreter::Arguments to add itself
379
326
  # to the keyword args hash _before_ freezing everything.
380
327
  extra_args[:argument_details] = :__arguments_add_self
381
- when :irep_node
382
- # This is used by `__typename` in order to support the legacy runtime,
383
- # but it has no use here (and it's always `nil`).
384
- # Stop adding it here to avoid the overhead of `.merge_extras` below.
328
+ when :parent
329
+ parent_result = selection_result.graphql_parent
330
+ extra_args[:parent] = parent_result&.graphql_application_value&.object
385
331
  else
386
332
  extra_args[extra] = field_defn.fetch_extra(extra, context)
387
333
  end
388
334
  end
389
- if extra_args.any?
335
+ if !extra_args.empty?
390
336
  resolved_arguments = resolved_arguments.merge_extras(extra_args)
391
337
  end
392
338
  resolved_arguments.keyword_arguments
393
339
  end
394
340
 
395
- set_all_interpreter_context(nil, nil, kwarg_arguments, nil)
341
+ evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, object, result_name, selection_result, runtime_state)
342
+ end
343
+ end
396
344
 
397
- # Optimize for the case that field is selected only once
398
- if field_ast_nodes.nil? || field_ast_nodes.size == 1
399
- next_selections = ast_node.selections
400
- directives = ast_node.directives
401
- else
402
- next_selections = []
403
- directives = []
404
- field_ast_nodes.each { |f|
405
- next_selections.concat(f.selections)
406
- directives.concat(f.directives)
407
- }
408
- end
345
+ def evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, object, result_name, selection_result, runtime_state) # rubocop:disable Metrics/ParameterLists
346
+ runtime_state.current_field = field_defn
347
+ runtime_state.current_arguments = resolved_arguments
348
+ runtime_state.current_result_name = result_name
349
+ runtime_state.current_result = selection_result
350
+ # Optimize for the case that field is selected only once
351
+ if field_ast_nodes.nil? || field_ast_nodes.size == 1
352
+ next_selections = ast_node.selections
353
+ directives = ast_node.directives
354
+ else
355
+ next_selections = []
356
+ directives = []
357
+ field_ast_nodes.each { |f|
358
+ next_selections.concat(f.selections)
359
+ directives.concat(f.directives)
360
+ }
361
+ end
409
362
 
410
- field_result = resolve_with_directives(object, directives) do
411
- # Actually call the field resolver and capture the result
412
- app_result = begin
413
- query.with_error_handling do
414
- 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
415
- field_defn.resolve(object, kwarg_arguments, context)
416
- end
417
- end
418
- rescue GraphQL::ExecutionError => err
419
- err
363
+ field_result = call_method_on_directives(:resolve, object, directives) do
364
+ if !directives.empty?
365
+ # This might be executed in a different context; reset this info
366
+ runtime_state = get_current_runtime_state
367
+ runtime_state.current_field = field_defn
368
+ runtime_state.current_arguments = resolved_arguments
369
+ runtime_state.current_result_name = result_name
370
+ runtime_state.current_result = selection_result
371
+ end
372
+ # Actually call the field resolver and capture the result
373
+ app_result = begin
374
+ @current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
375
+ field_defn.resolve(object, kwarg_arguments, context)
420
376
  end
421
- 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: kwarg_arguments, result_name: result_name, result: selection_result) do |inner_result|
422
- continue_value = continue_value(next_path, inner_result, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
423
- if HALT != continue_value
424
- continue_field(next_path, continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, kwarg_arguments, result_name, selection_result)
425
- end
377
+ rescue GraphQL::ExecutionError => err
378
+ err
379
+ rescue StandardError => err
380
+ begin
381
+ query.handle_or_reraise(err)
382
+ rescue GraphQL::ExecutionError => ex_err
383
+ ex_err
426
384
  end
427
385
  end
428
-
429
- # If this field is a root mutation field, immediately resolve
430
- # all of its child fields before moving on to the next root mutation field.
431
- # (Subselections of this mutation will still be resolved level-by-level.)
432
- if is_eager_field
433
- Interpreter::Resolve.resolve_all([field_result], @dataloader)
434
- else
435
- # Return this from `after_lazy` because it might be another lazy that needs to be resolved
436
- field_result
386
+ after_lazy(app_result, field: field_defn, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |inner_result, runtime_state|
387
+ owner_type = selection_result.graphql_result_type
388
+ return_type = field_defn.type
389
+ continue_value = continue_value(inner_result, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
390
+ if HALT != continue_value
391
+ was_scoped = runtime_state.was_authorized_by_scope_items
392
+ runtime_state.was_authorized_by_scope_items = nil
393
+ continue_field(continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result, was_scoped, runtime_state)
394
+ end
437
395
  end
438
396
  end
439
- end
440
-
441
- def dead_result?(selection_result)
442
- r = selection_result
443
- while r
444
- if r.graphql_dead
445
- return true
446
- else
447
- r = r.graphql_parent
448
- end
397
+ # If this field is a root mutation field, immediately resolve
398
+ # all of its child fields before moving on to the next root mutation field.
399
+ # (Subselections of this mutation will still be resolved level-by-level.)
400
+ if selection_result.graphql_is_eager
401
+ Interpreter::Resolve.resolve_all([field_result], @dataloader)
449
402
  end
450
- false
451
403
  end
452
404
 
453
- def set_result(selection_result, result_name, value)
454
- if !dead_result?(selection_result)
455
- if value.nil? &&
456
- ( # there are two conditions under which `nil` is not allowed in the response:
457
- (selection_result.graphql_non_null_list_items) || # this value would be written into a list that doesn't allow nils
458
- ((nn = selection_result.graphql_non_null_field_names) && nn.include?(result_name)) # this value would be written into a field that doesn't allow nils
459
- )
405
+ def set_result(selection_result, result_name, value, is_child_result, is_non_null)
406
+ if !selection_result.graphql_dead
407
+ if value.nil? && is_non_null
460
408
  # This is an invalid nil that should be propagated
461
409
  # One caller of this method passes a block,
462
410
  # namely when application code returns a `nil` to GraphQL and it doesn't belong there.
@@ -466,33 +414,62 @@ module GraphQL
466
414
  # TODO the code is trying to tell me something.
467
415
  yield if block_given?
468
416
  parent = selection_result.graphql_parent
469
- name_in_parent = selection_result.graphql_result_name
470
417
  if parent.nil? # This is a top-level result hash
471
418
  @response = nil
472
419
  else
473
- set_result(parent, name_in_parent, nil)
474
- # This is odd, but it's how it used to work. Even if `parent` _would_ accept
475
- # a `nil`, it's marked dead. TODO: check the spec, is there a reason for this?
476
- parent.graphql_dead = true
420
+ name_in_parent = selection_result.graphql_result_name
421
+ is_non_null_in_parent = selection_result.graphql_is_non_null_in_parent
422
+ set_result(parent, name_in_parent, nil, false, is_non_null_in_parent)
423
+ set_graphql_dead(selection_result)
477
424
  end
425
+ elsif is_child_result
426
+ selection_result.set_child_result(result_name, value)
478
427
  else
479
- selection_result[result_name] = value
428
+ selection_result.set_leaf(result_name, value)
480
429
  end
481
430
  end
482
431
  end
483
432
 
433
+ # Mark this node and any already-registered children as dead,
434
+ # so that it accepts no more writes.
435
+ def set_graphql_dead(selection_result)
436
+ case selection_result
437
+ when GraphQLResultArray
438
+ selection_result.graphql_dead = true
439
+ selection_result.values.each { |v| set_graphql_dead(v) }
440
+ when GraphQLResultHash
441
+ selection_result.graphql_dead = true
442
+ selection_result.each { |k, v| set_graphql_dead(v) }
443
+ else
444
+ # It's a scalar, no way to mark it dead.
445
+ end
446
+ end
447
+
448
+ def current_path
449
+ st = get_current_runtime_state
450
+ result = st.current_result
451
+ path = result && result.path
452
+ if path && (rn = st.current_result_name)
453
+ path = path.dup
454
+ path.push(rn)
455
+ end
456
+ path
457
+ end
458
+
484
459
  HALT = Object.new
485
- def continue_value(path, value, parent_type, field, is_non_null, ast_node, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
460
+ def continue_value(value, field, is_non_null, ast_node, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
486
461
  case value
487
462
  when nil
488
463
  if is_non_null
489
- set_result(selection_result, result_name, nil) do
464
+ set_result(selection_result, result_name, nil, false, is_non_null) do
465
+ # When this comes from a list item, use the parent object:
466
+ parent_type = selection_result.is_a?(GraphQLResultArray) ? selection_result.graphql_parent.graphql_result_type : selection_result.graphql_result_type
490
467
  # This block is called if `result_name` is not dead. (Maybe a previous invalid nil caused it be marked dead.)
491
468
  err = parent_type::InvalidNullError.new(parent_type, field, value)
492
469
  schema.type_error(err, context)
493
470
  end
494
471
  else
495
- set_result(selection_result, result_name, nil)
472
+ set_result(selection_result, result_name, nil, false, is_non_null)
496
473
  end
497
474
  HALT
498
475
  when GraphQL::Error
@@ -500,13 +477,25 @@ module GraphQL
500
477
  # to avoid the overhead of checking three different classes
501
478
  # every time.
502
479
  if value.is_a?(GraphQL::ExecutionError)
503
- if !dead_result?(selection_result)
504
- value.path ||= path
480
+ if selection_result.nil? || !selection_result.graphql_dead
481
+ value.path ||= current_path
505
482
  value.ast_node ||= ast_node
506
483
  context.errors << value
507
- set_result(selection_result, result_name, nil)
484
+ if selection_result
485
+ set_result(selection_result, result_name, nil, false, is_non_null)
486
+ end
508
487
  end
509
488
  HALT
489
+ elsif value.is_a?(GraphQL::UnauthorizedFieldError)
490
+ value.field ||= field
491
+ # this hook might raise & crash, or it might return
492
+ # a replacement value
493
+ next_value = begin
494
+ schema.unauthorized_field(value)
495
+ rescue GraphQL::ExecutionError => err
496
+ err
497
+ end
498
+ continue_value(next_value, field, is_non_null, ast_node, result_name, selection_result)
510
499
  elsif value.is_a?(GraphQL::UnauthorizedError)
511
500
  # this hook might raise & crash, or it might return
512
501
  # a replacement value
@@ -515,8 +504,19 @@ module GraphQL
515
504
  rescue GraphQL::ExecutionError => err
516
505
  err
517
506
  end
518
- continue_value(path, next_value, parent_type, field, is_non_null, ast_node, result_name, selection_result)
519
- elsif GraphQL::Execution::Execute::SKIP == value
507
+ continue_value(next_value, field, is_non_null, ast_node, result_name, selection_result)
508
+ elsif GraphQL::Execution::SKIP == value
509
+ # It's possible a lazy was already written here
510
+ case selection_result
511
+ when GraphQLResultHash
512
+ selection_result.delete(result_name)
513
+ when GraphQLResultArray
514
+ selection_result.graphql_skip_at(result_name)
515
+ when nil
516
+ # this can happen with directives
517
+ else
518
+ raise "Invariant: unexpected result class #{selection_result.class} (#{selection_result.inspect})"
519
+ end
520
520
  HALT
521
521
  else
522
522
  # What could this actually _be_? Anyhow,
@@ -525,14 +525,22 @@ module GraphQL
525
525
  end
526
526
  when Array
527
527
  # It's an array full of execution errors; add them all.
528
- if value.any? && value.all? { |v| v.is_a?(GraphQL::ExecutionError) }
529
- if !dead_result?(selection_result)
528
+ if !value.empty? && value.all?(GraphQL::ExecutionError)
529
+ list_type_at_all = (field && (field.type.list?))
530
+ if selection_result.nil? || !selection_result.graphql_dead
530
531
  value.each_with_index do |error, index|
531
532
  error.ast_node ||= ast_node
532
- error.path ||= path + (field.type.list? ? [index] : [])
533
+ error.path ||= current_path + (list_type_at_all ? [index] : [])
533
534
  context.errors << error
534
535
  end
535
- set_result(selection_result, result_name, nil)
536
+ if selection_result
537
+ if list_type_at_all
538
+ result_without_errors = value.map { |v| v.is_a?(GraphQL::ExecutionError) ? nil : v }
539
+ set_result(selection_result, result_name, result_without_errors, false, is_non_null)
540
+ else
541
+ set_result(selection_result, result_name, nil, false, is_non_null)
542
+ end
543
+ end
536
544
  end
537
545
  HALT
538
546
  else
@@ -540,7 +548,7 @@ module GraphQL
540
548
  end
541
549
  when GraphQL::Execution::Interpreter::RawValue
542
550
  # Write raw value directly to the response without resolving nested objects
543
- set_result(selection_result, result_name, value.resolve)
551
+ set_result(selection_result, result_name, value.resolve, false, is_non_null)
544
552
  HALT
545
553
  else
546
554
  value
@@ -555,7 +563,7 @@ module GraphQL
555
563
  # Location information from `path` and `ast_node`.
556
564
  #
557
565
  # @return [Lazy, Array, Hash, Object] Lazy, Array, and Hash are all traversed to resolve lazy values later
558
- 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
566
+ def continue_field(value, owner_type, field, current_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result, was_scoped, runtime_state) # rubocop:disable Metrics/ParameterLists
559
567
  if current_type.non_null?
560
568
  current_type = current_type.of_type
561
569
  is_non_null = true
@@ -563,135 +571,154 @@ module GraphQL
563
571
 
564
572
  case current_type.kind.name
565
573
  when "SCALAR", "ENUM"
566
- r = current_type.coerce_result(value, context)
567
- set_result(selection_result, result_name, r)
574
+ r = begin
575
+ current_type.coerce_result(value, context)
576
+ rescue StandardError => err
577
+ schema.handle_or_reraise(context, err)
578
+ end
579
+ set_result(selection_result, result_name, r, false, is_non_null)
568
580
  r
569
581
  when "UNION", "INTERFACE"
570
- resolved_type_or_lazy, resolved_value = resolve_type(current_type, value, path)
571
- resolved_value ||= value
572
-
573
- 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|
574
- possible_types = query.possible_types(current_type)
582
+ resolved_type_or_lazy = resolve_type(current_type, value)
583
+ after_lazy(resolved_type_or_lazy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |resolved_type_result, runtime_state|
584
+ if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
585
+ resolved_type, resolved_value = resolved_type_result
586
+ else
587
+ resolved_type = resolved_type_result
588
+ resolved_value = value
589
+ end
575
590
 
591
+ possible_types = query.types.possible_types(current_type)
576
592
  if !possible_types.include?(resolved_type)
577
593
  parent_type = field.owner_type
578
594
  err_class = current_type::UnresolvedTypeError
579
595
  type_error = err_class.new(resolved_value, field, parent_type, resolved_type, possible_types)
580
596
  schema.type_error(type_error, context)
581
- set_result(selection_result, result_name, nil)
597
+ set_result(selection_result, result_name, nil, false, is_non_null)
582
598
  nil
583
599
  else
584
- continue_field(path, resolved_value, owner_type, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result)
600
+ continue_field(resolved_value, owner_type, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result, was_scoped, runtime_state)
585
601
  end
586
602
  end
587
603
  when "OBJECT"
588
604
  object_proxy = begin
589
- authorized_new(current_type, value, context)
605
+ was_scoped ? current_type.wrap_scoped(value, context) : current_type.wrap(value, context)
590
606
  rescue GraphQL::ExecutionError => err
591
607
  err
592
608
  end
593
- 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|
594
- continue_value = continue_value(path, inner_object, owner_type, field, is_non_null, ast_node, result_name, selection_result)
609
+ after_lazy(object_proxy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |inner_object, runtime_state|
610
+ continue_value = continue_value(inner_object, field, is_non_null, ast_node, result_name, selection_result)
595
611
  if HALT != continue_value
596
- response_hash = GraphQLResultHash.new
597
- response_hash.graphql_parent = selection_result
598
- response_hash.graphql_result_name = result_name
599
- set_result(selection_result, result_name, response_hash)
600
- gathered_selections = gather_selections(continue_value, current_type, next_selections)
601
- # There are two possibilities for `gathered_selections`:
602
- # 1. All selections of this object should be evaluated together (there are no runtime directives modifying execution).
603
- # This case is handled below, and the result can be written right into the main `response_hash` above.
604
- # In this case, `gathered_selections` is a hash of selections.
605
- # 2. Some selections of this object have runtime directives that may or may not modify execution.
606
- # That part of the selection is evaluated in an isolated way, writing into a sub-response object which is
607
- # eventually merged into the final response. In this case, `gathered_selections` is an array of things to run in isolation.
608
- # (Technically, it's possible that one of those entries _doesn't_ require isolation.)
609
- tap_or_each(gathered_selections) do |selections, is_selection_array|
612
+ response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, next_selections, false)
613
+ set_result(selection_result, result_name, response_hash, true, is_non_null)
614
+ each_gathered_selections(response_hash) do |selections, is_selection_array|
610
615
  if is_selection_array
611
- this_result = GraphQLResultHash.new
612
- this_result.graphql_parent = selection_result
613
- this_result.graphql_result_name = result_name
616
+ this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, selections, false)
614
617
  final_result = response_hash
615
618
  else
616
619
  this_result = response_hash
617
620
  final_result = nil
618
621
  end
619
- set_all_interpreter_context(continue_value, nil, nil, path) # reset this mutable state
620
- resolve_with_directives(continue_value, selections.graphql_directives) do
621
- evaluate_selections(
622
- path,
623
- context.scoped_context,
624
- continue_value,
625
- current_type,
626
- false,
627
- selections,
628
- this_result,
629
- final_result,
630
- )
631
- this_result
632
- end
622
+
623
+ evaluate_selections(
624
+ selections,
625
+ this_result,
626
+ final_result,
627
+ runtime_state,
628
+ )
633
629
  end
634
630
  end
635
631
  end
636
632
  when "LIST"
637
633
  inner_type = current_type.of_type
638
- response_list = GraphQLResultArray.new
639
- response_list.graphql_non_null_list_items = inner_type.non_null?
640
- response_list.graphql_parent = selection_result
641
- response_list.graphql_result_name = result_name
642
- set_result(selection_result, result_name, response_list)
643
-
644
- idx = 0
645
- scoped_context = context.scoped_context
646
- begin
634
+ # This is true for objects, unions, and interfaces
635
+ use_dataloader_job = !inner_type.unwrap.kind.input?
636
+ inner_type_non_null = inner_type.non_null?
637
+ response_list = GraphQLResultArray.new(result_name, current_type, owner_object, selection_result, is_non_null, next_selections, false)
638
+ set_result(selection_result, result_name, response_list, true, is_non_null)
639
+ idx = nil
640
+ list_value = begin
647
641
  value.each do |inner_value|
648
- next_path = path.dup
649
- next_path << idx
642
+ idx ||= 0
650
643
  this_idx = idx
651
- next_path.freeze
652
644
  idx += 1
653
- # This will update `response_list` with the lazy
654
- 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|
655
- continue_value = continue_value(next_path, inner_inner_value, owner_type, field, inner_type.non_null?, ast_node, this_idx, response_list)
656
- if HALT != continue_value
657
- continue_field(next_path, continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list)
645
+ if use_dataloader_job
646
+ @dataloader.append_job do
647
+ resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state)
658
648
  end
649
+ else
650
+ resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state)
659
651
  end
660
652
  end
653
+
654
+ response_list
661
655
  rescue NoMethodError => err
662
656
  # Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.)
663
657
  if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
664
658
  # This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
665
- raise ListResultFailedError.new(value: value, field: field, path: path)
659
+ raise ListResultFailedError.new(value: value, field: field, path: current_path)
666
660
  else
667
661
  # This was some other NoMethodError -- let it bubble to reveal the real error.
668
662
  raise
669
663
  end
664
+ rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
665
+ ex_err
666
+ rescue StandardError => err
667
+ begin
668
+ query.handle_or_reraise(err)
669
+ rescue GraphQL::ExecutionError => ex_err
670
+ ex_err
671
+ end
670
672
  end
671
-
672
- response_list
673
+ # Detect whether this error came while calling `.each` (before `idx` is set) or while running list *items* (after `idx` is set)
674
+ error_is_non_null = idx.nil? ? is_non_null : inner_type.non_null?
675
+ continue_value(list_value, field, error_is_non_null, ast_node, result_name, selection_result)
673
676
  else
674
677
  raise "Invariant: Unhandled type kind #{current_type.kind} (#{current_type})"
675
678
  end
676
679
  end
677
680
 
678
- def resolve_with_directives(object, directives, &block)
681
+ def resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state) # rubocop:disable Metrics/ParameterLists
682
+ runtime_state.current_result_name = this_idx
683
+ runtime_state.current_result = response_list
684
+ call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
685
+ # This will update `response_list` with the lazy
686
+ after_lazy(inner_value, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list, runtime_state: runtime_state) do |inner_inner_value, runtime_state|
687
+ continue_value = continue_value(inner_inner_value, field, inner_type_non_null, ast_node, this_idx, response_list)
688
+ if HALT != continue_value
689
+ continue_field(continue_value, owner_type, field, inner_type, ast_node, response_list.graphql_selections, false, owner_object, arguments, this_idx, response_list, was_scoped, runtime_state)
690
+ end
691
+ end
692
+ end
693
+ end
694
+
695
+ def call_method_on_directives(method_name, object, directives, &block)
679
696
  return yield if directives.nil? || directives.empty?
680
- run_directive(object, directives, 0, &block)
697
+ run_directive(method_name, object, directives, 0, &block)
681
698
  end
682
699
 
683
- def run_directive(object, directives, idx, &block)
700
+ def run_directive(method_name, object, directives, idx, &block)
684
701
  dir_node = directives[idx]
685
702
  if !dir_node
686
703
  yield
687
704
  else
688
- dir_defn = schema.directives.fetch(dir_node.name)
689
- if !dir_defn.is_a?(Class)
690
- dir_defn = dir_defn.type_class || raise("Only class-based directives are supported (not `@#{dir_node.name}`)")
691
- end
692
- dir_args = arguments(nil, dir_defn, dir_node)
693
- dir_defn.resolve(object, dir_args, context) do
694
- run_directive(object, directives, idx + 1, &block)
705
+ dir_defn = @schema_directives.fetch(dir_node.name)
706
+ raw_dir_args = arguments(nil, dir_defn, dir_node)
707
+ dir_args = continue_value(
708
+ raw_dir_args, # value
709
+ nil, # field
710
+ false, # is_non_null
711
+ dir_node, # ast_node
712
+ nil, # result_name
713
+ nil, # selection_result
714
+ )
715
+
716
+ if dir_args == HALT
717
+ nil
718
+ else
719
+ dir_defn.public_send(method_name, object, dir_args, context) do
720
+ run_directive(method_name, object, directives, idx + 1, &block)
721
+ end
695
722
  end
696
723
  end
697
724
  end
@@ -699,7 +726,7 @@ module GraphQL
699
726
  # Check {Schema::Directive.include?} for each directive that's present
700
727
  def directives_include?(node, graphql_object, parent_type)
701
728
  node.directives.each do |dir_node|
702
- dir_defn = schema.directives.fetch(dir_node.name).type_class || raise("Only class-based directives are supported (not #{dir_node.name.inspect})")
729
+ dir_defn = @schema_directives.fetch(dir_node.name)
703
730
  args = arguments(graphql_object, dir_defn, dir_node)
704
731
  if !dir_defn.include?(graphql_object, args, context)
705
732
  return false
@@ -708,59 +735,79 @@ module GraphQL
708
735
  true
709
736
  end
710
737
 
711
- def set_all_interpreter_context(object, field, arguments, path)
712
- if object
713
- @context[:current_object] = @interpreter_context[:current_object] = object
714
- end
715
- if field
716
- @context[:current_field] = @interpreter_context[:current_field] = field
717
- end
718
- if arguments
719
- @context[:current_arguments] = @interpreter_context[:current_arguments] = arguments
720
- end
721
- if path
722
- @context[:current_path] = @interpreter_context[:current_path] = path
738
+ def get_current_runtime_state
739
+ current_state = Fiber[:__graphql_runtime_info] ||= {}.compare_by_identity
740
+ current_state[@query] ||= CurrentState.new
741
+ end
742
+
743
+ def minimal_after_lazy(value, &block)
744
+ if lazy?(value)
745
+ GraphQL::Execution::Lazy.new do
746
+ result = @schema.sync_lazy(value)
747
+ # The returned result might also be lazy, so check it, too
748
+ minimal_after_lazy(result, &block)
749
+ end
750
+ else
751
+ yield(value)
723
752
  end
724
753
  end
725
754
 
726
755
  # @param obj [Object] Some user-returned value that may want to be batched
727
- # @param path [Array<String>]
728
756
  # @param field [GraphQL::Schema::Field]
729
757
  # @param eager [Boolean] Set to `true` for mutation root fields only
730
758
  # @param trace [Boolean] If `false`, don't wrap this with field tracing
731
759
  # @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
732
- def after_lazy(lazy_obj, owner:, field:, path:, scoped_context:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, trace: true, &block)
760
+ def after_lazy(lazy_obj, field:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, runtime_state:, trace: true, &block)
733
761
  if lazy?(lazy_obj)
734
- lazy = GraphQL::Execution::Lazy.new(path: path, field: field) do
735
- set_all_interpreter_context(owner_object, field, arguments, path)
736
- context.scoped_context = scoped_context
762
+ orig_result = result
763
+ was_authorized_by_scope_items = runtime_state.was_authorized_by_scope_items
764
+ lazy = GraphQL::Execution::Lazy.new(field: field) do
765
+ # This block might be called in a new fiber;
766
+ # In that case, this will initialize a new state
767
+ # to avoid conflicting with the parent fiber.
768
+ runtime_state = get_current_runtime_state
769
+ runtime_state.current_field = field
770
+ runtime_state.current_arguments = arguments
771
+ runtime_state.current_result_name = result_name
772
+ runtime_state.current_result = orig_result
773
+ runtime_state.was_authorized_by_scope_items = was_authorized_by_scope_items
737
774
  # Wrap the execution of _this_ method with tracing,
738
775
  # but don't wrap the continuation below
739
776
  inner_obj = begin
740
- query.with_error_handling do
741
- if trace
742
- query.trace("execute_field_lazy", {owner: owner, field: field, path: path, query: query, object: owner_object, arguments: arguments, ast_node: ast_node}) do
743
- schema.sync_lazy(lazy_obj)
744
- end
745
- else
777
+ if trace
778
+ @current_trace.execute_field_lazy(field: field, query: query, object: owner_object, arguments: arguments, ast_node: ast_node) do
746
779
  schema.sync_lazy(lazy_obj)
747
780
  end
781
+ else
782
+ schema.sync_lazy(lazy_obj)
783
+ end
784
+ rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
785
+ ex_err
786
+ rescue StandardError => err
787
+ begin
788
+ query.handle_or_reraise(err)
789
+ rescue GraphQL::ExecutionError => ex_err
790
+ ex_err
748
791
  end
749
- rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => err
750
- err
751
792
  end
752
- yield(inner_obj)
793
+ yield(inner_obj, runtime_state)
753
794
  end
754
795
 
755
796
  if eager
756
797
  lazy.value
757
798
  else
758
- set_result(result, result_name, lazy)
799
+ set_result(result, result_name, lazy, false, false) # is_non_null is irrelevant here
800
+ current_depth = 0
801
+ while result
802
+ current_depth += 1
803
+ result = result.graphql_parent
804
+ end
805
+ @lazies_at_depth[current_depth] << lazy
759
806
  lazy
760
807
  end
761
808
  else
762
- set_all_interpreter_context(owner_object, field, arguments, path)
763
- yield(lazy_obj)
809
+ # Don't need to reset state here because it _wasn't_ lazy.
810
+ yield(lazy_obj, runtime_state)
764
811
  end
765
812
  end
766
813
 
@@ -773,27 +820,25 @@ module GraphQL
773
820
  end
774
821
  end
775
822
 
776
- # Set this pair in the Query context, but also in the interpeter namespace,
777
- # for compatibility.
778
- def set_interpreter_context(key, value)
779
- @interpreter_context[key] = value
780
- @context[key] = value
781
- end
782
-
783
- def delete_interpreter_context(key)
784
- @interpreter_context.delete(key)
785
- @context.delete(key)
823
+ def delete_all_interpreter_context
824
+ per_query_state = Fiber[:__graphql_runtime_info]
825
+ if per_query_state
826
+ per_query_state.delete(@query)
827
+ if per_query_state.size == 0
828
+ Fiber[:__graphql_runtime_info] = nil
829
+ end
830
+ end
831
+ nil
786
832
  end
787
833
 
788
- def resolve_type(type, value, path)
789
- trace_payload = { context: context, type: type, object: value, path: path }
790
- resolved_type, resolved_value = query.trace("resolve_type", trace_payload) do
834
+ def resolve_type(type, value)
835
+ resolved_type, resolved_value = @current_trace.resolve_type(query: query, type: type, object: value) do
791
836
  query.resolve_type(type, value)
792
837
  end
793
838
 
794
839
  if lazy?(resolved_type)
795
840
  GraphQL::Execution::Lazy.new do
796
- query.trace("resolve_type_lazy", trace_payload) do
841
+ @current_trace.resolve_type_lazy(query: query, type: type, object: value) do
797
842
  schema.sync_lazy(resolved_type)
798
843
  end
799
844
  end
@@ -802,14 +847,13 @@ module GraphQL
802
847
  end
803
848
  end
804
849
 
805
- def authorized_new(type, value, context)
806
- type.authorized_new(value, context)
807
- end
808
-
809
850
  def lazy?(object)
810
- @lazy_cache.fetch(object.class) {
811
- @lazy_cache[object.class] = @schema.lazy?(object)
812
- }
851
+ obj_class = object.class
852
+ is_lazy = @lazy_cache[obj_class]
853
+ if is_lazy.nil?
854
+ is_lazy = @lazy_cache[obj_class] = @schema.lazy?(object)
855
+ end
856
+ is_lazy
813
857
  end
814
858
  end
815
859
  end