graphql 1.9.17 → 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 (416) 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 +54 -38
  53. data/lib/graphql/analysis/ast.rb +16 -16
  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/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/directive_checks.rb +2 -2
  70. data/lib/graphql/execution/errors.rb +77 -45
  71. data/lib/graphql/execution/interpreter/argument_value.rb +28 -0
  72. data/lib/graphql/execution/interpreter/arguments.rb +88 -0
  73. data/lib/graphql/execution/interpreter/arguments_cache.rb +104 -0
  74. data/lib/graphql/execution/interpreter/handles_raw_value.rb +18 -0
  75. data/lib/graphql/execution/interpreter/resolve.rb +62 -24
  76. data/lib/graphql/execution/interpreter/runtime.rb +830 -417
  77. data/lib/graphql/execution/interpreter.rb +206 -74
  78. data/lib/graphql/execution/lazy/lazy_method_map.rb +4 -0
  79. data/lib/graphql/execution/lazy.rb +11 -21
  80. data/lib/graphql/execution/lookahead.rb +65 -136
  81. data/lib/graphql/execution/multiplex.rb +6 -152
  82. data/lib/graphql/execution.rb +11 -4
  83. data/lib/graphql/filter.rb +8 -3
  84. data/lib/graphql/integer_decoding_error.rb +17 -0
  85. data/lib/graphql/integer_encoding_error.rb +18 -2
  86. data/lib/graphql/introspection/base_object.rb +2 -5
  87. data/lib/graphql/introspection/directive_location_enum.rb +2 -2
  88. data/lib/graphql/introspection/directive_type.rb +12 -6
  89. data/lib/graphql/introspection/dynamic_fields.rb +3 -8
  90. data/lib/graphql/introspection/entry_points.rb +5 -18
  91. data/lib/graphql/introspection/enum_value_type.rb +2 -2
  92. data/lib/graphql/introspection/field_type.rb +9 -5
  93. data/lib/graphql/introspection/input_value_type.rb +41 -11
  94. data/lib/graphql/introspection/introspection_query.rb +6 -92
  95. data/lib/graphql/introspection/schema_type.rb +12 -12
  96. data/lib/graphql/introspection/type_type.rb +34 -17
  97. data/lib/graphql/introspection.rb +100 -0
  98. data/lib/graphql/invalid_null_error.rb +18 -0
  99. data/lib/graphql/language/block_string.rb +20 -5
  100. data/lib/graphql/language/cache.rb +37 -0
  101. data/lib/graphql/language/definition_slice.rb +21 -10
  102. data/lib/graphql/language/document_from_schema_definition.rb +136 -78
  103. data/lib/graphql/language/lexer.rb +216 -1462
  104. data/lib/graphql/language/nodes.rb +129 -132
  105. data/lib/graphql/language/parser.rb +994 -932
  106. data/lib/graphql/language/parser.y +152 -120
  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 +192 -84
  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 +77 -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 +205 -203
  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 +22 -9
  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 +95 -43
  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 +285 -36
  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 +348 -205
  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 +134 -15
  154. data/lib/graphql/schema/enum.rb +137 -39
  155. data/lib/graphql/schema/enum_value.rb +17 -23
  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 +504 -331
  159. data/lib/graphql/schema/field_extension.rb +86 -2
  160. data/lib/graphql/schema/find_inherited_value.rb +12 -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 +24 -49
  164. data/lib/graphql/schema/introspection_system.rb +103 -37
  165. data/lib/graphql/schema/late_bound_type.rb +9 -2
  166. data/lib/graphql/schema/list.rb +61 -3
  167. data/lib/graphql/schema/loader.rb +144 -96
  168. data/lib/graphql/schema/member/base_dsl_methods.rb +41 -37
  169. data/lib/graphql/schema/member/build_type.rb +24 -15
  170. data/lib/graphql/schema/member/has_arguments.rb +310 -26
  171. data/lib/graphql/schema/member/has_ast_node.rb +32 -0
  172. data/lib/graphql/schema/member/has_deprecation_reason.rb +24 -0
  173. data/lib/graphql/schema/member/has_directives.rb +118 -0
  174. data/lib/graphql/schema/member/has_fields.rb +166 -44
  175. data/lib/graphql/schema/member/has_interfaces.rb +129 -0
  176. data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
  177. data/lib/graphql/schema/member/has_validators.rb +57 -0
  178. data/lib/graphql/schema/member/relay_shortcuts.rb +47 -2
  179. data/lib/graphql/schema/member/type_system_helpers.rb +20 -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 +36 -60
  185. data/lib/graphql/schema/printer.rb +16 -35
  186. data/lib/graphql/schema/relay_classic_mutation.rb +91 -44
  187. data/lib/graphql/schema/resolver/has_payload_type.rb +51 -11
  188. data/lib/graphql/schema/resolver.rb +147 -94
  189. data/lib/graphql/schema/scalar.rb +40 -15
  190. data/lib/graphql/schema/subscription.rb +60 -31
  191. data/lib/graphql/schema/timeout.rb +45 -35
  192. data/lib/graphql/schema/type_expression.rb +21 -13
  193. data/lib/graphql/schema/type_membership.rb +23 -6
  194. data/lib/graphql/schema/union.rb +49 -15
  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 +213 -35
  206. data/lib/graphql/schema/wrapper.rb +0 -5
  207. data/lib/graphql/schema.rb +857 -884
  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 +69 -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 +15 -7
  221. data/lib/graphql/static_validation/rules/fields_will_merge.rb +96 -53
  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 +32 -20
  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 +84 -35
  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_trace.rb +16 -0
  252. data/lib/graphql/tracing/active_support_notifications_tracing.rb +8 -17
  253. data/lib/graphql/tracing/appoptics_trace.rb +231 -0
  254. data/lib/graphql/tracing/appoptics_tracing.rb +173 -0
  255. data/lib/graphql/tracing/appsignal_trace.rb +77 -0
  256. data/lib/graphql/tracing/appsignal_tracing.rb +23 -0
  257. data/lib/graphql/tracing/data_dog_trace.rb +148 -0
  258. data/lib/graphql/tracing/data_dog_tracing.rb +34 -2
  259. data/lib/graphql/tracing/legacy_trace.rb +65 -0
  260. data/lib/graphql/tracing/new_relic_trace.rb +75 -0
  261. data/lib/graphql/tracing/new_relic_tracing.rb +9 -12
  262. data/lib/graphql/tracing/notifications_trace.rb +42 -0
  263. data/lib/graphql/tracing/notifications_tracing.rb +59 -0
  264. data/lib/graphql/tracing/platform_trace.rb +109 -0
  265. data/lib/graphql/tracing/platform_tracing.rb +76 -35
  266. data/lib/graphql/tracing/prometheus_trace.rb +89 -0
  267. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +5 -2
  268. data/lib/graphql/tracing/prometheus_tracing.rb +11 -3
  269. data/lib/graphql/tracing/scout_trace.rb +72 -0
  270. data/lib/graphql/tracing/scout_tracing.rb +19 -0
  271. data/lib/graphql/tracing/statsd_trace.rb +56 -0
  272. data/lib/graphql/tracing/statsd_tracing.rb +42 -0
  273. data/lib/graphql/tracing/trace.rb +75 -0
  274. data/lib/graphql/tracing.rb +23 -67
  275. data/lib/graphql/type_kinds.rb +6 -3
  276. data/lib/graphql/types/big_int.rb +5 -1
  277. data/lib/graphql/types/int.rb +10 -3
  278. data/lib/graphql/types/iso_8601_date.rb +20 -9
  279. data/lib/graphql/types/iso_8601_date_time.rb +36 -10
  280. data/lib/graphql/types/relay/base_connection.rb +18 -90
  281. data/lib/graphql/types/relay/base_edge.rb +2 -34
  282. data/lib/graphql/types/relay/connection_behaviors.rb +176 -0
  283. data/lib/graphql/types/relay/edge_behaviors.rb +75 -0
  284. data/lib/graphql/types/relay/has_node_field.rb +41 -0
  285. data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
  286. data/lib/graphql/types/relay/node.rb +2 -4
  287. data/lib/graphql/types/relay/node_behaviors.rb +25 -0
  288. data/lib/graphql/types/relay/page_info.rb +2 -14
  289. data/lib/graphql/types/relay/page_info_behaviors.rb +30 -0
  290. data/lib/graphql/types/relay.rb +10 -5
  291. data/lib/graphql/types/string.rb +8 -2
  292. data/lib/graphql/unauthorized_error.rb +2 -2
  293. data/lib/graphql/unresolved_type_error.rb +2 -2
  294. data/lib/graphql/version.rb +1 -1
  295. data/lib/graphql.rb +63 -65
  296. data/readme.md +3 -6
  297. metadata +127 -236
  298. data/lib/graphql/analysis/analyze_query.rb +0 -91
  299. data/lib/graphql/analysis/field_usage.rb +0 -45
  300. data/lib/graphql/analysis/max_query_complexity.rb +0 -26
  301. data/lib/graphql/analysis/max_query_depth.rb +0 -26
  302. data/lib/graphql/analysis/query_complexity.rb +0 -88
  303. data/lib/graphql/analysis/query_depth.rb +0 -43
  304. data/lib/graphql/analysis/reducer_state.rb +0 -48
  305. data/lib/graphql/argument.rb +0 -159
  306. data/lib/graphql/authorization.rb +0 -82
  307. data/lib/graphql/backwards_compatibility.rb +0 -60
  308. data/lib/graphql/base_type.rb +0 -226
  309. data/lib/graphql/boolean_type.rb +0 -2
  310. data/lib/graphql/compatibility/execution_specification/counter_schema.rb +0 -53
  311. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +0 -200
  312. data/lib/graphql/compatibility/execution_specification.rb +0 -435
  313. data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +0 -111
  314. data/lib/graphql/compatibility/lazy_execution_specification.rb +0 -213
  315. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +0 -91
  316. data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +0 -79
  317. data/lib/graphql/compatibility/query_parser_specification.rb +0 -264
  318. data/lib/graphql/compatibility/schema_parser_specification.rb +0 -680
  319. data/lib/graphql/compatibility.rb +0 -5
  320. data/lib/graphql/define/assign_argument.rb +0 -12
  321. data/lib/graphql/define/assign_connection.rb +0 -13
  322. data/lib/graphql/define/assign_enum_value.rb +0 -18
  323. data/lib/graphql/define/assign_global_id_field.rb +0 -11
  324. data/lib/graphql/define/assign_mutation_function.rb +0 -34
  325. data/lib/graphql/define/assign_object_field.rb +0 -42
  326. data/lib/graphql/define/defined_object_proxy.rb +0 -50
  327. data/lib/graphql/define/instance_definable.rb +0 -300
  328. data/lib/graphql/define/no_definition_error.rb +0 -7
  329. data/lib/graphql/define/non_null_with_bang.rb +0 -16
  330. data/lib/graphql/define/type_definer.rb +0 -31
  331. data/lib/graphql/define.rb +0 -31
  332. data/lib/graphql/deprecated_dsl.rb +0 -42
  333. data/lib/graphql/directive/deprecated_directive.rb +0 -13
  334. data/lib/graphql/directive/include_directive.rb +0 -2
  335. data/lib/graphql/directive/skip_directive.rb +0 -2
  336. data/lib/graphql/directive.rb +0 -104
  337. data/lib/graphql/enum_type.rb +0 -193
  338. data/lib/graphql/execution/execute.rb +0 -326
  339. data/lib/graphql/execution/flatten.rb +0 -40
  340. data/lib/graphql/execution/instrumentation.rb +0 -92
  341. data/lib/graphql/execution/interpreter/hash_response.rb +0 -46
  342. data/lib/graphql/execution/lazy/resolve.rb +0 -91
  343. data/lib/graphql/execution/typecast.rb +0 -50
  344. data/lib/graphql/field/resolve.rb +0 -59
  345. data/lib/graphql/field.rb +0 -330
  346. data/lib/graphql/float_type.rb +0 -2
  347. data/lib/graphql/function.rb +0 -153
  348. data/lib/graphql/id_type.rb +0 -2
  349. data/lib/graphql/input_object_type.rb +0 -154
  350. data/lib/graphql/int_type.rb +0 -2
  351. data/lib/graphql/interface_type.rb +0 -86
  352. data/lib/graphql/internal_representation/document.rb +0 -27
  353. data/lib/graphql/internal_representation/node.rb +0 -206
  354. data/lib/graphql/internal_representation/print.rb +0 -51
  355. data/lib/graphql/internal_representation/rewrite.rb +0 -184
  356. data/lib/graphql/internal_representation/scope.rb +0 -88
  357. data/lib/graphql/internal_representation/visit.rb +0 -36
  358. data/lib/graphql/internal_representation.rb +0 -7
  359. data/lib/graphql/language/lexer.rl +0 -258
  360. data/lib/graphql/list_type.rb +0 -80
  361. data/lib/graphql/literal_validation_error.rb +0 -6
  362. data/lib/graphql/non_null_type.rb +0 -81
  363. data/lib/graphql/object_type.rb +0 -141
  364. data/lib/graphql/query/arguments.rb +0 -187
  365. data/lib/graphql/query/arguments_cache.rb +0 -25
  366. data/lib/graphql/query/executor.rb +0 -53
  367. data/lib/graphql/query/literal_input.rb +0 -116
  368. data/lib/graphql/query/serial_execution/field_resolution.rb +0 -92
  369. data/lib/graphql/query/serial_execution/operation_resolution.rb +0 -19
  370. data/lib/graphql/query/serial_execution/selection_resolution.rb +0 -23
  371. data/lib/graphql/query/serial_execution/value_resolution.rb +0 -87
  372. data/lib/graphql/query/serial_execution.rb +0 -39
  373. data/lib/graphql/relay/array_connection.rb +0 -85
  374. data/lib/graphql/relay/base_connection.rb +0 -172
  375. data/lib/graphql/relay/connection_instrumentation.rb +0 -54
  376. data/lib/graphql/relay/connection_resolve.rb +0 -43
  377. data/lib/graphql/relay/connection_type.rb +0 -40
  378. data/lib/graphql/relay/edge.rb +0 -27
  379. data/lib/graphql/relay/edge_type.rb +0 -18
  380. data/lib/graphql/relay/edges_instrumentation.rb +0 -40
  381. data/lib/graphql/relay/global_id_resolve.rb +0 -18
  382. data/lib/graphql/relay/mongo_relation_connection.rb +0 -50
  383. data/lib/graphql/relay/mutation/instrumentation.rb +0 -23
  384. data/lib/graphql/relay/mutation/resolve.rb +0 -56
  385. data/lib/graphql/relay/mutation/result.rb +0 -38
  386. data/lib/graphql/relay/mutation.rb +0 -190
  387. data/lib/graphql/relay/node.rb +0 -36
  388. data/lib/graphql/relay/page_info.rb +0 -7
  389. data/lib/graphql/relay/relation_connection.rb +0 -190
  390. data/lib/graphql/relay/type_extensions.rb +0 -30
  391. data/lib/graphql/scalar_type.rb +0 -133
  392. data/lib/graphql/schema/catchall_middleware.rb +0 -35
  393. data/lib/graphql/schema/default_parse_error.rb +0 -10
  394. data/lib/graphql/schema/default_type_error.rb +0 -15
  395. data/lib/graphql/schema/member/accepts_definition.rb +0 -152
  396. data/lib/graphql/schema/member/cached_graphql_definition.rb +0 -26
  397. data/lib/graphql/schema/member/instrumentation.rb +0 -132
  398. data/lib/graphql/schema/middleware_chain.rb +0 -82
  399. data/lib/graphql/schema/possible_types.rb +0 -39
  400. data/lib/graphql/schema/rescue_middleware.rb +0 -60
  401. data/lib/graphql/schema/timeout_middleware.rb +0 -86
  402. data/lib/graphql/schema/traversal.rb +0 -228
  403. data/lib/graphql/schema/validation.rb +0 -303
  404. data/lib/graphql/static_validation/default_visitor.rb +0 -15
  405. data/lib/graphql/static_validation/no_validate_visitor.rb +0 -10
  406. data/lib/graphql/string_type.rb +0 -2
  407. data/lib/graphql/subscriptions/subscription_root.rb +0 -66
  408. data/lib/graphql/tracing/skylight_tracing.rb +0 -62
  409. data/lib/graphql/types/relay/base_field.rb +0 -22
  410. data/lib/graphql/types/relay/base_interface.rb +0 -29
  411. data/lib/graphql/types/relay/base_object.rb +0 -26
  412. data/lib/graphql/types/relay/node_field.rb +0 -43
  413. data/lib/graphql/types/relay/nodes_field.rb +0 -45
  414. data/lib/graphql/union_type.rb +0 -128
  415. data/lib/graphql/upgrader/member.rb +0 -936
  416. data/lib/graphql/upgrader/schema.rb +0 -37
@@ -1,20 +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
10
+ include GraphQL::Schema::Member::HasAstNode
16
11
  include GraphQL::Schema::Member::HasPath
12
+ include GraphQL::Schema::Member::HasValidators
17
13
  extend GraphQL::Schema::FindInheritedValue
14
+ include GraphQL::EmptyObjects
15
+ include GraphQL::Schema::Member::HasDirectives
16
+ include GraphQL::Schema::Member::HasDeprecationReason
17
+
18
+ class FieldImplementationFailed < GraphQL::Error; end
18
19
 
19
20
  # @return [String] the GraphQL name for this field, camelized unless `camelize: false` is provided
20
21
  attr_reader :name
@@ -22,22 +23,39 @@ module GraphQL
22
23
 
23
24
  attr_writer :description
24
25
 
25
- # @return [String, nil] If present, the field is marked as deprecated with this documentation
26
- attr_accessor :deprecation_reason
27
-
28
26
  # @return [Symbol] Method or hash key on the underlying object to look up
29
27
  attr_reader :method_sym
30
28
 
31
29
  # @return [String] Method or hash key on the underlying object to look up
32
30
  attr_reader :method_str
33
31
 
32
+ attr_reader :hash_key
33
+ attr_reader :dig_keys
34
+
34
35
  # @return [Symbol] The method on the type to look up
35
- 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
36
43
 
37
- # @return [Class] The type that this field belongs to
38
- attr_reader :owner
44
+ # @return [Class] The thing this field was defined on (type, mutation, resolver)
45
+ attr_accessor :owner
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
39
57
 
40
- # @return [Symobol] the original name of the field, passed in by the user
58
+ # @return [Symbol] the original name of the field, passed in by the user
41
59
  attr_reader :original_name
42
60
 
43
61
  # @return [Class, nil] The {Schema::Resolver} this field was derived from, if there is one
@@ -45,13 +63,25 @@ module GraphQL
45
63
  @resolver_class
46
64
  end
47
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
+
48
75
  alias :mutation :resolver
49
76
 
50
77
  # @return [Boolean] Apply tracing to this field? (Default: skip scalars, this is the override value)
51
78
  attr_reader :trace
52
79
 
53
80
  # @return [String, nil]
54
- attr_reader :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
55
85
 
56
86
  # Create a field instance from a list of arguments, keyword arguments, and a block.
57
87
  #
@@ -65,21 +95,9 @@ module GraphQL
65
95
  # @return [GraphQL::Schema:Field] an instance of `self
66
96
  # @see {.initialize} for other options
67
97
  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)
98
+ if (resolver_class = resolver || mutation || subscription)
81
99
  # Add a reference to that parent class
82
- kwargs[:resolver_class] = parent_config
100
+ kwargs[:resolver_class] = resolver_class
83
101
  end
84
102
 
85
103
  if name
@@ -87,9 +105,6 @@ module GraphQL
87
105
  end
88
106
 
89
107
  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
108
  if desc
94
109
  if kwargs[:description]
95
110
  raise ArgumentError, "Provide description as a positional argument or `description:` keyword, but not both (#{desc.inspect}, #{kwargs[:description].inspect})"
@@ -97,12 +112,15 @@ module GraphQL
97
112
 
98
113
  kwargs[:description] = desc
99
114
  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
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
102
117
  kwargs[:description] = type
103
118
  else
104
119
  kwargs[:type] = type
105
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
106
124
  end
107
125
  new(**kwargs, &block)
108
126
  end
@@ -112,10 +130,10 @@ module GraphQL
112
130
  def connection?
113
131
  if @connection.nil?
114
132
  # 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
133
+ return_type_name = if @return_type_expr
118
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)
119
137
  else
120
138
  # As a last ditch, try to force loading the return type:
121
139
  type.unwrap.name
@@ -131,8 +149,18 @@ module GraphQL
131
149
  if !@scope.nil?
132
150
  # The default was overridden
133
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?
134
162
  else
135
- @return_type_expr && (@return_type_expr.is_a?(Array) || (@return_type_expr.is_a?(String) && @return_type_expr.include?("[")) || connection?)
163
+ false
136
164
  end
137
165
  end
138
166
 
@@ -152,21 +180,31 @@ module GraphQL
152
180
  end
153
181
  end
154
182
 
183
+ # @return Boolean
184
+ attr_reader :relay_node_field
185
+ # @return Boolean
186
+ attr_reader :relay_nodes_field
187
+
188
+ # @return [Boolean] Should we warn if this field's name conflicts with a built-in method?
189
+ def method_conflict_warning?
190
+ @method_conflict_warning
191
+ end
192
+
155
193
  # @param name [Symbol] The underscore-cased version of this field name (will be camelized for the GraphQL API)
156
194
  # @param type [Class, GraphQL::BaseType, Array] The return type of this field
157
195
  # @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`
196
+ # @param null [Boolean] (defaults to `true`) `true` if this field may return `null`, `false` if it is never `null`
159
197
  # @param description [String] Field description
160
198
  # @param deprecation_reason [String] If present, the field is marked "deprecated" with this message
161
199
  # @param method [Symbol] The method to call on the underlying object to resolve this field (defaults to `name`)
162
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
163
202
  # @param resolver_method [Symbol] The method on the type to call to resolve this field (defaults to `name`)
164
203
  # @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
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.
166
207
  # @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
208
  # @param resolver_class [Class] (Private) A {Schema::Resolver} which this field was derived from. Use `resolver:` to create a field with a resolver.
171
209
  # @param arguments [{String=>GraphQL::Schema::Argument, Hash}] Arguments for this field (may be added in the block, also)
172
210
  # @param camelize [Boolean] If true, the field name will be camelized when building the schema
@@ -174,37 +212,35 @@ module GraphQL
174
212
  # @param scope [Boolean] If true, the return type's `.scope_items` method will be called on the return value
175
213
  # @param subscription_scope [Symbol, String] A key in `context` which will be used to scope subscription payloads
176
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
177
216
  # @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)
217
+ # @param broadcastable [Boolean] Whether or not this field can be distributed in subscription broadcasts
218
+ # @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
219
+ # @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
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)
179
223
  if name.nil?
180
224
  raise ArgumentError, "missing first `name` argument or keyword `name:`"
181
225
  end
182
- if !(field || function || resolver_class)
226
+ if !(resolver_class)
183
227
  if type.nil?
184
228
  raise ArgumentError, "missing second `type` argument or keyword `type:`"
185
229
  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
230
  end
193
231
  @original_name = name
194
- @underscored_name = -Member::BuildType.underscore(name.to_s)
195
- @name = -(camelize ? Member::BuildType.camelize(name.to_s) : name.to_s)
232
+ name_s = -name.to_s
233
+
234
+ @underscored_name = -Member::BuildType.underscore(name_s)
235
+ @name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
236
+
196
237
  @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
201
- end
202
- @function = function
203
- @resolve = resolve
204
- @deprecation_reason = deprecation_reason
238
+ @type = @owner_type = @own_validators = @own_directives = @own_arguments = nil # these will be prepared later if necessary
239
+
240
+ self.deprecation_reason = deprecation_reason
205
241
 
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}`)"
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}`)"
208
244
  end
209
245
 
210
246
  if resolver_method
@@ -212,58 +248,95 @@ module GraphQL
212
248
  raise ArgumentError, "Provide `method:` _or_ `resolver_method:`, not both. (called with: `method: #{method.inspect}, resolver_method: #{resolver_method.inspect}`)"
213
249
  end
214
250
 
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}`)"
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}`)"
217
253
  end
218
254
  end
219
255
 
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
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
223
265
 
224
- @method_str = method_name.to_s
266
+ @method_str = -method_name.to_s
225
267
  @method_sym = method_name.to_sym
226
- @resolver_method = resolver_method
268
+ @resolver_method = (resolver_method || name_s).to_sym
227
269
  @complexity = complexity
228
270
  @return_type_expr = type
229
- @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
230
278
  @connection = connection
231
279
  @max_page_size = max_page_size
280
+ @default_page_size = default_page_size
232
281
  @introspection = introspection
233
282
  @extras = extras
283
+ @broadcastable = broadcastable
234
284
  @resolver_class = resolver_class
235
285
  @scope = scope
236
286
  @trace = trace
237
287
  @relay_node_field = relay_node_field
238
288
  @relay_nodes_field = relay_nodes_field
289
+ @ast_node = ast_node
290
+ @method_conflict_warning = method_conflict_warning
291
+ @fallback_value = fallback_value
239
292
 
240
- # Override the default from HasArguments
241
- @own_arguments = {}
242
293
  arguments.each do |name, arg|
243
- if arg.is_a?(Hash)
294
+ case arg
295
+ when Hash
244
296
  argument(name: name, **arg)
297
+ when GraphQL::Schema::Argument
298
+ add_argument(arg)
299
+ when Array
300
+ arg.each { |a| add_argument(a) }
245
301
  else
246
- @own_arguments[name] = arg
302
+ raise ArgumentError, "Unexpected argument config (#{arg.class}): #{arg.inspect}"
247
303
  end
248
304
  end
249
305
 
250
306
  @owner = owner
251
307
  @subscription_scope = subscription_scope
252
308
 
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
309
+ @extensions = EMPTY_ARRAY
310
+ @call_after_define = false
258
311
  # This should run before connection extension,
259
312
  # but should it run after the definition block?
260
313
  if scoped?
261
314
  self.extension(ScopeExtension)
262
315
  end
316
+
263
317
  # The problem with putting this after the definition_block
264
318
  # is that it would override arguments
265
- if connection?
266
- 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)
267
340
  end
268
341
 
269
342
  if definition_block
@@ -273,6 +346,22 @@ module GraphQL
273
346
  instance_eval(&definition_block)
274
347
  end
275
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
276
365
  end
277
366
 
278
367
  # @param text [String]
@@ -280,8 +369,12 @@ module GraphQL
280
369
  def description(text = nil)
281
370
  if text
282
371
  @description = text
283
- else
372
+ elsif !NOT_CONFIGURED.equal?(@description)
284
373
  @description
374
+ elsif @resolver_class
375
+ @resolver_class.description
376
+ else
377
+ nil
285
378
  end
286
379
  end
287
380
 
@@ -298,24 +391,20 @@ module GraphQL
298
391
  # @example adding an extension with options
299
392
  # extensions([MyExtensionClass, { AnotherExtensionClass => { filter: true } }])
300
393
  #
301
- # @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.
302
395
  # @return [Array<GraphQL::Schema::FieldExtension>] extensions to apply to this field
303
396
  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)
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)
313
402
  else
314
- extension_class = extension
315
- @extensions << extension_class.new(field: self, options: nil)
403
+ self.extension(extension_config)
316
404
  end
317
405
  end
318
406
  end
407
+ @extensions
319
408
  end
320
409
 
321
410
  # Add `extension` to this field, initialized with `options` if provided.
@@ -326,10 +415,19 @@ module GraphQL
326
415
  # @example adding an extension with options
327
416
  # extension(MyExtensionClass, filter: true)
328
417
  #
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}])
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
333
431
  end
334
432
 
335
433
  # Read extras (as symbols) from this field,
@@ -340,14 +438,87 @@ module GraphQL
340
438
  def extras(new_extras = nil)
341
439
  if new_extras.nil?
342
440
  # Read the value
343
- @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
344
447
  else
448
+ if @extras.frozen?
449
+ @extras = @extras.dup
450
+ end
345
451
  # Append to the set of extras on this field
346
452
  @extras.concat(new_extras)
347
453
  end
348
454
  end
349
455
 
350
- def complexity(new_complexity)
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
+
521
+ def complexity(new_complexity = nil)
351
522
  case new_complexity
352
523
  when Proc
353
524
  if new_complexity.parameters.size != 3
@@ -360,87 +531,68 @@ module GraphQL
360
531
  end
361
532
  when Numeric
362
533
  @complexity = new_complexity
534
+ when nil
535
+ if @resolver_class
536
+ @complexity || @resolver_class.complexity || 1
537
+ else
538
+ @complexity || 1
539
+ end
363
540
  else
364
541
  raise("Invalid complexity: #{new_complexity.inspect} on #{@name}")
365
542
  end
366
543
  end
367
544
 
368
- # @return [Integer, nil] Applied to connections if present
369
- 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
370
549
 
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)
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
377
556
  else
378
- GraphQL::Field.new
557
+ nil
379
558
  end
559
+ end
380
560
 
381
- field_defn.name = @name
382
- if @return_type_expr
383
- field_defn.type = -> { type }
384
- 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
385
565
 
386
- if @description
387
- field_defn.description = @description
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
388
574
  end
575
+ end
389
576
 
390
- if @deprecation_reason
391
- field_defn.deprecation_reason = @deprecation_reason
392
- end
577
+ class MissingReturnTypeError < GraphQL::Error; end
578
+ attr_writer :type
393
579
 
580
+ def type
394
581
  if @resolver_class
395
- if @resolver_class < GraphQL::Schema::Mutation
396
- field_defn.mutation = @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)"
397
585
  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
586
+ nullable = @return_type_null.nil? ? @resolver_class.null : @return_type_null
587
+ Member::BuildType.parse_type(return_type, null: nullable)
588
+ else
589
+ @type ||= Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
432
590
  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
591
+ rescue GraphQL::Schema::InvalidDocumentError, MissingReturnTypeError => err
592
+ # Let this propagate up
593
+ raise err
594
+ rescue StandardError => err
595
+ raise MissingReturnTypeError, "Failed to build return type for #{@owner.graphql_name}.#{name} from #{@return_type_expr.inspect}: (#{err.class}) #{err.message}", err.backtrace
444
596
  end
445
597
 
446
598
  def visible?(context)
@@ -451,56 +603,47 @@ module GraphQL
451
603
  end
452
604
  end
453
605
 
454
- def accessible?(context)
455
- if @resolver_class
456
- @resolver_class.accessible?(context)
457
- else
458
- true
459
- end
460
- end
461
-
462
- def authorized?(object, context)
606
+ def authorized?(object, args, context)
463
607
  if @resolver_class
464
- # The resolver will check itself during `resolve()`
608
+ # The resolver _instance_ will check itself during `resolve()`
465
609
  @resolver_class.authorized?(object, context)
466
610
  else
467
- # Faster than `.any?`
468
- arguments.each_value do |arg|
469
- if !arg.authorized?(object, context)
470
- 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
471
619
  end
472
- end
473
- true
474
- end
475
- end
476
620
 
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)
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
642
+ end
496
643
  end
497
- else
498
- public_send_field(after_obj, ruby_args, ctx)
499
644
  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
645
  end
646
+ true
504
647
  end
505
648
  end
506
649
 
@@ -509,92 +652,114 @@ module GraphQL
509
652
  # @param object [GraphQL::Schema::Object] An instance of some type class, wrapping an application object
510
653
  # @param args [Hash] A symbol-keyed hash of Ruby keyword arguments. (Empty if no args)
511
654
  # @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
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
527
671
  end
528
- @resolver_class.new(object: resolver_obj, context: ctx, field: self)
529
- else
530
- extended_obj
672
+ obj = @resolver_class.new(object: obj, context: query_ctx, field: self)
531
673
  end
532
674
 
533
- 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
534
693
  # Call the method with kwargs, if there are any
535
- if extended_args.any?
536
- 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)
537
716
  else
538
- field_receiver.public_send(@resolver_method)
717
+ inner_object.public_send(@method_sym)
539
718
  end
719
+ elsif @fallback_value != NOT_CONFIGURED
720
+ @fallback_value
540
721
  else
541
- 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
542
731
  end
543
732
  end
544
733
  else
545
- err = GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: ctx, field: self)
546
- ctx.schema.unauthorized_field(err)
734
+ raise GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: query_ctx, field: self)
547
735
  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
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
554
756
  rescue GraphQL::ExecutionError => err
555
757
  err
556
758
  end
557
759
 
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
760
  # @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
592
- end
593
- end
594
-
595
- # @param ctx [GraphQL::Query::Context::FieldResolutionContext]
596
761
  def fetch_extra(extra_name, ctx)
597
- if extra_name != :path && respond_to?(extra_name)
762
+ if extra_name != :path && extra_name != :ast_node && respond_to?(extra_name)
598
763
  self.public_send(extra_name)
599
764
  elsif ctx.respond_to?(extra_name)
600
765
  ctx.public_send(extra_name)
@@ -605,55 +770,43 @@ module GraphQL
605
770
 
606
771
  private
607
772
 
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])
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."
625
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
626
794
  end
795
+ end
627
796
 
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
797
+ if encountered_keyrest
798
+ unsatisfied_ruby_kwargs.clear
635
799
  end
636
- end
637
800
 
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
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:
647
804
 
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
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
657
810
  end
658
811
  end
659
812
 
@@ -664,32 +817,52 @@ module GraphQL
664
817
  if @extensions.empty?
665
818
  yield(obj, args)
666
819
  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)
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)
674
830
  end
675
831
 
832
+ extended_obj = extended[:obj]
833
+ extended_args = extended[:args]
834
+ memos = extended[:memos] || EMPTY_HASH
835
+
676
836
  ctx.schema.after_lazy(value) do |resolved_value|
677
- @extensions.each_with_index do |ext, idx|
837
+ idx = 0
838
+ @extensions.each do |ext|
678
839
  memo = memos[idx]
679
840
  # TODO after_lazy?
680
- 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
681
843
  end
682
844
  resolved_value
683
845
  end
684
846
  end
685
847
  end
686
848
 
687
- def run_extensions_before_resolve(memos, obj, args, ctx, idx: 0)
849
+ def run_extensions_before_resolve(obj, args, ctx, extended, idx: 0)
688
850
  extension = @extensions[idx]
689
851
  if extension
690
852
  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) }
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) }
693
866
  end
694
867
  else
695
868
  yield(obj, args)