graphql 2.0.32 → 2.5.22

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.
Files changed (308) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/detailed_trace_generator.rb +77 -0
  3. data/lib/generators/graphql/install/mutation_root_generator.rb +2 -2
  4. data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
  5. data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
  6. data/lib/generators/graphql/install_generator.rb +49 -0
  7. data/lib/generators/graphql/orm_mutations_base.rb +1 -1
  8. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  9. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  10. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  11. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  12. data/lib/generators/graphql/templates/base_field.erb +2 -0
  13. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  14. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  15. data/lib/generators/graphql/templates/base_object.erb +2 -0
  16. data/lib/generators/graphql/templates/base_resolver.erb +8 -0
  17. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  18. data/lib/generators/graphql/templates/base_union.erb +2 -0
  19. data/lib/generators/graphql/templates/create_graphql_detailed_traces.erb +10 -0
  20. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  21. data/lib/generators/graphql/templates/loader.erb +2 -0
  22. data/lib/generators/graphql/templates/mutation.erb +2 -0
  23. data/lib/generators/graphql/templates/node_type.erb +2 -0
  24. data/lib/generators/graphql/templates/query_type.erb +2 -0
  25. data/lib/generators/graphql/templates/schema.erb +5 -0
  26. data/lib/generators/graphql/type_generator.rb +1 -1
  27. data/lib/graphql/analysis/analyzer.rb +90 -0
  28. data/lib/graphql/analysis/field_usage.rb +82 -0
  29. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  30. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  31. data/lib/graphql/analysis/query_complexity.rb +263 -0
  32. data/lib/graphql/analysis/query_depth.rb +58 -0
  33. data/lib/graphql/analysis/visitor.rb +280 -0
  34. data/lib/graphql/analysis.rb +95 -1
  35. data/lib/graphql/autoload.rb +38 -0
  36. data/lib/graphql/backtrace/table.rb +118 -55
  37. data/lib/graphql/backtrace.rb +1 -19
  38. data/lib/graphql/coercion_error.rb +1 -9
  39. data/lib/graphql/current.rb +57 -0
  40. data/lib/graphql/dashboard/application_controller.rb +41 -0
  41. data/lib/graphql/dashboard/detailed_traces.rb +47 -0
  42. data/lib/graphql/dashboard/installable.rb +22 -0
  43. data/lib/graphql/dashboard/landings_controller.rb +9 -0
  44. data/lib/graphql/dashboard/limiters.rb +93 -0
  45. data/lib/graphql/dashboard/operation_store.rb +199 -0
  46. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
  47. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
  48. data/lib/graphql/dashboard/statics/charts.min.css +1 -0
  49. data/lib/graphql/dashboard/statics/dashboard.css +30 -0
  50. data/lib/graphql/dashboard/statics/dashboard.js +143 -0
  51. data/lib/graphql/dashboard/statics/header-icon.png +0 -0
  52. data/lib/graphql/dashboard/statics/icon.png +0 -0
  53. data/lib/graphql/dashboard/statics_controller.rb +31 -0
  54. data/lib/graphql/dashboard/subscriptions.rb +97 -0
  55. data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
  56. data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
  57. data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
  58. data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
  59. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +24 -0
  60. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
  61. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
  62. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
  63. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
  64. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
  65. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
  66. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
  67. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
  68. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
  69. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
  70. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
  71. data/lib/graphql/dashboard.rb +96 -0
  72. data/lib/graphql/dataloader/active_record_association_source.rb +84 -0
  73. data/lib/graphql/dataloader/active_record_source.rb +47 -0
  74. data/lib/graphql/dataloader/async_dataloader.rb +112 -0
  75. data/lib/graphql/dataloader/null_dataloader.rb +55 -10
  76. data/lib/graphql/dataloader/request.rb +5 -0
  77. data/lib/graphql/dataloader/source.rb +35 -12
  78. data/lib/graphql/dataloader.rb +224 -149
  79. data/lib/graphql/date_encoding_error.rb +1 -1
  80. data/lib/graphql/dig.rb +2 -1
  81. data/lib/graphql/duration_encoding_error.rb +16 -0
  82. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  83. data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
  84. data/lib/graphql/execution/interpreter/resolve.rb +23 -25
  85. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +228 -0
  86. data/lib/graphql/execution/interpreter/runtime.rb +363 -434
  87. data/lib/graphql/execution/interpreter.rb +91 -164
  88. data/lib/graphql/execution/lookahead.rb +105 -31
  89. data/lib/graphql/execution/multiplex.rb +7 -6
  90. data/lib/graphql/execution/next/field_resolve_step.rb +711 -0
  91. data/lib/graphql/execution/next/load_argument_step.rb +60 -0
  92. data/lib/graphql/execution/next/prepare_object_step.rb +129 -0
  93. data/lib/graphql/execution/next/runner.rb +389 -0
  94. data/lib/graphql/execution/next/selections_step.rb +37 -0
  95. data/lib/graphql/execution/next.rb +70 -0
  96. data/lib/graphql/execution.rb +1 -0
  97. data/lib/graphql/execution_error.rb +13 -10
  98. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  99. data/lib/graphql/introspection/directive_type.rb +7 -3
  100. data/lib/graphql/introspection/dynamic_fields.rb +5 -1
  101. data/lib/graphql/introspection/entry_points.rb +20 -6
  102. data/lib/graphql/introspection/enum_value_type.rb +5 -5
  103. data/lib/graphql/introspection/field_type.rb +13 -5
  104. data/lib/graphql/introspection/input_value_type.rb +21 -13
  105. data/lib/graphql/introspection/schema_type.rb +8 -11
  106. data/lib/graphql/introspection/type_type.rb +64 -28
  107. data/lib/graphql/invalid_name_error.rb +1 -1
  108. data/lib/graphql/invalid_null_error.rb +26 -17
  109. data/lib/graphql/language/block_string.rb +34 -18
  110. data/lib/graphql/language/cache.rb +13 -0
  111. data/lib/graphql/language/comment.rb +18 -0
  112. data/lib/graphql/language/definition_slice.rb +1 -1
  113. data/lib/graphql/language/document_from_schema_definition.rb +90 -61
  114. data/lib/graphql/language/lexer.rb +319 -193
  115. data/lib/graphql/language/nodes.rb +136 -77
  116. data/lib/graphql/language/parser.rb +807 -1985
  117. data/lib/graphql/language/printer.rb +324 -151
  118. data/lib/graphql/language/sanitized_printer.rb +21 -23
  119. data/lib/graphql/language/static_visitor.rb +171 -0
  120. data/lib/graphql/language/visitor.rb +23 -83
  121. data/lib/graphql/language.rb +71 -1
  122. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  123. data/lib/graphql/pagination/array_connection.rb +6 -6
  124. data/lib/graphql/pagination/connection.rb +30 -1
  125. data/lib/graphql/pagination/connections.rb +32 -0
  126. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  127. data/lib/graphql/query/context/scoped_context.rb +101 -0
  128. data/lib/graphql/query/context.rb +82 -144
  129. data/lib/graphql/query/null_context.rb +15 -18
  130. data/lib/graphql/query/partial.rb +179 -0
  131. data/lib/graphql/query/validation_pipeline.rb +4 -4
  132. data/lib/graphql/query/variable_validation_error.rb +1 -1
  133. data/lib/graphql/query/variables.rb +3 -3
  134. data/lib/graphql/query.rb +126 -81
  135. data/lib/graphql/railtie.rb +16 -6
  136. data/lib/graphql/rake_task.rb +3 -12
  137. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  138. data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
  139. data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
  140. data/lib/graphql/rubocop.rb +2 -0
  141. data/lib/graphql/schema/addition.rb +26 -13
  142. data/lib/graphql/schema/always_visible.rb +7 -2
  143. data/lib/graphql/schema/argument.rb +75 -9
  144. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  145. data/lib/graphql/schema/build_from_definition.rb +123 -60
  146. data/lib/graphql/schema/directive/flagged.rb +4 -2
  147. data/lib/graphql/schema/directive/one_of.rb +12 -0
  148. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  149. data/lib/graphql/schema/directive.rb +54 -2
  150. data/lib/graphql/schema/enum.rb +110 -27
  151. data/lib/graphql/schema/enum_value.rb +10 -2
  152. data/lib/graphql/schema/field/connection_extension.rb +15 -49
  153. data/lib/graphql/schema/field/scope_extension.rb +23 -7
  154. data/lib/graphql/schema/field.rb +245 -118
  155. data/lib/graphql/schema/field_extension.rb +34 -1
  156. data/lib/graphql/schema/has_single_input_argument.rb +160 -0
  157. data/lib/graphql/schema/input_object.rb +116 -60
  158. data/lib/graphql/schema/interface.rb +34 -16
  159. data/lib/graphql/schema/introspection_system.rb +8 -17
  160. data/lib/graphql/schema/late_bound_type.rb +4 -0
  161. data/lib/graphql/schema/list.rb +3 -3
  162. data/lib/graphql/schema/loader.rb +3 -4
  163. data/lib/graphql/schema/member/base_dsl_methods.rb +18 -2
  164. data/lib/graphql/schema/member/has_arguments.rb +132 -100
  165. data/lib/graphql/schema/member/has_authorization.rb +35 -0
  166. data/lib/graphql/schema/member/has_dataloader.rb +99 -0
  167. data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
  168. data/lib/graphql/schema/member/has_directives.rb +4 -4
  169. data/lib/graphql/schema/member/has_fields.rb +115 -15
  170. data/lib/graphql/schema/member/has_interfaces.rb +26 -12
  171. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  172. data/lib/graphql/schema/member/has_validators.rb +1 -1
  173. data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
  174. data/lib/graphql/schema/member/scoped.rb +19 -0
  175. data/lib/graphql/schema/member/type_system_helpers.rb +17 -4
  176. data/lib/graphql/schema/member/validates_input.rb +3 -3
  177. data/lib/graphql/schema/member.rb +6 -0
  178. data/lib/graphql/schema/mutation.rb +7 -0
  179. data/lib/graphql/schema/object.rb +34 -8
  180. data/lib/graphql/schema/printer.rb +9 -7
  181. data/lib/graphql/schema/ractor_shareable.rb +79 -0
  182. data/lib/graphql/schema/relay_classic_mutation.rb +6 -129
  183. data/lib/graphql/schema/resolver.rb +90 -32
  184. data/lib/graphql/schema/scalar.rb +4 -9
  185. data/lib/graphql/schema/subscription.rb +63 -10
  186. data/lib/graphql/schema/timeout.rb +19 -2
  187. data/lib/graphql/schema/type_expression.rb +2 -2
  188. data/lib/graphql/schema/union.rb +2 -2
  189. data/lib/graphql/schema/unique_within_type.rb +1 -1
  190. data/lib/graphql/schema/validator/all_validator.rb +62 -0
  191. data/lib/graphql/schema/validator/required_validator.rb +92 -11
  192. data/lib/graphql/schema/validator.rb +3 -1
  193. data/lib/graphql/schema/visibility/migration.rb +188 -0
  194. data/lib/graphql/schema/visibility/profile.rb +445 -0
  195. data/lib/graphql/schema/visibility/visit.rb +190 -0
  196. data/lib/graphql/schema/visibility.rb +311 -0
  197. data/lib/graphql/schema/warden.rb +275 -103
  198. data/lib/graphql/schema.rb +950 -210
  199. data/lib/graphql/static_validation/all_rules.rb +3 -3
  200. data/lib/graphql/static_validation/base_visitor.rb +7 -6
  201. data/lib/graphql/static_validation/literal_validator.rb +6 -7
  202. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  203. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  204. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -2
  205. data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
  206. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
  207. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +12 -2
  208. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
  209. data/lib/graphql/static_validation/rules/fields_will_merge.rb +88 -25
  210. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
  211. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  212. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +12 -2
  213. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  214. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  215. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  216. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
  217. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  218. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +5 -5
  219. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
  220. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
  221. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +7 -3
  222. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  223. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  224. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
  225. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +11 -2
  226. data/lib/graphql/static_validation/validation_context.rb +21 -5
  227. data/lib/graphql/static_validation/validator.rb +9 -1
  228. data/lib/graphql/static_validation.rb +0 -1
  229. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -5
  230. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  231. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
  232. data/lib/graphql/subscriptions/event.rb +21 -4
  233. data/lib/graphql/subscriptions/serialize.rb +3 -1
  234. data/lib/graphql/subscriptions.rb +21 -17
  235. data/lib/graphql/testing/helpers.rb +161 -0
  236. data/lib/graphql/testing/mock_action_cable.rb +111 -0
  237. data/lib/graphql/testing.rb +3 -0
  238. data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
  239. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  240. data/lib/graphql/tracing/appoptics_trace.rb +7 -3
  241. data/lib/graphql/tracing/appoptics_tracing.rb +9 -2
  242. data/lib/graphql/tracing/appsignal_trace.rb +32 -59
  243. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  244. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  245. data/lib/graphql/tracing/data_dog_trace.rb +46 -162
  246. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  247. data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +74 -0
  248. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  249. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  250. data/lib/graphql/tracing/detailed_trace.rb +156 -0
  251. data/lib/graphql/tracing/legacy_hooks_trace.rb +75 -0
  252. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  253. data/lib/graphql/tracing/monitor_trace.rb +283 -0
  254. data/lib/graphql/tracing/new_relic_trace.rb +47 -54
  255. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  256. data/lib/graphql/tracing/notifications_trace.rb +183 -37
  257. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  258. data/lib/graphql/tracing/null_trace.rb +9 -0
  259. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  260. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  261. data/lib/graphql/tracing/perfetto_trace.rb +864 -0
  262. data/lib/graphql/tracing/platform_tracing.rb +3 -1
  263. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +5 -1
  264. data/lib/graphql/tracing/prometheus_trace.rb +73 -73
  265. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  266. data/lib/graphql/tracing/scout_trace.rb +32 -58
  267. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  268. data/lib/graphql/tracing/sentry_trace.rb +82 -0
  269. data/lib/graphql/tracing/statsd_trace.rb +33 -45
  270. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  271. data/lib/graphql/tracing/trace.rb +112 -1
  272. data/lib/graphql/tracing.rb +31 -28
  273. data/lib/graphql/type_kinds.rb +2 -1
  274. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  275. data/lib/graphql/types/relay/connection_behaviors.rb +44 -2
  276. data/lib/graphql/types/relay/edge_behaviors.rb +18 -0
  277. data/lib/graphql/types/relay/has_node_field.rb +13 -8
  278. data/lib/graphql/types/relay/has_nodes_field.rb +13 -8
  279. data/lib/graphql/types/relay/node_behaviors.rb +13 -2
  280. data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
  281. data/lib/graphql/types.rb +18 -10
  282. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  283. data/lib/graphql/unauthorized_error.rb +5 -1
  284. data/lib/graphql/version.rb +1 -1
  285. data/lib/graphql.rb +71 -54
  286. data/readme.md +12 -2
  287. metadata +233 -37
  288. data/lib/graphql/analysis/ast/analyzer.rb +0 -84
  289. data/lib/graphql/analysis/ast/field_usage.rb +0 -57
  290. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  291. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  292. data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
  293. data/lib/graphql/analysis/ast/query_depth.rb +0 -55
  294. data/lib/graphql/analysis/ast/visitor.rb +0 -276
  295. data/lib/graphql/analysis/ast.rb +0 -81
  296. data/lib/graphql/backtrace/inspect_result.rb +0 -50
  297. data/lib/graphql/backtrace/trace.rb +0 -96
  298. data/lib/graphql/backtrace/tracer.rb +0 -80
  299. data/lib/graphql/deprecation.rb +0 -9
  300. data/lib/graphql/filter.rb +0 -59
  301. data/lib/graphql/language/parser.y +0 -560
  302. data/lib/graphql/language/token.rb +0 -34
  303. data/lib/graphql/schema/base_64_bp.rb +0 -26
  304. data/lib/graphql/schema/invalid_type_error.rb +0 -7
  305. data/lib/graphql/schema/null_mask.rb +0 -11
  306. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
  307. data/lib/graphql/static_validation/type_stack.rb +0 -216
  308. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
@@ -8,6 +8,7 @@ module GraphQL
8
8
  include GraphQL::Schema::Member::HasArguments
9
9
  include GraphQL::Schema::Member::HasArguments::FieldConfigured
10
10
  include GraphQL::Schema::Member::HasAstNode
11
+ include GraphQL::Schema::Member::HasAuthorization
11
12
  include GraphQL::Schema::Member::HasPath
12
13
  include GraphQL::Schema::Member::HasValidators
13
14
  extend GraphQL::Schema::FindInheritedValue
@@ -41,6 +42,32 @@ module GraphQL
41
42
  end
42
43
  end
43
44
 
45
+ # @return [String, nil]
46
+ def deprecation_reason
47
+ super || @resolver_class&.deprecation_reason
48
+ end
49
+
50
+ def directives
51
+ if @resolver_class && !(r_dirs = @resolver_class.directives).empty?
52
+ if !(own_dirs = super).empty?
53
+ new_dirs = own_dirs.dup
54
+ r_dirs.each do |r_dir|
55
+ if r_dir.class.repeatable? ||
56
+ ( (r_dir_name = r_dir.graphql_name) &&
57
+ (!new_dirs.any? { |d| d.graphql_name == r_dir_name })
58
+ )
59
+ new_dirs << r_dir
60
+ end
61
+ end
62
+ new_dirs
63
+ else
64
+ r_dirs
65
+ end
66
+ else
67
+ super
68
+ end
69
+ end
70
+
44
71
  # @return [Class] The thing this field was defined on (type, mutation, resolver)
45
72
  attr_accessor :owner
46
73
 
@@ -69,7 +96,7 @@ module GraphQL
69
96
  end
70
97
 
71
98
  def inspect
72
- "#<#{self.class} #{path}#{all_argument_definitions.any? ? "(...)" : ""}: #{type.to_type_signature}>"
99
+ "#<#{self.class} #{path}#{!all_argument_definitions.empty? ? "(...)" : ""}: #{type.to_type_signature}>"
73
100
  end
74
101
 
75
102
  alias :mutation :resolver
@@ -83,48 +110,6 @@ module GraphQL
83
110
  end
84
111
  attr_writer :subscription_scope
85
112
 
86
- # Create a field instance from a list of arguments, keyword arguments, and a block.
87
- #
88
- # This method implements prioritization between the `resolver` or `mutation` defaults
89
- # and the local overrides via other keywords.
90
- #
91
- # It also normalizes positional arguments into keywords for {Schema::Field#initialize}.
92
- # @param resolver [Class] A {GraphQL::Schema::Resolver} class to use for field configuration
93
- # @param mutation [Class] A {GraphQL::Schema::Mutation} class to use for field configuration
94
- # @param subscription [Class] A {GraphQL::Schema::Subscription} class to use for field configuration
95
- # @return [GraphQL::Schema:Field] an instance of `self`
96
- # @see {.initialize} for other options
97
- def self.from_options(name = nil, type = nil, desc = nil, resolver: nil, mutation: nil, subscription: nil,**kwargs, &block)
98
- if (resolver_class = resolver || mutation || subscription)
99
- # Add a reference to that parent class
100
- kwargs[:resolver_class] = resolver_class
101
- end
102
-
103
- if name
104
- kwargs[:name] = name
105
- end
106
-
107
- if !type.nil?
108
- if desc
109
- if kwargs[:description]
110
- raise ArgumentError, "Provide description as a positional argument or `description:` keyword, but not both (#{desc.inspect}, #{kwargs[:description].inspect})"
111
- end
112
-
113
- kwargs[:description] = desc
114
- kwargs[:type] = type
115
- elsif (resolver || mutation) && type.is_a?(String)
116
- # The return type should be copied from the resolver, and the second positional argument is the description
117
- kwargs[:description] = type
118
- else
119
- kwargs[:type] = type
120
- end
121
- if type.is_a?(Class) && type < GraphQL::Schema::Mutation
122
- raise ArgumentError, "Use `field #{name.inspect}, mutation: Mutation, ...` to provide a mutation to this field instead"
123
- end
124
- end
125
- new(**kwargs, &block)
126
- end
127
-
128
113
  # Can be set with `connection: true|false` or inferred from a type name ending in `*Connection`
129
114
  # @return [Boolean] if true, this field will be wrapped with Relay connection behavior
130
115
  def connection?
@@ -134,11 +119,16 @@ module GraphQL
134
119
  Member::BuildType.to_type_name(@return_type_expr)
135
120
  elsif @resolver_class && @resolver_class.type
136
121
  Member::BuildType.to_type_name(@resolver_class.type)
137
- else
122
+ elsif type
138
123
  # As a last ditch, try to force loading the return type:
139
124
  type.unwrap.name
140
125
  end
141
- @connection = return_type_name.end_with?("Connection")
126
+ if return_type_name
127
+ @connection = return_type_name.end_with?("Connection") && return_type_name != "Connection"
128
+ else
129
+ # TODO set this when type is set by method
130
+ false # not loaded yet?
131
+ end
142
132
  else
143
133
  @connection
144
134
  end
@@ -195,6 +185,7 @@ module GraphQL
195
185
  # @param owner [Class] The type that this field belongs to
196
186
  # @param null [Boolean] (defaults to `true`) `true` if this field may return `null`, `false` if it is never `null`
197
187
  # @param description [String] Field description
188
+ # @param comment [String] Field comment
198
189
  # @param deprecation_reason [String] If present, the field is marked "deprecated" with this message
199
190
  # @param method [Symbol] The method to call on the underlying object to resolve this field (defaults to `name`)
200
191
  # @param hash_key [String, Symbol] The hash key to lookup on the underlying object (if its a Hash) to resolve this field (defaults to `name` or `name.to_s`)
@@ -202,6 +193,11 @@ module GraphQL
202
193
  # @param resolver_method [Symbol] The method on the type to call to resolve this field (defaults to `name`)
203
194
  # @param connection [Boolean] `true` if this field should get automagic connection behavior; default is to infer by `*Connection` in the return type name
204
195
  # @param connection_extension [Class] The extension to add, to implement connections. If `nil`, no extension is added.
196
+ # @param resolve_static [Symbol, true, nil] Used by {Schema.execute_next} to produce a single value, shared by all objects which resolve this field. Called on the owner type class with `context, **arguments`
197
+ # @param resolve_batch [Symbol, true, nil] Used by {Schema.execute_next} map `objects` to a same-sized Array of results. Called on the owner type class with `objects, context, **arguments`.
198
+ # @param resolve_each [Symbol, true, nil] Used by {Schema.execute_next} to get a value value for each item. Called on the owner type class with `object, context, **arguments`.
199
+ # @param resolve_legacy_instance_method [Symbol, true, nil] Used by {Schema.execute_next} to get a value value for each item. Calls an instance method on the object type class.
200
+ # @param dataload [Class, Hash] Shorthand for making dataloader calls
205
201
  # @param max_page_size [Integer, nil] For connections, the maximum number of items to return from this field, or `nil` to allow unlimited results.
206
202
  # @param default_page_size [Integer, nil] For connections, the default number of items to return from this field, or `nil` to return unlimited results.
207
203
  # @param introspection [Boolean] If true, this field will be marked as `#introspection?` and the name may begin with `__`
@@ -218,14 +214,19 @@ module GraphQL
218
214
  # @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
219
215
  # @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
220
216
  # @param validates [Array<Hash>] Configurations for validating this field
221
- # @fallback_value [Object] A fallback value if the method is not defined
222
- def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, &definition_block)
217
+ # @param fallback_value [Object] A fallback value if the method is not defined
218
+ # @param dynamic_introspection [Boolean] (Private, used by GraphQL-Ruby)
219
+ # @param relay_node_field [Boolean] (Private, used by GraphQL-Ruby)
220
+ # @param relay_nodes_field [Boolean] (Private, used by GraphQL-Ruby)
221
+ # @param extras [Array<:ast_node, :parent, :lookahead, :owner, :execution_errors, :graphql_name, :argument_details, Symbol>] Extra arguments to be injected into the resolver for this field
222
+ # @param definition_block [Proc] an additional block for configuring the field. Receive the field as a block param, or, if no block params are defined, then the block is `instance_eval`'d on the new {Field}.
223
+ def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, comment: NOT_CONFIGURED, deprecation_reason: nil, method: nil, resolve_legacy_instance_method: nil, resolve_static: nil, resolve_each: nil, resolve_batch: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, dataload: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, dynamic_introspection: false, &definition_block)
223
224
  if name.nil?
224
225
  raise ArgumentError, "missing first `name` argument or keyword `name:`"
225
226
  end
226
227
  if !(resolver_class)
227
- if type.nil?
228
- raise ArgumentError, "missing second `type` argument or keyword `type:`"
228
+ if type.nil? && !block_given?
229
+ raise ArgumentError, "missing second `type` argument, keyword `type:`, or a block containing `type(...)`"
229
230
  end
230
231
  end
231
232
  @original_name = name
@@ -235,6 +236,7 @@ module GraphQL
235
236
  @name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
236
237
  NameValidator.validate!(@name)
237
238
  @description = description
239
+ @comment = comment
238
240
  @type = @owner_type = @own_validators = @own_directives = @own_arguments = @arguments_statically_coercible = nil # these will be prepared later if necessary
239
241
 
240
242
  self.deprecation_reason = deprecation_reason
@@ -266,7 +268,38 @@ module GraphQL
266
268
  @method_str = -method_name.to_s
267
269
  @method_sym = method_name.to_sym
268
270
  @resolver_method = (resolver_method || name_s).to_sym
271
+
272
+ if resolve_static
273
+ @execution_next_mode = :resolve_static
274
+ @execution_next_mode_key = resolve_static == true ? @method_sym : resolve_static
275
+ elsif resolve_batch
276
+ @execution_next_mode = :resolve_batch
277
+ @execution_next_mode_key = resolve_batch == true ? @method_sym : resolve_batch
278
+ elsif resolve_each
279
+ @execution_next_mode = :resolve_each
280
+ @execution_next_mode_key = resolve_each == true ? @method_sym : resolve_each
281
+ elsif hash_key
282
+ @execution_next_mode = :hash_key
283
+ @execution_next_mode_key = hash_key
284
+ elsif dig
285
+ @execution_next_mode = :dig
286
+ @execution_next_mode_key = dig
287
+ elsif resolver_class
288
+ @execution_next_mode = :resolver_class
289
+ @execution_next_mode_key = resolver_class
290
+ elsif resolve_legacy_instance_method
291
+ @execution_next_mode = :resolve_legacy_instance_method
292
+ @execution_next_mode_key = resolve_legacy_instance_method == true ? @method_sym : resolve_legacy_instance_method
293
+ elsif dataload
294
+ @execution_next_mode = :dataload
295
+ @execution_next_mode_key = dataload
296
+ else
297
+ @execution_next_mode = :direct_send
298
+ @execution_next_mode_key = @method_sym
299
+ end
300
+
269
301
  @complexity = complexity
302
+ @dynamic_introspection = dynamic_introspection
270
303
  @return_type_expr = type
271
304
  @return_type_null = if !null.nil?
272
305
  null
@@ -289,6 +322,7 @@ module GraphQL
289
322
  @ast_node = ast_node
290
323
  @method_conflict_warning = method_conflict_warning
291
324
  @fallback_value = fallback_value
325
+ @definition_block = definition_block
292
326
 
293
327
  arguments.each do |name, arg|
294
328
  case arg
@@ -308,28 +342,17 @@ module GraphQL
308
342
 
309
343
  @extensions = EMPTY_ARRAY
310
344
  @call_after_define = false
311
- # This should run before connection extension,
312
- # but should it run after the definition block?
313
- if scoped?
314
- self.extension(ScopeExtension)
315
- end
316
-
317
- # The problem with putting this after the definition_block
318
- # is that it would override arguments
319
- if connection? && connection_extension
320
- self.extension(connection_extension)
321
- end
322
-
345
+ set_pagination_extensions(connection_extension: NOT_CONFIGURED.equal?(connection_extension) ? self.class.connection_extension : connection_extension)
323
346
  # Do this last so we have as much context as possible when initializing them:
324
- if extensions.any?
347
+ if !extensions.empty?
325
348
  self.extensions(extensions)
326
349
  end
327
350
 
328
- if resolver_class && resolver_class.extensions.any?
351
+ if resolver_class && !resolver_class.extensions.empty?
329
352
  self.extensions(resolver_class.extensions)
330
353
  end
331
354
 
332
- if directives.any?
355
+ if !directives.empty?
333
356
  directives.each do |(dir_class, options)|
334
357
  self.directive(dir_class, **options)
335
358
  end
@@ -339,18 +362,36 @@ module GraphQL
339
362
  self.validates(validates)
340
363
  end
341
364
 
342
- if block_given?
343
- if definition_block.arity == 1
344
- yield self
365
+ if @definition_block.nil?
366
+ self.extensions.each(&:after_define_apply)
367
+ @call_after_define = true
368
+ end
369
+ end
370
+
371
+ # @api private
372
+ attr_reader :execution_next_mode_key, :execution_next_mode
373
+
374
+ # Calls the definition block, if one was given.
375
+ # This is deferred so that references to the return type
376
+ # can be lazily evaluated, reducing Rails boot time.
377
+ # @return [self]
378
+ # @api private
379
+ def ensure_loaded
380
+ if @definition_block
381
+ if @definition_block.arity == 1
382
+ @definition_block.call(self)
345
383
  else
346
- instance_eval(&definition_block)
384
+ instance_exec(self, &@definition_block)
347
385
  end
386
+ self.extensions.each(&:after_define_apply)
387
+ @call_after_define = true
388
+ @definition_block = nil
348
389
  end
349
-
350
- self.extensions.each(&:after_define_apply)
351
- @call_after_define = true
390
+ self
352
391
  end
353
392
 
393
+ attr_accessor :dynamic_introspection
394
+
354
395
  # If true, subscription updates with this field can be shared between viewers
355
396
  # @return [Boolean, nil]
356
397
  # @see GraphQL::Subscriptions::BroadcastAnalyzer
@@ -378,6 +419,20 @@ module GraphQL
378
419
  end
379
420
  end
380
421
 
422
+ # @param text [String]
423
+ # @return [String, nil]
424
+ def comment(text = nil)
425
+ if text
426
+ @comment = text
427
+ elsif !NOT_CONFIGURED.equal?(@comment)
428
+ @comment
429
+ elsif @resolver_class
430
+ @resolver_class.comment
431
+ else
432
+ nil
433
+ end
434
+ end
435
+
381
436
  # Read extension instances from this field,
382
437
  # or add new classes/options to be initialized on this field.
383
438
  # Extensions are executed in the order they are added.
@@ -398,7 +453,7 @@ module GraphQL
398
453
  new_extensions.each do |extension_config|
399
454
  if extension_config.is_a?(Hash)
400
455
  extension_class, options = *extension_config.to_a[0]
401
- self.extension(extension_class, options)
456
+ self.extension(extension_class, **options)
402
457
  else
403
458
  self.extension(extension_config)
404
459
  end
@@ -418,7 +473,7 @@ module GraphQL
418
473
  # @param extension_class [Class] subclass of {Schema::FieldExtension}
419
474
  # @param options [Hash] if provided, given as `options:` when initializing `extension`.
420
475
  # @return [void]
421
- def extension(extension_class, options = nil)
476
+ def extension(extension_class, **options)
422
477
  extension_inst = extension_class.new(field: self, options: options)
423
478
  if @extensions.frozen?
424
479
  @extensions = @extensions.dup
@@ -439,7 +494,7 @@ module GraphQL
439
494
  if new_extras.nil?
440
495
  # Read the value
441
496
  field_extras = @extras
442
- if @resolver_class && @resolver_class.extras.any?
497
+ if @resolver_class && !@resolver_class.extras.empty?
443
498
  field_extras + @resolver_class.extras
444
499
  else
445
500
  field_extras
@@ -468,6 +523,8 @@ module GraphQL
468
523
  if arguments[:last] && (max_possible_page_size.nil? || arguments[:last] > max_possible_page_size)
469
524
  max_possible_page_size = arguments[:last]
470
525
  end
526
+ elsif arguments.is_a?(GraphQL::ExecutionError) || arguments.is_a?(GraphQL::UnauthorizedError)
527
+ raise arguments
471
528
  end
472
529
 
473
530
  if max_possible_page_size.nil?
@@ -480,41 +537,24 @@ module GraphQL
480
537
  metadata_complexity = 0
481
538
  lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
482
539
 
483
- if (page_info_lookahead = lookahead.selection(:page_info)).selected?
484
- metadata_complexity += 1 # pageInfo
485
- metadata_complexity += page_info_lookahead.selections.size # subfields
486
- end
487
-
488
- if lookahead.selects?(:total) || lookahead.selects?(:total_count) || lookahead.selects?(:count)
489
- metadata_complexity += 1
540
+ lookahead.selections.each do |next_lookahead|
541
+ # this includes `pageInfo`, `nodes` and `edges` and any custom fields
542
+ # TODO this doesn't support procs yet -- unlikely to need it.
543
+ metadata_complexity += next_lookahead.field.complexity
544
+ if next_lookahead.name != :nodes && next_lookahead.name != :edges
545
+ # subfields, eg, for pageInfo -- assumes no subselections
546
+ metadata_complexity += next_lookahead.selections.size
547
+ end
490
548
  end
491
549
 
492
- nodes_edges_complexity = 0
493
- nodes_edges_complexity += 1 if lookahead.selects?(:edges)
494
- nodes_edges_complexity += 1 if lookahead.selects?(:nodes)
495
-
496
550
  # Possible bug: selections on `edges` and `nodes` are _both_ multiplied here. Should they be?
497
- items_complexity = child_complexity - metadata_complexity - nodes_edges_complexity
498
- # Add 1 for _this_ field
499
- 1 + (max_possible_page_size * items_complexity) + metadata_complexity + nodes_edges_complexity
551
+ items_complexity = child_complexity - metadata_complexity
552
+ subfields_complexity = (max_possible_page_size * items_complexity) + metadata_complexity
553
+ # Apply this field's own complexity
554
+ apply_own_complexity_to(subfields_complexity, query, nodes)
500
555
  end
501
556
  else
502
- defined_complexity = complexity
503
- case defined_complexity
504
- when Proc
505
- arguments = query.arguments_for(nodes.first, self)
506
- if arguments.is_a?(GraphQL::ExecutionError)
507
- return child_complexity
508
- elsif arguments.respond_to?(:keyword_arguments)
509
- arguments = arguments.keyword_arguments
510
- end
511
-
512
- defined_complexity.call(query.context, arguments, child_complexity)
513
- when Numeric
514
- defined_complexity + child_complexity
515
- else
516
- raise("Invalid complexity: #{defined_complexity.inspect} on #{path} (#{inspect})")
517
- end
557
+ apply_own_complexity_to(child_complexity, query, nodes)
518
558
  end
519
559
  end
520
560
 
@@ -574,19 +614,40 @@ module GraphQL
574
614
  end
575
615
  end
576
616
 
617
+ def freeze
618
+ type
619
+ owner_type
620
+ arguments_statically_coercible?
621
+ connection?
622
+ super
623
+ end
624
+
577
625
  class MissingReturnTypeError < GraphQL::Error; end
578
626
  attr_writer :type
579
627
 
580
- def type
581
- if @resolver_class
582
- return_type = @return_type_expr || @resolver_class.type_expr
583
- if return_type.nil?
584
- raise MissingReturnTypeError, "Can't determine the return type for #{self.path} (it has `resolver: #{@resolver_class}`, perhaps that class is missing a `type ...` declaration, or perhaps its type causes a cyclical loading issue)"
628
+ # Get or set the return type of this field.
629
+ #
630
+ # It may return nil if no type was configured or if the given definition block wasn't called yet.
631
+ # @param new_type [Module, GraphQL::Schema::NonNull, GraphQL::Schema::List] A GraphQL return type
632
+ # @return [Module, GraphQL::Schema::NonNull, GraphQL::Schema::List, nil] the configured type for this field
633
+ def type(new_type = NOT_CONFIGURED)
634
+ if NOT_CONFIGURED.equal?(new_type)
635
+ if @resolver_class
636
+ return_type = @return_type_expr || @resolver_class.type_expr
637
+ if return_type.nil?
638
+ raise MissingReturnTypeError, "Can't determine the return type for #{self.path} (it has `resolver: #{@resolver_class}`, perhaps that class is missing a `type ...` declaration, or perhaps its type causes a cyclical loading issue)"
639
+ end
640
+ nullable = @return_type_null.nil? ? @resolver_class.null : @return_type_null
641
+ Member::BuildType.parse_type(return_type, null: nullable)
642
+ elsif !@return_type_expr.nil?
643
+ @type ||= Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
585
644
  end
586
- nullable = @return_type_null.nil? ? @resolver_class.null : @return_type_null
587
- Member::BuildType.parse_type(return_type, null: nullable)
588
645
  else
589
- @type ||= Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
646
+ @return_type_expr = new_type
647
+ # If `type` is set in the definition block, then the `connection_extension: ...` given as a keyword won't be used, hmm...
648
+ # Also, arguments added by `connection_extension` will clobber anything previously defined,
649
+ # so `type(...)` should go first.
650
+ set_pagination_extensions(connection_extension: self.class.connection_extension)
590
651
  end
591
652
  rescue GraphQL::Schema::InvalidDocumentError, MissingReturnTypeError => err
592
653
  # Let this propagate up
@@ -603,6 +664,13 @@ module GraphQL
603
664
  end
604
665
  end
605
666
 
667
+ def authorizes?(context)
668
+ method(:authorized?).owner != GraphQL::Schema::Field || (
669
+ (args = context.types.arguments(self)) &&
670
+ (args.any? { |a| a.authorizes?(context) })
671
+ )
672
+ end
673
+
606
674
  def authorized?(object, args, context)
607
675
  if @resolver_class
608
676
  # The resolver _instance_ will check itself during `resolve()`
@@ -618,7 +686,7 @@ module GraphQL
618
686
  using_arg_values = false
619
687
  end
620
688
 
621
- args = context.warden.arguments(self)
689
+ args = context.types.arguments(self)
622
690
  args.each do |arg|
623
691
  arg_key = arg.keyword
624
692
  if arg_values.key?(arg_key)
@@ -659,7 +727,7 @@ module GraphQL
659
727
  method_to_call = nil
660
728
  method_args = nil
661
729
 
662
- Schema::Validator.validate!(validators, application_object, query_ctx, args)
730
+ @own_validators && Schema::Validator.validate!(validators, application_object, query_ctx, args)
663
731
 
664
732
  query_ctx.query.after_lazy(self.authorized?(application_object, args, query_ctx)) do |is_authorized|
665
733
  if is_authorized
@@ -691,7 +759,7 @@ module GraphQL
691
759
  method_to_call = resolver_method
692
760
  method_receiver = obj
693
761
  # Call the method with kwargs, if there are any
694
- if ruby_kwargs.any?
762
+ if !ruby_kwargs.empty?
695
763
  obj.public_send(resolver_method, **ruby_kwargs)
696
764
  else
697
765
  obj.public_send(resolver_method)
@@ -701,7 +769,7 @@ module GraphQL
701
769
  inner_object.dig(*@dig_keys)
702
770
  elsif inner_object.key?(@method_sym)
703
771
  inner_object[@method_sym]
704
- elsif inner_object.key?(@method_str)
772
+ elsif inner_object.key?(@method_str) || !inner_object.default_proc.nil?
705
773
  inner_object[@method_str]
706
774
  elsif @fallback_value != NOT_CONFIGURED
707
775
  @fallback_value
@@ -711,7 +779,7 @@ module GraphQL
711
779
  elsif inner_object.respond_to?(@method_sym)
712
780
  method_to_call = @method_sym
713
781
  method_receiver = obj.object
714
- if ruby_kwargs.any?
782
+ if !ruby_kwargs.empty?
715
783
  inner_object.public_send(@method_sym, **ruby_kwargs)
716
784
  else
717
785
  inner_object.public_send(@method_sym)
@@ -798,7 +866,7 @@ module GraphQL
798
866
  unsatisfied_ruby_kwargs.clear
799
867
  end
800
868
 
801
- if unsatisfied_ruby_kwargs.any? || unsatisfied_method_params.any?
869
+ if !unsatisfied_ruby_kwargs.empty? || !unsatisfied_method_params.empty?
802
870
  raise FieldImplementationFailed.new, <<-ERR
803
871
  Failed to call `#{method_name.inspect}` on #{receiver.inspect} because the Ruby method params were incompatible with the GraphQL arguments:
804
872
 
@@ -857,6 +925,33 @@ ERR
857
925
  end
858
926
  end
859
927
 
928
+ public
929
+
930
+ def run_next_extensions_before_resolve(objs, args, ctx, extended, idx: 0, &block)
931
+ extension = @extensions[idx]
932
+ if extension
933
+ extension.resolve_next(objects: objs, arguments: args, context: ctx) do |extended_objs, extended_args, memo|
934
+ if memo
935
+ memos = extended.memos ||= {}
936
+ memos[idx] = memo
937
+ end
938
+
939
+ if (extras = extension.added_extras)
940
+ ae = extended.added_extras ||= []
941
+ ae.concat(extras)
942
+ end
943
+
944
+ extended.object = extended_objs
945
+ extended.arguments = extended_args
946
+ run_next_extensions_before_resolve(extended_objs, extended_args, ctx, extended, idx: idx + 1, &block)
947
+ end
948
+ else
949
+ yield(objs, args)
950
+ end
951
+ end
952
+
953
+ private
954
+
860
955
  def run_extensions_before_resolve(obj, args, ctx, extended, idx: 0)
861
956
  extension = @extensions[idx]
862
957
  if extension
@@ -879,6 +974,38 @@ ERR
879
974
  yield(obj, args)
880
975
  end
881
976
  end
977
+
978
+ def apply_own_complexity_to(child_complexity, query, nodes)
979
+ case (own_complexity = complexity)
980
+ when Numeric
981
+ own_complexity + child_complexity
982
+ when Proc
983
+ arguments = query.arguments_for(nodes.first, self)
984
+ if arguments.is_a?(GraphQL::ExecutionError)
985
+ return child_complexity
986
+ elsif arguments.respond_to?(:keyword_arguments)
987
+ arguments = arguments.keyword_arguments
988
+ end
989
+
990
+ own_complexity.call(query.context, arguments, child_complexity)
991
+ else
992
+ raise ArgumentError, "Invalid complexity for #{self.path}: #{own_complexity.inspect}"
993
+ end
994
+ end
995
+
996
+ def set_pagination_extensions(connection_extension:)
997
+ # This should run before connection extension,
998
+ # but should it run after the definition block?
999
+ if scoped?
1000
+ self.extension(ScopeExtension, call_after_define: false)
1001
+ end
1002
+
1003
+ # The problem with putting this after the definition_block
1004
+ # is that it would override arguments
1005
+ if connection? && connection_extension
1006
+ self.extension(connection_extension, call_after_define: false)
1007
+ end
1008
+ end
882
1009
  end
883
1010
  end
884
1011
  end
@@ -104,7 +104,7 @@ module GraphQL
104
104
  end
105
105
  end
106
106
  end
107
- if (extras = self.class.extras).any?
107
+ if !(extras = self.class.extras).empty?
108
108
  @added_extras = extras - field.extras
109
109
  field.extras(@added_extras)
110
110
  else
@@ -134,6 +134,24 @@ module GraphQL
134
134
  yield(object, arguments, nil)
135
135
  end
136
136
 
137
+ # Called before batch-resolving {#field}. It should either:
138
+ #
139
+ # - `yield` values to continue execution; OR
140
+ # - return something else to shortcut field execution.
141
+ #
142
+ # Whatever this method returns will be used for execution.
143
+ #
144
+ # @param objects [Array<Object>] The objects the field is being resolved on
145
+ # @param arguments [Hash] Ruby keyword arguments for resolving this field
146
+ # @param context [Query::Context] the context for this query
147
+ # @yieldparam objects [Array<Object>] The objects to continue resolving the field on. Length must be the same as passed-in `objects:`
148
+ # @yieldparam arguments [Hash] The keyword arguments to continue resolving with
149
+ # @yieldparam memo [Object] Any extension-specific value which will be passed to {#after_resolve} later
150
+ # @return [Array<Object>] The return value for this field, length matching passed-in `objects:`.
151
+ def resolve_next(objects:, arguments:, context:)
152
+ yield(objects, arguments, nil)
153
+ end
154
+
137
155
  # Called after {#field} was resolved, and after any lazy values (like `Promise`s) were synced,
138
156
  # but before the value was added to the GraphQL response.
139
157
  #
@@ -148,6 +166,21 @@ module GraphQL
148
166
  def after_resolve(object:, arguments:, context:, value:, memo:)
149
167
  value
150
168
  end
169
+
170
+ # Called after {#field} was batch-resolved, and after any lazy values (like `Promise`s) were synced,
171
+ # but before the value was added to the GraphQL response.
172
+ #
173
+ # Whatever this hook returns will be used as the return value.
174
+ #
175
+ # @param objects [Array<Object>] The objects the field is being resolved on
176
+ # @param arguments [Hash] Ruby keyword arguments for resolving this field
177
+ # @param context [Query::Context] the context for this query
178
+ # @param values [Array<Object>] Whatever the field returned, one for each of `objects`
179
+ # @param memo [Object] The third value yielded by {#resolve}, or `nil` if there wasn't one
180
+ # @return [Array<Object>] The return values for this field, length matching `objects:`.
181
+ def after_resolve_next(objects:, arguments:, context:, values:, memo:)
182
+ values
183
+ end
151
184
  end
152
185
  end
153
186
  end