graphql 1.12.12 → 2.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (428) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +3 -8
  3. data/lib/generators/graphql/enum_generator.rb +4 -10
  4. data/lib/generators/graphql/field_extractor.rb +31 -0
  5. data/lib/generators/graphql/input_generator.rb +50 -0
  6. data/lib/generators/graphql/install/mutation_root_generator.rb +34 -0
  7. data/lib/generators/graphql/{templates → install/templates}/base_mutation.erb +2 -0
  8. data/lib/generators/graphql/{templates → install/templates}/mutation_type.erb +2 -0
  9. data/lib/generators/graphql/install_generator.rb +60 -4
  10. data/lib/generators/graphql/interface_generator.rb +7 -7
  11. data/lib/generators/graphql/mutation_create_generator.rb +22 -0
  12. data/lib/generators/graphql/mutation_delete_generator.rb +22 -0
  13. data/lib/generators/graphql/mutation_generator.rb +5 -30
  14. data/lib/generators/graphql/mutation_update_generator.rb +22 -0
  15. data/lib/generators/graphql/object_generator.rb +10 -38
  16. data/lib/generators/graphql/orm_mutations_base.rb +40 -0
  17. data/lib/generators/graphql/relay.rb +23 -12
  18. data/lib/generators/graphql/scalar_generator.rb +4 -2
  19. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  20. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  21. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  22. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  23. data/lib/generators/graphql/templates/base_field.erb +2 -0
  24. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  25. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  26. data/lib/generators/graphql/templates/base_object.erb +2 -0
  27. data/lib/generators/graphql/templates/base_resolver.erb +8 -0
  28. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  29. data/lib/generators/graphql/templates/base_union.erb +2 -0
  30. data/lib/generators/graphql/templates/enum.erb +5 -1
  31. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  32. data/lib/generators/graphql/templates/input.erb +9 -0
  33. data/lib/generators/graphql/templates/interface.erb +4 -2
  34. data/lib/generators/graphql/templates/loader.erb +2 -0
  35. data/lib/generators/graphql/templates/mutation.erb +3 -1
  36. data/lib/generators/graphql/templates/mutation_create.erb +20 -0
  37. data/lib/generators/graphql/templates/mutation_delete.erb +20 -0
  38. data/lib/generators/graphql/templates/mutation_update.erb +21 -0
  39. data/lib/generators/graphql/templates/node_type.erb +2 -0
  40. data/lib/generators/graphql/templates/object.erb +4 -2
  41. data/lib/generators/graphql/templates/query_type.erb +2 -0
  42. data/lib/generators/graphql/templates/scalar.erb +3 -1
  43. data/lib/generators/graphql/templates/schema.erb +22 -2
  44. data/lib/generators/graphql/templates/union.erb +4 -2
  45. data/lib/generators/graphql/type_generator.rb +46 -10
  46. data/lib/generators/graphql/union_generator.rb +5 -5
  47. data/lib/graphql/analysis/analyzer.rb +89 -0
  48. data/lib/graphql/analysis/field_usage.rb +65 -28
  49. data/lib/graphql/analysis/max_query_complexity.rb +11 -17
  50. data/lib/graphql/analysis/max_query_depth.rb +13 -19
  51. data/lib/graphql/analysis/query_complexity.rb +156 -61
  52. data/lib/graphql/analysis/query_depth.rb +38 -23
  53. data/lib/graphql/analysis/visitor.rb +283 -0
  54. data/lib/graphql/analysis.rb +90 -6
  55. data/lib/graphql/autoload.rb +38 -0
  56. data/lib/graphql/backtrace/inspect_result.rb +0 -12
  57. data/lib/graphql/backtrace/table.rb +4 -22
  58. data/lib/graphql/backtrace/trace.rb +93 -0
  59. data/lib/graphql/backtrace/tracer.rb +8 -6
  60. data/lib/graphql/backtrace.rb +3 -8
  61. data/lib/graphql/coercion_error.rb +1 -9
  62. data/lib/graphql/current.rb +52 -0
  63. data/lib/graphql/dataloader/async_dataloader.rb +89 -0
  64. data/lib/graphql/dataloader/null_dataloader.rb +4 -2
  65. data/lib/graphql/dataloader/request.rb +5 -0
  66. data/lib/graphql/dataloader/source.rb +125 -33
  67. data/lib/graphql/dataloader.rb +193 -143
  68. data/lib/graphql/date_encoding_error.rb +16 -0
  69. data/lib/graphql/dig.rb +1 -1
  70. data/lib/graphql/duration_encoding_error.rb +16 -0
  71. data/lib/graphql/execution/errors.rb +12 -81
  72. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  73. data/lib/graphql/execution/interpreter/arguments.rb +2 -2
  74. data/lib/graphql/execution/interpreter/arguments_cache.rb +33 -36
  75. data/lib/graphql/execution/interpreter/resolve.rb +38 -4
  76. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +175 -0
  77. data/lib/graphql/execution/interpreter/runtime.rb +447 -403
  78. data/lib/graphql/execution/interpreter.rb +126 -80
  79. data/lib/graphql/execution/lazy.rb +11 -21
  80. data/lib/graphql/execution/lookahead.rb +133 -55
  81. data/lib/graphql/execution/multiplex.rb +4 -172
  82. data/lib/graphql/execution.rb +11 -4
  83. data/lib/graphql/integer_encoding_error.rb +18 -2
  84. data/lib/graphql/introspection/directive_location_enum.rb +2 -2
  85. data/lib/graphql/introspection/directive_type.rb +6 -4
  86. data/lib/graphql/introspection/dynamic_fields.rb +3 -8
  87. data/lib/graphql/introspection/entry_points.rb +11 -18
  88. data/lib/graphql/introspection/enum_value_type.rb +2 -2
  89. data/lib/graphql/introspection/field_type.rb +4 -4
  90. data/lib/graphql/introspection/input_value_type.rb +10 -4
  91. data/lib/graphql/introspection/schema_type.rb +17 -15
  92. data/lib/graphql/introspection/type_type.rb +29 -16
  93. data/lib/graphql/introspection.rb +6 -2
  94. data/lib/graphql/invalid_null_error.rb +1 -1
  95. data/lib/graphql/language/block_string.rb +37 -25
  96. data/lib/graphql/language/cache.rb +13 -0
  97. data/lib/graphql/language/comment.rb +18 -0
  98. data/lib/graphql/language/definition_slice.rb +1 -1
  99. data/lib/graphql/language/document_from_schema_definition.rb +122 -81
  100. data/lib/graphql/language/lexer.rb +364 -1467
  101. data/lib/graphql/language/nodes.rb +197 -106
  102. data/lib/graphql/language/parser.rb +799 -1920
  103. data/lib/graphql/language/printer.rb +372 -160
  104. data/lib/graphql/language/sanitized_printer.rb +25 -27
  105. data/lib/graphql/language/static_visitor.rb +167 -0
  106. data/lib/graphql/language/visitor.rb +188 -141
  107. data/lib/graphql/language.rb +62 -1
  108. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  109. data/lib/graphql/name_validator.rb +0 -4
  110. data/lib/graphql/pagination/active_record_relation_connection.rb +37 -8
  111. data/lib/graphql/pagination/array_connection.rb +8 -6
  112. data/lib/graphql/pagination/connection.rb +61 -7
  113. data/lib/graphql/pagination/connections.rb +22 -23
  114. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  115. data/lib/graphql/pagination/relation_connection.rb +60 -28
  116. data/lib/graphql/query/context/scoped_context.rb +101 -0
  117. data/lib/graphql/query/context.rb +146 -222
  118. data/lib/graphql/query/input_validation_result.rb +10 -1
  119. data/lib/graphql/query/null_context.rb +15 -32
  120. data/lib/graphql/query/validation_pipeline.rb +15 -39
  121. data/lib/graphql/query/variable_validation_error.rb +3 -3
  122. data/lib/graphql/query/variables.rb +35 -17
  123. data/lib/graphql/query.rb +149 -82
  124. data/lib/graphql/railtie.rb +15 -109
  125. data/lib/graphql/rake_task/validate.rb +1 -1
  126. data/lib/graphql/rake_task.rb +30 -11
  127. data/lib/graphql/relay/range_add.rb +9 -16
  128. data/lib/graphql/relay.rb +0 -15
  129. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  130. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  131. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  132. data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
  133. data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
  134. data/lib/graphql/rubocop.rb +6 -0
  135. data/lib/graphql/schema/addition.rb +98 -54
  136. data/lib/graphql/schema/always_visible.rb +14 -0
  137. data/lib/graphql/schema/argument.rb +179 -82
  138. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  139. data/lib/graphql/schema/build_from_definition.rb +77 -39
  140. data/lib/graphql/schema/directive/feature.rb +1 -1
  141. data/lib/graphql/schema/directive/flagged.rb +4 -4
  142. data/lib/graphql/schema/directive/include.rb +1 -1
  143. data/lib/graphql/schema/directive/one_of.rb +24 -0
  144. data/lib/graphql/schema/directive/skip.rb +1 -1
  145. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  146. data/lib/graphql/schema/directive/transform.rb +2 -2
  147. data/lib/graphql/schema/directive.rb +36 -22
  148. data/lib/graphql/schema/enum.rb +158 -63
  149. data/lib/graphql/schema/enum_value.rb +12 -21
  150. data/lib/graphql/schema/field/connection_extension.rb +7 -17
  151. data/lib/graphql/schema/field/scope_extension.rb +8 -1
  152. data/lib/graphql/schema/field.rb +521 -359
  153. data/lib/graphql/schema/field_extension.rb +86 -2
  154. data/lib/graphql/schema/find_inherited_value.rb +3 -7
  155. data/lib/graphql/schema/finder.rb +5 -5
  156. data/lib/graphql/schema/has_single_input_argument.rb +160 -0
  157. data/lib/graphql/schema/input_object.rb +148 -99
  158. data/lib/graphql/schema/interface.rb +41 -64
  159. data/lib/graphql/schema/introspection_system.rb +12 -26
  160. data/lib/graphql/schema/late_bound_type.rb +12 -2
  161. data/lib/graphql/schema/list.rb +18 -7
  162. data/lib/graphql/schema/loader.rb +6 -5
  163. data/lib/graphql/schema/member/base_dsl_methods.rb +32 -18
  164. data/lib/graphql/schema/member/build_type.rb +16 -13
  165. data/lib/graphql/schema/member/has_arguments.rb +270 -86
  166. data/lib/graphql/schema/member/has_ast_node.rb +12 -0
  167. data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
  168. data/lib/graphql/schema/member/has_directives.rb +81 -61
  169. data/lib/graphql/schema/member/has_fields.rb +169 -31
  170. data/lib/graphql/schema/member/has_interfaces.rb +143 -0
  171. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  172. data/lib/graphql/schema/member/has_validators.rb +32 -6
  173. data/lib/graphql/schema/member/relay_shortcuts.rb +47 -2
  174. data/lib/graphql/schema/member/scoped.rb +19 -0
  175. data/lib/graphql/schema/member/type_system_helpers.rb +16 -0
  176. data/lib/graphql/schema/member/validates_input.rb +6 -6
  177. data/lib/graphql/schema/member.rb +1 -6
  178. data/lib/graphql/schema/mutation.rb +7 -9
  179. data/lib/graphql/schema/non_null.rb +7 -7
  180. data/lib/graphql/schema/object.rb +38 -119
  181. data/lib/graphql/schema/printer.rb +24 -25
  182. data/lib/graphql/schema/relay_classic_mutation.rb +13 -91
  183. data/lib/graphql/schema/resolver/has_payload_type.rb +46 -11
  184. data/lib/graphql/schema/resolver.rb +118 -115
  185. data/lib/graphql/schema/scalar.rb +20 -21
  186. data/lib/graphql/schema/subscription.rb +95 -21
  187. data/lib/graphql/schema/timeout.rb +25 -29
  188. data/lib/graphql/schema/type_expression.rb +2 -2
  189. data/lib/graphql/schema/type_membership.rb +21 -4
  190. data/lib/graphql/schema/union.rb +16 -16
  191. data/lib/graphql/schema/unique_within_type.rb +1 -1
  192. data/lib/graphql/schema/validator/all_validator.rb +62 -0
  193. data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
  194. data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
  195. data/lib/graphql/schema/validator/exclusion_validator.rb +3 -1
  196. data/lib/graphql/schema/validator/format_validator.rb +4 -5
  197. data/lib/graphql/schema/validator/inclusion_validator.rb +3 -1
  198. data/lib/graphql/schema/validator/length_validator.rb +5 -3
  199. data/lib/graphql/schema/validator/numericality_validator.rb +13 -2
  200. data/lib/graphql/schema/validator/required_validator.rb +56 -18
  201. data/lib/graphql/schema/validator.rb +38 -28
  202. data/lib/graphql/schema/visibility/migration.rb +188 -0
  203. data/lib/graphql/schema/visibility/profile.rb +359 -0
  204. data/lib/graphql/schema/visibility/visit.rb +190 -0
  205. data/lib/graphql/schema/visibility.rb +294 -0
  206. data/lib/graphql/schema/warden.rb +423 -134
  207. data/lib/graphql/schema/wrapper.rb +0 -5
  208. data/lib/graphql/schema.rb +1015 -1057
  209. data/lib/graphql/static_validation/all_rules.rb +3 -1
  210. data/lib/graphql/static_validation/base_visitor.rb +15 -28
  211. data/lib/graphql/static_validation/definition_dependencies.rb +7 -2
  212. data/lib/graphql/static_validation/error.rb +3 -1
  213. data/lib/graphql/static_validation/literal_validator.rb +24 -7
  214. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  215. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  216. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +4 -3
  217. data/lib/graphql/static_validation/rules/directives_are_defined.rb +13 -7
  218. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +15 -13
  219. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +12 -2
  220. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +13 -5
  221. data/lib/graphql/static_validation/rules/fields_will_merge.rb +62 -35
  222. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
  223. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  224. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +12 -2
  225. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
  226. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  227. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  228. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  229. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +66 -0
  230. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -0
  231. data/lib/graphql/static_validation/rules/query_root_exists.rb +17 -0
  232. data/lib/graphql/static_validation/rules/query_root_exists_error.rb +26 -0
  233. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +7 -5
  234. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
  235. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
  236. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +14 -8
  237. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  238. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  239. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +14 -8
  240. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +11 -2
  241. data/lib/graphql/static_validation/validation_context.rb +32 -6
  242. data/lib/graphql/static_validation/validator.rb +11 -27
  243. data/lib/graphql/static_validation.rb +0 -3
  244. data/lib/graphql/string_encoding_error.rb +13 -3
  245. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +49 -11
  246. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  247. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +40 -1
  248. data/lib/graphql/subscriptions/event.rb +87 -38
  249. data/lib/graphql/subscriptions/serialize.rb +27 -3
  250. data/lib/graphql/subscriptions.rb +63 -49
  251. data/lib/graphql/testing/helpers.rb +155 -0
  252. data/lib/graphql/testing.rb +2 -0
  253. data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
  254. data/lib/graphql/tracing/active_support_notifications_tracing.rb +6 -20
  255. data/lib/graphql/tracing/appoptics_trace.rb +253 -0
  256. data/lib/graphql/tracing/appoptics_tracing.rb +4 -2
  257. data/lib/graphql/tracing/appsignal_trace.rb +79 -0
  258. data/lib/graphql/tracing/appsignal_tracing.rb +17 -0
  259. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  260. data/lib/graphql/tracing/data_dog_trace.rb +185 -0
  261. data/lib/graphql/tracing/data_dog_tracing.rb +27 -15
  262. data/lib/graphql/{execution/instrumentation.rb → tracing/legacy_hooks_trace.rb} +11 -28
  263. data/lib/graphql/tracing/legacy_trace.rb +12 -0
  264. data/lib/graphql/tracing/new_relic_trace.rb +77 -0
  265. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  266. data/lib/graphql/tracing/notifications_trace.rb +45 -0
  267. data/lib/graphql/tracing/notifications_tracing.rb +61 -0
  268. data/lib/graphql/tracing/null_trace.rb +9 -0
  269. data/lib/graphql/tracing/platform_trace.rb +118 -0
  270. data/lib/graphql/tracing/platform_tracing.rb +46 -49
  271. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +6 -2
  272. data/lib/graphql/tracing/prometheus_trace.rb +94 -0
  273. data/lib/graphql/tracing/prometheus_tracing.rb +5 -3
  274. data/lib/graphql/tracing/scout_trace.rb +74 -0
  275. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  276. data/lib/graphql/tracing/sentry_trace.rb +114 -0
  277. data/lib/graphql/tracing/statsd_trace.rb +58 -0
  278. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  279. data/lib/graphql/tracing/trace.rb +79 -0
  280. data/lib/graphql/tracing.rb +29 -52
  281. data/lib/graphql/type_kinds.rb +7 -4
  282. data/lib/graphql/types/big_int.rb +5 -1
  283. data/lib/graphql/types/int.rb +1 -1
  284. data/lib/graphql/types/iso_8601_date.rb +17 -6
  285. data/lib/graphql/types/iso_8601_date_time.rb +12 -1
  286. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  287. data/lib/graphql/types/relay/base_connection.rb +16 -6
  288. data/lib/graphql/types/relay/connection_behaviors.rb +92 -32
  289. data/lib/graphql/types/relay/edge_behaviors.rb +46 -7
  290. data/lib/graphql/types/relay/has_node_field.rb +2 -2
  291. data/lib/graphql/types/relay/has_nodes_field.rb +2 -2
  292. data/lib/graphql/types/relay/node_behaviors.rb +12 -2
  293. data/lib/graphql/types/relay/page_info_behaviors.rb +11 -2
  294. data/lib/graphql/types/relay.rb +0 -3
  295. data/lib/graphql/types/string.rb +2 -2
  296. data/lib/graphql/types.rb +18 -10
  297. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  298. data/lib/graphql/unauthorized_error.rb +1 -1
  299. data/lib/graphql/version.rb +1 -1
  300. data/lib/graphql.rb +82 -137
  301. data/readme.md +13 -6
  302. metadata +127 -186
  303. data/lib/graphql/analysis/analyze_query.rb +0 -98
  304. data/lib/graphql/analysis/ast/analyzer.rb +0 -84
  305. data/lib/graphql/analysis/ast/field_usage.rb +0 -28
  306. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -23
  307. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  308. data/lib/graphql/analysis/ast/query_complexity.rb +0 -234
  309. data/lib/graphql/analysis/ast/query_depth.rb +0 -56
  310. data/lib/graphql/analysis/ast/visitor.rb +0 -268
  311. data/lib/graphql/analysis/ast.rb +0 -91
  312. data/lib/graphql/analysis/reducer_state.rb +0 -48
  313. data/lib/graphql/argument.rb +0 -131
  314. data/lib/graphql/authorization.rb +0 -82
  315. data/lib/graphql/backtrace/legacy_tracer.rb +0 -56
  316. data/lib/graphql/backwards_compatibility.rb +0 -61
  317. data/lib/graphql/base_type.rb +0 -230
  318. data/lib/graphql/boolean_type.rb +0 -2
  319. data/lib/graphql/compatibility/execution_specification/counter_schema.rb +0 -53
  320. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +0 -200
  321. data/lib/graphql/compatibility/execution_specification.rb +0 -436
  322. data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +0 -111
  323. data/lib/graphql/compatibility/lazy_execution_specification.rb +0 -215
  324. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +0 -87
  325. data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +0 -79
  326. data/lib/graphql/compatibility/query_parser_specification.rb +0 -266
  327. data/lib/graphql/compatibility/schema_parser_specification.rb +0 -682
  328. data/lib/graphql/compatibility.rb +0 -5
  329. data/lib/graphql/define/assign_argument.rb +0 -12
  330. data/lib/graphql/define/assign_connection.rb +0 -13
  331. data/lib/graphql/define/assign_enum_value.rb +0 -18
  332. data/lib/graphql/define/assign_global_id_field.rb +0 -11
  333. data/lib/graphql/define/assign_mutation_function.rb +0 -34
  334. data/lib/graphql/define/assign_object_field.rb +0 -42
  335. data/lib/graphql/define/defined_object_proxy.rb +0 -53
  336. data/lib/graphql/define/instance_definable.rb +0 -240
  337. data/lib/graphql/define/no_definition_error.rb +0 -7
  338. data/lib/graphql/define/non_null_with_bang.rb +0 -16
  339. data/lib/graphql/define/type_definer.rb +0 -31
  340. data/lib/graphql/define.rb +0 -31
  341. data/lib/graphql/deprecated_dsl.rb +0 -47
  342. data/lib/graphql/deprecation.rb +0 -13
  343. data/lib/graphql/directive/deprecated_directive.rb +0 -2
  344. data/lib/graphql/directive/include_directive.rb +0 -2
  345. data/lib/graphql/directive/skip_directive.rb +0 -2
  346. data/lib/graphql/directive.rb +0 -111
  347. data/lib/graphql/enum_type.rb +0 -129
  348. data/lib/graphql/execution/execute.rb +0 -333
  349. data/lib/graphql/execution/flatten.rb +0 -40
  350. data/lib/graphql/execution/lazy/resolve.rb +0 -91
  351. data/lib/graphql/execution/typecast.rb +0 -50
  352. data/lib/graphql/field/resolve.rb +0 -59
  353. data/lib/graphql/field.rb +0 -226
  354. data/lib/graphql/filter.rb +0 -53
  355. data/lib/graphql/float_type.rb +0 -2
  356. data/lib/graphql/function.rb +0 -128
  357. data/lib/graphql/id_type.rb +0 -2
  358. data/lib/graphql/input_object_type.rb +0 -138
  359. data/lib/graphql/int_type.rb +0 -2
  360. data/lib/graphql/interface_type.rb +0 -72
  361. data/lib/graphql/internal_representation/document.rb +0 -27
  362. data/lib/graphql/internal_representation/node.rb +0 -206
  363. data/lib/graphql/internal_representation/print.rb +0 -51
  364. data/lib/graphql/internal_representation/rewrite.rb +0 -184
  365. data/lib/graphql/internal_representation/scope.rb +0 -88
  366. data/lib/graphql/internal_representation/visit.rb +0 -36
  367. data/lib/graphql/internal_representation.rb +0 -7
  368. data/lib/graphql/language/lexer.rl +0 -262
  369. data/lib/graphql/language/parser.y +0 -543
  370. data/lib/graphql/language/token.rb +0 -38
  371. data/lib/graphql/list_type.rb +0 -80
  372. data/lib/graphql/non_null_type.rb +0 -71
  373. data/lib/graphql/object_type.rb +0 -130
  374. data/lib/graphql/query/arguments.rb +0 -189
  375. data/lib/graphql/query/arguments_cache.rb +0 -24
  376. data/lib/graphql/query/executor.rb +0 -52
  377. data/lib/graphql/query/literal_input.rb +0 -136
  378. data/lib/graphql/query/serial_execution/field_resolution.rb +0 -92
  379. data/lib/graphql/query/serial_execution/operation_resolution.rb +0 -19
  380. data/lib/graphql/query/serial_execution/selection_resolution.rb +0 -23
  381. data/lib/graphql/query/serial_execution/value_resolution.rb +0 -87
  382. data/lib/graphql/query/serial_execution.rb +0 -40
  383. data/lib/graphql/relay/array_connection.rb +0 -83
  384. data/lib/graphql/relay/base_connection.rb +0 -189
  385. data/lib/graphql/relay/connection_instrumentation.rb +0 -54
  386. data/lib/graphql/relay/connection_resolve.rb +0 -43
  387. data/lib/graphql/relay/connection_type.rb +0 -41
  388. data/lib/graphql/relay/edge.rb +0 -27
  389. data/lib/graphql/relay/edge_type.rb +0 -19
  390. data/lib/graphql/relay/edges_instrumentation.rb +0 -40
  391. data/lib/graphql/relay/global_id_resolve.rb +0 -18
  392. data/lib/graphql/relay/mongo_relation_connection.rb +0 -50
  393. data/lib/graphql/relay/mutation/instrumentation.rb +0 -23
  394. data/lib/graphql/relay/mutation/resolve.rb +0 -56
  395. data/lib/graphql/relay/mutation/result.rb +0 -38
  396. data/lib/graphql/relay/mutation.rb +0 -106
  397. data/lib/graphql/relay/node.rb +0 -39
  398. data/lib/graphql/relay/page_info.rb +0 -7
  399. data/lib/graphql/relay/relation_connection.rb +0 -188
  400. data/lib/graphql/relay/type_extensions.rb +0 -32
  401. data/lib/graphql/scalar_type.rb +0 -91
  402. data/lib/graphql/schema/base_64_bp.rb +0 -26
  403. data/lib/graphql/schema/catchall_middleware.rb +0 -35
  404. data/lib/graphql/schema/default_parse_error.rb +0 -10
  405. data/lib/graphql/schema/default_type_error.rb +0 -17
  406. data/lib/graphql/schema/invalid_type_error.rb +0 -7
  407. data/lib/graphql/schema/member/accepts_definition.rb +0 -152
  408. data/lib/graphql/schema/member/cached_graphql_definition.rb +0 -31
  409. data/lib/graphql/schema/member/instrumentation.rb +0 -131
  410. data/lib/graphql/schema/middleware_chain.rb +0 -82
  411. data/lib/graphql/schema/possible_types.rb +0 -44
  412. data/lib/graphql/schema/rescue_middleware.rb +0 -60
  413. data/lib/graphql/schema/timeout_middleware.rb +0 -88
  414. data/lib/graphql/schema/traversal.rb +0 -228
  415. data/lib/graphql/schema/validation.rb +0 -313
  416. data/lib/graphql/static_validation/default_visitor.rb +0 -15
  417. data/lib/graphql/static_validation/no_validate_visitor.rb +0 -10
  418. data/lib/graphql/static_validation/type_stack.rb +0 -216
  419. data/lib/graphql/string_type.rb +0 -2
  420. data/lib/graphql/subscriptions/instrumentation.rb +0 -79
  421. data/lib/graphql/subscriptions/subscription_root.rb +0 -76
  422. data/lib/graphql/tracing/skylight_tracing.rb +0 -70
  423. data/lib/graphql/types/relay/default_relay.rb +0 -27
  424. data/lib/graphql/types/relay/node_field.rb +0 -25
  425. data/lib/graphql/types/relay/nodes_field.rb +0 -27
  426. data/lib/graphql/union_type.rb +0 -115
  427. data/lib/graphql/upgrader/member.rb +0 -937
  428. data/lib/graphql/upgrader/schema.rb +0 -38
@@ -6,7 +6,11 @@ module GraphQL
6
6
  # Called by {Dataloader} to prepare the {Source}'s internal state
7
7
  # @api private
8
8
  def setup(dataloader)
9
- @pending_keys = []
9
+ # These keys have been requested but haven't been fetched yet
10
+ @pending = {}
11
+ # These keys have been passed to `fetch` but haven't been finished yet
12
+ @fetching = {}
13
+ # { key => result }
10
14
  @results = {}
11
15
  @dataloader = dataloader
12
16
  end
@@ -14,42 +18,66 @@ module GraphQL
14
18
  attr_reader :dataloader
15
19
 
16
20
  # @return [Dataloader::Request] a pending request for a value from `key`. Call `.load` on that object to wait for the result.
17
- def request(key)
18
- if !@results.key?(key)
19
- @pending_keys << key
21
+ def request(value)
22
+ res_key = result_key_for(value)
23
+ if !@results.key?(res_key)
24
+ @pending[res_key] ||= value
20
25
  end
21
- Dataloader::Request.new(self, key)
26
+ Dataloader::Request.new(self, value)
27
+ end
28
+
29
+ # Implement this method to return a stable identifier if different
30
+ # key objects should load the same data value.
31
+ #
32
+ # @param value [Object] A value passed to `.request` or `.load`, for which a value will be loaded
33
+ # @return [Object] The key for tracking this pending data
34
+ def result_key_for(value)
35
+ value
22
36
  end
23
37
 
24
38
  # @return [Dataloader::Request] a pending request for a values from `keys`. Call `.load` on that object to wait for the results.
25
- def request_all(keys)
26
- pending_keys = keys.select { |k| !@results.key?(k) }
27
- @pending_keys.concat(pending_keys)
28
- Dataloader::RequestAll.new(self, keys)
39
+ def request_all(values)
40
+ values.each do |v|
41
+ res_key = result_key_for(v)
42
+ if !@results.key?(res_key)
43
+ @pending[res_key] ||= v
44
+ end
45
+ end
46
+ Dataloader::RequestAll.new(self, values)
29
47
  end
30
48
 
31
- # @param key [Object] A loading key which will be passed to {#fetch} if it isn't already in the internal cache.
49
+ # @param value [Object] A loading value which will be passed to {#fetch} if it isn't already in the internal cache.
32
50
  # @return [Object] The result from {#fetch} for `key`. If `key` hasn't been loaded yet, the Fiber will yield until it's loaded.
33
- def load(key)
34
- if @results.key?(key)
35
- result_for(key)
51
+ def load(value)
52
+ result_key = result_key_for(value)
53
+ if @results.key?(result_key)
54
+ result_for(result_key)
36
55
  else
37
- @pending_keys << key
38
- sync
39
- result_for(key)
56
+ @pending[result_key] ||= value
57
+ sync([result_key])
58
+ result_for(result_key)
40
59
  end
41
60
  end
42
61
 
43
- # @param keys [Array<Object>] Loading keys which will be passed to `#fetch` (or read from the internal cache).
62
+ # @param values [Array<Object>] Loading keys which will be passed to `#fetch` (or read from the internal cache).
44
63
  # @return [Object] The result from {#fetch} for `keys`. If `keys` haven't been loaded yet, the Fiber will yield until they're loaded.
45
- def load_all(keys)
46
- if keys.any? { |k| !@results.key?(k) }
47
- pending_keys = keys.select { |k| !@results.key?(k) }
48
- @pending_keys.concat(pending_keys)
49
- sync
64
+ def load_all(values)
65
+ result_keys = []
66
+ pending_keys = []
67
+ values.each { |v|
68
+ k = result_key_for(v)
69
+ result_keys << k
70
+ if !@results.key?(k)
71
+ @pending[k] ||= v
72
+ pending_keys << k
73
+ end
74
+ }
75
+
76
+ if !pending_keys.empty?
77
+ sync(pending_keys)
50
78
  end
51
79
 
52
- keys.map { |k| result_for(k) }
80
+ result_keys.map { |k| result_for(k) }
53
81
  end
54
82
 
55
83
  # Subclasses must implement this method to return a value for each of `keys`
@@ -60,35 +88,89 @@ module GraphQL
60
88
  raise "Implement `#{self.class}#fetch(#{keys.inspect}) to return a record for each of the keys"
61
89
  end
62
90
 
91
+ MAX_ITERATIONS = 1000
63
92
  # Wait for a batch, if there's anything to batch.
64
93
  # Then run the batch and update the cache.
65
94
  # @return [void]
66
- def sync
95
+ def sync(pending_result_keys)
67
96
  @dataloader.yield
97
+ iterations = 0
98
+ while pending_result_keys.any? { |key| !@results.key?(key) }
99
+ iterations += 1
100
+ if iterations > MAX_ITERATIONS
101
+ raise "#{self.class}#sync tried #{MAX_ITERATIONS} times to load pending keys (#{pending_result_keys}), but they still weren't loaded. There is likely a circular dependency#{@dataloader.fiber_limit ? " or `fiber_limit: #{@dataloader.fiber_limit}` is set too low" : ""}."
102
+ end
103
+ @dataloader.yield
104
+ end
105
+ nil
68
106
  end
69
107
 
70
108
  # @return [Boolean] True if this source has any pending requests for data.
71
109
  def pending?
72
- @pending_keys.any?
110
+ !@pending.empty?
111
+ end
112
+
113
+ # Add these key-value pairs to this source's cache
114
+ # (future loads will use these merged values).
115
+ # @param new_results [Hash<Object => Object>] key-value pairs to cache in this source
116
+ # @return [void]
117
+ def merge(new_results)
118
+ new_results.each do |new_k, new_v|
119
+ key = result_key_for(new_k)
120
+ @results[key] = new_v
121
+ end
122
+ nil
73
123
  end
74
124
 
75
125
  # Called by {GraphQL::Dataloader} to resolve and pending requests to this source.
76
126
  # @api private
77
127
  # @return [void]
78
128
  def run_pending_keys
79
- return if @pending_keys.empty?
80
- fetch_keys = @pending_keys.uniq
81
- @pending_keys = []
82
- results = fetch(fetch_keys)
83
- fetch_keys.each_with_index do |key, idx|
129
+ if !@fetching.empty?
130
+ @fetching.each_key { |k| @pending.delete(k) }
131
+ end
132
+ return if @pending.empty?
133
+ fetch_h = @pending
134
+ @pending = {}
135
+ @fetching.merge!(fetch_h)
136
+ results = fetch(fetch_h.values)
137
+ fetch_h.each_with_index do |(key, _value), idx|
84
138
  @results[key] = results[idx]
85
139
  end
140
+ nil
86
141
  rescue StandardError => error
87
- fetch_keys.each { |key| @results[key] = error }
142
+ fetch_h.each_key { |key| @results[key] = error }
88
143
  ensure
144
+ fetch_h && fetch_h.each_key { |k| @fetching.delete(k) }
145
+ end
146
+
147
+ # These arguments are given to `dataloader.with(source_class, ...)`. The object
148
+ # returned from this method is used to de-duplicate batch loads under the hood
149
+ # by using it as a Hash key.
150
+ #
151
+ # By default, the arguments are all put in an Array. To customize how this source's
152
+ # batches are merged, override this method to return something else.
153
+ #
154
+ # For example, if you pass `ActiveRecord::Relation`s to `.with(...)`, you could override
155
+ # this method to call `.to_sql` on them, thus merging `.load(...)` calls when they apply
156
+ # to equivalent relations.
157
+ #
158
+ # @param batch_args [Array<Object>]
159
+ # @param batch_kwargs [Hash]
160
+ # @return [Object]
161
+ def self.batch_key_for(*batch_args, **batch_kwargs)
162
+ [*batch_args, **batch_kwargs]
163
+ end
164
+
165
+ # Clear any already-loaded objects for this source
166
+ # @return [void]
167
+ def clear_cache
168
+ @results.clear
89
169
  nil
90
170
  end
91
171
 
172
+ attr_reader :pending, :results
173
+
92
174
  private
93
175
 
94
176
  # Reads and returns the result for the key from the internal cache, or raises an error if the result was an error
@@ -96,9 +178,19 @@ module GraphQL
96
178
  # @return [Object] The result from {#fetch} for `key`.
97
179
  # @api private
98
180
  def result_for(key)
99
- result = @results[key]
181
+ if !@results.key?(key)
182
+ raise GraphQL::InvariantError, <<-ERR
183
+ Fetching result for a key on #{self.class} that hasn't been loaded yet (#{key.inspect}, loaded: #{@results.keys})
100
184
 
101
- raise result if result.class <= StandardError
185
+ This key should have been loaded already. This is a bug in GraphQL::Dataloader, please report it on GitHub: https://github.com/rmosolgo/graphql-ruby/issues/new.
186
+ ERR
187
+ end
188
+ result = @results[key]
189
+ if result.is_a?(StandardError)
190
+ # Dup it because the rescuer may modify it.
191
+ # (This happens for GraphQL::ExecutionErrors, at least)
192
+ raise result.dup
193
+ end
102
194
 
103
195
  result
104
196
  end
@@ -23,23 +23,80 @@ module GraphQL
23
23
  # end
24
24
  #
25
25
  class Dataloader
26
- def self.use(schema)
27
- schema.dataloader_class = self
26
+ class << self
27
+ attr_accessor :default_nonblocking, :default_fiber_limit
28
28
  end
29
29
 
30
- def initialize
31
- @source_cache = Hash.new { |h, source_class| h[source_class] = Hash.new { |h2, batch_parameters|
32
- source = if RUBY_VERSION < "3"
33
- source_class.new(*batch_parameters)
34
- else
35
- batch_args, batch_kwargs = batch_parameters
36
- source_class.new(*batch_args, **batch_kwargs)
37
- end
38
- source.setup(self)
39
- h2[batch_parameters] = source
40
- }
30
+ def self.use(schema, nonblocking: nil, fiber_limit: nil)
31
+ dataloader_class = if nonblocking
32
+ warn("`nonblocking: true` is deprecated from `GraphQL::Dataloader`, please use `GraphQL::Dataloader::AsyncDataloader` instead. Docs: https://graphql-ruby.org/dataloader/async_dataloader.")
33
+ Class.new(self) { self.default_nonblocking = true }
34
+ else
35
+ self
36
+ end
37
+
38
+ if fiber_limit
39
+ dataloader_class = Class.new(dataloader_class)
40
+ dataloader_class.default_fiber_limit = fiber_limit
41
+ end
42
+
43
+ schema.dataloader_class = dataloader_class
44
+ end
45
+
46
+ # Call the block with a Dataloader instance,
47
+ # then run all enqueued jobs and return the result of the block.
48
+ def self.with_dataloading(&block)
49
+ dataloader = self.new
50
+ result = nil
51
+ dataloader.append_job {
52
+ result = block.call(dataloader)
41
53
  }
54
+ dataloader.run
55
+ result
56
+ end
57
+
58
+ def initialize(nonblocking: self.class.default_nonblocking, fiber_limit: self.class.default_fiber_limit)
59
+ @source_cache = Hash.new { |h, k| h[k] = {} }
42
60
  @pending_jobs = []
61
+ if !nonblocking.nil?
62
+ @nonblocking = nonblocking
63
+ end
64
+ @fiber_limit = fiber_limit
65
+ end
66
+
67
+ # @return [Integer, nil]
68
+ attr_reader :fiber_limit
69
+
70
+ def nonblocking?
71
+ @nonblocking
72
+ end
73
+
74
+ # This is called before the fiber is spawned, from the parent context (i.e. from
75
+ # the thread or fiber that it is scheduled from).
76
+ #
77
+ # @return [Hash<Symbol, Object>] Current fiber-local variables
78
+ def get_fiber_variables
79
+ fiber_vars = {}
80
+ Thread.current.keys.each do |fiber_var_key|
81
+ fiber_vars[fiber_var_key] = Thread.current[fiber_var_key]
82
+ end
83
+ fiber_vars
84
+ end
85
+
86
+ # Set up the fiber variables in a new fiber.
87
+ #
88
+ # This is called within the fiber, right after it is spawned.
89
+ #
90
+ # @param vars [Hash<Symbol, Object>] Fiber-local variables from {get_fiber_variables}
91
+ # @return [void]
92
+ def set_fiber_variables(vars)
93
+ vars.each { |k, v| Thread.current[k] = v }
94
+ nil
95
+ end
96
+
97
+ # This method is called when Dataloader is finished using a fiber.
98
+ # Use it to perform any cleanup, such as releasing database connections (if required manually)
99
+ def cleanup_fiber
43
100
  end
44
101
 
45
102
  # Get a Source instance from this dataloader, for calling `.load(...)` or `.request(...)` on.
@@ -48,17 +105,25 @@ module GraphQL
48
105
  # @param batch_parameters [Array<Object>]
49
106
  # @return [GraphQL::Dataloader::Source] An instance of {source_class}, initialized with `self, *batch_parameters`,
50
107
  # and cached for the lifetime of this {Multiplex}.
51
- if RUBY_VERSION < "3"
52
- def with(source_class, *batch_parameters)
53
- @source_cache[source_class][batch_parameters]
108
+ if RUBY_VERSION < "3" || RUBY_ENGINE != "ruby" # truffle-ruby wasn't doing well with the implementation below
109
+ def with(source_class, *batch_args)
110
+ batch_key = source_class.batch_key_for(*batch_args)
111
+ @source_cache[source_class][batch_key] ||= begin
112
+ source = source_class.new(*batch_args)
113
+ source.setup(self)
114
+ source
115
+ end
54
116
  end
55
117
  else
56
118
  def with(source_class, *batch_args, **batch_kwargs)
57
- batch_parameters = [batch_args, batch_kwargs]
58
- @source_cache[source_class][batch_parameters]
119
+ batch_key = source_class.batch_key_for(*batch_args, **batch_kwargs)
120
+ @source_cache[source_class][batch_key] ||= begin
121
+ source = source_class.new(*batch_args, **batch_kwargs)
122
+ source.setup(self)
123
+ source
124
+ end
59
125
  end
60
126
  end
61
-
62
127
  # Tell the dataloader that this fiber is waiting for data.
63
128
  #
64
129
  # Dataloader will resume the fiber after the requested data has been loaded (by another Fiber).
@@ -77,9 +142,28 @@ module GraphQL
77
142
  nil
78
143
  end
79
144
 
145
+ # Clear any already-loaded objects from {Source} caches
146
+ # @return [void]
147
+ def clear_cache
148
+ @source_cache.each do |_source_class, batched_sources|
149
+ batched_sources.each_value(&:clear_cache)
150
+ end
151
+ nil
152
+ end
153
+
80
154
  # Use a self-contained queue for the work in the block.
81
155
  def run_isolated
82
156
  prev_queue = @pending_jobs
157
+ prev_pending_keys = {}
158
+ @source_cache.each do |source_class, batched_sources|
159
+ batched_sources.each do |batch_args, batched_source_instance|
160
+ if batched_source_instance.pending?
161
+ prev_pending_keys[batched_source_instance] = batched_source_instance.pending.dup
162
+ batched_source_instance.pending.clear
163
+ end
164
+ end
165
+ end
166
+
83
167
  @pending_jobs = []
84
168
  res = nil
85
169
  # Make sure the block is inside a Fiber, so it can `Fiber.yield`
@@ -90,113 +174,109 @@ module GraphQL
90
174
  res
91
175
  ensure
92
176
  @pending_jobs = prev_queue
177
+ prev_pending_keys.each do |source_instance, pending|
178
+ pending.each do |key, value|
179
+ if !source_instance.results.key?(key)
180
+ source_instance.pending[key] = value
181
+ end
182
+ end
183
+ end
93
184
  end
94
185
 
95
- # @api private Move along, move along
96
186
  def run
97
- # At a high level, the algorithm is:
98
- #
99
- # A) Inside Fibers, run jobs from the queue one-by-one
100
- # - When one of the jobs yields to the dataloader (`Fiber.yield`), then that fiber will pause
101
- # - In that case, if there are still pending jobs, a new Fiber will be created to run jobs
102
- # - Continue until all jobs have been _started_ by a Fiber. (Any number of those Fibers may be waiting to be resumed, after their data is loaded)
103
- # B) Once all known jobs have been run until they are complete or paused for data, run all pending data sources.
104
- # - Similarly, create a Fiber to consume pending sources and tell them to load their data.
105
- # - If one of those Fibers pauses, then create a new Fiber to continue working through remaining pending sources.
106
- # - When a source causes another source to become pending, run the newly-pending source _first_, since it's a dependency of the previous one.
107
- # C) After all pending sources have been completely loaded (there are no more pending sources), resume any Fibers that were waiting for data.
108
- # - Those Fibers assume that source caches will have been populated with the data they were waiting for.
109
- # - Those Fibers may request data from a source again, in which case they will yeilded and be added to a new pending fiber list.
110
- # D) Once all pending fibers have been resumed once, return to `A` above.
111
- #
112
- # For whatever reason, the best implementation I could find was to order the steps `[D, A, B, C]`, with a special case for skipping `D`
113
- # on the first pass. I just couldn't find a better way to write the loops in a way that was DRY and easy to read.
114
- #
115
- pending_fibers = []
116
- next_fibers = []
187
+ jobs_fiber_limit, total_fiber_limit = calculate_fiber_limit
188
+ job_fibers = []
189
+ next_job_fibers = []
190
+ source_fibers = []
191
+ next_source_fibers = []
117
192
  first_pass = true
118
-
119
- while first_pass || (f = pending_fibers.shift)
120
- if first_pass
193
+ manager = spawn_fiber do
194
+ while first_pass || !job_fibers.empty?
121
195
  first_pass = false
122
- else
123
- # These fibers were previously waiting for sources to load data,
124
- # resume them. (They might wait again, in which case, re-enqueue them.)
125
- resume(f)
126
- if f.alive?
127
- next_fibers << f
128
- end
129
- end
130
196
 
131
- while @pending_jobs.any?
132
- # Create a Fiber to consume jobs until one of the jobs yields
133
- # or jobs run out
134
- f = spawn_fiber {
135
- while (job = @pending_jobs.shift)
136
- job.call
197
+ while (f = (job_fibers.shift || (((next_job_fibers.size + job_fibers.size) < jobs_fiber_limit) && spawn_job_fiber)))
198
+ if f.alive?
199
+ finished = run_fiber(f)
200
+ if !finished
201
+ next_job_fibers << f
202
+ end
137
203
  end
138
- }
139
- resume(f)
140
- # In this case, the job yielded. Queue it up to run again after
141
- # we load whatever it's waiting for.
142
- if f.alive?
143
- next_fibers << f
144
204
  end
145
- end
146
-
147
- if pending_fibers.empty?
148
- # Now, run all Sources which have become pending _before_ resuming GraphQL execution.
149
- # Sources might queue up other Sources, which is fine -- those will also run before resuming execution.
150
- #
151
- # This is where an evented approach would be even better -- can we tell which
152
- # fibers are ready to continue, and continue execution there?
153
- #
154
- source_fiber_queue = if (first_source_fiber = create_source_fiber)
155
- [first_source_fiber]
156
- else
157
- nil
158
- end
159
-
160
- if source_fiber_queue
161
- while (outer_source_fiber = source_fiber_queue.shift)
162
- resume(outer_source_fiber)
163
-
164
- # If this source caused more sources to become pending, run those before running this one again:
165
- next_source_fiber = create_source_fiber
166
- if next_source_fiber
167
- source_fiber_queue << next_source_fiber
168
- end
205
+ join_queues(job_fibers, next_job_fibers)
169
206
 
170
- if outer_source_fiber.alive?
171
- source_fiber_queue << outer_source_fiber
207
+ while (!source_fibers.empty? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) })
208
+ while (f = source_fibers.shift || (((job_fibers.size + source_fibers.size + next_source_fibers.size + next_job_fibers.size) < total_fiber_limit) && spawn_source_fiber))
209
+ if f.alive?
210
+ finished = run_fiber(f)
211
+ if !finished
212
+ next_source_fibers << f
213
+ end
172
214
  end
173
215
  end
216
+ join_queues(source_fibers, next_source_fibers)
174
217
  end
175
- # Move newly-enqueued Fibers on to the list to be resumed.
176
- # Clear out the list of next-round Fibers, so that
177
- # any Fibers that pause can be put on it.
178
- pending_fibers.concat(next_fibers)
179
- next_fibers.clear
180
218
  end
181
219
  end
182
220
 
183
- if @pending_jobs.any?
184
- raise "Invariant: #{@pending_jobs.size} pending jobs"
185
- elsif pending_fibers.any?
186
- raise "Invariant: #{pending_fibers.size} pending fibers"
187
- elsif next_fibers.any?
188
- raise "Invariant: #{next_fibers.size} next fibers"
221
+ run_fiber(manager)
222
+
223
+ if manager.alive?
224
+ raise "Invariant: Manager fiber didn't terminate properly."
189
225
  end
190
- nil
226
+
227
+ if !job_fibers.empty?
228
+ raise "Invariant: job fibers should have exited but #{job_fibers.size} remained"
229
+ end
230
+ if !source_fibers.empty?
231
+ raise "Invariant: source fibers should have exited but #{source_fibers.size} remained"
232
+ end
233
+ rescue UncaughtThrowError => e
234
+ throw e.tag, e.value
235
+ end
236
+
237
+ def run_fiber(f)
238
+ f.resume
239
+ end
240
+
241
+ def spawn_fiber
242
+ fiber_vars = get_fiber_variables
243
+ Fiber.new(blocking: !@nonblocking) {
244
+ set_fiber_variables(fiber_vars)
245
+ yield
246
+ cleanup_fiber
247
+ }
191
248
  end
192
249
 
193
250
  private
194
251
 
195
- # If there are pending sources, return a fiber for running them.
196
- # Otherwise, return `nil`.
197
- #
198
- # @return [Fiber, nil]
199
- def create_source_fiber
252
+ def calculate_fiber_limit
253
+ total_fiber_limit = @fiber_limit || Float::INFINITY
254
+ if total_fiber_limit < 4
255
+ raise ArgumentError, "Dataloader fiber limit is too low (#{total_fiber_limit}), it must be at least 4"
256
+ end
257
+ total_fiber_limit -= 1 # deduct one fiber for `manager`
258
+ # Deduct at least one fiber for sources
259
+ jobs_fiber_limit = total_fiber_limit - 2
260
+ return jobs_fiber_limit, total_fiber_limit
261
+ end
262
+
263
+ def join_queues(prev_queue, new_queue)
264
+ @nonblocking && Fiber.scheduler.run
265
+ prev_queue.concat(new_queue)
266
+ new_queue.clear
267
+ end
268
+
269
+ def spawn_job_fiber
270
+ if !@pending_jobs.empty?
271
+ spawn_fiber do
272
+ while job = @pending_jobs.shift
273
+ job.call
274
+ end
275
+ end
276
+ end
277
+ end
278
+
279
+ def spawn_source_fiber
200
280
  pending_sources = nil
201
281
  @source_cache.each_value do |source_by_batch_params|
202
282
  source_by_batch_params.each_value do |source|
@@ -208,45 +288,15 @@ module GraphQL
208
288
  end
209
289
 
210
290
  if pending_sources
211
- # By passing the whole array into this Fiber, it's possible that we set ourselves up for a bunch of no-ops.
212
- # For example, if you have sources `[a, b, c]`, and `a` is loaded, then `b` yields to wait for `d`, then
213
- # the next fiber would be dispatched with `[c, d]`. It would fulfill `c`, then `d`, then eventually
214
- # the previous fiber would start up again. `c` would no longer be pending, but it would still receive `.run_pending_keys`.
215
- # That method is short-circuited since it isn't pending any more, but it's still a waste.
216
- #
217
- # This design could probably be improved by maintaining a `@pending_sources` queue which is shared by the fibers,
218
- # similar to `@pending_jobs`. That way, when a fiber is resumed, it would never pick up work that was finished by a different fiber.
219
- source_fiber = spawn_fiber do
220
- pending_sources.each(&:run_pending_keys)
291
+ spawn_fiber do
292
+ pending_sources.each do |source|
293
+ Fiber[:__graphql_current_dataloader_source] = source
294
+ source.run_pending_keys
295
+ end
221
296
  end
222
297
  end
223
-
224
- source_fiber
225
- end
226
-
227
- def resume(fiber)
228
- fiber.resume
229
- rescue UncaughtThrowError => e
230
- throw e.tag, e.value
231
- end
232
-
233
- # Copies the thread local vars into the fiber thread local vars. Many
234
- # gems (such as RequestStore, MiniRacer, etc.) rely on thread local vars
235
- # to keep track of execution context, and without this they do not
236
- # behave as expected.
237
- #
238
- # @see https://github.com/rmosolgo/graphql-ruby/issues/3449
239
- def spawn_fiber
240
- fiber_locals = {}
241
-
242
- Thread.current.keys.each do |fiber_var_key|
243
- fiber_locals[fiber_var_key] = Thread.current[fiber_var_key]
244
- end
245
-
246
- Fiber.new do
247
- fiber_locals.each { |k, v| Thread.current[k] = v }
248
- yield
249
- end
250
298
  end
251
299
  end
252
300
  end
301
+
302
+ require "graphql/dataloader/async_dataloader"
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ # This error is raised when `Types::ISO8601Date` is asked to return a value
4
+ # that cannot be parsed to a Ruby Date.
5
+ #
6
+ # @see GraphQL::Types::ISO8601Date which raises this error
7
+ class DateEncodingError < GraphQL::RuntimeTypeError
8
+ # The value which couldn't be encoded
9
+ attr_reader :date_value
10
+
11
+ def initialize(value)
12
+ @date_value = value
13
+ super("Date cannot be parsed: #{value}. \nDate must be be able to be parsed as a Ruby Date object.")
14
+ end
15
+ end
16
+ end
data/lib/graphql/dig.rb CHANGED
@@ -2,7 +2,7 @@
2
2
  module GraphQL
3
3
  module Dig
4
4
  # implemented using the old activesupport #dig instead of the ruby built-in
5
- # so we can use some of the magic in Schema::InputObject and Query::Arguments
5
+ # so we can use some of the magic in Schema::InputObject and Interpreter::Arguments
6
6
  # to handle stringified/symbolized keys.
7
7
  #
8
8
  # @param args [Array<[String, Symbol>] Retrieves the value object corresponding to the each key objects repeatedly
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ # This error is raised when `Types::ISO8601Duration` is asked to return a value
4
+ # that cannot be parsed as an ISO8601-formatted duration by ActiveSupport::Duration.
5
+ #
6
+ # @see GraphQL::Types::ISO8601Duration which raises this error
7
+ class DurationEncodingError < GraphQL::RuntimeTypeError
8
+ # The value which couldn't be encoded
9
+ attr_reader :duration_value
10
+
11
+ def initialize(value)
12
+ @duration_value = value
13
+ super("Duration cannot be parsed: #{value}. \nDuration must be an ISO8601-formatted duration.")
14
+ end
15
+ end
16
+ end