graphql 1.9.21 → 2.0.16

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