graphql 1.10.2 → 2.0.21

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