graphql 1.9.5 → 1.13.19

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 (659) hide show
  1. checksums.yaml +5 -5
  2. data/lib/generators/graphql/core.rb +21 -9
  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/install/templates/base_mutation.erb +10 -0
  8. data/lib/generators/graphql/{templates → install/templates}/mutation_type.erb +2 -0
  9. data/lib/generators/graphql/install_generator.rb +48 -8
  10. data/lib/generators/graphql/interface_generator.rb +7 -7
  11. data/lib/generators/graphql/loader_generator.rb +1 -0
  12. data/lib/generators/graphql/mutation_create_generator.rb +22 -0
  13. data/lib/generators/graphql/mutation_delete_generator.rb +22 -0
  14. data/lib/generators/graphql/mutation_generator.rb +6 -30
  15. data/lib/generators/graphql/mutation_update_generator.rb +22 -0
  16. data/lib/generators/graphql/object_generator.rb +28 -12
  17. data/lib/generators/graphql/orm_mutations_base.rb +40 -0
  18. data/lib/generators/graphql/relay.rb +63 -0
  19. data/lib/generators/graphql/relay_generator.rb +21 -0
  20. data/lib/generators/graphql/scalar_generator.rb +4 -2
  21. data/lib/generators/graphql/templates/base_argument.erb +6 -0
  22. data/lib/generators/graphql/templates/base_connection.erb +8 -0
  23. data/lib/generators/graphql/templates/base_edge.erb +8 -0
  24. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  25. data/lib/generators/graphql/templates/base_field.erb +7 -0
  26. data/lib/generators/graphql/templates/base_input_object.erb +3 -0
  27. data/lib/generators/graphql/templates/base_interface.erb +4 -0
  28. data/lib/generators/graphql/templates/base_object.erb +3 -0
  29. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  30. data/lib/generators/graphql/templates/base_union.erb +2 -0
  31. data/lib/generators/graphql/templates/enum.erb +7 -1
  32. data/lib/generators/graphql/templates/graphql_controller.erb +21 -12
  33. data/lib/generators/graphql/templates/input.erb +9 -0
  34. data/lib/generators/graphql/templates/interface.erb +6 -2
  35. data/lib/generators/graphql/templates/loader.erb +2 -0
  36. data/lib/generators/graphql/templates/mutation.erb +3 -1
  37. data/lib/generators/graphql/templates/mutation_create.erb +20 -0
  38. data/lib/generators/graphql/templates/mutation_delete.erb +20 -0
  39. data/lib/generators/graphql/templates/mutation_update.erb +21 -0
  40. data/lib/generators/graphql/templates/node_type.erb +9 -0
  41. data/lib/generators/graphql/templates/object.erb +7 -3
  42. data/lib/generators/graphql/templates/query_type.erb +3 -3
  43. data/lib/generators/graphql/templates/scalar.erb +5 -1
  44. data/lib/generators/graphql/templates/schema.erb +23 -28
  45. data/lib/generators/graphql/templates/union.erb +6 -2
  46. data/lib/generators/graphql/type_generator.rb +47 -10
  47. data/lib/generators/graphql/union_generator.rb +5 -5
  48. data/lib/graphql/analysis/analyze_query.rb +7 -0
  49. data/lib/graphql/analysis/ast/analyzer.rb +34 -12
  50. data/lib/graphql/analysis/ast/field_usage.rb +29 -2
  51. data/lib/graphql/analysis/ast/max_query_complexity.rb +3 -3
  52. data/lib/graphql/analysis/ast/max_query_depth.rb +7 -3
  53. data/lib/graphql/analysis/ast/query_complexity.rb +179 -64
  54. data/lib/graphql/analysis/ast/query_depth.rb +16 -26
  55. data/lib/graphql/analysis/ast/visitor.rb +46 -32
  56. data/lib/graphql/analysis/ast.rb +23 -14
  57. data/lib/graphql/analysis/query_depth.rb +4 -17
  58. data/lib/graphql/argument.rb +16 -38
  59. data/lib/graphql/backtrace/inspect_result.rb +0 -1
  60. data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
  61. data/lib/graphql/backtrace/table.rb +44 -5
  62. data/lib/graphql/backtrace/traced_error.rb +0 -1
  63. data/lib/graphql/backtrace/tracer.rb +40 -9
  64. data/lib/graphql/backtrace.rb +28 -19
  65. data/lib/graphql/backwards_compatibility.rb +2 -1
  66. data/lib/graphql/base_type.rb +11 -5
  67. data/lib/graphql/boolean_type.rb +1 -1
  68. data/lib/graphql/coercion_error.rb +8 -0
  69. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +2 -2
  70. data/lib/graphql/compatibility/execution_specification.rb +1 -0
  71. data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +1 -1
  72. data/lib/graphql/compatibility/lazy_execution_specification.rb +2 -0
  73. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +5 -9
  74. data/lib/graphql/compatibility/query_parser_specification.rb +2 -0
  75. data/lib/graphql/compatibility/schema_parser_specification.rb +2 -0
  76. data/lib/graphql/dataloader/null_dataloader.rb +22 -0
  77. data/lib/graphql/dataloader/request.rb +19 -0
  78. data/lib/graphql/dataloader/request_all.rb +19 -0
  79. data/lib/graphql/dataloader/source.rb +155 -0
  80. data/lib/graphql/dataloader.rb +308 -0
  81. data/lib/graphql/date_encoding_error.rb +16 -0
  82. data/lib/graphql/define/assign_enum_value.rb +1 -1
  83. data/lib/graphql/define/assign_global_id_field.rb +2 -2
  84. data/lib/graphql/define/assign_object_field.rb +3 -3
  85. data/lib/graphql/define/defined_object_proxy.rb +3 -0
  86. data/lib/graphql/define/instance_definable.rb +65 -110
  87. data/lib/graphql/define/type_definer.rb +5 -5
  88. data/lib/graphql/deprecated_dsl.rb +18 -5
  89. data/lib/graphql/deprecation.rb +9 -0
  90. data/lib/graphql/directive/deprecated_directive.rb +1 -12
  91. data/lib/graphql/directive/include_directive.rb +1 -1
  92. data/lib/graphql/directive/skip_directive.rb +1 -1
  93. data/lib/graphql/directive.rb +9 -6
  94. data/lib/graphql/enum_type.rb +14 -74
  95. data/lib/graphql/execution/directive_checks.rb +2 -2
  96. data/lib/graphql/execution/errors.rb +163 -0
  97. data/lib/graphql/execution/execute.rb +10 -3
  98. data/lib/graphql/execution/instrumentation.rb +1 -1
  99. data/lib/graphql/execution/interpreter/argument_value.rb +28 -0
  100. data/lib/graphql/execution/interpreter/arguments.rb +88 -0
  101. data/lib/graphql/execution/interpreter/arguments_cache.rb +105 -0
  102. data/lib/graphql/execution/interpreter/handles_raw_value.rb +18 -0
  103. data/lib/graphql/execution/interpreter/resolve.rb +37 -25
  104. data/lib/graphql/execution/interpreter/runtime.rb +737 -376
  105. data/lib/graphql/execution/interpreter.rb +42 -19
  106. data/lib/graphql/execution/lazy/lazy_method_map.rb +4 -0
  107. data/lib/graphql/execution/lazy.rb +5 -1
  108. data/lib/graphql/execution/lookahead.rb +39 -114
  109. data/lib/graphql/execution/multiplex.rb +58 -29
  110. data/lib/graphql/execution.rb +1 -0
  111. data/lib/graphql/execution_error.rb +3 -2
  112. data/lib/graphql/field.rb +15 -119
  113. data/lib/graphql/filter.rb +1 -1
  114. data/lib/graphql/float_type.rb +1 -1
  115. data/lib/graphql/function.rb +6 -31
  116. data/lib/graphql/id_type.rb +1 -1
  117. data/lib/graphql/input_object_type.rb +11 -26
  118. data/lib/graphql/int_type.rb +1 -1
  119. data/lib/graphql/integer_decoding_error.rb +17 -0
  120. data/lib/graphql/integer_encoding_error.rb +18 -2
  121. data/lib/graphql/interface_type.rb +10 -24
  122. data/lib/graphql/internal_representation/document.rb +2 -2
  123. data/lib/graphql/internal_representation/rewrite.rb +1 -1
  124. data/lib/graphql/internal_representation/scope.rb +2 -2
  125. data/lib/graphql/internal_representation/visit.rb +2 -2
  126. data/lib/graphql/introspection/base_object.rb +2 -5
  127. data/lib/graphql/introspection/directive_location_enum.rb +2 -2
  128. data/lib/graphql/introspection/directive_type.rb +12 -6
  129. data/lib/graphql/introspection/entry_points.rb +9 -8
  130. data/lib/graphql/introspection/enum_value_type.rb +2 -2
  131. data/lib/graphql/introspection/field_type.rb +9 -5
  132. data/lib/graphql/introspection/input_value_type.rb +41 -11
  133. data/lib/graphql/introspection/introspection_query.rb +6 -92
  134. data/lib/graphql/introspection/schema_type.rb +13 -12
  135. data/lib/graphql/introspection/type_type.rb +27 -17
  136. data/lib/graphql/introspection.rb +99 -0
  137. data/lib/graphql/invalid_null_error.rb +18 -0
  138. data/lib/graphql/language/block_string.rb +22 -7
  139. data/lib/graphql/language/cache.rb +37 -0
  140. data/lib/graphql/language/definition_slice.rb +21 -10
  141. data/lib/graphql/language/document_from_schema_definition.rb +126 -67
  142. data/lib/graphql/language/lexer.rb +180 -141
  143. data/lib/graphql/language/lexer.rl +75 -51
  144. data/lib/graphql/language/nodes.rb +92 -108
  145. data/lib/graphql/language/parser.rb +933 -897
  146. data/lib/graphql/language/parser.y +129 -103
  147. data/lib/graphql/language/printer.rb +11 -2
  148. data/lib/graphql/language/sanitized_printer.rb +222 -0
  149. data/lib/graphql/language/token.rb +1 -1
  150. data/lib/graphql/language/visitor.rb +11 -8
  151. data/lib/graphql/language.rb +3 -1
  152. data/lib/graphql/name_validator.rb +2 -7
  153. data/lib/graphql/non_null_type.rb +0 -10
  154. data/lib/graphql/object_type.rb +47 -58
  155. data/lib/graphql/pagination/active_record_relation_connection.rb +85 -0
  156. data/lib/graphql/pagination/array_connection.rb +77 -0
  157. data/lib/graphql/pagination/connection.rb +226 -0
  158. data/lib/graphql/pagination/connections.rb +160 -0
  159. data/lib/graphql/pagination/mongoid_relation_connection.rb +25 -0
  160. data/lib/graphql/pagination/relation_connection.rb +226 -0
  161. data/lib/graphql/pagination/sequel_dataset_connection.rb +28 -0
  162. data/lib/graphql/pagination.rb +6 -0
  163. data/lib/graphql/parse_error.rb +0 -1
  164. data/lib/graphql/query/arguments.rb +29 -19
  165. data/lib/graphql/query/arguments_cache.rb +1 -2
  166. data/lib/graphql/query/context.rb +81 -14
  167. data/lib/graphql/query/executor.rb +0 -1
  168. data/lib/graphql/query/fingerprint.rb +26 -0
  169. data/lib/graphql/query/input_validation_result.rb +32 -6
  170. data/lib/graphql/query/literal_input.rb +31 -10
  171. data/lib/graphql/query/null_context.rb +28 -8
  172. data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
  173. data/lib/graphql/query/serial_execution.rb +1 -0
  174. data/lib/graphql/query/validation_pipeline.rb +6 -4
  175. data/lib/graphql/query/variable_validation_error.rb +3 -3
  176. data/lib/graphql/query/variables.rb +51 -9
  177. data/lib/graphql/query.rb +97 -23
  178. data/lib/graphql/railtie.rb +9 -1
  179. data/lib/graphql/rake_task/validate.rb +3 -0
  180. data/lib/graphql/rake_task.rb +12 -9
  181. data/lib/graphql/relay/array_connection.rb +11 -13
  182. data/lib/graphql/relay/base_connection.rb +33 -16
  183. data/lib/graphql/relay/connection_instrumentation.rb +4 -4
  184. data/lib/graphql/relay/connection_type.rb +18 -4
  185. data/lib/graphql/relay/edge_type.rb +1 -0
  186. data/lib/graphql/relay/edges_instrumentation.rb +1 -2
  187. data/lib/graphql/relay/global_id_resolve.rb +1 -2
  188. data/lib/graphql/relay/mutation.rb +3 -87
  189. data/lib/graphql/relay/node.rb +5 -2
  190. data/lib/graphql/relay/page_info.rb +1 -1
  191. data/lib/graphql/relay/range_add.rb +27 -9
  192. data/lib/graphql/relay/relation_connection.rb +17 -15
  193. data/lib/graphql/relay/type_extensions.rb +2 -0
  194. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  195. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  196. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  197. data/lib/graphql/rubocop.rb +4 -0
  198. data/lib/graphql/scalar_type.rb +18 -62
  199. data/lib/graphql/schema/addition.rb +247 -0
  200. data/lib/graphql/schema/argument.rb +289 -11
  201. data/lib/graphql/schema/base_64_bp.rb +3 -2
  202. data/lib/graphql/schema/base_64_encoder.rb +2 -0
  203. data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +1 -1
  204. data/lib/graphql/schema/build_from_definition/resolve_map.rb +13 -5
  205. data/lib/graphql/schema/build_from_definition.rb +323 -203
  206. data/lib/graphql/schema/built_in_types.rb +5 -5
  207. data/lib/graphql/schema/default_type_error.rb +2 -0
  208. data/lib/graphql/schema/directive/deprecated.rb +18 -0
  209. data/lib/graphql/schema/directive/feature.rb +2 -2
  210. data/lib/graphql/schema/directive/flagged.rb +57 -0
  211. data/lib/graphql/schema/directive/include.rb +2 -2
  212. data/lib/graphql/schema/directive/skip.rb +2 -2
  213. data/lib/graphql/schema/directive/transform.rb +14 -2
  214. data/lib/graphql/schema/directive.rb +136 -6
  215. data/lib/graphql/schema/enum.rb +121 -12
  216. data/lib/graphql/schema/enum_value.rb +23 -6
  217. data/lib/graphql/schema/field/connection_extension.rb +46 -20
  218. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  219. data/lib/graphql/schema/field.rb +515 -191
  220. data/lib/graphql/schema/field_extension.rb +89 -2
  221. data/lib/graphql/schema/find_inherited_value.rb +36 -0
  222. data/lib/graphql/schema/finder.rb +16 -14
  223. data/lib/graphql/schema/input_object.rb +178 -34
  224. data/lib/graphql/schema/interface.rb +35 -25
  225. data/lib/graphql/schema/introspection_system.rb +111 -35
  226. data/lib/graphql/schema/late_bound_type.rb +3 -2
  227. data/lib/graphql/schema/list.rb +65 -1
  228. data/lib/graphql/schema/loader.rb +147 -97
  229. data/lib/graphql/schema/member/accepts_definition.rb +15 -3
  230. data/lib/graphql/schema/member/base_dsl_methods.rb +40 -45
  231. data/lib/graphql/schema/member/build_type.rb +19 -8
  232. data/lib/graphql/schema/member/cached_graphql_definition.rb +34 -2
  233. data/lib/graphql/schema/member/has_arguments.rb +212 -22
  234. data/lib/graphql/schema/member/has_ast_node.rb +20 -0
  235. data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
  236. data/lib/graphql/schema/member/has_directives.rb +98 -0
  237. data/lib/graphql/schema/member/has_fields.rb +113 -28
  238. data/lib/graphql/schema/member/has_interfaces.rb +100 -0
  239. data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
  240. data/lib/graphql/schema/member/has_validators.rb +31 -0
  241. data/lib/graphql/schema/member/instrumentation.rb +6 -2
  242. data/lib/graphql/schema/member/relay_shortcuts.rb +2 -2
  243. data/lib/graphql/schema/member/type_system_helpers.rb +3 -3
  244. data/lib/graphql/schema/member/validates_input.rb +33 -0
  245. data/lib/graphql/schema/member.rb +11 -0
  246. data/lib/graphql/schema/middleware_chain.rb +1 -1
  247. data/lib/graphql/schema/mutation.rb +5 -1
  248. data/lib/graphql/schema/non_null.rb +37 -1
  249. data/lib/graphql/schema/object.rb +52 -37
  250. data/lib/graphql/schema/possible_types.rb +12 -7
  251. data/lib/graphql/schema/printer.rb +17 -34
  252. data/lib/graphql/schema/relay_classic_mutation.rb +51 -7
  253. data/lib/graphql/schema/resolver/has_payload_type.rb +36 -5
  254. data/lib/graphql/schema/resolver.rb +159 -59
  255. data/lib/graphql/schema/scalar.rb +43 -3
  256. data/lib/graphql/schema/subscription.rb +83 -25
  257. data/lib/graphql/schema/timeout.rb +123 -0
  258. data/lib/graphql/schema/timeout_middleware.rb +6 -3
  259. data/lib/graphql/schema/traversal.rb +2 -2
  260. data/lib/graphql/schema/type_expression.rb +21 -13
  261. data/lib/graphql/schema/type_membership.rb +48 -0
  262. data/lib/graphql/schema/union.rb +67 -6
  263. data/lib/graphql/schema/unique_within_type.rb +1 -2
  264. data/lib/graphql/schema/validation.rb +30 -8
  265. data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
  266. data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
  267. data/lib/graphql/schema/validator/exclusion_validator.rb +33 -0
  268. data/lib/graphql/schema/validator/format_validator.rb +48 -0
  269. data/lib/graphql/schema/validator/inclusion_validator.rb +35 -0
  270. data/lib/graphql/schema/validator/length_validator.rb +59 -0
  271. data/lib/graphql/schema/validator/numericality_validator.rb +82 -0
  272. data/lib/graphql/schema/validator/required_validator.rb +82 -0
  273. data/lib/graphql/schema/validator.rb +171 -0
  274. data/lib/graphql/schema/warden.rb +263 -30
  275. data/lib/graphql/schema/wrapper.rb +1 -1
  276. data/lib/graphql/schema.rb +1016 -256
  277. data/lib/graphql/static_validation/all_rules.rb +2 -0
  278. data/lib/graphql/static_validation/base_visitor.rb +17 -10
  279. data/lib/graphql/static_validation/definition_dependencies.rb +21 -13
  280. data/lib/graphql/static_validation/error.rb +3 -1
  281. data/lib/graphql/static_validation/literal_validator.rb +52 -27
  282. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +46 -79
  283. data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +23 -6
  284. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +35 -26
  285. data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +4 -2
  286. data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -1
  287. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -2
  288. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
  289. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -5
  290. data/lib/graphql/static_validation/rules/fields_will_merge.rb +105 -55
  291. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
  292. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  293. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
  294. data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
  295. data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
  296. data/lib/graphql/static_validation/rules/query_root_exists.rb +17 -0
  297. data/lib/graphql/static_validation/rules/query_root_exists_error.rb +26 -0
  298. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +5 -3
  299. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +9 -10
  300. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
  301. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +12 -13
  302. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +19 -14
  303. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  304. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +5 -3
  305. data/lib/graphql/static_validation/type_stack.rb +2 -2
  306. data/lib/graphql/static_validation/validation_context.rb +13 -3
  307. data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
  308. data/lib/graphql/static_validation/validator.rb +43 -9
  309. data/lib/graphql/static_validation.rb +1 -0
  310. data/lib/graphql/string_encoding_error.rb +13 -3
  311. data/lib/graphql/string_type.rb +1 -1
  312. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +123 -22
  313. data/lib/graphql/subscriptions/broadcast_analyzer.rb +81 -0
  314. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +21 -0
  315. data/lib/graphql/subscriptions/event.rb +94 -26
  316. data/lib/graphql/subscriptions/instrumentation.rb +10 -6
  317. data/lib/graphql/subscriptions/serialize.rb +53 -6
  318. data/lib/graphql/subscriptions/subscription_root.rb +15 -5
  319. data/lib/graphql/subscriptions.rb +122 -54
  320. data/lib/graphql/tracing/active_support_notifications_tracing.rb +8 -17
  321. data/lib/graphql/tracing/appoptics_tracing.rb +173 -0
  322. data/lib/graphql/tracing/appsignal_tracing.rb +23 -0
  323. data/lib/graphql/tracing/data_dog_tracing.rb +32 -2
  324. data/lib/graphql/tracing/new_relic_tracing.rb +9 -12
  325. data/lib/graphql/tracing/notifications_tracing.rb +59 -0
  326. data/lib/graphql/tracing/platform_tracing.rb +66 -7
  327. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
  328. data/lib/graphql/tracing/prometheus_tracing.rb +8 -0
  329. data/lib/graphql/tracing/scout_tracing.rb +19 -0
  330. data/lib/graphql/tracing/skylight_tracing.rb +9 -0
  331. data/lib/graphql/tracing/statsd_tracing.rb +42 -0
  332. data/lib/graphql/tracing.rb +15 -35
  333. data/lib/graphql/types/big_int.rb +5 -1
  334. data/lib/graphql/types/int.rb +10 -3
  335. data/lib/graphql/types/iso_8601_date.rb +42 -0
  336. data/lib/graphql/types/iso_8601_date_time.rb +32 -8
  337. data/lib/graphql/types/relay/base_connection.rb +6 -88
  338. data/lib/graphql/types/relay/base_edge.rb +2 -37
  339. data/lib/graphql/types/relay/connection_behaviors.rb +174 -0
  340. data/lib/graphql/types/relay/default_relay.rb +31 -0
  341. data/lib/graphql/types/relay/edge_behaviors.rb +64 -0
  342. data/lib/graphql/types/relay/has_node_field.rb +41 -0
  343. data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
  344. data/lib/graphql/types/relay/node.rb +2 -4
  345. data/lib/graphql/types/relay/node_behaviors.rb +15 -0
  346. data/lib/graphql/types/relay/node_field.rb +4 -23
  347. data/lib/graphql/types/relay/nodes_field.rb +16 -18
  348. data/lib/graphql/types/relay/page_info.rb +2 -14
  349. data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
  350. data/lib/graphql/types/relay.rb +11 -3
  351. data/lib/graphql/types/string.rb +8 -2
  352. data/lib/graphql/types.rb +1 -0
  353. data/lib/graphql/unauthorized_error.rb +2 -2
  354. data/lib/graphql/union_type.rb +61 -46
  355. data/lib/graphql/unresolved_type_error.rb +2 -2
  356. data/lib/graphql/upgrader/member.rb +150 -112
  357. data/lib/graphql/upgrader/schema.rb +1 -0
  358. data/lib/graphql/version.rb +1 -1
  359. data/lib/graphql.rb +89 -18
  360. data/readme.md +4 -7
  361. metadata +115 -703
  362. data/lib/graphql/execution/interpreter/hash_response.rb +0 -46
  363. data/lib/graphql/literal_validation_error.rb +0 -6
  364. data/lib/graphql/types/relay/base_field.rb +0 -22
  365. data/lib/graphql/types/relay/base_interface.rb +0 -29
  366. data/lib/graphql/types/relay/base_object.rb +0 -26
  367. data/spec/dummy/Gemfile +0 -12
  368. data/spec/dummy/Gemfile.lock +0 -157
  369. data/spec/dummy/README.md +0 -24
  370. data/spec/dummy/Rakefile +0 -7
  371. data/spec/dummy/app/assets/config/manifest.js +0 -1
  372. data/spec/dummy/app/assets/javascripts/application.js +0 -66
  373. data/spec/dummy/app/channels/application_cable/channel.rb +0 -5
  374. data/spec/dummy/app/channels/application_cable/connection.rb +0 -5
  375. data/spec/dummy/app/channels/graphql_channel.rb +0 -116
  376. data/spec/dummy/app/controllers/application_controller.rb +0 -4
  377. data/spec/dummy/app/controllers/pages_controller.rb +0 -5
  378. data/spec/dummy/app/helpers/application_helper.rb +0 -3
  379. data/spec/dummy/app/jobs/application_job.rb +0 -3
  380. data/spec/dummy/app/views/layouts/application.html.erb +0 -12
  381. data/spec/dummy/app/views/pages/show.html +0 -16
  382. data/spec/dummy/bin/bundle +0 -4
  383. data/spec/dummy/bin/rails +0 -5
  384. data/spec/dummy/bin/rake +0 -5
  385. data/spec/dummy/bin/setup +0 -31
  386. data/spec/dummy/bin/update +0 -27
  387. data/spec/dummy/bin/yarn +0 -12
  388. data/spec/dummy/config/application.rb +0 -30
  389. data/spec/dummy/config/boot.rb +0 -4
  390. data/spec/dummy/config/cable.yml +0 -10
  391. data/spec/dummy/config/environment.rb +0 -6
  392. data/spec/dummy/config/environments/development.rb +0 -40
  393. data/spec/dummy/config/environments/production.rb +0 -76
  394. data/spec/dummy/config/environments/test.rb +0 -37
  395. data/spec/dummy/config/initializers/application_controller_renderer.rb +0 -9
  396. data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -8
  397. data/spec/dummy/config/initializers/cookies_serializer.rb +0 -6
  398. data/spec/dummy/config/initializers/filter_parameter_logging.rb +0 -5
  399. data/spec/dummy/config/initializers/inflections.rb +0 -17
  400. data/spec/dummy/config/initializers/mime_types.rb +0 -5
  401. data/spec/dummy/config/initializers/wrap_parameters.rb +0 -10
  402. data/spec/dummy/config/locales/en.yml +0 -33
  403. data/spec/dummy/config/puma.rb +0 -57
  404. data/spec/dummy/config/routes.rb +0 -4
  405. data/spec/dummy/config/secrets.yml +0 -32
  406. data/spec/dummy/config.ru +0 -6
  407. data/spec/dummy/log/test.log +0 -199
  408. data/spec/dummy/package.json +0 -5
  409. data/spec/dummy/public/404.html +0 -67
  410. data/spec/dummy/public/422.html +0 -67
  411. data/spec/dummy/public/500.html +0 -66
  412. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  413. data/spec/dummy/public/apple-touch-icon.png +0 -0
  414. data/spec/dummy/public/favicon.ico +0 -0
  415. data/spec/dummy/public/robots.txt +0 -1
  416. data/spec/dummy/test/application_system_test_case.rb +0 -6
  417. data/spec/dummy/test/system/action_cable_subscription_test.rb +0 -45
  418. data/spec/dummy/test/test_helper.rb +0 -4
  419. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/4w/4wzXRZrAkwKdgYaSE0pid5eB-fer8vSfSku_NPg4rMA.cache +0 -0
  420. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/7I/7IHVBiJT06QSpgLpLoJIxboQ0B-D_tMTxsvoezBTV3Q.cache +0 -1
  421. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/8w/8wY_SKagj8wHuwGNAAf6JnQ8joMbC6cEYpHrTAI8Urc.cache +0 -1
  422. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/AK/AKzz1u6bGb4auXcrObA_g5LL-oV0ejNGa448AgAi_WQ.cache +0 -1
  423. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ET/ETW4uxvaYpruL8y6_ZptUH82ZowMaHIqvg5WexBFdEM.cache +0 -3
  424. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/F1/F1TWpjjyA56k9Z90n5B3xRn7DUdGjX73QCkYC6k07JQ.cache +0 -0
  425. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/F8/F8MUNRzORGFgr329fNM0xLaoWCXdv3BIalT7dsvLfjs.cache +0 -2
  426. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/KB/KB07ZaKNC5uXJ7TjLi-WqnY6g7dq8wWp_8N3HNjBNxg.cache +0 -2
  427. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Ms/MsKSimH_UCB-H1tLvDABDHuvGciuoW6kVqQWDrXU5FQ.cache +0 -0
  428. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Mt/Mtci-Kim50aPOmeClD4AIicKn1d1WJ0n454IjSd94sk.cache +0 -0
  429. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/QH/QHt3Tc1Y6M66Oo_pDuMyWrQNs4Pp3SMeZR5K1wJj2Ts.cache +0 -1
  430. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/XU/XU4k1OXnfMils5SrirorPvDSyDSqiOWLZNtmAH1HH8k.cache +0 -0
  431. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ZI/ZIof7mZxWWCnraIFOCuV6a8QRWzKJXJnx2Xd7C0ZyX0.cache +0 -1
  432. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/cG/cGc_puuPS5pZKgUcy1Y_i1L6jl5UtsiIrMH59rTzR6c.cache +0 -3
  433. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/df/dfro_B6bx3KP1Go-7jEOqqZ2j4hVRseXIc3es9PKQno.cache +0 -1
  434. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/jO/jO1DfbqnG0mTULsjJJANc3fefrG2zt7DIMmcptMT628.cache +0 -1
  435. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/pE/pE7gO6pQ-z187Swb4hT554wmqsq-cNzgPWLrCz-LQQQ.cache +0 -0
  436. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/r9/r9iU1l58a6rxkZSW5RSC52_tD-_UQuHxoMVnkfJ7Mhs.cache +0 -1
  437. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/xi/xitPPFfPIyDMpaznV0sBBcw8eSCV8PJcLLWin78sCgE.cache +0 -0
  438. data/spec/dummy/tmp/screenshots/failures_test_it_handles_subscriptions.png +0 -0
  439. data/spec/fixtures/upgrader/account.original.rb +0 -19
  440. data/spec/fixtures/upgrader/account.transformed.rb +0 -20
  441. data/spec/fixtures/upgrader/blame_range.original.rb +0 -43
  442. data/spec/fixtures/upgrader/blame_range.transformed.rb +0 -30
  443. data/spec/fixtures/upgrader/date_time.original.rb +0 -24
  444. data/spec/fixtures/upgrader/date_time.transformed.rb +0 -23
  445. data/spec/fixtures/upgrader/delete_project.original.rb +0 -28
  446. data/spec/fixtures/upgrader/delete_project.transformed.rb +0 -27
  447. data/spec/fixtures/upgrader/gist_order_field.original.rb +0 -14
  448. data/spec/fixtures/upgrader/gist_order_field.transformed.rb +0 -13
  449. data/spec/fixtures/upgrader/increment_count.original.rb +0 -59
  450. data/spec/fixtures/upgrader/increment_count.transformed.rb +0 -50
  451. data/spec/fixtures/upgrader/photo.original.rb +0 -10
  452. data/spec/fixtures/upgrader/photo.transformed.rb +0 -12
  453. data/spec/fixtures/upgrader/release_order.original.rb +0 -15
  454. data/spec/fixtures/upgrader/release_order.transformed.rb +0 -14
  455. data/spec/fixtures/upgrader/starrable.original.rb +0 -49
  456. data/spec/fixtures/upgrader/starrable.transformed.rb +0 -46
  457. data/spec/fixtures/upgrader/subscribable.original.rb +0 -55
  458. data/spec/fixtures/upgrader/subscribable.transformed.rb +0 -51
  459. data/spec/fixtures/upgrader/type_x.original.rb +0 -65
  460. data/spec/fixtures/upgrader/type_x.transformed.rb +0 -56
  461. data/spec/graphql/analysis/analyze_query_spec.rb +0 -272
  462. data/spec/graphql/analysis/ast/field_usage_spec.rb +0 -51
  463. data/spec/graphql/analysis/ast/max_query_complexity_spec.rb +0 -120
  464. data/spec/graphql/analysis/ast/max_query_depth_spec.rb +0 -114
  465. data/spec/graphql/analysis/ast/query_complexity_spec.rb +0 -299
  466. data/spec/graphql/analysis/ast/query_depth_spec.rb +0 -108
  467. data/spec/graphql/analysis/ast_spec.rb +0 -269
  468. data/spec/graphql/analysis/field_usage_spec.rb +0 -62
  469. data/spec/graphql/analysis/max_query_complexity_spec.rb +0 -102
  470. data/spec/graphql/analysis/max_query_depth_spec.rb +0 -101
  471. data/spec/graphql/analysis/query_complexity_spec.rb +0 -301
  472. data/spec/graphql/analysis/query_depth_spec.rb +0 -81
  473. data/spec/graphql/argument_spec.rb +0 -159
  474. data/spec/graphql/authorization_spec.rb +0 -974
  475. data/spec/graphql/backtrace_spec.rb +0 -206
  476. data/spec/graphql/base_type_spec.rb +0 -171
  477. data/spec/graphql/boolean_type_spec.rb +0 -21
  478. data/spec/graphql/compatibility/execution_specification_spec.rb +0 -4
  479. data/spec/graphql/compatibility/lazy_execution_specification_spec.rb +0 -4
  480. data/spec/graphql/compatibility/query_parser_specification_spec.rb +0 -6
  481. data/spec/graphql/compatibility/schema_parser_specification_spec.rb +0 -6
  482. data/spec/graphql/define/assign_argument_spec.rb +0 -61
  483. data/spec/graphql/define/instance_definable_spec.rb +0 -203
  484. data/spec/graphql/directive/skip_directive_spec.rb +0 -9
  485. data/spec/graphql/directive_spec.rb +0 -295
  486. data/spec/graphql/enum_type_spec.rb +0 -158
  487. data/spec/graphql/execution/execute_spec.rb +0 -303
  488. data/spec/graphql/execution/instrumentation_spec.rb +0 -184
  489. data/spec/graphql/execution/interpreter_spec.rb +0 -485
  490. data/spec/graphql/execution/lazy/lazy_method_map_spec.rb +0 -57
  491. data/spec/graphql/execution/lazy_spec.rb +0 -247
  492. data/spec/graphql/execution/lookahead_spec.rb +0 -390
  493. data/spec/graphql/execution/multiplex_spec.rb +0 -211
  494. data/spec/graphql/execution/typecast_spec.rb +0 -47
  495. data/spec/graphql/execution_error_spec.rb +0 -325
  496. data/spec/graphql/field_spec.rb +0 -246
  497. data/spec/graphql/float_type_spec.rb +0 -16
  498. data/spec/graphql/function_spec.rb +0 -152
  499. data/spec/graphql/id_type_spec.rb +0 -33
  500. data/spec/graphql/input_object_type_spec.rb +0 -25
  501. data/spec/graphql/int_type_spec.rb +0 -35
  502. data/spec/graphql/interface_type_spec.rb +0 -196
  503. data/spec/graphql/internal_representation/print_spec.rb +0 -41
  504. data/spec/graphql/internal_representation/rewrite_spec.rb +0 -376
  505. data/spec/graphql/introspection/directive_type_spec.rb +0 -61
  506. data/spec/graphql/introspection/input_value_type_spec.rb +0 -144
  507. data/spec/graphql/introspection/introspection_query_spec.rb +0 -59
  508. data/spec/graphql/introspection/schema_type_spec.rb +0 -56
  509. data/spec/graphql/introspection/type_type_spec.rb +0 -155
  510. data/spec/graphql/language/block_string_spec.rb +0 -70
  511. data/spec/graphql/language/definition_slice_spec.rb +0 -226
  512. data/spec/graphql/language/document_from_schema_definition_spec.rb +0 -770
  513. data/spec/graphql/language/equality_spec.rb +0 -84
  514. data/spec/graphql/language/generation_spec.rb +0 -38
  515. data/spec/graphql/language/lexer_spec.rb +0 -135
  516. data/spec/graphql/language/nodes_spec.rb +0 -67
  517. data/spec/graphql/language/parser_spec.rb +0 -183
  518. data/spec/graphql/language/printer_spec.rb +0 -215
  519. data/spec/graphql/language/visitor_spec.rb +0 -419
  520. data/spec/graphql/list_type_spec.rb +0 -57
  521. data/spec/graphql/non_null_type_spec.rb +0 -48
  522. data/spec/graphql/object_type_spec.rb +0 -197
  523. data/spec/graphql/query/arguments_spec.rb +0 -346
  524. data/spec/graphql/query/context_spec.rb +0 -292
  525. data/spec/graphql/query/executor_spec.rb +0 -341
  526. data/spec/graphql/query/literal_input_spec.rb +0 -91
  527. data/spec/graphql/query/result_spec.rb +0 -29
  528. data/spec/graphql/query/serial_execution/value_resolution_spec.rb +0 -109
  529. data/spec/graphql/query_spec.rb +0 -800
  530. data/spec/graphql/rake_task_spec.rb +0 -59
  531. data/spec/graphql/scalar_type_spec.rb +0 -66
  532. data/spec/graphql/schema/argument_spec.rb +0 -152
  533. data/spec/graphql/schema/build_from_definition_spec.rb +0 -1187
  534. data/spec/graphql/schema/catchall_middleware_spec.rb +0 -35
  535. data/spec/graphql/schema/directive/feature_spec.rb +0 -81
  536. data/spec/graphql/schema/directive/transform_spec.rb +0 -39
  537. data/spec/graphql/schema/enum_spec.rb +0 -83
  538. data/spec/graphql/schema/enum_value_spec.rb +0 -24
  539. data/spec/graphql/schema/field_extension_spec.rb +0 -137
  540. data/spec/graphql/schema/field_spec.rb +0 -301
  541. data/spec/graphql/schema/finder_spec.rb +0 -135
  542. data/spec/graphql/schema/input_object_spec.rb +0 -347
  543. data/spec/graphql/schema/instrumentation_spec.rb +0 -43
  544. data/spec/graphql/schema/interface_spec.rb +0 -215
  545. data/spec/graphql/schema/introspection_system_spec.rb +0 -56
  546. data/spec/graphql/schema/list_spec.rb +0 -46
  547. data/spec/graphql/schema/loader_spec.rb +0 -286
  548. data/spec/graphql/schema/member/accepts_definition_spec.rb +0 -115
  549. data/spec/graphql/schema/member/build_type_spec.rb +0 -63
  550. data/spec/graphql/schema/member/scoped_spec.rb +0 -217
  551. data/spec/graphql/schema/member/type_system_helpers_spec.rb +0 -63
  552. data/spec/graphql/schema/middleware_chain_spec.rb +0 -57
  553. data/spec/graphql/schema/mutation_spec.rb +0 -150
  554. data/spec/graphql/schema/non_null_spec.rb +0 -46
  555. data/spec/graphql/schema/object_spec.rb +0 -315
  556. data/spec/graphql/schema/printer_spec.rb +0 -869
  557. data/spec/graphql/schema/relay_classic_mutation_spec.rb +0 -252
  558. data/spec/graphql/schema/rescue_middleware_spec.rb +0 -88
  559. data/spec/graphql/schema/resolver_spec.rb +0 -714
  560. data/spec/graphql/schema/scalar_spec.rb +0 -101
  561. data/spec/graphql/schema/subscription_spec.rb +0 -440
  562. data/spec/graphql/schema/timeout_middleware_spec.rb +0 -188
  563. data/spec/graphql/schema/traversal_spec.rb +0 -222
  564. data/spec/graphql/schema/type_expression_spec.rb +0 -39
  565. data/spec/graphql/schema/union_spec.rb +0 -72
  566. data/spec/graphql/schema/unique_within_type_spec.rb +0 -44
  567. data/spec/graphql/schema/validation_spec.rb +0 -355
  568. data/spec/graphql/schema/warden_spec.rb +0 -926
  569. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +0 -413
  570. data/spec/graphql/static_validation/rules/argument_names_are_unique_spec.rb +0 -44
  571. data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +0 -112
  572. data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +0 -35
  573. data/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb +0 -42
  574. data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +0 -167
  575. data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +0 -66
  576. data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +0 -740
  577. data/spec/graphql/static_validation/rules/fragment_names_are_unique_spec.rb +0 -28
  578. data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +0 -52
  579. data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +0 -42
  580. data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +0 -123
  581. data/spec/graphql/static_validation/rules/fragments_are_named_spec.rb +0 -24
  582. data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +0 -56
  583. data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +0 -61
  584. data/spec/graphql/static_validation/rules/mutation_root_exists_spec.rb +0 -39
  585. data/spec/graphql/static_validation/rules/no_definitions_are_present_spec.rb +0 -62
  586. data/spec/graphql/static_validation/rules/operation_names_are_valid_spec.rb +0 -82
  587. data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +0 -73
  588. data/spec/graphql/static_validation/rules/required_input_object_attributes_are_present_spec.rb +0 -86
  589. data/spec/graphql/static_validation/rules/subscription_root_exists_spec.rb +0 -34
  590. data/spec/graphql/static_validation/rules/unique_directives_per_location_spec.rb +0 -188
  591. data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +0 -196
  592. data/spec/graphql/static_validation/rules/variable_names_are_unique_spec.rb +0 -23
  593. data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +0 -236
  594. data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +0 -78
  595. data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +0 -81
  596. data/spec/graphql/static_validation/type_stack_spec.rb +0 -29
  597. data/spec/graphql/static_validation/validator_spec.rb +0 -204
  598. data/spec/graphql/string_type_spec.rb +0 -80
  599. data/spec/graphql/subscriptions/serialize_spec.rb +0 -49
  600. data/spec/graphql/subscriptions_spec.rb +0 -540
  601. data/spec/graphql/tracing/new_relic_tracing_spec.rb +0 -84
  602. data/spec/graphql/tracing/platform_tracing_spec.rb +0 -141
  603. data/spec/graphql/tracing/prometheus_tracing_spec.rb +0 -42
  604. data/spec/graphql/tracing/scout_tracing_spec.rb +0 -17
  605. data/spec/graphql/tracing/skylight_tracing_spec.rb +0 -63
  606. data/spec/graphql/tracing_spec.rb +0 -52
  607. data/spec/graphql/types/big_int_spec.rb +0 -24
  608. data/spec/graphql/types/iso_8601_date_time_spec.rb +0 -137
  609. data/spec/graphql/types/relay/base_edge_spec.rb +0 -33
  610. data/spec/graphql/union_type_spec.rb +0 -211
  611. data/spec/graphql/upgrader/member_spec.rb +0 -583
  612. data/spec/graphql/upgrader/schema_spec.rb +0 -82
  613. data/spec/integration/mongoid/graphql/relay/mongo_relation_connection_spec.rb +0 -528
  614. data/spec/integration/mongoid/spec_helper.rb +0 -2
  615. data/spec/integration/mongoid/star_trek/data.rb +0 -126
  616. data/spec/integration/mongoid/star_trek/schema.rb +0 -425
  617. data/spec/integration/rails/data.rb +0 -110
  618. data/spec/integration/rails/generators/base_generator_test.rb +0 -7
  619. data/spec/integration/rails/generators/graphql/enum_generator_spec.rb +0 -30
  620. data/spec/integration/rails/generators/graphql/install_generator_spec.rb +0 -218
  621. data/spec/integration/rails/generators/graphql/interface_generator_spec.rb +0 -34
  622. data/spec/integration/rails/generators/graphql/loader_generator_spec.rb +0 -59
  623. data/spec/integration/rails/generators/graphql/mutation_generator_spec.rb +0 -71
  624. data/spec/integration/rails/generators/graphql/object_generator_spec.rb +0 -54
  625. data/spec/integration/rails/generators/graphql/scalar_generator_spec.rb +0 -28
  626. data/spec/integration/rails/generators/graphql/union_generator_spec.rb +0 -67
  627. data/spec/integration/rails/graphql/input_object_type_spec.rb +0 -364
  628. data/spec/integration/rails/graphql/query/variables_spec.rb +0 -375
  629. data/spec/integration/rails/graphql/relay/array_connection_spec.rb +0 -290
  630. data/spec/integration/rails/graphql/relay/base_connection_spec.rb +0 -101
  631. data/spec/integration/rails/graphql/relay/connection_instrumentation_spec.rb +0 -80
  632. data/spec/integration/rails/graphql/relay/connection_resolve_spec.rb +0 -79
  633. data/spec/integration/rails/graphql/relay/connection_type_spec.rb +0 -106
  634. data/spec/integration/rails/graphql/relay/edge_spec.rb +0 -10
  635. data/spec/integration/rails/graphql/relay/mutation_spec.rb +0 -387
  636. data/spec/integration/rails/graphql/relay/node_spec.rb +0 -263
  637. data/spec/integration/rails/graphql/relay/page_info_spec.rb +0 -111
  638. data/spec/integration/rails/graphql/relay/range_add_spec.rb +0 -117
  639. data/spec/integration/rails/graphql/relay/relation_connection_spec.rb +0 -837
  640. data/spec/integration/rails/graphql/schema_spec.rb +0 -489
  641. data/spec/integration/rails/graphql/tracing/active_support_notifications_tracing_spec.rb +0 -62
  642. data/spec/integration/rails/spec_helper.rb +0 -25
  643. data/spec/integration/tmp/app/graphql/types/bird_type.rb +0 -7
  644. data/spec/spec_helper.rb +0 -116
  645. data/spec/support/dummy/data.rb +0 -45
  646. data/spec/support/dummy/schema.rb +0 -511
  647. data/spec/support/error_bubbling_helpers.rb +0 -23
  648. data/spec/support/global_id.rb +0 -23
  649. data/spec/support/jazz.rb +0 -772
  650. data/spec/support/lazy_helpers.rb +0 -192
  651. data/spec/support/magic_cards/schema.graphql +0 -33
  652. data/spec/support/minimum_input_object.rb +0 -21
  653. data/spec/support/new_relic.rb +0 -27
  654. data/spec/support/parser/filename_example.graphql +0 -5
  655. data/spec/support/parser/filename_example_error_1.graphql +0 -4
  656. data/spec/support/parser/filename_example_error_2.graphql +0 -5
  657. data/spec/support/skylight.rb +0 -39
  658. data/spec/support/star_wars/schema.rb +0 -465
  659. data/spec/support/static_validation_helpers.rb +0 -32
@@ -8,67 +8,279 @@ module GraphQL
8
8
  #
9
9
  # @api private
10
10
  class Runtime
11
+
12
+ module GraphQLResult
13
+ def initialize(result_name, parent_result)
14
+ @graphql_parent = parent_result
15
+ if parent_result && parent_result.graphql_dead
16
+ @graphql_dead = true
17
+ end
18
+ @graphql_result_name = result_name
19
+ # Jump through some hoops to avoid creating this duplicate storage if at all possible.
20
+ @graphql_metadata = nil
21
+ end
22
+
23
+ attr_accessor :graphql_dead
24
+ attr_reader :graphql_parent, :graphql_result_name
25
+
26
+ # Although these are used by only one of the Result classes,
27
+ # it's handy to have the methods implemented on both (even though they just return `nil`)
28
+ # because it makes it easy to check if anything is assigned.
29
+ # @return [nil, Array<String>]
30
+ attr_accessor :graphql_non_null_field_names
31
+ # @return [nil, true]
32
+ attr_accessor :graphql_non_null_list_items
33
+
34
+ # @return [Hash] Plain-Ruby result data (`@graphql_metadata` contains Result wrapper objects)
35
+ attr_accessor :graphql_result_data
36
+ end
37
+
38
+ class GraphQLResultHash
39
+ def initialize(_result_name, _parent_result)
40
+ super
41
+ @graphql_result_data = {}
42
+ end
43
+
44
+ include GraphQLResult
45
+
46
+ attr_accessor :graphql_merged_into
47
+
48
+ def []=(key, value)
49
+ # This is a hack.
50
+ # Basically, this object is merged into the root-level result at some point.
51
+ # But the problem is, some lazies are created whose closures retain reference to _this_
52
+ # object. When those lazies are resolved, they cause an update to this object.
53
+ #
54
+ # In order to return a proper top-level result, we have to update that top-level result object.
55
+ # In order to return a proper partial result (eg, for a directive), we have to update this object, too.
56
+ # Yowza.
57
+ if (t = @graphql_merged_into)
58
+ t[key] = value
59
+ end
60
+
61
+ if value.respond_to?(:graphql_result_data)
62
+ @graphql_result_data[key] = value.graphql_result_data
63
+ # If we encounter some part of this response that requires metadata tracking,
64
+ # then create the metadata hash if necessary. It will be kept up-to-date after this.
65
+ (@graphql_metadata ||= @graphql_result_data.dup)[key] = value
66
+ else
67
+ @graphql_result_data[key] = value
68
+ # keep this up-to-date if it's been initialized
69
+ @graphql_metadata && @graphql_metadata[key] = value
70
+ end
71
+
72
+ value
73
+ end
74
+
75
+ def delete(key)
76
+ @graphql_metadata && @graphql_metadata.delete(key)
77
+ @graphql_result_data.delete(key)
78
+ end
79
+
80
+ def each
81
+ (@graphql_metadata || @graphql_result_data).each { |k, v| yield(k, v) }
82
+ end
83
+
84
+ def values
85
+ (@graphql_metadata || @graphql_result_data).values
86
+ end
87
+
88
+ def key?(k)
89
+ @graphql_result_data.key?(k)
90
+ end
91
+
92
+ def [](k)
93
+ (@graphql_metadata || @graphql_result_data)[k]
94
+ end
95
+ end
96
+
97
+ class GraphQLResultArray
98
+ include GraphQLResult
99
+
100
+ def initialize(_result_name, _parent_result)
101
+ super
102
+ @graphql_result_data = []
103
+ end
104
+
105
+ def graphql_skip_at(index)
106
+ # Mark this index as dead. It's tricky because some indices may already be storing
107
+ # `Lazy`s. So the runtime is still holding indexes _before_ skipping,
108
+ # this object has to coordinate incoming writes to account for any already-skipped indices.
109
+ @skip_indices ||= []
110
+ @skip_indices << index
111
+ offset_by = @skip_indices.count { |skipped_idx| skipped_idx < index}
112
+ delete_at_index = index - offset_by
113
+ @graphql_metadata && @graphql_metadata.delete_at(delete_at_index)
114
+ @graphql_result_data.delete_at(delete_at_index)
115
+ end
116
+
117
+ def []=(idx, value)
118
+ if @skip_indices
119
+ offset_by = @skip_indices.count { |skipped_idx| skipped_idx < idx }
120
+ idx -= offset_by
121
+ end
122
+ if value.respond_to?(:graphql_result_data)
123
+ @graphql_result_data[idx] = value.graphql_result_data
124
+ (@graphql_metadata ||= @graphql_result_data.dup)[idx] = value
125
+ else
126
+ @graphql_result_data[idx] = value
127
+ @graphql_metadata && @graphql_metadata[idx] = value
128
+ end
129
+
130
+ value
131
+ end
132
+
133
+ def values
134
+ (@graphql_metadata || @graphql_result_data)
135
+ end
136
+ end
137
+
138
+ class GraphQLSelectionSet < Hash
139
+ attr_accessor :graphql_directives
140
+ end
141
+
11
142
  # @return [GraphQL::Query]
12
143
  attr_reader :query
13
144
 
14
- # @return [Class]
145
+ # @return [Class<GraphQL::Schema>]
15
146
  attr_reader :schema
16
147
 
17
148
  # @return [GraphQL::Query::Context]
18
149
  attr_reader :context
19
150
 
20
- def initialize(query:, response:)
151
+ def initialize(query:)
21
152
  @query = query
153
+ @dataloader = query.multiplex.dataloader
22
154
  @schema = query.schema
23
155
  @context = query.context
156
+ @multiplex_context = query.multiplex.context
24
157
  @interpreter_context = @context.namespace(:interpreter)
25
- @response = response
26
- @dead_paths = {}
27
- @types_at_paths = {}
158
+ @response = GraphQLResultHash.new(nil, nil)
159
+ # Identify runtime directives by checking which of this schema's directives have overridden `def self.resolve`
160
+ @runtime_directive_names = []
161
+ noop_resolve_owner = GraphQL::Schema::Directive.singleton_class
162
+ @schema_directives = schema.directives
163
+ @schema_directives.each do |name, dir_defn|
164
+ if dir_defn.method(:resolve).owner != noop_resolve_owner
165
+ @runtime_directive_names << name
166
+ end
167
+ end
28
168
  # A cache of { Class => { String => Schema::Field } }
29
169
  # Which assumes that MyObject.get_field("myField") will return the same field
30
170
  # during the lifetime of a query
31
171
  @fields_cache = Hash.new { |h, k| h[k] = {} }
172
+ # { Class => Boolean }
173
+ @lazy_cache = {}
32
174
  end
33
175
 
34
- def final_value
35
- @response.final_value
176
+ def final_result
177
+ @response && @response.graphql_result_data
36
178
  end
37
179
 
38
180
  def inspect
39
181
  "#<#{self.class.name} response=#{@response.inspect}>"
40
182
  end
41
183
 
184
+ def tap_or_each(obj_or_array)
185
+ if obj_or_array.is_a?(Array)
186
+ obj_or_array.each do |item|
187
+ yield(item, true)
188
+ end
189
+ else
190
+ yield(obj_or_array, false)
191
+ end
192
+ end
193
+
42
194
  # This _begins_ the execution. Some deferred work
43
195
  # might be stored up in lazies.
44
196
  # @return [void]
45
197
  def run_eager
46
198
  root_operation = query.selected_operation
47
199
  root_op_type = root_operation.operation_type || "query"
48
- legacy_root_type = schema.root_type_for_operation(root_op_type)
49
- root_type = legacy_root_type.metadata[:type_class] || raise("Invariant: type must be class-based: #{legacy_root_type}")
50
- object_proxy = root_type.authorized_new(query.root_value, context)
200
+ root_type = schema.root_type_for_operation(root_op_type)
201
+ path = []
202
+ set_all_interpreter_context(query.root_value, nil, nil, path)
203
+ object_proxy = authorized_new(root_type, query.root_value, context)
51
204
  object_proxy = schema.sync_lazy(object_proxy)
205
+
52
206
  if object_proxy.nil?
53
207
  # Root .authorized? returned false.
54
- write_in_response([], nil)
55
- nil
208
+ @response = nil
56
209
  else
57
- path = []
58
- evaluate_selections(path, object_proxy, root_type, root_operation.selections, root_operation_type: root_op_type)
59
- nil
210
+ call_method_on_directives(:resolve, object_proxy, root_operation.directives) do # execute query level directives
211
+ gathered_selections = gather_selections(object_proxy, root_type, root_operation.selections)
212
+ # This is kind of a hack -- `gathered_selections` is an Array if any of the selections
213
+ # require isolation during execution (because of runtime directives). In that case,
214
+ # make a new, isolated result hash for writing the result into. (That isolated response
215
+ # is eventually merged back into the main response)
216
+ #
217
+ # Otherwise, `gathered_selections` is a hash of selections which can be
218
+ # directly evaluated and the results can be written right into the main response hash.
219
+ tap_or_each(gathered_selections) do |selections, is_selection_array|
220
+ if is_selection_array
221
+ selection_response = GraphQLResultHash.new(nil, nil)
222
+ final_response = @response
223
+ else
224
+ selection_response = @response
225
+ final_response = nil
226
+ end
227
+
228
+ @dataloader.append_job {
229
+ set_all_interpreter_context(query.root_value, nil, nil, path)
230
+ call_method_on_directives(:resolve, object_proxy, selections.graphql_directives) do
231
+ evaluate_selections(
232
+ path,
233
+ context.scoped_context,
234
+ object_proxy,
235
+ root_type,
236
+ root_op_type == "mutation",
237
+ selections,
238
+ selection_response,
239
+ final_response,
240
+ nil,
241
+ )
242
+ end
243
+ }
244
+ end
245
+ end
246
+ end
247
+ delete_interpreter_context(:current_path)
248
+ delete_interpreter_context(:current_field)
249
+ delete_interpreter_context(:current_object)
250
+ delete_interpreter_context(:current_arguments)
251
+ nil
252
+ end
253
+
254
+ # @return [void]
255
+ def deep_merge_selection_result(from_result, into_result)
256
+ from_result.each do |key, value|
257
+ if !into_result.key?(key)
258
+ into_result[key] = value
259
+ else
260
+ case value
261
+ when GraphQLResultHash
262
+ deep_merge_selection_result(value, into_result[key])
263
+ else
264
+ # We have to assume that, since this passed the `fields_will_merge` selection,
265
+ # that the old and new values are the same.
266
+ # There's no special handling of arrays because currently, there's no way to split the execution
267
+ # of a list over several concurrent flows.
268
+ into_result[key] = value
269
+ end
270
+ end
60
271
  end
272
+ from_result.graphql_merged_into = into_result
273
+ nil
61
274
  end
62
275
 
63
- def gather_selections(owner_object, owner_type, selections, selections_by_name)
276
+ def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = GraphQLSelectionSet.new)
64
277
  selections.each do |node|
65
278
  # Skip gathering this if the directive says so
66
279
  if !directives_include?(node, owner_object, owner_type)
67
280
  next
68
281
  end
69
282
 
70
- case node
71
- when GraphQL::Language::Nodes::Field
283
+ if node.is_a?(GraphQL::Language::Nodes::Field)
72
284
  response_key = node.alias || node.name
73
285
  selections = selections_by_name[response_key]
74
286
  # if there was already a selection of this field,
@@ -84,134 +296,228 @@ module GraphQL
84
296
  # No selection was found for this field yet
85
297
  selections_by_name[response_key] = node
86
298
  end
87
- when GraphQL::Language::Nodes::InlineFragment
88
- if node.type
89
- type_defn = schema.types[node.type.name]
90
- type_defn = type_defn.metadata[:type_class]
91
- # Faster than .map{}.include?()
92
- query.warden.possible_types(type_defn).each do |t|
93
- if t.metadata[:type_class] == owner_type
94
- gather_selections(owner_object, owner_type, node.selections, selections_by_name)
95
- break
96
- end
299
+ else
300
+ # This is an InlineFragment or a FragmentSpread
301
+ if @runtime_directive_names.any? && node.directives.any? { |d| @runtime_directive_names.include?(d.name) }
302
+ next_selections = GraphQLSelectionSet.new
303
+ next_selections.graphql_directives = node.directives
304
+ if selections_to_run
305
+ selections_to_run << next_selections
306
+ else
307
+ selections_to_run = []
308
+ selections_to_run << selections_by_name
309
+ selections_to_run << next_selections
97
310
  end
98
311
  else
99
- # it's an untyped fragment, definitely continue
100
- gather_selections(owner_object, owner_type, node.selections, selections_by_name)
312
+ next_selections = selections_by_name
101
313
  end
102
- when GraphQL::Language::Nodes::FragmentSpread
103
- fragment_def = query.fragments[node.name]
104
- type_defn = schema.types[fragment_def.type.name]
105
- type_defn = type_defn.metadata[:type_class]
106
- schema.possible_types(type_defn).each do |t|
107
- if t.metadata[:type_class] == owner_type
108
- gather_selections(owner_object, owner_type, fragment_def.selections, selections_by_name)
109
- break
314
+
315
+ case node
316
+ when GraphQL::Language::Nodes::InlineFragment
317
+ if node.type
318
+ type_defn = schema.get_type(node.type.name, context)
319
+
320
+ # Faster than .map{}.include?()
321
+ query.warden.possible_types(type_defn).each do |t|
322
+ if t == owner_type
323
+ gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
324
+ break
325
+ end
326
+ end
327
+ else
328
+ # it's an untyped fragment, definitely continue
329
+ gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
110
330
  end
331
+ when GraphQL::Language::Nodes::FragmentSpread
332
+ fragment_def = query.fragments[node.name]
333
+ type_defn = query.get_type(fragment_def.type.name)
334
+ possible_types = query.warden.possible_types(type_defn)
335
+ possible_types.each do |t|
336
+ if t == owner_type
337
+ gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
338
+ break
339
+ end
340
+ end
341
+ else
342
+ raise "Invariant: unexpected selection class: #{node.class}"
111
343
  end
112
- else
113
- raise "Invariant: unexpected selection class: #{node.class}"
114
344
  end
115
345
  end
346
+ selections_to_run || selections_by_name
116
347
  end
117
348
 
118
- def evaluate_selections(path, owner_object, owner_type, selections, root_operation_type: nil)
119
- selections_by_name = {}
120
- gather_selections(owner_object, owner_type, selections, selections_by_name)
121
- selections_by_name.each do |result_name, field_ast_nodes_or_ast_node|
122
- # As a performance optimization, the hash key will be a `Node` if
123
- # there's only one selection of the field. But if there are multiple
124
- # selections of the field, it will be an Array of nodes
125
- if field_ast_nodes_or_ast_node.is_a?(Array)
126
- field_ast_nodes = field_ast_nodes_or_ast_node
127
- ast_node = field_ast_nodes.first
128
- else
129
- field_ast_nodes = nil
130
- ast_node = field_ast_nodes_or_ast_node
131
- end
132
- field_name = ast_node.name
133
- field_defn = @fields_cache[owner_type][field_name] ||= owner_type.get_field(field_name)
134
- is_introspection = false
135
- if field_defn.nil?
136
- field_defn = if owner_type == schema.query.metadata[:type_class] && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
137
- is_introspection = true
138
- entry_point_field.metadata[:type_class]
139
- elsif (dynamic_field = schema.introspection_system.dynamic_field(name: field_name))
140
- is_introspection = true
141
- dynamic_field.metadata[:type_class]
142
- else
143
- raise "Invariant: no field for #{owner_type}.#{field_name}"
349
+ NO_ARGS = {}.freeze
350
+
351
+ # @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)
354
+
355
+ finished_jobs = 0
356
+ enqueued_jobs = gathered_selections.size
357
+ gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
358
+ @dataloader.append_job {
359
+ 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
361
+ )
362
+ finished_jobs += 1
363
+ if target_result && finished_jobs == enqueued_jobs
364
+ deep_merge_selection_result(selections_result, target_result)
144
365
  end
366
+ }
367
+ end
368
+
369
+ selections_result
370
+ end
371
+
372
+ attr_reader :progress_path
373
+
374
+ # @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
376
+ return if dead_result?(selections_result)
377
+ # As a performance optimization, the hash key will be a `Node` if
378
+ # there's only one selection of the field. But if there are multiple
379
+ # selections of the field, it will be an Array of nodes
380
+ if field_ast_nodes_or_ast_node.is_a?(Array)
381
+ field_ast_nodes = field_ast_nodes_or_ast_node
382
+ ast_node = field_ast_nodes.first
383
+ else
384
+ field_ast_nodes = nil
385
+ ast_node = field_ast_nodes_or_ast_node
386
+ end
387
+ field_name = ast_node.name
388
+ # This can't use `query.get_field` because it gets confused on introspection below if `field_defn` isn't `nil`,
389
+ # because of how `is_introspection` is used to call `.authorized_new` later on.
390
+ field_defn = @fields_cache[owner_type][field_name] ||= owner_type.get_field(field_name, @context)
391
+ is_introspection = false
392
+ if field_defn.nil?
393
+ field_defn = if owner_type == schema.query && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
394
+ is_introspection = true
395
+ entry_point_field
396
+ elsif (dynamic_field = schema.introspection_system.dynamic_field(name: field_name))
397
+ is_introspection = true
398
+ dynamic_field
399
+ else
400
+ raise "Invariant: no field for #{owner_type}.#{field_name}"
145
401
  end
402
+ end
403
+
404
+ return_type = field_defn.type
146
405
 
147
- return_type = resolve_if_late_bound_type(field_defn.type)
406
+ next_path = path.dup
407
+ next_path << result_name
408
+ next_path.freeze
148
409
 
149
- next_path = path.dup
150
- next_path << result_name
151
- next_path.freeze
410
+ # This seems janky, but we need to know
411
+ # the field's return type at this path in order
412
+ # to propagate `null`
413
+ if return_type.non_null?
414
+ (selections_result.graphql_non_null_field_names ||= []).push(result_name)
415
+ end
416
+ # Set this before calling `run_with_directives`, so that the directive can have the latest path
417
+ set_all_interpreter_context(nil, field_defn, nil, next_path)
152
418
 
153
- # This seems janky, but we need to know
154
- # the field's return type at this path in order
155
- # to propagate `null`
156
- set_type_at_path(next_path, return_type)
157
- # Set this before calling `run_with_directives`, so that the directive can have the latest path
158
- @interpreter_context[:current_path] = next_path
159
- @interpreter_context[:current_field] = field_defn
419
+ context.scoped_context = scoped_context
420
+ object = owner_object
160
421
 
161
- object = owner_object
422
+ if is_introspection
423
+ object = authorized_new(field_defn.owner, object, context)
424
+ end
162
425
 
163
- if is_introspection
164
- object = field_defn.owner.authorized_new(object, context)
426
+ total_args_count = field_defn.arguments(context).size
427
+ if total_args_count == 0
428
+ resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
429
+ 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, return_type)
430
+ else
431
+ # TODO remove all arguments(...) usages?
432
+ @query.arguments_cache.dataload_for(ast_node, field_defn, object) do |resolved_arguments|
433
+ 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, return_type)
165
434
  end
435
+ end
436
+ end
166
437
 
167
- begin
168
- kwarg_arguments = arguments(object, field_defn, ast_node)
169
- rescue GraphQL::ExecutionError => e
170
- continue_value(next_path, e, field_defn, return_type.non_null?, ast_node)
438
+ 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, return_type) # rubocop:disable Metrics/ParameterLists
439
+ context.scoped_context = scoped_context
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|
441
+ 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)
171
443
  next
172
444
  end
173
445
 
174
- # It might turn out that making arguments for every field is slow.
175
- # If we have to cache them, we'll need a more subtle approach here.
176
- field_defn.extras.each do |extra|
177
- case extra
178
- when :ast_node
179
- kwarg_arguments[:ast_node] = ast_node
180
- when :execution_errors
181
- kwarg_arguments[:execution_errors] = ExecutionErrors.new(context, ast_node, next_path)
182
- when :path
183
- kwarg_arguments[:path] = next_path
184
- when :lookahead
185
- if !field_ast_nodes
186
- field_ast_nodes = [ast_node]
446
+ kwarg_arguments = if resolved_arguments.empty? && field_defn.extras.empty?
447
+ # We can avoid allocating the `{ Symbol => Object }` hash in this case
448
+ NO_ARGS
449
+ else
450
+ # Bundle up the extras, then make a new arguments instance
451
+ # that includes the extras, too.
452
+ extra_args = {}
453
+ field_defn.extras.each do |extra|
454
+ case extra
455
+ when :ast_node
456
+ extra_args[:ast_node] = ast_node
457
+ when :execution_errors
458
+ extra_args[:execution_errors] = ExecutionErrors.new(context, ast_node, next_path)
459
+ when :path
460
+ extra_args[:path] = next_path
461
+ when :lookahead
462
+ if !field_ast_nodes
463
+ field_ast_nodes = [ast_node]
464
+ end
465
+
466
+ extra_args[:lookahead] = Execution::Lookahead.new(
467
+ query: query,
468
+ ast_nodes: field_ast_nodes,
469
+ field: field_defn,
470
+ )
471
+ when :argument_details
472
+ # Use this flag to tell Interpreter::Arguments to add itself
473
+ # to the keyword args hash _before_ freezing everything.
474
+ 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
+ when :parent
480
+ extra_args[:parent] = parent_object
481
+ else
482
+ extra_args[extra] = field_defn.fetch_extra(extra, context)
187
483
  end
188
- kwarg_arguments[:lookahead] = Execution::Lookahead.new(
189
- query: query,
190
- ast_nodes: field_ast_nodes,
191
- field: field_defn,
192
- )
193
- else
194
- kwarg_arguments[extra] = field_defn.fetch_extra(extra, context)
195
484
  end
485
+ if extra_args.any?
486
+ resolved_arguments = resolved_arguments.merge_extras(extra_args)
487
+ end
488
+ resolved_arguments.keyword_arguments
196
489
  end
197
490
 
491
+ set_all_interpreter_context(nil, nil, resolved_arguments, nil)
492
+
198
493
  # Optimize for the case that field is selected only once
199
494
  if field_ast_nodes.nil? || field_ast_nodes.size == 1
200
495
  next_selections = ast_node.selections
496
+ directives = ast_node.directives
201
497
  else
202
498
  next_selections = []
203
- field_ast_nodes.each { |f| next_selections.concat(f.selections) }
499
+ directives = []
500
+ field_ast_nodes.each { |f|
501
+ next_selections.concat(f.selections)
502
+ directives.concat(f.directives)
503
+ }
204
504
  end
205
505
 
206
- field_result = resolve_with_directives(object, ast_node) do
506
+ field_result = call_method_on_directives(:resolve, object, directives) do
207
507
  # Actually call the field resolver and capture the result
208
- app_result = query.trace("execute_field", {owner: owner_type, field: field_defn, path: next_path, query: query}) do
209
- field_defn.resolve(object, kwarg_arguments, context)
508
+ app_result = begin
509
+ query.with_error_handling do
510
+ query.trace("execute_field", {owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments}) do
511
+ field_defn.resolve(object, kwarg_arguments, context)
512
+ end
513
+ end
514
+ rescue GraphQL::ExecutionError => err
515
+ err
210
516
  end
211
- after_lazy(app_result, owner: owner_type, field: field_defn, path: next_path) do |inner_result|
212
- continue_value = continue_value(next_path, inner_result, field_defn, return_type.non_null?, ast_node)
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)
213
519
  if HALT != continue_value
214
- continue_field(next_path, continue_value, field_defn, return_type, ast_node, next_selections, false)
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)
215
521
  end
216
522
  end
217
523
  end
@@ -219,47 +525,144 @@ module GraphQL
219
525
  # If this field is a root mutation field, immediately resolve
220
526
  # all of its child fields before moving on to the next root mutation field.
221
527
  # (Subselections of this mutation will still be resolved level-by-level.)
222
- if root_operation_type == "mutation"
223
- Interpreter::Resolve.resolve_all([field_result])
528
+ if is_eager_field
529
+ Interpreter::Resolve.resolve_all([field_result], @dataloader)
224
530
  else
531
+ # Return this from `after_lazy` because it might be another lazy that needs to be resolved
225
532
  field_result
226
533
  end
227
534
  end
228
535
  end
229
536
 
537
+ def dead_result?(selection_result)
538
+ selection_result.graphql_dead || ((parent = selection_result.graphql_parent) && parent.graphql_dead)
539
+ end
540
+
541
+ def set_result(selection_result, result_name, value)
542
+ if !dead_result?(selection_result)
543
+ if value.nil? &&
544
+ ( # there are two conditions under which `nil` is not allowed in the response:
545
+ (selection_result.graphql_non_null_list_items) || # this value would be written into a list that doesn't allow nils
546
+ ((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
547
+ )
548
+ # This is an invalid nil that should be propagated
549
+ # One caller of this method passes a block,
550
+ # namely when application code returns a `nil` to GraphQL and it doesn't belong there.
551
+ # The other possibility for reaching here is when a field returns an ExecutionError, so we write
552
+ # `nil` to the response, not knowing whether it's an invalid `nil` or not.
553
+ # (And in that case, we don't have to call the schema's handler, since it's not a bug in the application.)
554
+ # TODO the code is trying to tell me something.
555
+ yield if block_given?
556
+ parent = selection_result.graphql_parent
557
+ name_in_parent = selection_result.graphql_result_name
558
+ if parent.nil? # This is a top-level result hash
559
+ @response = nil
560
+ else
561
+ set_result(parent, name_in_parent, nil)
562
+ set_graphql_dead(selection_result)
563
+ end
564
+ else
565
+ selection_result[result_name] = value
566
+ end
567
+ end
568
+ end
569
+
570
+ # Mark this node and any already-registered children as dead,
571
+ # so that it accepts no more writes.
572
+ def set_graphql_dead(selection_result)
573
+ case selection_result
574
+ when GraphQLResultArray
575
+ selection_result.graphql_dead = true
576
+ selection_result.values.each { |v| set_graphql_dead(v) }
577
+ when GraphQLResultHash
578
+ selection_result.graphql_dead = true
579
+ selection_result.each { |k, v| set_graphql_dead(v) }
580
+ else
581
+ # It's a scalar, no way to mark it dead.
582
+ end
583
+ end
584
+
230
585
  HALT = Object.new
231
- def continue_value(path, value, field, is_non_null, ast_node)
232
- if value.nil?
586
+ def continue_value(path, value, parent_type, field, is_non_null, ast_node, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
587
+ case value
588
+ when nil
233
589
  if is_non_null
234
- err = GraphQL::InvalidNullError.new(field.owner, field, value)
235
- write_invalid_null_in_response(path, err)
590
+ set_result(selection_result, result_name, nil) do
591
+ # This block is called if `result_name` is not dead. (Maybe a previous invalid nil caused it be marked dead.)
592
+ err = parent_type::InvalidNullError.new(parent_type, field, value)
593
+ schema.type_error(err, context)
594
+ end
236
595
  else
237
- write_in_response(path, nil)
596
+ set_result(selection_result, result_name, nil)
238
597
  end
239
598
  HALT
240
- elsif value.is_a?(GraphQL::ExecutionError)
241
- value.path ||= path
242
- value.ast_node ||= ast_node
243
- write_execution_errors_in_response(path, [value])
244
- HALT
245
- elsif value.is_a?(Array) && value.any? && value.all? { |v| v.is_a?(GraphQL::ExecutionError) }
246
- value.each do |v|
247
- v.path ||= path
248
- v.ast_node ||= ast_node
599
+ when GraphQL::Error
600
+ # Handle these cases inside a single `when`
601
+ # to avoid the overhead of checking three different classes
602
+ # every time.
603
+ if value.is_a?(GraphQL::ExecutionError)
604
+ if selection_result.nil? || !dead_result?(selection_result)
605
+ value.path ||= path
606
+ value.ast_node ||= ast_node
607
+ context.errors << value
608
+ if selection_result
609
+ set_result(selection_result, result_name, nil)
610
+ end
611
+ end
612
+ HALT
613
+ elsif value.is_a?(GraphQL::UnauthorizedError)
614
+ # this hook might raise & crash, or it might return
615
+ # a replacement value
616
+ next_value = begin
617
+ schema.unauthorized_object(value)
618
+ rescue GraphQL::ExecutionError => err
619
+ err
620
+ 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
623
+ # It's possible a lazy was already written here
624
+ case selection_result
625
+ when GraphQLResultHash
626
+ selection_result.delete(result_name)
627
+ when GraphQLResultArray
628
+ selection_result.graphql_skip_at(result_name)
629
+ when nil
630
+ # this can happen with directives
631
+ else
632
+ raise "Invariant: unexpected result class #{selection_result.class} (#{selection_result.inspect})"
633
+ end
634
+ HALT
635
+ else
636
+ # What could this actually _be_? Anyhow,
637
+ # preserve the default behavior of doing nothing with it.
638
+ value
249
639
  end
250
- write_execution_errors_in_response(path, value)
251
- HALT
252
- elsif value.is_a?(GraphQL::UnauthorizedError)
253
- # this hook might raise & crash, or it might return
254
- # a replacement value
255
- next_value = begin
256
- schema.unauthorized_object(value)
257
- rescue GraphQL::ExecutionError => err
258
- err
640
+ when Array
641
+ # It's an array full of execution errors; add them all.
642
+ if value.any? && value.all? { |v| v.is_a?(GraphQL::ExecutionError) }
643
+ list_type_at_all = (field && (field.type.list?))
644
+ if selection_result.nil? || !dead_result?(selection_result)
645
+ value.each_with_index do |error, index|
646
+ error.ast_node ||= ast_node
647
+ error.path ||= path + (list_type_at_all ? [index] : [])
648
+ context.errors << error
649
+ end
650
+ if selection_result
651
+ if list_type_at_all
652
+ result_without_errors = value.map { |v| v.is_a?(GraphQL::ExecutionError) ? nil : v }
653
+ set_result(selection_result, result_name, result_without_errors)
654
+ else
655
+ set_result(selection_result, result_name, nil)
656
+ end
657
+ end
658
+ end
659
+ HALT
660
+ else
661
+ value
259
662
  end
260
-
261
- continue_value(path, next_value, field, is_non_null, ast_node)
262
- elsif GraphQL::Execution::Execute::SKIP == value
663
+ when GraphQL::Execution::Interpreter::RawValue
664
+ # Write raw value directly to the response without resolving nested objects
665
+ set_result(selection_result, result_name, value.resolve)
263
666
  HALT
264
667
  else
265
668
  value
@@ -274,92 +677,169 @@ module GraphQL
274
677
  # Location information from `path` and `ast_node`.
275
678
  #
276
679
  # @return [Lazy, Array, Hash, Object] Lazy, Array, and Hash are all traversed to resolve lazy values later
277
- def continue_field(path, value, field, type, ast_node, next_selections, is_non_null)
278
- case type.kind.name
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
681
+ if current_type.non_null?
682
+ current_type = current_type.of_type
683
+ is_non_null = true
684
+ end
685
+
686
+ case current_type.kind.name
279
687
  when "SCALAR", "ENUM"
280
- r = type.coerce_result(value, context)
281
- write_in_response(path, r)
688
+ r = current_type.coerce_result(value, context)
689
+ set_result(selection_result, result_name, r)
282
690
  r
283
691
  when "UNION", "INTERFACE"
284
- resolved_type_or_lazy = query.resolve_type(type, value)
285
- after_lazy(resolved_type_or_lazy, owner: type, path: path, field: field) do |resolved_type|
286
- possible_types = query.possible_types(type)
692
+ resolved_type_or_lazy, resolved_value = resolve_type(current_type, value, path)
693
+ resolved_value ||= value
694
+
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
+ possible_types = query.possible_types(current_type)
287
697
 
288
698
  if !possible_types.include?(resolved_type)
289
- parent_type = field.owner
290
- type_error = GraphQL::UnresolvedTypeError.new(value, field, parent_type, resolved_type, possible_types)
699
+ parent_type = field.owner_type
700
+ err_class = current_type::UnresolvedTypeError
701
+ type_error = err_class.new(resolved_value, field, parent_type, resolved_type, possible_types)
291
702
  schema.type_error(type_error, context)
292
- write_in_response(path, nil)
703
+ set_result(selection_result, result_name, nil)
293
704
  nil
294
705
  else
295
- resolved_type = resolved_type.metadata[:type_class]
296
- continue_field(path, value, field, resolved_type, ast_node, next_selections, is_non_null)
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)
297
707
  end
298
708
  end
299
709
  when "OBJECT"
300
710
  object_proxy = begin
301
- type.authorized_new(value, context)
711
+ authorized_new(current_type, value, context)
302
712
  rescue GraphQL::ExecutionError => err
303
713
  err
304
714
  end
305
- after_lazy(object_proxy, owner: type, path: path, field: field) do |inner_object|
306
- continue_value = continue_value(path, inner_object, field, is_non_null, ast_node)
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)
307
717
  if HALT != continue_value
308
- response_hash = {}
309
- write_in_response(path, response_hash)
310
- evaluate_selections(path, continue_value, type, next_selections)
311
- response_hash
718
+ response_hash = GraphQLResultHash.new(result_name, selection_result)
719
+ set_result(selection_result, result_name, response_hash)
720
+ gathered_selections = gather_selections(continue_value, current_type, next_selections)
721
+ # There are two possibilities for `gathered_selections`:
722
+ # 1. All selections of this object should be evaluated together (there are no runtime directives modifying execution).
723
+ # This case is handled below, and the result can be written right into the main `response_hash` above.
724
+ # In this case, `gathered_selections` is a hash of selections.
725
+ # 2. Some selections of this object have runtime directives that may or may not modify execution.
726
+ # That part of the selection is evaluated in an isolated way, writing into a sub-response object which is
727
+ # eventually merged into the final response. In this case, `gathered_selections` is an array of things to run in isolation.
728
+ # (Technically, it's possible that one of those entries _doesn't_ require isolation.)
729
+ tap_or_each(gathered_selections) do |selections, is_selection_array|
730
+ if is_selection_array
731
+ this_result = GraphQLResultHash.new(result_name, selection_result)
732
+ final_result = response_hash
733
+ else
734
+ this_result = response_hash
735
+ final_result = nil
736
+ end
737
+ set_all_interpreter_context(continue_value, nil, nil, path) # reset this mutable state
738
+ call_method_on_directives(:resolve, continue_value, selections.graphql_directives) do
739
+ evaluate_selections(
740
+ path,
741
+ context.scoped_context,
742
+ continue_value,
743
+ current_type,
744
+ false,
745
+ selections,
746
+ this_result,
747
+ final_result,
748
+ owner_object.object,
749
+ )
750
+ this_result
751
+ end
752
+ end
312
753
  end
313
754
  end
314
755
  when "LIST"
315
- response_list = []
316
- write_in_response(path, response_list)
317
- inner_type = type.of_type
756
+ inner_type = current_type.of_type
757
+ # This is true for objects, unions, and interfaces
758
+ use_dataloader_job = !inner_type.unwrap.kind.input?
759
+ response_list = GraphQLResultArray.new(result_name, selection_result)
760
+ response_list.graphql_non_null_list_items = inner_type.non_null?
761
+ set_result(selection_result, result_name, response_list)
762
+
318
763
  idx = 0
319
- value.each do |inner_value|
320
- next_path = path.dup
321
- next_path << idx
322
- next_path.freeze
323
- idx += 1
324
- set_type_at_path(next_path, inner_type)
325
- # This will update `response_list` with the lazy
326
- after_lazy(inner_value, owner: inner_type, path: next_path, field: field) do |inner_inner_value|
327
- # reset `is_non_null` here and below, because the inner type will have its own nullability constraint
328
- continue_value = continue_value(next_path, inner_inner_value, field, false, ast_node)
329
- if HALT != continue_value
330
- continue_field(next_path, continue_value, field, inner_type, ast_node, next_selections, false)
764
+ scoped_context = context.scoped_context
765
+ begin
766
+ value.each do |inner_value|
767
+ break if dead_result?(response_list)
768
+ next_path = path.dup
769
+ next_path << idx
770
+ this_idx = idx
771
+ next_path.freeze
772
+ idx += 1
773
+ if use_dataloader_job
774
+ @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)
776
+ end
777
+ 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)
331
779
  end
332
780
  end
781
+ rescue NoMethodError => err
782
+ # Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.)
783
+ if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
784
+ # 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)
786
+ else
787
+ # This was some other NoMethodError -- let it bubble to reveal the real error.
788
+ raise
789
+ end
333
790
  end
791
+
334
792
  response_list
335
- when "NON_NULL"
336
- inner_type = type.of_type
337
- # For fields like `__schema: __Schema!`
338
- inner_type = resolve_if_late_bound_type(inner_type)
339
- # Don't `set_type_at_path` because we want the static type,
340
- # we're going to use that to determine whether a `nil` should be propagated or not.
341
- continue_field(path, value, field, inner_type, ast_node, next_selections, true)
342
793
  else
343
- raise "Invariant: Unhandled type kind #{type.kind} (#{type})"
794
+ raise "Invariant: Unhandled type kind #{current_type.kind} (#{current_type})"
795
+ end
796
+ end
797
+
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)
800
+ call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
801
+ # 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)
804
+ 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)
806
+ end
807
+ end
344
808
  end
345
809
  end
346
810
 
347
- def resolve_with_directives(object, ast_node)
348
- run_directive(object, ast_node, 0) { yield }
811
+ def call_method_on_directives(method_name, object, directives, &block)
812
+ return yield if directives.nil? || directives.empty?
813
+ run_directive(method_name, object, directives, 0, &block)
349
814
  end
350
815
 
351
- def run_directive(object, ast_node, idx)
352
- dir_node = ast_node.directives[idx]
816
+ def run_directive(method_name, object, directives, idx, &block)
817
+ dir_node = directives[idx]
353
818
  if !dir_node
354
819
  yield
355
820
  else
356
- dir_defn = schema.directives.fetch(dir_node.name)
821
+ dir_defn = @schema_directives.fetch(dir_node.name)
357
822
  if !dir_defn.is_a?(Class)
358
- dir_defn = dir_defn.metadata[:type_class] || raise("Only class-based directives are supported (not `@#{dir_node.name}`)")
359
- end
360
- dir_args = arguments(nil, dir_defn, dir_node)
361
- dir_defn.resolve(object, dir_args, context) do
362
- run_directive(object, ast_node, idx + 1) { yield }
823
+ dir_defn = dir_defn.type_class || raise("Only class-based directives are supported (not `@#{dir_node.name}`)")
824
+ end
825
+ raw_dir_args = arguments(nil, dir_defn, dir_node)
826
+ dir_args = continue_value(
827
+ @context[:current_path], # path
828
+ raw_dir_args, # value
829
+ dir_defn, # parent_type
830
+ nil, # field
831
+ false, # is_non_null
832
+ dir_node, # ast_node
833
+ nil, # result_name
834
+ nil, # selection_result
835
+ )
836
+
837
+ if dir_args == HALT
838
+ nil
839
+ else
840
+ dir_defn.public_send(method_name, object, dir_args, context) do
841
+ run_directive(method_name, object, directives, idx + 1, &block)
842
+ end
363
843
  end
364
844
  end
365
845
  end
@@ -367,7 +847,7 @@ module GraphQL
367
847
  # Check {Schema::Directive.include?} for each directive that's present
368
848
  def directives_include?(node, graphql_object, parent_type)
369
849
  node.directives.each do |dir_node|
370
- dir_defn = schema.directives.fetch(dir_node.name).metadata[:type_class] || raise("Only class-based directives are supported (not #{dir_node.name.inspect})")
850
+ dir_defn = @schema_directives.fetch(dir_node.name).type_class || raise("Only class-based directives are supported (not #{dir_node.name.inspect})")
371
851
  args = arguments(graphql_object, dir_defn, dir_node)
372
852
  if !dir_defn.include?(graphql_object, args, context)
373
853
  return false
@@ -376,11 +856,18 @@ module GraphQL
376
856
  true
377
857
  end
378
858
 
379
- def resolve_if_late_bound_type(type)
380
- if type.is_a?(GraphQL::Schema::LateBoundType)
381
- query.warden.get_type(type.name).metadata[:type_class]
382
- else
383
- type
859
+ def set_all_interpreter_context(object, field, arguments, path)
860
+ if object
861
+ @context[:current_object] = @interpreter_context[:current_object] = object
862
+ end
863
+ if field
864
+ @context[:current_field] = @interpreter_context[:current_field] = field
865
+ end
866
+ if arguments
867
+ @context[:current_arguments] = @interpreter_context[:current_arguments] = arguments
868
+ end
869
+ if path
870
+ @context[:current_path] = @interpreter_context[:current_path] = path
384
871
  end
385
872
  end
386
873
 
@@ -388,219 +875,93 @@ module GraphQL
388
875
  # @param path [Array<String>]
389
876
  # @param field [GraphQL::Schema::Field]
390
877
  # @param eager [Boolean] Set to `true` for mutation root fields only
878
+ # @param trace [Boolean] If `false`, don't wrap this with field tracing
391
879
  # @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
392
- def after_lazy(obj, owner:, field:, path:, eager: false)
393
- @interpreter_context[:current_path] = path
394
- @interpreter_context[:current_field] = field
395
- if schema.lazy?(obj)
880
+ def after_lazy(lazy_obj, owner:, field:, path:, scoped_context:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, trace: true, &block)
881
+ if lazy?(lazy_obj)
396
882
  lazy = GraphQL::Execution::Lazy.new(path: path, field: field) do
397
- @interpreter_context[:current_path] = path
398
- @interpreter_context[:current_field] = field
883
+ set_all_interpreter_context(owner_object, field, arguments, path)
884
+ context.scoped_context = scoped_context
399
885
  # Wrap the execution of _this_ method with tracing,
400
886
  # but don't wrap the continuation below
401
- inner_obj = query.trace("execute_field_lazy", {owner: owner, field: field, path: path, query: query}) do
402
- begin
403
- schema.sync_lazy(obj)
404
- rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => err
405
- yield(err)
887
+ 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
899
+ end
406
900
  end
901
+ rescue GraphQL::ExecutionError => ex_err
902
+ ex_err
407
903
  end
408
- after_lazy(inner_obj, owner: owner, field: field, path: path, eager: eager) do |really_inner_obj|
409
- yield(really_inner_obj)
410
- end
904
+ yield(inner_obj)
411
905
  end
412
906
 
413
907
  if eager
414
908
  lazy.value
415
909
  else
416
- write_in_response(path, lazy)
910
+ set_result(result, result_name, lazy)
417
911
  lazy
418
912
  end
419
913
  else
420
- yield(obj)
914
+ set_all_interpreter_context(owner_object, field, arguments, path)
915
+ yield(lazy_obj)
421
916
  end
422
917
  end
423
918
 
424
- def each_argument_pair(ast_args_or_hash)
425
- case ast_args_or_hash
426
- when GraphQL::Language::Nodes::Field, GraphQL::Language::Nodes::InputObject, GraphQL::Language::Nodes::Directive
427
- ast_args_or_hash.arguments.each do |arg|
428
- yield(arg.name, arg.value)
429
- end
430
- when Hash
431
- ast_args_or_hash.each do |key, value|
432
- normalized_name = GraphQL::Schema::Member::BuildType.camelize(key.to_s)
433
- yield(normalized_name, value)
434
- end
919
+ def arguments(graphql_object, arg_owner, ast_node)
920
+ if arg_owner.arguments_statically_coercible?
921
+ query.arguments_for(ast_node, arg_owner)
435
922
  else
436
- raise "Invariant, unexpected #{ast_args_or_hash.inspect}"
923
+ # The arguments must be prepared in the context of the given object
924
+ query.arguments_for(ast_node, arg_owner, parent_object: graphql_object)
437
925
  end
438
926
  end
439
927
 
440
- def arguments(graphql_object, arg_owner, ast_node_or_hash)
441
- kwarg_arguments = {}
442
- arg_defns = arg_owner.arguments
443
- each_argument_pair(ast_node_or_hash) do |arg_name, arg_value|
444
- arg_defn = arg_defns[arg_name]
445
- # Need to distinguish between client-provided `nil`
446
- # and nothing-at-all
447
- is_present, value = arg_to_value(graphql_object, arg_defn.type, arg_value)
448
- if is_present
449
- # This doesn't apply to directives, which are legacy
450
- # Can remove this when Skip and Include use classes or something.
451
- if graphql_object
452
- value = arg_defn.prepare_value(graphql_object, value)
453
- end
454
- kwarg_arguments[arg_defn.keyword] = value
455
- end
456
- end
457
- arg_defns.each do |name, arg_defn|
458
- if arg_defn.default_value? && !kwarg_arguments.key?(arg_defn.keyword)
459
- _is_present, value = arg_to_value(graphql_object, arg_defn.type, arg_defn.default_value)
460
- kwarg_arguments[arg_defn.keyword] = value
461
- end
462
- end
463
- kwarg_arguments
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
464
933
  end
465
934
 
466
- # Get a Ruby-ready value from a client query.
467
- # @param graphql_object [Object] The owner of the field whose argument this is
468
- # @param arg_type [Class, GraphQL::Schema::NonNull, GraphQL::Schema::List]
469
- # @param ast_value [GraphQL::Language::Nodes::VariableIdentifier, String, Integer, Float, Boolean]
470
- # @return [Array(is_present, value)]
471
- def arg_to_value(graphql_object, arg_type, ast_value)
472
- if ast_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
473
- # If it's not here, it will get added later
474
- if query.variables.key?(ast_value.name)
475
- return true, query.variables[ast_value.name]
476
- else
477
- return false, nil
478
- end
479
- elsif ast_value.is_a?(GraphQL::Language::Nodes::NullValue)
480
- return true, nil
481
- elsif arg_type.is_a?(GraphQL::Schema::NonNull)
482
- arg_to_value(graphql_object, arg_type.of_type, ast_value)
483
- elsif arg_type.is_a?(GraphQL::Schema::List)
484
- # Treat a single value like a list
485
- arg_value = Array(ast_value)
486
- list = []
487
- arg_value.map do |inner_v|
488
- _present, value = arg_to_value(graphql_object, arg_type.of_type, inner_v)
489
- list << value
490
- end
491
- return true, list
492
- elsif arg_type.is_a?(Class) && arg_type < GraphQL::Schema::InputObject
493
- # For these, `prepare` is applied during `#initialize`.
494
- # Pass `nil` so it will be skipped in `#arguments`.
495
- # What a mess.
496
- args = arguments(nil, arg_type, ast_value)
497
- # We're not tracking defaults_used, but for our purposes
498
- # we compare the value to the default value.
499
- return true, arg_type.new(ruby_kwargs: args, context: context, defaults_used: nil)
500
- else
501
- flat_value = flatten_ast_value(ast_value)
502
- return true, arg_type.coerce_input(flat_value, context)
503
- end
935
+ def delete_interpreter_context(key)
936
+ @interpreter_context.delete(key)
937
+ @context.delete(key)
504
938
  end
505
939
 
506
- def flatten_ast_value(v)
507
- case v
508
- when GraphQL::Language::Nodes::Enum
509
- v.name
510
- when GraphQL::Language::Nodes::InputObject
511
- h = {}
512
- v.arguments.each do |arg|
513
- h[arg.name] = flatten_ast_value(arg.value)
514
- end
515
- h
516
- when Array
517
- v.map { |v2| flatten_ast_value(v2) }
518
- when GraphQL::Language::Nodes::VariableIdentifier
519
- flatten_ast_value(query.variables[v.name])
520
- else
521
- v
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
943
+ query.resolve_type(type, value)
522
944
  end
523
- end
524
-
525
- def write_invalid_null_in_response(path, invalid_null_error)
526
- if !dead_path?(path)
527
- schema.type_error(invalid_null_error, context)
528
- write_in_response(path, nil)
529
- add_dead_path(path)
530
- end
531
- end
532
945
 
533
- def write_execution_errors_in_response(path, errors)
534
- if !dead_path?(path)
535
- errors.each do |v|
536
- context.errors << v
946
+ if lazy?(resolved_type)
947
+ GraphQL::Execution::Lazy.new do
948
+ query.trace("resolve_type_lazy", trace_payload) do
949
+ schema.sync_lazy(resolved_type)
950
+ end
537
951
  end
538
- write_in_response(path, nil)
539
- add_dead_path(path)
540
- end
541
- end
542
-
543
- def write_in_response(path, value)
544
- if dead_path?(path)
545
- return
546
952
  else
547
- if value.nil? && path.any? && type_at(path).non_null?
548
- # This nil is invalid, try writing it at the previous spot
549
- propagate_path = path[0..-2]
550
- write_in_response(propagate_path, value)
551
- add_dead_path(propagate_path)
552
- else
553
- @response.write(path, value)
554
- end
953
+ [resolved_type, resolved_value]
555
954
  end
556
955
  end
557
956
 
558
- # To propagate nulls, we have to know what the field type was
559
- # at previous parts of the response.
560
- # This hash matches the response
561
- def type_at(path)
562
- t = @types_at_paths
563
- path.each do |part|
564
- t = t[part] || (raise("Invariant: #{part.inspect} not found in #{t}"))
565
- end
566
- t = t[:__type]
567
- t
568
- end
569
-
570
- def set_type_at_path(path, type)
571
- types = @types_at_paths
572
- path.each do |part|
573
- types = types[part] ||= {}
574
- end
575
- # Use this magic key so that the hash contains:
576
- # - string keys for nested fields
577
- # - :__type for the object type of a selection
578
- types[:__type] ||= type
579
- nil
580
- end
581
-
582
- # Mark `path` as having been permanently nulled out.
583
- # No values will be added beyond that path.
584
- def add_dead_path(path)
585
- dead = @dead_paths
586
- path.each do |part|
587
- dead = dead[part] ||= {}
588
- end
589
- dead[:__dead] = true
957
+ def authorized_new(type, value, context)
958
+ type.authorized_new(value, context)
590
959
  end
591
960
 
592
- def dead_path?(path)
593
- res = @dead_paths
594
- path.each do |part|
595
- if res
596
- if res[:__dead]
597
- break
598
- else
599
- res = res[part]
600
- end
601
- end
602
- end
603
- res && res[:__dead]
961
+ def lazy?(object)
962
+ @lazy_cache.fetch(object.class) {
963
+ @lazy_cache[object.class] = @schema.lazy?(object)
964
+ }
604
965
  end
605
966
  end
606
967
  end