graphql 1.12.12 → 2.2.14

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 (392) 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 +14 -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 +6 -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 +19 -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/ast/analyzer.rb +7 -0
  48. data/lib/graphql/analysis/ast/field_usage.rb +55 -1
  49. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -1
  50. data/lib/graphql/analysis/ast/query_complexity.rb +88 -140
  51. data/lib/graphql/analysis/ast/query_depth.rb +7 -3
  52. data/lib/graphql/analysis/ast/visitor.rb +50 -42
  53. data/lib/graphql/analysis/ast.rb +26 -23
  54. data/lib/graphql/analysis.rb +0 -7
  55. data/lib/graphql/backtrace/table.rb +4 -22
  56. data/lib/graphql/backtrace/trace.rb +93 -0
  57. data/lib/graphql/backtrace/tracer.rb +8 -6
  58. data/lib/graphql/backtrace.rb +3 -8
  59. data/lib/graphql/coercion_error.rb +1 -9
  60. data/lib/graphql/dataloader/async_dataloader.rb +85 -0
  61. data/lib/graphql/dataloader/null_dataloader.rb +3 -1
  62. data/lib/graphql/dataloader/request.rb +5 -0
  63. data/lib/graphql/dataloader/source.rb +120 -31
  64. data/lib/graphql/dataloader.rb +168 -142
  65. data/lib/graphql/date_encoding_error.rb +16 -0
  66. data/lib/graphql/dig.rb +1 -1
  67. data/lib/graphql/duration_encoding_error.rb +16 -0
  68. data/lib/graphql/execution/errors.rb +12 -81
  69. data/lib/graphql/execution/interpreter/arguments.rb +2 -2
  70. data/lib/graphql/execution/interpreter/arguments_cache.rb +36 -34
  71. data/lib/graphql/execution/interpreter/resolve.rb +32 -2
  72. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +170 -0
  73. data/lib/graphql/execution/interpreter/runtime.rb +414 -341
  74. data/lib/graphql/execution/interpreter.rb +122 -80
  75. data/lib/graphql/execution/lazy.rb +11 -21
  76. data/lib/graphql/execution/lookahead.rb +125 -54
  77. data/lib/graphql/execution/multiplex.rb +4 -172
  78. data/lib/graphql/execution.rb +11 -4
  79. data/lib/graphql/integer_encoding_error.rb +18 -2
  80. data/lib/graphql/introspection/directive_location_enum.rb +2 -2
  81. data/lib/graphql/introspection/directive_type.rb +5 -3
  82. data/lib/graphql/introspection/dynamic_fields.rb +3 -8
  83. data/lib/graphql/introspection/entry_points.rb +11 -18
  84. data/lib/graphql/introspection/enum_value_type.rb +2 -2
  85. data/lib/graphql/introspection/field_type.rb +3 -3
  86. data/lib/graphql/introspection/input_value_type.rb +10 -4
  87. data/lib/graphql/introspection/schema_type.rb +12 -5
  88. data/lib/graphql/introspection/type_type.rb +25 -12
  89. data/lib/graphql/introspection.rb +6 -2
  90. data/lib/graphql/language/block_string.rb +37 -25
  91. data/lib/graphql/language/definition_slice.rb +1 -1
  92. data/lib/graphql/language/document_from_schema_definition.rb +78 -65
  93. data/lib/graphql/language/lexer.rb +345 -1467
  94. data/lib/graphql/language/nodes.rb +145 -91
  95. data/lib/graphql/language/parser.rb +701 -1921
  96. data/lib/graphql/language/printer.rb +351 -155
  97. data/lib/graphql/language/sanitized_printer.rb +25 -27
  98. data/lib/graphql/language/static_visitor.rb +167 -0
  99. data/lib/graphql/language/token.rb +0 -4
  100. data/lib/graphql/language/visitor.rb +188 -141
  101. data/lib/graphql/language.rb +1 -0
  102. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  103. data/lib/graphql/name_validator.rb +0 -4
  104. data/lib/graphql/pagination/active_record_relation_connection.rb +37 -8
  105. data/lib/graphql/pagination/array_connection.rb +8 -6
  106. data/lib/graphql/pagination/connection.rb +61 -7
  107. data/lib/graphql/pagination/connections.rb +22 -23
  108. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  109. data/lib/graphql/pagination/relation_connection.rb +60 -28
  110. data/lib/graphql/query/context/scoped_context.rb +101 -0
  111. data/lib/graphql/query/context.rb +109 -189
  112. data/lib/graphql/query/input_validation_result.rb +10 -1
  113. data/lib/graphql/query/null_context.rb +14 -29
  114. data/lib/graphql/query/validation_pipeline.rb +15 -39
  115. data/lib/graphql/query/variable_validation_error.rb +2 -2
  116. data/lib/graphql/query/variables.rb +35 -17
  117. data/lib/graphql/query.rb +78 -65
  118. data/lib/graphql/railtie.rb +8 -109
  119. data/lib/graphql/rake_task/validate.rb +1 -1
  120. data/lib/graphql/rake_task.rb +30 -11
  121. data/lib/graphql/relay/range_add.rb +9 -16
  122. data/lib/graphql/relay.rb +0 -15
  123. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  124. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  125. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  126. data/lib/graphql/rubocop.rb +4 -0
  127. data/lib/graphql/schema/addition.rb +78 -45
  128. data/lib/graphql/schema/always_visible.rb +10 -0
  129. data/lib/graphql/schema/argument.rb +134 -80
  130. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  131. data/lib/graphql/schema/build_from_definition.rb +60 -38
  132. data/lib/graphql/schema/directive/feature.rb +1 -1
  133. data/lib/graphql/schema/directive/flagged.rb +2 -2
  134. data/lib/graphql/schema/directive/include.rb +1 -1
  135. data/lib/graphql/schema/directive/one_of.rb +24 -0
  136. data/lib/graphql/schema/directive/skip.rb +1 -1
  137. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  138. data/lib/graphql/schema/directive/transform.rb +2 -2
  139. data/lib/graphql/schema/directive.rb +33 -21
  140. data/lib/graphql/schema/enum.rb +93 -46
  141. data/lib/graphql/schema/enum_value.rb +4 -21
  142. data/lib/graphql/schema/field/connection_extension.rb +6 -16
  143. data/lib/graphql/schema/field/scope_extension.rb +8 -1
  144. data/lib/graphql/schema/field.rb +432 -337
  145. data/lib/graphql/schema/field_extension.rb +86 -2
  146. data/lib/graphql/schema/find_inherited_value.rb +3 -7
  147. data/lib/graphql/schema/finder.rb +5 -5
  148. data/lib/graphql/schema/has_single_input_argument.rb +156 -0
  149. data/lib/graphql/schema/input_object.rb +88 -90
  150. data/lib/graphql/schema/interface.rb +19 -59
  151. data/lib/graphql/schema/introspection_system.rb +6 -9
  152. data/lib/graphql/schema/late_bound_type.rb +8 -2
  153. data/lib/graphql/schema/list.rb +18 -7
  154. data/lib/graphql/schema/loader.rb +3 -3
  155. data/lib/graphql/schema/member/base_dsl_methods.rb +18 -19
  156. data/lib/graphql/schema/member/build_type.rb +16 -13
  157. data/lib/graphql/schema/member/has_arguments.rb +288 -84
  158. data/lib/graphql/schema/member/has_ast_node.rb +12 -0
  159. data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
  160. data/lib/graphql/schema/member/has_directives.rb +81 -61
  161. data/lib/graphql/schema/member/has_fields.rb +149 -31
  162. data/lib/graphql/schema/member/has_interfaces.rb +143 -0
  163. data/lib/graphql/schema/member/has_validators.rb +32 -6
  164. data/lib/graphql/schema/member/relay_shortcuts.rb +47 -2
  165. data/lib/graphql/schema/member/scoped.rb +19 -0
  166. data/lib/graphql/schema/member/type_system_helpers.rb +17 -0
  167. data/lib/graphql/schema/member/validates_input.rb +6 -6
  168. data/lib/graphql/schema/member.rb +1 -6
  169. data/lib/graphql/schema/mutation.rb +0 -9
  170. data/lib/graphql/schema/non_null.rb +7 -7
  171. data/lib/graphql/schema/object.rb +30 -119
  172. data/lib/graphql/schema/printer.rb +23 -25
  173. data/lib/graphql/schema/relay_classic_mutation.rb +13 -90
  174. data/lib/graphql/schema/resolver/has_payload_type.rb +46 -11
  175. data/lib/graphql/schema/resolver.rb +101 -102
  176. data/lib/graphql/schema/scalar.rb +20 -21
  177. data/lib/graphql/schema/subscription.rb +45 -17
  178. data/lib/graphql/schema/timeout.rb +25 -29
  179. data/lib/graphql/schema/type_expression.rb +1 -1
  180. data/lib/graphql/schema/type_membership.rb +21 -4
  181. data/lib/graphql/schema/union.rb +15 -15
  182. data/lib/graphql/schema/unique_within_type.rb +1 -1
  183. data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
  184. data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
  185. data/lib/graphql/schema/validator/exclusion_validator.rb +3 -1
  186. data/lib/graphql/schema/validator/format_validator.rb +4 -5
  187. data/lib/graphql/schema/validator/inclusion_validator.rb +3 -1
  188. data/lib/graphql/schema/validator/length_validator.rb +5 -3
  189. data/lib/graphql/schema/validator/numericality_validator.rb +13 -2
  190. data/lib/graphql/schema/validator/required_validator.rb +29 -15
  191. data/lib/graphql/schema/validator.rb +35 -27
  192. data/lib/graphql/schema/warden.rb +259 -132
  193. data/lib/graphql/schema/wrapper.rb +0 -5
  194. data/lib/graphql/schema.rb +658 -989
  195. data/lib/graphql/static_validation/all_rules.rb +3 -1
  196. data/lib/graphql/static_validation/base_visitor.rb +14 -28
  197. data/lib/graphql/static_validation/definition_dependencies.rb +7 -2
  198. data/lib/graphql/static_validation/error.rb +3 -1
  199. data/lib/graphql/static_validation/literal_validator.rb +21 -4
  200. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  201. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
  202. data/lib/graphql/static_validation/rules/directives_are_defined.rb +11 -5
  203. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +13 -13
  204. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -4
  205. data/lib/graphql/static_validation/rules/fields_will_merge.rb +54 -28
  206. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
  207. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
  208. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +66 -0
  209. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -0
  210. data/lib/graphql/static_validation/rules/query_root_exists.rb +17 -0
  211. data/lib/graphql/static_validation/rules/query_root_exists_error.rb +26 -0
  212. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +5 -3
  213. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -4
  214. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +13 -7
  215. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +1 -1
  216. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +13 -7
  217. data/lib/graphql/static_validation/validation_context.rb +16 -6
  218. data/lib/graphql/static_validation/validator.rb +11 -27
  219. data/lib/graphql/static_validation.rb +0 -3
  220. data/lib/graphql/string_encoding_error.rb +13 -3
  221. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +46 -9
  222. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +38 -1
  223. data/lib/graphql/subscriptions/event.rb +75 -37
  224. data/lib/graphql/subscriptions/serialize.rb +25 -3
  225. data/lib/graphql/subscriptions.rb +59 -47
  226. data/lib/graphql/testing/helpers.rb +129 -0
  227. data/lib/graphql/testing.rb +2 -0
  228. data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
  229. data/lib/graphql/tracing/active_support_notifications_tracing.rb +6 -20
  230. data/lib/graphql/tracing/appoptics_trace.rb +251 -0
  231. data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
  232. data/lib/graphql/tracing/appsignal_trace.rb +77 -0
  233. data/lib/graphql/tracing/appsignal_tracing.rb +15 -0
  234. data/lib/graphql/tracing/data_dog_trace.rb +183 -0
  235. data/lib/graphql/tracing/data_dog_tracing.rb +25 -15
  236. data/lib/graphql/{execution/instrumentation.rb → tracing/legacy_hooks_trace.rb} +10 -28
  237. data/lib/graphql/tracing/legacy_trace.rb +69 -0
  238. data/lib/graphql/tracing/new_relic_trace.rb +75 -0
  239. data/lib/graphql/tracing/notifications_trace.rb +45 -0
  240. data/lib/graphql/tracing/notifications_tracing.rb +59 -0
  241. data/lib/graphql/tracing/platform_trace.rb +118 -0
  242. data/lib/graphql/tracing/platform_tracing.rb +46 -49
  243. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +4 -2
  244. data/lib/graphql/tracing/prometheus_trace.rb +89 -0
  245. data/lib/graphql/tracing/prometheus_tracing.rb +3 -3
  246. data/lib/graphql/tracing/scout_trace.rb +72 -0
  247. data/lib/graphql/tracing/sentry_trace.rb +112 -0
  248. data/lib/graphql/tracing/statsd_trace.rb +56 -0
  249. data/lib/graphql/tracing/trace.rb +76 -0
  250. data/lib/graphql/tracing.rb +20 -41
  251. data/lib/graphql/type_kinds.rb +6 -3
  252. data/lib/graphql/types/big_int.rb +5 -1
  253. data/lib/graphql/types/int.rb +1 -1
  254. data/lib/graphql/types/iso_8601_date.rb +17 -6
  255. data/lib/graphql/types/iso_8601_date_time.rb +12 -1
  256. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  257. data/lib/graphql/types/relay/base_connection.rb +16 -6
  258. data/lib/graphql/types/relay/connection_behaviors.rb +82 -32
  259. data/lib/graphql/types/relay/edge_behaviors.rb +36 -7
  260. data/lib/graphql/types/relay/has_node_field.rb +2 -2
  261. data/lib/graphql/types/relay/has_nodes_field.rb +2 -2
  262. data/lib/graphql/types/relay/node_behaviors.rb +12 -2
  263. data/lib/graphql/types/relay/page_info_behaviors.rb +7 -2
  264. data/lib/graphql/types/relay.rb +0 -3
  265. data/lib/graphql/types/string.rb +2 -2
  266. data/lib/graphql/types.rb +1 -0
  267. data/lib/graphql/unauthorized_error.rb +1 -1
  268. data/lib/graphql/version.rb +1 -1
  269. data/lib/graphql.rb +33 -95
  270. data/readme.md +13 -6
  271. metadata +102 -185
  272. data/lib/graphql/analysis/analyze_query.rb +0 -98
  273. data/lib/graphql/analysis/field_usage.rb +0 -45
  274. data/lib/graphql/analysis/max_query_complexity.rb +0 -26
  275. data/lib/graphql/analysis/max_query_depth.rb +0 -26
  276. data/lib/graphql/analysis/query_complexity.rb +0 -88
  277. data/lib/graphql/analysis/query_depth.rb +0 -43
  278. data/lib/graphql/analysis/reducer_state.rb +0 -48
  279. data/lib/graphql/argument.rb +0 -131
  280. data/lib/graphql/authorization.rb +0 -82
  281. data/lib/graphql/backtrace/legacy_tracer.rb +0 -56
  282. data/lib/graphql/backwards_compatibility.rb +0 -61
  283. data/lib/graphql/base_type.rb +0 -230
  284. data/lib/graphql/boolean_type.rb +0 -2
  285. data/lib/graphql/compatibility/execution_specification/counter_schema.rb +0 -53
  286. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +0 -200
  287. data/lib/graphql/compatibility/execution_specification.rb +0 -436
  288. data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +0 -111
  289. data/lib/graphql/compatibility/lazy_execution_specification.rb +0 -215
  290. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +0 -87
  291. data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +0 -79
  292. data/lib/graphql/compatibility/query_parser_specification.rb +0 -266
  293. data/lib/graphql/compatibility/schema_parser_specification.rb +0 -682
  294. data/lib/graphql/compatibility.rb +0 -5
  295. data/lib/graphql/define/assign_argument.rb +0 -12
  296. data/lib/graphql/define/assign_connection.rb +0 -13
  297. data/lib/graphql/define/assign_enum_value.rb +0 -18
  298. data/lib/graphql/define/assign_global_id_field.rb +0 -11
  299. data/lib/graphql/define/assign_mutation_function.rb +0 -34
  300. data/lib/graphql/define/assign_object_field.rb +0 -42
  301. data/lib/graphql/define/defined_object_proxy.rb +0 -53
  302. data/lib/graphql/define/instance_definable.rb +0 -240
  303. data/lib/graphql/define/no_definition_error.rb +0 -7
  304. data/lib/graphql/define/non_null_with_bang.rb +0 -16
  305. data/lib/graphql/define/type_definer.rb +0 -31
  306. data/lib/graphql/define.rb +0 -31
  307. data/lib/graphql/deprecated_dsl.rb +0 -47
  308. data/lib/graphql/deprecation.rb +0 -13
  309. data/lib/graphql/directive/deprecated_directive.rb +0 -2
  310. data/lib/graphql/directive/include_directive.rb +0 -2
  311. data/lib/graphql/directive/skip_directive.rb +0 -2
  312. data/lib/graphql/directive.rb +0 -111
  313. data/lib/graphql/enum_type.rb +0 -129
  314. data/lib/graphql/execution/execute.rb +0 -333
  315. data/lib/graphql/execution/flatten.rb +0 -40
  316. data/lib/graphql/execution/lazy/resolve.rb +0 -91
  317. data/lib/graphql/execution/typecast.rb +0 -50
  318. data/lib/graphql/field/resolve.rb +0 -59
  319. data/lib/graphql/field.rb +0 -226
  320. data/lib/graphql/filter.rb +0 -53
  321. data/lib/graphql/float_type.rb +0 -2
  322. data/lib/graphql/function.rb +0 -128
  323. data/lib/graphql/id_type.rb +0 -2
  324. data/lib/graphql/input_object_type.rb +0 -138
  325. data/lib/graphql/int_type.rb +0 -2
  326. data/lib/graphql/interface_type.rb +0 -72
  327. data/lib/graphql/internal_representation/document.rb +0 -27
  328. data/lib/graphql/internal_representation/node.rb +0 -206
  329. data/lib/graphql/internal_representation/print.rb +0 -51
  330. data/lib/graphql/internal_representation/rewrite.rb +0 -184
  331. data/lib/graphql/internal_representation/scope.rb +0 -88
  332. data/lib/graphql/internal_representation/visit.rb +0 -36
  333. data/lib/graphql/internal_representation.rb +0 -7
  334. data/lib/graphql/language/lexer.rl +0 -262
  335. data/lib/graphql/language/parser.y +0 -543
  336. data/lib/graphql/list_type.rb +0 -80
  337. data/lib/graphql/non_null_type.rb +0 -71
  338. data/lib/graphql/object_type.rb +0 -130
  339. data/lib/graphql/query/arguments.rb +0 -189
  340. data/lib/graphql/query/arguments_cache.rb +0 -24
  341. data/lib/graphql/query/executor.rb +0 -52
  342. data/lib/graphql/query/literal_input.rb +0 -136
  343. data/lib/graphql/query/serial_execution/field_resolution.rb +0 -92
  344. data/lib/graphql/query/serial_execution/operation_resolution.rb +0 -19
  345. data/lib/graphql/query/serial_execution/selection_resolution.rb +0 -23
  346. data/lib/graphql/query/serial_execution/value_resolution.rb +0 -87
  347. data/lib/graphql/query/serial_execution.rb +0 -40
  348. data/lib/graphql/relay/array_connection.rb +0 -83
  349. data/lib/graphql/relay/base_connection.rb +0 -189
  350. data/lib/graphql/relay/connection_instrumentation.rb +0 -54
  351. data/lib/graphql/relay/connection_resolve.rb +0 -43
  352. data/lib/graphql/relay/connection_type.rb +0 -41
  353. data/lib/graphql/relay/edge.rb +0 -27
  354. data/lib/graphql/relay/edge_type.rb +0 -19
  355. data/lib/graphql/relay/edges_instrumentation.rb +0 -40
  356. data/lib/graphql/relay/global_id_resolve.rb +0 -18
  357. data/lib/graphql/relay/mongo_relation_connection.rb +0 -50
  358. data/lib/graphql/relay/mutation/instrumentation.rb +0 -23
  359. data/lib/graphql/relay/mutation/resolve.rb +0 -56
  360. data/lib/graphql/relay/mutation/result.rb +0 -38
  361. data/lib/graphql/relay/mutation.rb +0 -106
  362. data/lib/graphql/relay/node.rb +0 -39
  363. data/lib/graphql/relay/page_info.rb +0 -7
  364. data/lib/graphql/relay/relation_connection.rb +0 -188
  365. data/lib/graphql/relay/type_extensions.rb +0 -32
  366. data/lib/graphql/scalar_type.rb +0 -91
  367. data/lib/graphql/schema/base_64_bp.rb +0 -26
  368. data/lib/graphql/schema/catchall_middleware.rb +0 -35
  369. data/lib/graphql/schema/default_parse_error.rb +0 -10
  370. data/lib/graphql/schema/default_type_error.rb +0 -17
  371. data/lib/graphql/schema/member/accepts_definition.rb +0 -152
  372. data/lib/graphql/schema/member/cached_graphql_definition.rb +0 -31
  373. data/lib/graphql/schema/member/instrumentation.rb +0 -131
  374. data/lib/graphql/schema/middleware_chain.rb +0 -82
  375. data/lib/graphql/schema/possible_types.rb +0 -44
  376. data/lib/graphql/schema/rescue_middleware.rb +0 -60
  377. data/lib/graphql/schema/timeout_middleware.rb +0 -88
  378. data/lib/graphql/schema/traversal.rb +0 -228
  379. data/lib/graphql/schema/validation.rb +0 -313
  380. data/lib/graphql/static_validation/default_visitor.rb +0 -15
  381. data/lib/graphql/static_validation/no_validate_visitor.rb +0 -10
  382. data/lib/graphql/static_validation/type_stack.rb +0 -216
  383. data/lib/graphql/string_type.rb +0 -2
  384. data/lib/graphql/subscriptions/instrumentation.rb +0 -79
  385. data/lib/graphql/subscriptions/subscription_root.rb +0 -76
  386. data/lib/graphql/tracing/skylight_tracing.rb +0 -70
  387. data/lib/graphql/types/relay/default_relay.rb +0 -27
  388. data/lib/graphql/types/relay/node_field.rb +0 -25
  389. data/lib/graphql/types/relay/nodes_field.rb +0 -27
  390. data/lib/graphql/union_type.rb +0 -115
  391. data/lib/graphql/upgrader/member.rb +0 -937
  392. data/lib/graphql/upgrader/schema.rb +0 -38
@@ -5,21 +5,18 @@ require "graphql/schema/field/scope_extension"
5
5
  module GraphQL
6
6
  class Schema
7
7
  class Field
8
- if !String.method_defined?(:-@)
9
- using GraphQL::StringDedupBackport
10
- end
11
-
12
- include GraphQL::Schema::Member::CachedGraphQLDefinition
13
- include GraphQL::Schema::Member::AcceptsDefinition
14
8
  include GraphQL::Schema::Member::HasArguments
9
+ include GraphQL::Schema::Member::HasArguments::FieldConfigured
15
10
  include GraphQL::Schema::Member::HasAstNode
16
11
  include GraphQL::Schema::Member::HasPath
17
12
  include GraphQL::Schema::Member::HasValidators
18
13
  extend GraphQL::Schema::FindInheritedValue
19
- include GraphQL::Schema::FindInheritedValue::EmptyObjects
14
+ include GraphQL::EmptyObjects
20
15
  include GraphQL::Schema::Member::HasDirectives
21
16
  include GraphQL::Schema::Member::HasDeprecationReason
22
17
 
18
+ class FieldImplementationFailed < GraphQL::Error; end
19
+
23
20
  # @return [String] the GraphQL name for this field, camelized unless `camelize: false` is provided
24
21
  attr_reader :name
25
22
  alias :graphql_name :name
@@ -32,15 +29,26 @@ module GraphQL
32
29
  # @return [String] Method or hash key on the underlying object to look up
33
30
  attr_reader :method_str
34
31
 
32
+ attr_reader :hash_key
33
+ attr_reader :dig_keys
34
+
35
35
  # @return [Symbol] The method on the type to look up
36
- attr_reader :resolver_method
36
+ def resolver_method
37
+ if @resolver_class
38
+ @resolver_class.resolver_method
39
+ else
40
+ @resolver_method
41
+ end
42
+ end
37
43
 
38
44
  # @return [Class] The thing this field was defined on (type, mutation, resolver)
39
45
  attr_accessor :owner
40
46
 
41
47
  # @return [Class] The GraphQL type this field belongs to. (For fields defined on mutations, it's the payload type)
42
48
  def owner_type
43
- @owner_type ||= if owner < GraphQL::Schema::Mutation
49
+ @owner_type ||= if owner.nil?
50
+ raise GraphQL::InvariantError, "Field #{original_name.inspect} (graphql name: #{graphql_name.inspect}) has no owner, but all fields should have an owner. How did this happen?!"
51
+ elsif owner < GraphQL::Schema::Mutation
44
52
  owner.payload_type
45
53
  else
46
54
  owner
@@ -61,7 +69,7 @@ module GraphQL
61
69
  end
62
70
 
63
71
  def inspect
64
- "#<#{self.class} #{path}#{arguments.any? ? "(...)" : ""}: #{type.to_type_signature}>"
72
+ "#<#{self.class} #{path}#{all_argument_definitions.any? ? "(...)" : ""}: #{type.to_type_signature}>"
65
73
  end
66
74
 
67
75
  alias :mutation :resolver
@@ -70,7 +78,10 @@ module GraphQL
70
78
  attr_reader :trace
71
79
 
72
80
  # @return [String, nil]
73
- attr_accessor :subscription_scope
81
+ def subscription_scope
82
+ @subscription_scope || (@resolver_class.respond_to?(:subscription_scope) ? @resolver_class.subscription_scope : nil)
83
+ end
84
+ attr_writer :subscription_scope
74
85
 
75
86
  # Create a field instance from a list of arguments, keyword arguments, and a block.
76
87
  #
@@ -81,24 +92,12 @@ module GraphQL
81
92
  # @param resolver [Class] A {GraphQL::Schema::Resolver} class to use for field configuration
82
93
  # @param mutation [Class] A {GraphQL::Schema::Mutation} class to use for field configuration
83
94
  # @param subscription [Class] A {GraphQL::Schema::Subscription} class to use for field configuration
84
- # @return [GraphQL::Schema:Field] an instance of `self
95
+ # @return [GraphQL::Schema:Field] an instance of `self`
85
96
  # @see {.initialize} for other options
86
97
  def self.from_options(name = nil, type = nil, desc = nil, resolver: nil, mutation: nil, subscription: nil,**kwargs, &block)
87
- if kwargs[:field]
88
- if kwargs[:field].is_a?(GraphQL::Field) && kwargs[:field] == GraphQL::Types::Relay::NodeField.graphql_definition
89
- GraphQL::Deprecation.warn("Legacy-style `GraphQL::Relay::Node.field` is being added to a class-based type. See `GraphQL::Types::Relay::NodeField` for a replacement.")
90
- return GraphQL::Types::Relay::NodeField
91
- elsif kwargs[:field].is_a?(GraphQL::Field) && kwargs[:field] == GraphQL::Types::Relay::NodesField.graphql_definition
92
- GraphQL::Deprecation.warn("Legacy-style `GraphQL::Relay::Node.plural_field` is being added to a class-based type. See `GraphQL::Types::Relay::NodesField` for a replacement.")
93
- return GraphQL::Types::Relay::NodesField
94
- end
95
- end
96
-
97
- if (parent_config = resolver || mutation || subscription)
98
- # Get the parent config, merge in local overrides
99
- kwargs = parent_config.field_options.merge(kwargs)
98
+ if (resolver_class = resolver || mutation || subscription)
100
99
  # Add a reference to that parent class
101
- kwargs[:resolver_class] = parent_config
100
+ kwargs[:resolver_class] = resolver_class
102
101
  end
103
102
 
104
103
  if name
@@ -106,9 +105,6 @@ module GraphQL
106
105
  end
107
106
 
108
107
  if !type.nil?
109
- if type.is_a?(GraphQL::Field)
110
- raise ArgumentError, "A GraphQL::Field was passed as the second argument, use the `field:` keyword for this instead."
111
- end
112
108
  if desc
113
109
  if kwargs[:description]
114
110
  raise ArgumentError, "Provide description as a positional argument or `description:` keyword, but not both (#{desc.inspect}, #{kwargs[:description].inspect})"
@@ -116,12 +112,15 @@ module GraphQL
116
112
 
117
113
  kwargs[:description] = desc
118
114
  kwargs[:type] = type
119
- elsif (kwargs[:field] || kwargs[:function] || resolver || mutation) && type.is_a?(String)
120
- # The return type should be copied from `field` or `function`, and the second positional argument is the description
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
121
117
  kwargs[:description] = type
122
118
  else
123
119
  kwargs[:type] = type
124
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
125
124
  end
126
125
  new(**kwargs, &block)
127
126
  end
@@ -131,15 +130,15 @@ module GraphQL
131
130
  def connection?
132
131
  if @connection.nil?
133
132
  # Provide default based on type name
134
- return_type_name = if (contains_type = @field || @function)
135
- Member::BuildType.to_type_name(contains_type.type)
136
- elsif @return_type_expr
133
+ return_type_name = if @return_type_expr
137
134
  Member::BuildType.to_type_name(@return_type_expr)
135
+ elsif @resolver_class && @resolver_class.type
136
+ Member::BuildType.to_type_name(@resolver_class.type)
138
137
  else
139
138
  # As a last ditch, try to force loading the return type:
140
139
  type.unwrap.name
141
140
  end
142
- @connection = return_type_name.end_with?("Connection")
141
+ @connection = return_type_name.end_with?("Connection") && return_type_name != "Connection"
143
142
  else
144
143
  @connection
145
144
  end
@@ -150,8 +149,18 @@ module GraphQL
150
149
  if !@scope.nil?
151
150
  # The default was overridden
152
151
  @scope
152
+ elsif @return_type_expr
153
+ # Detect a list return type, but don't call `type` since that may eager-load an otherwise lazy-loaded type
154
+ @return_type_expr.is_a?(Array) ||
155
+ (@return_type_expr.is_a?(String) && @return_type_expr.include?("[")) ||
156
+ connection?
157
+ elsif @resolver_class
158
+ resolver_type = @resolver_class.type_expr
159
+ resolver_type.is_a?(Array) ||
160
+ (resolver_type.is_a?(String) && resolver_type.include?("[")) ||
161
+ connection?
153
162
  else
154
- @return_type_expr && (@return_type_expr.is_a?(Array) || (@return_type_expr.is_a?(String) && @return_type_expr.include?("[")) || connection?)
163
+ false
155
164
  end
156
165
  end
157
166
 
@@ -173,6 +182,8 @@ module GraphQL
173
182
 
174
183
  # @return Boolean
175
184
  attr_reader :relay_node_field
185
+ # @return Boolean
186
+ attr_reader :relay_nodes_field
176
187
 
177
188
  # @return [Boolean] Should we warn if this field's name conflicts with a built-in method?
178
189
  def method_conflict_warning?
@@ -182,19 +193,18 @@ module GraphQL
182
193
  # @param name [Symbol] The underscore-cased version of this field name (will be camelized for the GraphQL API)
183
194
  # @param type [Class, GraphQL::BaseType, Array] The return type of this field
184
195
  # @param owner [Class] The type that this field belongs to
185
- # @param null [Boolean] `true` if this field may return `null`, `false` if it is never `null`
196
+ # @param null [Boolean] (defaults to `true`) `true` if this field may return `null`, `false` if it is never `null`
186
197
  # @param description [String] Field description
187
198
  # @param deprecation_reason [String] If present, the field is marked "deprecated" with this message
188
199
  # @param method [Symbol] The method to call on the underlying object to resolve this field (defaults to `name`)
189
200
  # @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`)
201
+ # @param dig [Array<String, Symbol>] The nested hash keys to lookup on the underlying hash to resolve this field using dig
190
202
  # @param resolver_method [Symbol] The method on the type to call to resolve this field (defaults to `name`)
191
203
  # @param connection [Boolean] `true` if this field should get automagic connection behavior; default is to infer by `*Connection` in the return type name
192
204
  # @param connection_extension [Class] The extension to add, to implement connections. If `nil`, no extension is added.
193
205
  # @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
+ # @param default_page_size [Integer, nil] For connections, the default number of items to return from this field, or `nil` to return unlimited results.
194
207
  # @param introspection [Boolean] If true, this field will be marked as `#introspection?` and the name may begin with `__`
195
- # @param resolve [<#call(obj, args, ctx)>] **deprecated** for compatibility with <1.8.0
196
- # @param field [GraphQL::Field, GraphQL::Schema::Field] **deprecated** for compatibility with <1.8.0
197
- # @param function [GraphQL::Function] **deprecated** for compatibility with <1.8.0
198
208
  # @param resolver_class [Class] (Private) A {Schema::Resolver} which this field was derived from. Use `resolver:` to create a field with a resolver.
199
209
  # @param arguments [{String=>GraphQL::Schema::Argument, Hash}] Arguments for this field (may be added in the block, also)
200
210
  # @param camelize [Boolean] If true, the field name will be camelized when building the schema
@@ -208,38 +218,29 @@ module GraphQL
208
218
  # @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
209
219
  # @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
210
220
  # @param validates [Array<Hash>] Configurations for validating this field
211
- # @param legacy_edge_class [Class, nil] (DEPRECATED) If present, pass this along to the legacy field definition
212
- def initialize(type: nil, name: nil, owner: nil, null: nil, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, 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: nil, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, legacy_edge_class: nil, &definition_block)
221
+ # @param 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, dynamic_introspection: false, &definition_block)
213
223
  if name.nil?
214
224
  raise ArgumentError, "missing first `name` argument or keyword `name:`"
215
225
  end
216
- if !(field || function || resolver_class)
226
+ if !(resolver_class)
217
227
  if type.nil?
218
228
  raise ArgumentError, "missing second `type` argument or keyword `type:`"
219
229
  end
220
- if null.nil?
221
- raise ArgumentError, "missing keyword argument null:"
222
- end
223
- end
224
- if (field || function || resolve) && extras.any?
225
- raise ArgumentError, "keyword `extras:` may only be used with method-based resolve and class-based field such as mutation class, please remove `field:`, `function:` or `resolve:`"
226
230
  end
227
231
  @original_name = name
228
232
  name_s = -name.to_s
233
+
229
234
  @underscored_name = -Member::BuildType.underscore(name_s)
230
235
  @name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
236
+
231
237
  @description = description
232
- if field.is_a?(GraphQL::Schema::Field)
233
- raise ArgumentError, "Instead of passing a field as `field:`, use `add_field(field)` to add an already-defined field."
234
- else
235
- @field = field
236
- end
237
- @function = function
238
- @resolve = resolve
238
+ @type = @owner_type = @own_validators = @own_directives = @own_arguments = @arguments_statically_coercible = nil # these will be prepared later if necessary
239
+
239
240
  self.deprecation_reason = deprecation_reason
240
241
 
241
- if method && hash_key
242
- raise ArgumentError, "Provide `method:` _or_ `hash_key:`, not both. (called with: `method: #{method.inspect}, hash_key: #{hash_key.inspect}`)"
242
+ if method && hash_key && dig
243
+ raise ArgumentError, "Provide `method:`, `hash_key:` _or_ `dig:`, not multiple. (called with: `method: #{method.inspect}, hash_key: #{hash_key.inspect}, dig: #{dig.inspect}`)"
243
244
  end
244
245
 
245
246
  if resolver_method
@@ -247,24 +248,37 @@ module GraphQL
247
248
  raise ArgumentError, "Provide `method:` _or_ `resolver_method:`, not both. (called with: `method: #{method.inspect}, resolver_method: #{resolver_method.inspect}`)"
248
249
  end
249
250
 
250
- if hash_key
251
- raise ArgumentError, "Provide `hash_key:` _or_ `resolver_method:`, not both. (called with: `hash_key: #{hash_key.inspect}, resolver_method: #{resolver_method.inspect}`)"
251
+ if hash_key || dig
252
+ raise ArgumentError, "Provide `hash_key:`, `dig:`, _or_ `resolver_method:`, not multiple. (called with: `hash_key: #{hash_key.inspect}, dig: #{dig.inspect}, resolver_method: #{resolver_method.inspect}`)"
252
253
  end
253
254
  end
254
255
 
255
- # TODO: I think non-string/symbol hash keys are wrongly normalized (eg `1` will not work)
256
256
  method_name = method || hash_key || name_s
257
- resolver_method ||= name_s.to_sym
257
+ @dig_keys = dig
258
+ if hash_key
259
+ @hash_key = hash_key
260
+ @hash_key_str = hash_key.to_s
261
+ else
262
+ @hash_key = NOT_CONFIGURED
263
+ @hash_key_str = NOT_CONFIGURED
264
+ end
258
265
 
259
266
  @method_str = -method_name.to_s
260
267
  @method_sym = method_name.to_sym
261
- @resolver_method = resolver_method
268
+ @resolver_method = (resolver_method || name_s).to_sym
262
269
  @complexity = complexity
270
+ @dynamic_introspection = dynamic_introspection
263
271
  @return_type_expr = type
264
- @return_type_null = null
272
+ @return_type_null = if !null.nil?
273
+ null
274
+ elsif resolver_class
275
+ nil
276
+ else
277
+ true
278
+ end
265
279
  @connection = connection
266
- @has_max_page_size = max_page_size != :not_given
267
- @max_page_size = max_page_size == :not_given ? nil : max_page_size
280
+ @max_page_size = max_page_size
281
+ @default_page_size = default_page_size
268
282
  @introspection = introspection
269
283
  @extras = extras
270
284
  @broadcastable = broadcastable
@@ -275,13 +289,18 @@ module GraphQL
275
289
  @relay_nodes_field = relay_nodes_field
276
290
  @ast_node = ast_node
277
291
  @method_conflict_warning = method_conflict_warning
278
- @legacy_edge_class = legacy_edge_class
292
+ @fallback_value = fallback_value
279
293
 
280
294
  arguments.each do |name, arg|
281
- if arg.is_a?(Hash)
295
+ case arg
296
+ when Hash
282
297
  argument(name: name, **arg)
283
- else
298
+ when GraphQL::Schema::Argument
284
299
  add_argument(arg)
300
+ when Array
301
+ arg.each { |a| add_argument(a) }
302
+ else
303
+ raise ArgumentError, "Unexpected argument config (#{arg.class}): #{arg.inspect}"
285
304
  end
286
305
  end
287
306
 
@@ -289,6 +308,7 @@ module GraphQL
289
308
  @subscription_scope = subscription_scope
290
309
 
291
310
  @extensions = EMPTY_ARRAY
311
+ @call_after_define = false
292
312
  # This should run before connection extension,
293
313
  # but should it run after the definition block?
294
314
  if scoped?
@@ -306,28 +326,45 @@ module GraphQL
306
326
  self.extensions(extensions)
307
327
  end
308
328
 
329
+ if resolver_class && resolver_class.extensions.any?
330
+ self.extensions(resolver_class.extensions)
331
+ end
332
+
309
333
  if directives.any?
310
334
  directives.each do |(dir_class, options)|
311
335
  self.directive(dir_class, **options)
312
336
  end
313
337
  end
314
338
 
315
- self.validates(validates)
339
+ if !validates.empty?
340
+ self.validates(validates)
341
+ end
316
342
 
317
- if definition_block
343
+ if block_given?
318
344
  if definition_block.arity == 1
319
345
  yield self
320
346
  else
321
347
  instance_eval(&definition_block)
322
348
  end
323
349
  end
350
+
351
+ self.extensions.each(&:after_define_apply)
352
+ @call_after_define = true
324
353
  end
325
354
 
355
+ attr_accessor :dynamic_introspection
356
+
326
357
  # If true, subscription updates with this field can be shared between viewers
327
358
  # @return [Boolean, nil]
328
359
  # @see GraphQL::Subscriptions::BroadcastAnalyzer
329
360
  def broadcastable?
330
- @broadcastable
361
+ if !NOT_CONFIGURED.equal?(@broadcastable)
362
+ @broadcastable
363
+ elsif @resolver_class
364
+ @resolver_class.broadcastable?
365
+ else
366
+ nil
367
+ end
331
368
  end
332
369
 
333
370
  # @param text [String]
@@ -335,8 +372,12 @@ module GraphQL
335
372
  def description(text = nil)
336
373
  if text
337
374
  @description = text
338
- else
375
+ elsif !NOT_CONFIGURED.equal?(@description)
339
376
  @description
377
+ elsif @resolver_class
378
+ @resolver_class.description
379
+ else
380
+ nil
340
381
  end
341
382
  end
342
383
 
@@ -353,27 +394,20 @@ module GraphQL
353
394
  # @example adding an extension with options
354
395
  # extensions([MyExtensionClass, { AnotherExtensionClass => { filter: true } }])
355
396
  #
356
- # @param extensions [Array<Class, Hash<Class => Object>>] Add extensions to this field. For hash elements, only the first key/value is used.
397
+ # @param extensions [Array<Class, Hash<Class => Hash>>] Add extensions to this field. For hash elements, only the first key/value is used.
357
398
  # @return [Array<GraphQL::Schema::FieldExtension>] extensions to apply to this field
358
399
  def extensions(new_extensions = nil)
359
- if new_extensions.nil?
360
- # Read the value
361
- @extensions
362
- else
363
- if @extensions.frozen?
364
- @extensions = @extensions.dup
365
- end
366
- new_extensions.each do |extension|
367
- if extension.is_a?(Hash)
368
- extension = extension.to_a[0]
369
- extension_class, options = *extension
370
- @extensions << extension_class.new(field: self, options: options)
400
+ if new_extensions
401
+ new_extensions.each do |extension_config|
402
+ if extension_config.is_a?(Hash)
403
+ extension_class, options = *extension_config.to_a[0]
404
+ self.extension(extension_class, options)
371
405
  else
372
- extension_class = extension
373
- @extensions << extension_class.new(field: self, options: nil)
406
+ self.extension(extension_config)
374
407
  end
375
408
  end
376
409
  end
410
+ @extensions
377
411
  end
378
412
 
379
413
  # Add `extension` to this field, initialized with `options` if provided.
@@ -384,10 +418,19 @@ module GraphQL
384
418
  # @example adding an extension with options
385
419
  # extension(MyExtensionClass, filter: true)
386
420
  #
387
- # @param extension [Class] subclass of {Schema::Fieldextension}
388
- # @param options [Object] if provided, given as `options:` when initializing `extension`.
389
- def extension(extension, options = nil)
390
- extensions([{extension => options}])
421
+ # @param extension_class [Class] subclass of {Schema::FieldExtension}
422
+ # @param options [Hash] if provided, given as `options:` when initializing `extension`.
423
+ # @return [void]
424
+ def extension(extension_class, options = nil)
425
+ extension_inst = extension_class.new(field: self, options: options)
426
+ if @extensions.frozen?
427
+ @extensions = @extensions.dup
428
+ end
429
+ if @call_after_define
430
+ extension_inst.after_define_apply
431
+ end
432
+ @extensions << extension_inst
433
+ nil
391
434
  end
392
435
 
393
436
  # Read extras (as symbols) from this field,
@@ -398,7 +441,12 @@ module GraphQL
398
441
  def extras(new_extras = nil)
399
442
  if new_extras.nil?
400
443
  # Read the value
401
- @extras
444
+ field_extras = @extras
445
+ if @resolver_class && @resolver_class.extras.any?
446
+ field_extras + @resolver_class.extras
447
+ else
448
+ field_extras
449
+ end
402
450
  else
403
451
  if @extras.frozen?
404
452
  @extras = @extras.dup
@@ -408,6 +456,56 @@ module GraphQL
408
456
  end
409
457
  end
410
458
 
459
+ def calculate_complexity(query:, nodes:, child_complexity:)
460
+ if respond_to?(:complexity_for)
461
+ lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
462
+ complexity_for(child_complexity: child_complexity, query: query, lookahead: lookahead)
463
+ elsif connection?
464
+ arguments = query.arguments_for(nodes.first, self)
465
+ max_possible_page_size = nil
466
+ if arguments.respond_to?(:[]) # It might have been an error
467
+ if arguments[:first]
468
+ max_possible_page_size = arguments[:first]
469
+ end
470
+
471
+ if arguments[:last] && (max_possible_page_size.nil? || arguments[:last] > max_possible_page_size)
472
+ max_possible_page_size = arguments[:last]
473
+ end
474
+ elsif arguments.is_a?(GraphQL::UnauthorizedError)
475
+ raise arguments
476
+ end
477
+
478
+ if max_possible_page_size.nil?
479
+ max_possible_page_size = default_page_size || query.schema.default_page_size || max_page_size || query.schema.default_max_page_size
480
+ end
481
+
482
+ if max_possible_page_size.nil?
483
+ raise GraphQL::Error, "Can't calculate complexity for #{path}, no `first:`, `last:`, `default_page_size`, `max_page_size` or `default_max_page_size`"
484
+ else
485
+ metadata_complexity = 0
486
+ lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
487
+
488
+ lookahead.selections.each do |next_lookahead|
489
+ # this includes `pageInfo`, `nodes` and `edges` and any custom fields
490
+ # TODO this doesn't support procs yet -- unlikely to need it.
491
+ metadata_complexity += next_lookahead.field.complexity
492
+ if next_lookahead.name != :nodes && next_lookahead.name != :edges
493
+ # subfields, eg, for pageInfo -- assumes no subselections
494
+ metadata_complexity += next_lookahead.selections.size
495
+ end
496
+ end
497
+
498
+ # Possible bug: selections on `edges` and `nodes` are _both_ multiplied here. Should they be?
499
+ items_complexity = child_complexity - metadata_complexity
500
+ subfields_complexity = (max_possible_page_size * items_complexity) + metadata_complexity
501
+ # Apply this field's own complexity
502
+ apply_own_complexity_to(subfields_complexity, query, nodes)
503
+ end
504
+ else
505
+ apply_own_complexity_to(child_complexity, query, nodes)
506
+ end
507
+ end
508
+
411
509
  def complexity(new_complexity = nil)
412
510
  case new_complexity
413
511
  when Proc
@@ -422,7 +520,11 @@ module GraphQL
422
520
  when Numeric
423
521
  @complexity = new_complexity
424
522
  when nil
425
- @complexity
523
+ if @resolver_class
524
+ @complexity || @resolver_class.complexity || 1
525
+ else
526
+ @complexity || 1
527
+ end
426
528
  else
427
529
  raise("Invalid complexity: #{new_complexity.inspect} on #{@name}")
428
530
  end
@@ -430,101 +532,55 @@ module GraphQL
430
532
 
431
533
  # @return [Boolean] True if this field's {#max_page_size} should override the schema default.
432
534
  def has_max_page_size?
433
- @has_max_page_size
535
+ !NOT_CONFIGURED.equal?(@max_page_size) || (@resolver_class && @resolver_class.has_max_page_size?)
434
536
  end
435
537
 
436
538
  # @return [Integer, nil] Applied to connections if {#has_max_page_size?}
437
- attr_reader :max_page_size
438
-
439
- # @return [GraphQL::Field]
440
- def to_graphql
441
- field_defn = if @field
442
- @field.dup
443
- elsif @function
444
- GraphQL::Function.build_field(@function)
539
+ def max_page_size
540
+ if !NOT_CONFIGURED.equal?(@max_page_size)
541
+ @max_page_size
542
+ elsif @resolver_class && @resolver_class.has_max_page_size?
543
+ @resolver_class.max_page_size
445
544
  else
446
- GraphQL::Field.new
447
- end
448
-
449
- field_defn.name = @name
450
- if @return_type_expr
451
- field_defn.type = -> { type }
452
- end
453
-
454
- if @description
455
- field_defn.description = @description
456
- end
457
-
458
- if self.deprecation_reason
459
- field_defn.deprecation_reason = self.deprecation_reason
460
- end
461
-
462
- if @resolver_class
463
- if @resolver_class < GraphQL::Schema::Mutation
464
- field_defn.mutation = @resolver_class
465
- end
466
- field_defn.metadata[:resolver] = @resolver_class
467
- end
468
-
469
- if !@trace.nil?
470
- field_defn.trace = @trace
471
- end
472
-
473
- if @relay_node_field
474
- field_defn.relay_node_field = @relay_node_field
475
- end
476
-
477
- if @relay_nodes_field
478
- field_defn.relay_nodes_field = @relay_nodes_field
545
+ nil
479
546
  end
547
+ end
480
548
 
481
- if @legacy_edge_class
482
- field_defn.edge_class = @legacy_edge_class
483
- end
484
-
485
- field_defn.resolve = self.method(:resolve_field)
486
- field_defn.connection = connection?
487
- field_defn.connection_max_page_size = max_page_size
488
- field_defn.introspection = @introspection
489
- field_defn.complexity = @complexity
490
- field_defn.subscription_scope = @subscription_scope
491
- field_defn.ast_node = ast_node
492
-
493
- arguments.each do |name, defn|
494
- arg_graphql = defn.to_graphql
495
- field_defn.arguments[arg_graphql.name] = arg_graphql
496
- end
549
+ # @return [Boolean] True if this field's {#default_page_size} should override the schema default.
550
+ def has_default_page_size?
551
+ !NOT_CONFIGURED.equal?(@default_page_size) || (@resolver_class && @resolver_class.has_default_page_size?)
552
+ end
497
553
 
498
- # Support a passed-in proc, one way or another
499
- @resolve_proc = if @resolve
500
- @resolve
501
- elsif @function
502
- @function
503
- elsif @field
504
- @field.resolve_proc
554
+ # @return [Integer, nil] Applied to connections if {#has_default_page_size?}
555
+ def default_page_size
556
+ if !NOT_CONFIGURED.equal?(@default_page_size)
557
+ @default_page_size
558
+ elsif @resolver_class && @resolver_class.has_default_page_size?
559
+ @resolver_class.default_page_size
560
+ else
561
+ nil
505
562
  end
506
-
507
- # Ok, `self` isn't a class, but this is for consistency with the classes
508
- field_defn.metadata[:type_class] = self
509
- field_defn.arguments_class = GraphQL::Query::Arguments.construct_arguments_class(field_defn)
510
- field_defn
511
563
  end
512
564
 
565
+ class MissingReturnTypeError < GraphQL::Error; end
513
566
  attr_writer :type
514
567
 
515
568
  def type
516
- @type ||= if @function
517
- Member::BuildType.parse_type(@function.type, null: false)
518
- elsif @field
519
- Member::BuildType.parse_type(@field.type, null: false)
569
+ if @resolver_class
570
+ return_type = @return_type_expr || @resolver_class.type_expr
571
+ if return_type.nil?
572
+ 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)"
573
+ end
574
+ nullable = @return_type_null.nil? ? @resolver_class.null : @return_type_null
575
+ Member::BuildType.parse_type(return_type, null: nullable)
520
576
  else
521
- Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
577
+ @type ||= Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
522
578
  end
523
- rescue GraphQL::Schema::InvalidDocumentError => err
579
+ rescue GraphQL::Schema::InvalidDocumentError, MissingReturnTypeError => err
524
580
  # Let this propagate up
525
581
  raise err
526
582
  rescue StandardError => err
527
- raise ArgumentError, "Failed to build return type for #{@owner.graphql_name}.#{name} from #{@return_type_expr.inspect}: (#{err.class}) #{err.message}", err.backtrace
583
+ raise MissingReturnTypeError, "Failed to build return type for #{@owner.graphql_name}.#{name} from #{@return_type_expr.inspect}: (#{err.class}) #{err.message}", err.backtrace
528
584
  end
529
585
 
530
586
  def visible?(context)
@@ -535,57 +591,47 @@ module GraphQL
535
591
  end
536
592
  end
537
593
 
538
- def accessible?(context)
539
- if @resolver_class
540
- @resolver_class.accessible?(context)
541
- else
542
- true
543
- end
544
- end
545
-
546
594
  def authorized?(object, args, context)
547
595
  if @resolver_class
548
- # The resolver will check itself during `resolve()`
596
+ # The resolver _instance_ will check itself during `resolve()`
549
597
  @resolver_class.authorized?(object, context)
550
598
  else
551
- # Faster than `.any?`
552
- arguments.each_value do |arg|
553
- if args.key?(arg.keyword) && !arg.authorized?(object, args[arg.keyword], context)
554
- return false
599
+ if args.size > 0
600
+ if (arg_values = context[:current_arguments])
601
+ # ^^ that's provided by the interpreter at runtime, and includes info about whether the default value was used or not.
602
+ using_arg_values = true
603
+ arg_values = arg_values.argument_values
604
+ else
605
+ arg_values = args
606
+ using_arg_values = false
555
607
  end
556
- end
557
- true
558
- end
559
- end
560
608
 
561
- # Implement {GraphQL::Field}'s resolve API.
562
- #
563
- # Eventually, we might hook up field instances to execution in another way. TBD.
564
- # @see #resolve for how the interpreter hooks up to it
565
- def resolve_field(obj, args, ctx)
566
- ctx.schema.after_lazy(obj) do |after_obj|
567
- # First, apply auth ...
568
- query_ctx = ctx.query.context
569
- # Some legacy fields can have `nil` here, not exactly sure why.
570
- # @see https://github.com/rmosolgo/graphql-ruby/issues/1990 before removing
571
- inner_obj = after_obj && after_obj.object
572
- ctx.schema.after_lazy(to_ruby_args(after_obj, args, ctx)) do |ruby_args|
573
- if authorized?(inner_obj, ruby_args, query_ctx)
574
- # Then if it passed, resolve the field
575
- if @resolve_proc
576
- # Might be nil, still want to call the func in that case
577
- with_extensions(inner_obj, ruby_args, query_ctx) do |extended_obj, extended_args|
578
- # Pass the GraphQL args here for compatibility:
579
- @resolve_proc.call(extended_obj, args, ctx)
609
+ args = context.warden.arguments(self)
610
+ args.each do |arg|
611
+ arg_key = arg.keyword
612
+ if arg_values.key?(arg_key)
613
+ arg_value = arg_values[arg_key]
614
+ if using_arg_values
615
+ if arg_value.default_used?
616
+ # pass -- no auth required for default used
617
+ next
618
+ else
619
+ application_arg_value = arg_value.value
620
+ if application_arg_value.is_a?(GraphQL::Execution::Interpreter::Arguments)
621
+ application_arg_value.keyword_arguments
622
+ end
623
+ end
624
+ else
625
+ application_arg_value = arg_value
626
+ end
627
+
628
+ if !arg.authorized?(object, application_arg_value, context)
629
+ return false
580
630
  end
581
- else
582
- public_send_field(after_obj, ruby_args, query_ctx)
583
631
  end
584
- else
585
- err = GraphQL::UnauthorizedFieldError.new(object: inner_obj, type: obj.class, context: ctx, field: self)
586
- query_ctx.schema.unauthorized_field(err)
587
632
  end
588
633
  end
634
+ true
589
635
  end
590
636
  end
591
637
 
@@ -594,35 +640,112 @@ module GraphQL
594
640
  # @param object [GraphQL::Schema::Object] An instance of some type class, wrapping an application object
595
641
  # @param args [Hash] A symbol-keyed hash of Ruby keyword arguments. (Empty if no args)
596
642
  # @param ctx [GraphQL::Query::Context]
597
- def resolve(object, args, ctx)
598
- if @resolve_proc
599
- raise "Can't run resolve proc for #{path} when using GraphQL::Execution::Interpreter"
600
- end
601
- begin
602
- # Unwrap the GraphQL object to get the application object.
603
- application_object = object.object
643
+ def resolve(object, args, query_ctx)
644
+ # Unwrap the GraphQL object to get the application object.
645
+ application_object = object.object
646
+ method_receiver = nil
647
+ method_to_call = nil
648
+ method_args = nil
649
+
650
+ @own_validators && Schema::Validator.validate!(validators, application_object, query_ctx, args)
651
+
652
+ query_ctx.query.after_lazy(self.authorized?(application_object, args, query_ctx)) do |is_authorized|
653
+ if is_authorized
654
+ with_extensions(object, args, query_ctx) do |obj, ruby_kwargs|
655
+ method_args = ruby_kwargs
656
+ if @resolver_class
657
+ if obj.is_a?(GraphQL::Schema::Object)
658
+ obj = obj.object
659
+ end
660
+ obj = @resolver_class.new(object: obj, context: query_ctx, field: self)
661
+ end
604
662
 
605
- Schema::Validator.validate!(validators, application_object, ctx, args)
663
+ inner_object = obj.object
606
664
 
607
- ctx.schema.after_lazy(self.authorized?(application_object, args, ctx)) do |is_authorized|
608
- if is_authorized
609
- public_send_field(object, args, ctx)
610
- else
611
- err = GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: ctx, field: self)
612
- ctx.schema.unauthorized_field(err)
665
+ if !NOT_CONFIGURED.equal?(@hash_key)
666
+ hash_value = if inner_object.is_a?(Hash)
667
+ inner_object.key?(@hash_key) ? inner_object[@hash_key] : inner_object[@hash_key_str]
668
+ elsif inner_object.respond_to?(:[])
669
+ inner_object[@hash_key]
670
+ else
671
+ nil
672
+ end
673
+ if hash_value == false
674
+ hash_value
675
+ else
676
+ hash_value || (@fallback_value != NOT_CONFIGURED ? @fallback_value : nil)
677
+ end
678
+ elsif obj.respond_to?(resolver_method)
679
+ method_to_call = resolver_method
680
+ method_receiver = obj
681
+ # Call the method with kwargs, if there are any
682
+ if ruby_kwargs.any?
683
+ obj.public_send(resolver_method, **ruby_kwargs)
684
+ else
685
+ obj.public_send(resolver_method)
686
+ end
687
+ elsif inner_object.is_a?(Hash)
688
+ if @dig_keys
689
+ inner_object.dig(*@dig_keys)
690
+ elsif inner_object.key?(@method_sym)
691
+ inner_object[@method_sym]
692
+ elsif inner_object.key?(@method_str) || !inner_object.default_proc.nil?
693
+ inner_object[@method_str]
694
+ elsif @fallback_value != NOT_CONFIGURED
695
+ @fallback_value
696
+ else
697
+ nil
698
+ end
699
+ elsif inner_object.respond_to?(@method_sym)
700
+ method_to_call = @method_sym
701
+ method_receiver = obj.object
702
+ if ruby_kwargs.any?
703
+ inner_object.public_send(@method_sym, **ruby_kwargs)
704
+ else
705
+ inner_object.public_send(@method_sym)
706
+ end
707
+ elsif @fallback_value != NOT_CONFIGURED
708
+ @fallback_value
709
+ else
710
+ raise <<-ERR
711
+ Failed to implement #{@owner.graphql_name}.#{@name}, tried:
712
+
713
+ - `#{obj.class}##{resolver_method}`, which did not exist
714
+ - `#{inner_object.class}##{@method_sym}`, which did not exist
715
+ - Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{inner_object}`, but it wasn't a Hash
716
+
717
+ To implement this field, define one of the methods above (and check for typos), or supply a `fallback_value`.
718
+ ERR
719
+ end
613
720
  end
721
+ else
722
+ raise GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: query_ctx, field: self)
614
723
  end
615
- rescue GraphQL::UnauthorizedFieldError => err
616
- err.field ||= self
617
- ctx.schema.unauthorized_field(err)
618
- rescue GraphQL::UnauthorizedError => err
619
- ctx.schema.unauthorized_object(err)
620
724
  end
725
+ rescue GraphQL::UnauthorizedFieldError => err
726
+ err.field ||= self
727
+ begin
728
+ query_ctx.schema.unauthorized_field(err)
729
+ rescue GraphQL::ExecutionError => err
730
+ err
731
+ end
732
+ rescue GraphQL::UnauthorizedError => err
733
+ begin
734
+ query_ctx.schema.unauthorized_object(err)
735
+ rescue GraphQL::ExecutionError => err
736
+ err
737
+ end
738
+ rescue ArgumentError
739
+ if method_receiver && method_to_call
740
+ assert_satisfactory_implementation(method_receiver, method_to_call, method_args)
741
+ end
742
+ # if the line above doesn't raise, re-raise
743
+ raise
621
744
  rescue GraphQL::ExecutionError => err
622
745
  err
623
746
  end
624
747
 
625
- # @param ctx [GraphQL::Query::Context::FieldResolutionContext]
748
+ # @param ctx [GraphQL::Query::Context]
626
749
  def fetch_extra(extra_name, ctx)
627
750
  if extra_name != :path && extra_name != :ast_node && respond_to?(extra_name)
628
751
  self.public_send(extra_name)
@@ -635,111 +758,55 @@ module GraphQL
635
758
 
636
759
  private
637
760
 
638
- NO_ARGS = {}.freeze
639
-
640
- # Convert a GraphQL arguments instance into a Ruby-style hash.
641
- #
642
- # @param obj [GraphQL::Schema::Object] The object where this field is being resolved
643
- # @param graphql_args [GraphQL::Query::Arguments]
644
- # @param field_ctx [GraphQL::Query::Context::FieldResolutionContext]
645
- # @return [Hash<Symbol => Any>]
646
- def to_ruby_args(obj, graphql_args, field_ctx)
647
- if graphql_args.any? || @extras.any?
648
- # Splat the GraphQL::Arguments to Ruby keyword arguments
649
- ruby_kwargs = graphql_args.to_kwargs
650
- maybe_lazies = []
651
- # Apply any `prepare` methods. Not great code organization, can this go somewhere better?
652
- arguments.each do |name, arg_defn|
653
- ruby_kwargs_key = arg_defn.keyword
654
-
655
- if ruby_kwargs.key?(ruby_kwargs_key)
656
- loads = arg_defn.loads
657
- value = ruby_kwargs[ruby_kwargs_key]
658
- loaded_value = if loads && !arg_defn.from_resolver?
659
- if arg_defn.type.list?
660
- loaded_values = value.map { |val| load_application_object(arg_defn, loads, val, field_ctx.query.context) }
661
- field_ctx.schema.after_any_lazies(loaded_values) { |result| result }
662
- else
663
- load_application_object(arg_defn, loads, value, field_ctx.query.context)
664
- end
665
- elsif arg_defn.type.list? && value.is_a?(Array)
666
- field_ctx.schema.after_any_lazies(value, &:itself)
667
- else
668
- value
669
- end
670
-
671
- maybe_lazies << field_ctx.schema.after_lazy(loaded_value) do |loaded_value|
672
- prepared_value = if arg_defn.prepare
673
- arg_defn.prepare_value(obj, loaded_value)
674
- else
675
- loaded_value
676
- end
677
-
678
- ruby_kwargs[ruby_kwargs_key] = prepared_value
679
- end
761
+ def assert_satisfactory_implementation(receiver, method_name, ruby_kwargs)
762
+ method_defn = receiver.method(method_name)
763
+ unsatisfied_ruby_kwargs = ruby_kwargs.dup
764
+ unsatisfied_method_params = []
765
+ encountered_keyrest = false
766
+ method_defn.parameters.each do |(param_type, param_name)|
767
+ case param_type
768
+ when :key
769
+ unsatisfied_ruby_kwargs.delete(param_name)
770
+ when :keyreq
771
+ if unsatisfied_ruby_kwargs.key?(param_name)
772
+ unsatisfied_ruby_kwargs.delete(param_name)
773
+ else
774
+ unsatisfied_method_params << "- `#{param_name}:` is required by Ruby, but not by GraphQL. Consider `#{param_name}: nil` instead, or making this argument required in GraphQL."
680
775
  end
776
+ when :keyrest
777
+ encountered_keyrest = true
778
+ when :req
779
+ unsatisfied_method_params << "- `#{param_name}` is required by Ruby, but GraphQL doesn't pass positional arguments. If it's meant to be a GraphQL argument, use `#{param_name}:` instead. Otherwise, remove it."
780
+ when :opt, :rest
781
+ # This is fine, although it will never be present
681
782
  end
682
-
683
- @extras.each do |extra_arg|
684
- ruby_kwargs[extra_arg] = fetch_extra(extra_arg, field_ctx)
685
- end
686
-
687
- field_ctx.schema.after_any_lazies(maybe_lazies) do
688
- ruby_kwargs
689
- end
690
- else
691
- NO_ARGS
692
783
  end
693
- end
694
784
 
695
- def public_send_field(unextended_obj, unextended_ruby_kwargs, query_ctx)
696
- with_extensions(unextended_obj, unextended_ruby_kwargs, query_ctx) do |obj, ruby_kwargs|
697
- if @resolver_class
698
- if obj.is_a?(GraphQL::Schema::Object)
699
- obj = obj.object
700
- end
701
- obj = @resolver_class.new(object: obj, context: query_ctx, field: self)
702
- end
785
+ if encountered_keyrest
786
+ unsatisfied_ruby_kwargs.clear
787
+ end
703
788
 
704
- # Find a way to resolve this field, checking:
705
- #
706
- # - A method on the type instance;
707
- # - Hash keys, if the wrapped object is a hash;
708
- # - A method on the wrapped object;
709
- # - Or, raise not implemented.
710
- #
711
- if obj.respond_to?(@resolver_method)
712
- # Call the method with kwargs, if there are any
713
- if ruby_kwargs.any?
714
- obj.public_send(@resolver_method, **ruby_kwargs)
715
- else
716
- obj.public_send(@resolver_method)
717
- end
718
- elsif obj.object.is_a?(Hash)
719
- inner_object = obj.object
720
- if inner_object.key?(@method_sym)
721
- inner_object[@method_sym]
722
- else
723
- inner_object[@method_str]
724
- end
725
- elsif obj.object.respond_to?(@method_sym)
726
- if ruby_kwargs.any?
727
- obj.object.public_send(@method_sym, **ruby_kwargs)
728
- else
729
- obj.object.public_send(@method_sym)
730
- end
731
- else
732
- raise <<-ERR
733
- Failed to implement #{@owner.graphql_name}.#{@name}, tried:
789
+ if unsatisfied_ruby_kwargs.any? || unsatisfied_method_params.any?
790
+ raise FieldImplementationFailed.new, <<-ERR
791
+ Failed to call `#{method_name.inspect}` on #{receiver.inspect} because the Ruby method params were incompatible with the GraphQL arguments:
734
792
 
735
- - `#{obj.class}##{@resolver_method}`, which did not exist
736
- - `#{obj.object.class}##{@method_sym}`, which did not exist
737
- - Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
793
+ #{ unsatisfied_ruby_kwargs
794
+ .map { |key, value| "- `#{key}: #{value}` was given by GraphQL but not defined in the Ruby method. Add `#{key}:` to the method parameters." }
795
+ .concat(unsatisfied_method_params)
796
+ .join("\n") }
797
+ ERR
798
+ end
799
+ end
738
800
 
739
- To implement this field, define one of the methods above (and check for typos)
740
- ERR
741
- end
801
+ class ExtendedState
802
+ def initialize(args, object)
803
+ @arguments = args
804
+ @object = object
805
+ @memos = nil
806
+ @added_extras = nil
742
807
  end
808
+
809
+ attr_accessor :arguments, :object, :memos, :added_extras
743
810
  end
744
811
 
745
812
  # Wrap execution with hooks.
@@ -752,16 +819,20 @@ module GraphQL
752
819
  # This is a hack to get the _last_ value for extended obj and args,
753
820
  # in case one of the extensions doesn't `yield`.
754
821
  # (There's another implementation that uses multiple-return, but I'm wary of the perf cost of the extra arrays)
755
- extended = { args: args, obj: obj, memos: nil }
822
+ extended = ExtendedState.new(args, obj)
756
823
  value = run_extensions_before_resolve(obj, args, ctx, extended) do |obj, args|
824
+ if (added_extras = extended.added_extras)
825
+ args = args.dup
826
+ added_extras.each { |e| args.delete(e) }
827
+ end
757
828
  yield(obj, args)
758
829
  end
759
830
 
760
- extended_obj = extended[:obj]
761
- extended_args = extended[:args]
762
- memos = extended[:memos] || EMPTY_HASH
831
+ extended_obj = extended.object
832
+ extended_args = extended.arguments # rubocop:disable Development/ContextIsPassedCop
833
+ memos = extended.memos || EMPTY_HASH
763
834
 
764
- ctx.schema.after_lazy(value) do |resolved_value|
835
+ ctx.query.after_lazy(value) do |resolved_value|
765
836
  idx = 0
766
837
  @extensions.each do |ext|
767
838
  memo = memos[idx]
@@ -779,17 +850,41 @@ module GraphQL
779
850
  if extension
780
851
  extension.resolve(object: obj, arguments: args, context: ctx) do |extended_obj, extended_args, memo|
781
852
  if memo
782
- memos = extended[:memos] ||= {}
853
+ memos = extended.memos ||= {}
783
854
  memos[idx] = memo
784
855
  end
785
- extended[:obj] = extended_obj
786
- extended[:args] = extended_args
856
+
857
+ if (extras = extension.added_extras)
858
+ ae = extended.added_extras ||= []
859
+ ae.concat(extras)
860
+ end
861
+
862
+ extended.object = extended_obj
863
+ extended.arguments = extended_args
787
864
  run_extensions_before_resolve(extended_obj, extended_args, ctx, extended, idx: idx + 1) { |o, a| yield(o, a) }
788
865
  end
789
866
  else
790
867
  yield(obj, args)
791
868
  end
792
869
  end
870
+
871
+ def apply_own_complexity_to(child_complexity, query, nodes)
872
+ case (own_complexity = complexity)
873
+ when Numeric
874
+ own_complexity + child_complexity
875
+ when Proc
876
+ arguments = query.arguments_for(nodes.first, self)
877
+ if arguments.is_a?(GraphQL::ExecutionError)
878
+ return child_complexity
879
+ elsif arguments.respond_to?(:keyword_arguments)
880
+ arguments = arguments.keyword_arguments
881
+ end
882
+
883
+ own_complexity.call(query.context, arguments, child_complexity)
884
+ else
885
+ raise ArgumentError, "Invalid complexity for #{self.path}: #{own_complexity.inspect}"
886
+ end
887
+ end
793
888
  end
794
889
  end
795
890
  end