graphql 1.9.17 → 2.0.20

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 (413) 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/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 +62 -24
  75. data/lib/graphql/execution/interpreter/runtime.rb +773 -399
  76. data/lib/graphql/execution/interpreter.rb +206 -74
  77. data/lib/graphql/execution/lazy/lazy_method_map.rb +4 -0
  78. data/lib/graphql/execution/lazy.rb +11 -21
  79. data/lib/graphql/execution/lookahead.rb +65 -136
  80. data/lib/graphql/execution/multiplex.rb +6 -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 +12 -6
  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 +12 -12
  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 +113 -71
  102. data/lib/graphql/language/lexer.rb +216 -1462
  103. data/lib/graphql/language/nodes.rb +128 -131
  104. data/lib/graphql/language/parser.rb +957 -912
  105. data/lib/graphql/language/parser.y +148 -120
  106. data/lib/graphql/language/printer.rb +48 -23
  107. data/lib/graphql/language/sanitized_printer.rb +222 -0
  108. data/lib/graphql/language/token.rb +0 -4
  109. data/lib/graphql/language/visitor.rb +192 -84
  110. data/lib/graphql/language.rb +3 -1
  111. data/lib/graphql/name_validator.rb +2 -7
  112. data/lib/graphql/pagination/active_record_relation_connection.rb +77 -0
  113. data/lib/graphql/pagination/array_connection.rb +79 -0
  114. data/lib/graphql/pagination/connection.rb +253 -0
  115. data/lib/graphql/pagination/connections.rb +135 -0
  116. data/lib/graphql/pagination/mongoid_relation_connection.rb +25 -0
  117. data/lib/graphql/pagination/relation_connection.rb +228 -0
  118. data/lib/graphql/pagination/sequel_dataset_connection.rb +28 -0
  119. data/lib/graphql/pagination.rb +6 -0
  120. data/lib/graphql/parse_error.rb +0 -1
  121. data/lib/graphql/query/context.rb +204 -203
  122. data/lib/graphql/query/fingerprint.rb +26 -0
  123. data/lib/graphql/query/input_validation_result.rb +33 -7
  124. data/lib/graphql/query/null_context.rb +21 -8
  125. data/lib/graphql/query/validation_pipeline.rb +16 -38
  126. data/lib/graphql/query/variable_validation_error.rb +3 -3
  127. data/lib/graphql/query/variables.rb +39 -12
  128. data/lib/graphql/query.rb +88 -40
  129. data/lib/graphql/railtie.rb +6 -102
  130. data/lib/graphql/rake_task/validate.rb +4 -1
  131. data/lib/graphql/rake_task.rb +41 -10
  132. data/lib/graphql/relay/range_add.rb +17 -10
  133. data/lib/graphql/relay.rb +0 -15
  134. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  135. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  136. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  137. data/lib/graphql/rubocop.rb +4 -0
  138. data/lib/graphql/schema/addition.rb +245 -0
  139. data/lib/graphql/schema/argument.rb +284 -33
  140. data/lib/graphql/schema/base_64_encoder.rb +2 -0
  141. data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +1 -1
  142. data/lib/graphql/schema/build_from_definition/resolve_map.rb +13 -5
  143. data/lib/graphql/schema/build_from_definition.rb +336 -205
  144. data/lib/graphql/schema/built_in_types.rb +5 -5
  145. data/lib/graphql/schema/directive/deprecated.rb +18 -0
  146. data/lib/graphql/schema/directive/feature.rb +1 -1
  147. data/lib/graphql/schema/directive/flagged.rb +57 -0
  148. data/lib/graphql/schema/directive/include.rb +2 -2
  149. data/lib/graphql/schema/directive/one_of.rb +12 -0
  150. data/lib/graphql/schema/directive/skip.rb +2 -2
  151. data/lib/graphql/schema/directive/transform.rb +14 -2
  152. data/lib/graphql/schema/directive.rb +134 -15
  153. data/lib/graphql/schema/enum.rb +137 -39
  154. data/lib/graphql/schema/enum_value.rb +20 -23
  155. data/lib/graphql/schema/field/connection_extension.rb +50 -20
  156. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  157. data/lib/graphql/schema/field.rb +503 -331
  158. data/lib/graphql/schema/field_extension.rb +89 -2
  159. data/lib/graphql/schema/find_inherited_value.rb +17 -1
  160. data/lib/graphql/schema/finder.rb +16 -14
  161. data/lib/graphql/schema/input_object.rb +182 -60
  162. data/lib/graphql/schema/interface.rb +24 -49
  163. data/lib/graphql/schema/introspection_system.rb +103 -37
  164. data/lib/graphql/schema/late_bound_type.rb +9 -2
  165. data/lib/graphql/schema/list.rb +61 -3
  166. data/lib/graphql/schema/loader.rb +144 -96
  167. data/lib/graphql/schema/member/base_dsl_methods.rb +41 -37
  168. data/lib/graphql/schema/member/build_type.rb +24 -15
  169. data/lib/graphql/schema/member/has_arguments.rb +310 -26
  170. data/lib/graphql/schema/member/has_ast_node.rb +32 -0
  171. data/lib/graphql/schema/member/has_deprecation_reason.rb +24 -0
  172. data/lib/graphql/schema/member/has_directives.rb +120 -0
  173. data/lib/graphql/schema/member/has_fields.rb +112 -34
  174. data/lib/graphql/schema/member/has_interfaces.rb +129 -0
  175. data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
  176. data/lib/graphql/schema/member/has_validators.rb +57 -0
  177. data/lib/graphql/schema/member/relay_shortcuts.rb +47 -2
  178. data/lib/graphql/schema/member/type_system_helpers.rb +20 -3
  179. data/lib/graphql/schema/member/validates_input.rb +33 -0
  180. data/lib/graphql/schema/member.rb +11 -6
  181. data/lib/graphql/schema/mutation.rb +4 -9
  182. data/lib/graphql/schema/non_null.rb +34 -4
  183. data/lib/graphql/schema/object.rb +36 -60
  184. data/lib/graphql/schema/printer.rb +16 -35
  185. data/lib/graphql/schema/relay_classic_mutation.rb +91 -44
  186. data/lib/graphql/schema/resolver/has_payload_type.rb +51 -11
  187. data/lib/graphql/schema/resolver.rb +147 -94
  188. data/lib/graphql/schema/scalar.rb +40 -15
  189. data/lib/graphql/schema/subscription.rb +60 -31
  190. data/lib/graphql/schema/timeout.rb +45 -35
  191. data/lib/graphql/schema/type_expression.rb +21 -13
  192. data/lib/graphql/schema/type_membership.rb +23 -6
  193. data/lib/graphql/schema/union.rb +49 -15
  194. data/lib/graphql/schema/unique_within_type.rb +1 -2
  195. data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
  196. data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
  197. data/lib/graphql/schema/validator/exclusion_validator.rb +33 -0
  198. data/lib/graphql/schema/validator/format_validator.rb +48 -0
  199. data/lib/graphql/schema/validator/inclusion_validator.rb +35 -0
  200. data/lib/graphql/schema/validator/length_validator.rb +59 -0
  201. data/lib/graphql/schema/validator/numericality_validator.rb +82 -0
  202. data/lib/graphql/schema/validator/required_validator.rb +82 -0
  203. data/lib/graphql/schema/validator.rb +171 -0
  204. data/lib/graphql/schema/warden.rb +211 -35
  205. data/lib/graphql/schema/wrapper.rb +0 -5
  206. data/lib/graphql/schema.rb +833 -889
  207. data/lib/graphql/static_validation/all_rules.rb +3 -0
  208. data/lib/graphql/static_validation/base_visitor.rb +21 -31
  209. data/lib/graphql/static_validation/definition_dependencies.rb +7 -2
  210. data/lib/graphql/static_validation/error.rb +3 -1
  211. data/lib/graphql/static_validation/literal_validator.rb +69 -26
  212. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +45 -83
  213. data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +22 -6
  214. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +35 -26
  215. data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +4 -2
  216. data/lib/graphql/static_validation/rules/directives_are_defined.rb +12 -6
  217. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +14 -14
  218. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
  219. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -5
  220. data/lib/graphql/static_validation/rules/fields_will_merge.rb +94 -51
  221. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
  222. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  223. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
  224. data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
  225. data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
  226. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +66 -0
  227. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -0
  228. data/lib/graphql/static_validation/rules/query_root_exists.rb +17 -0
  229. data/lib/graphql/static_validation/rules/query_root_exists_error.rb +26 -0
  230. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -2
  231. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +9 -10
  232. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +13 -7
  233. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +12 -13
  234. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +19 -14
  235. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  236. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +5 -3
  237. data/lib/graphql/static_validation/type_stack.rb +2 -2
  238. data/lib/graphql/static_validation/validation_context.rb +13 -3
  239. data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
  240. data/lib/graphql/static_validation/validator.rb +32 -20
  241. data/lib/graphql/static_validation.rb +1 -2
  242. data/lib/graphql/string_encoding_error.rb +13 -3
  243. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +129 -22
  244. data/lib/graphql/subscriptions/broadcast_analyzer.rb +81 -0
  245. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +58 -0
  246. data/lib/graphql/subscriptions/event.rb +84 -35
  247. data/lib/graphql/subscriptions/instrumentation.rb +0 -47
  248. data/lib/graphql/subscriptions/serialize.rb +53 -6
  249. data/lib/graphql/subscriptions.rb +137 -57
  250. data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
  251. data/lib/graphql/tracing/active_support_notifications_tracing.rb +8 -17
  252. data/lib/graphql/tracing/appoptics_trace.rb +231 -0
  253. data/lib/graphql/tracing/appoptics_tracing.rb +173 -0
  254. data/lib/graphql/tracing/appsignal_trace.rb +71 -0
  255. data/lib/graphql/tracing/appsignal_tracing.rb +23 -0
  256. data/lib/graphql/tracing/data_dog_trace.rb +148 -0
  257. data/lib/graphql/tracing/data_dog_tracing.rb +34 -2
  258. data/lib/graphql/tracing/new_relic_trace.rb +75 -0
  259. data/lib/graphql/tracing/new_relic_tracing.rb +9 -12
  260. data/lib/graphql/tracing/notifications_trace.rb +41 -0
  261. data/lib/graphql/tracing/notifications_tracing.rb +59 -0
  262. data/lib/graphql/tracing/platform_trace.rb +107 -0
  263. data/lib/graphql/tracing/platform_tracing.rb +76 -35
  264. data/lib/graphql/tracing/prometheus_trace.rb +89 -0
  265. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
  266. data/lib/graphql/tracing/prometheus_tracing.rb +11 -3
  267. data/lib/graphql/tracing/scout_trace.rb +72 -0
  268. data/lib/graphql/tracing/scout_tracing.rb +19 -0
  269. data/lib/graphql/tracing/statsd_trace.rb +56 -0
  270. data/lib/graphql/tracing/statsd_tracing.rb +42 -0
  271. data/lib/graphql/tracing.rb +143 -67
  272. data/lib/graphql/type_kinds.rb +6 -3
  273. data/lib/graphql/types/big_int.rb +5 -1
  274. data/lib/graphql/types/int.rb +10 -3
  275. data/lib/graphql/types/iso_8601_date.rb +20 -9
  276. data/lib/graphql/types/iso_8601_date_time.rb +36 -10
  277. data/lib/graphql/types/relay/base_connection.rb +18 -90
  278. data/lib/graphql/types/relay/base_edge.rb +2 -34
  279. data/lib/graphql/types/relay/connection_behaviors.rb +176 -0
  280. data/lib/graphql/types/relay/edge_behaviors.rb +75 -0
  281. data/lib/graphql/types/relay/has_node_field.rb +41 -0
  282. data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
  283. data/lib/graphql/types/relay/node.rb +2 -4
  284. data/lib/graphql/types/relay/node_behaviors.rb +25 -0
  285. data/lib/graphql/types/relay/page_info.rb +2 -14
  286. data/lib/graphql/types/relay/page_info_behaviors.rb +30 -0
  287. data/lib/graphql/types/relay.rb +10 -5
  288. data/lib/graphql/types/string.rb +8 -2
  289. data/lib/graphql/unauthorized_error.rb +2 -2
  290. data/lib/graphql/unresolved_type_error.rb +2 -2
  291. data/lib/graphql/version.rb +1 -1
  292. data/lib/graphql.rb +60 -66
  293. data/readme.md +3 -6
  294. metadata +124 -236
  295. data/lib/graphql/analysis/analyze_query.rb +0 -91
  296. data/lib/graphql/analysis/field_usage.rb +0 -45
  297. data/lib/graphql/analysis/max_query_complexity.rb +0 -26
  298. data/lib/graphql/analysis/max_query_depth.rb +0 -26
  299. data/lib/graphql/analysis/query_complexity.rb +0 -88
  300. data/lib/graphql/analysis/query_depth.rb +0 -43
  301. data/lib/graphql/analysis/reducer_state.rb +0 -48
  302. data/lib/graphql/argument.rb +0 -159
  303. data/lib/graphql/authorization.rb +0 -82
  304. data/lib/graphql/backwards_compatibility.rb +0 -60
  305. data/lib/graphql/base_type.rb +0 -226
  306. data/lib/graphql/boolean_type.rb +0 -2
  307. data/lib/graphql/compatibility/execution_specification/counter_schema.rb +0 -53
  308. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +0 -200
  309. data/lib/graphql/compatibility/execution_specification.rb +0 -435
  310. data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +0 -111
  311. data/lib/graphql/compatibility/lazy_execution_specification.rb +0 -213
  312. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +0 -91
  313. data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +0 -79
  314. data/lib/graphql/compatibility/query_parser_specification.rb +0 -264
  315. data/lib/graphql/compatibility/schema_parser_specification.rb +0 -680
  316. data/lib/graphql/compatibility.rb +0 -5
  317. data/lib/graphql/define/assign_argument.rb +0 -12
  318. data/lib/graphql/define/assign_connection.rb +0 -13
  319. data/lib/graphql/define/assign_enum_value.rb +0 -18
  320. data/lib/graphql/define/assign_global_id_field.rb +0 -11
  321. data/lib/graphql/define/assign_mutation_function.rb +0 -34
  322. data/lib/graphql/define/assign_object_field.rb +0 -42
  323. data/lib/graphql/define/defined_object_proxy.rb +0 -50
  324. data/lib/graphql/define/instance_definable.rb +0 -300
  325. data/lib/graphql/define/no_definition_error.rb +0 -7
  326. data/lib/graphql/define/non_null_with_bang.rb +0 -16
  327. data/lib/graphql/define/type_definer.rb +0 -31
  328. data/lib/graphql/define.rb +0 -31
  329. data/lib/graphql/deprecated_dsl.rb +0 -42
  330. data/lib/graphql/directive/deprecated_directive.rb +0 -13
  331. data/lib/graphql/directive/include_directive.rb +0 -2
  332. data/lib/graphql/directive/skip_directive.rb +0 -2
  333. data/lib/graphql/directive.rb +0 -104
  334. data/lib/graphql/enum_type.rb +0 -193
  335. data/lib/graphql/execution/execute.rb +0 -326
  336. data/lib/graphql/execution/flatten.rb +0 -40
  337. data/lib/graphql/execution/instrumentation.rb +0 -92
  338. data/lib/graphql/execution/interpreter/hash_response.rb +0 -46
  339. data/lib/graphql/execution/lazy/resolve.rb +0 -91
  340. data/lib/graphql/execution/typecast.rb +0 -50
  341. data/lib/graphql/field/resolve.rb +0 -59
  342. data/lib/graphql/field.rb +0 -330
  343. data/lib/graphql/float_type.rb +0 -2
  344. data/lib/graphql/function.rb +0 -153
  345. data/lib/graphql/id_type.rb +0 -2
  346. data/lib/graphql/input_object_type.rb +0 -154
  347. data/lib/graphql/int_type.rb +0 -2
  348. data/lib/graphql/interface_type.rb +0 -86
  349. data/lib/graphql/internal_representation/document.rb +0 -27
  350. data/lib/graphql/internal_representation/node.rb +0 -206
  351. data/lib/graphql/internal_representation/print.rb +0 -51
  352. data/lib/graphql/internal_representation/rewrite.rb +0 -184
  353. data/lib/graphql/internal_representation/scope.rb +0 -88
  354. data/lib/graphql/internal_representation/visit.rb +0 -36
  355. data/lib/graphql/internal_representation.rb +0 -7
  356. data/lib/graphql/language/lexer.rl +0 -258
  357. data/lib/graphql/list_type.rb +0 -80
  358. data/lib/graphql/literal_validation_error.rb +0 -6
  359. data/lib/graphql/non_null_type.rb +0 -81
  360. data/lib/graphql/object_type.rb +0 -141
  361. data/lib/graphql/query/arguments.rb +0 -187
  362. data/lib/graphql/query/arguments_cache.rb +0 -25
  363. data/lib/graphql/query/executor.rb +0 -53
  364. data/lib/graphql/query/literal_input.rb +0 -116
  365. data/lib/graphql/query/serial_execution/field_resolution.rb +0 -92
  366. data/lib/graphql/query/serial_execution/operation_resolution.rb +0 -19
  367. data/lib/graphql/query/serial_execution/selection_resolution.rb +0 -23
  368. data/lib/graphql/query/serial_execution/value_resolution.rb +0 -87
  369. data/lib/graphql/query/serial_execution.rb +0 -39
  370. data/lib/graphql/relay/array_connection.rb +0 -85
  371. data/lib/graphql/relay/base_connection.rb +0 -172
  372. data/lib/graphql/relay/connection_instrumentation.rb +0 -54
  373. data/lib/graphql/relay/connection_resolve.rb +0 -43
  374. data/lib/graphql/relay/connection_type.rb +0 -40
  375. data/lib/graphql/relay/edge.rb +0 -27
  376. data/lib/graphql/relay/edge_type.rb +0 -18
  377. data/lib/graphql/relay/edges_instrumentation.rb +0 -40
  378. data/lib/graphql/relay/global_id_resolve.rb +0 -18
  379. data/lib/graphql/relay/mongo_relation_connection.rb +0 -50
  380. data/lib/graphql/relay/mutation/instrumentation.rb +0 -23
  381. data/lib/graphql/relay/mutation/resolve.rb +0 -56
  382. data/lib/graphql/relay/mutation/result.rb +0 -38
  383. data/lib/graphql/relay/mutation.rb +0 -190
  384. data/lib/graphql/relay/node.rb +0 -36
  385. data/lib/graphql/relay/page_info.rb +0 -7
  386. data/lib/graphql/relay/relation_connection.rb +0 -190
  387. data/lib/graphql/relay/type_extensions.rb +0 -30
  388. data/lib/graphql/scalar_type.rb +0 -133
  389. data/lib/graphql/schema/catchall_middleware.rb +0 -35
  390. data/lib/graphql/schema/default_parse_error.rb +0 -10
  391. data/lib/graphql/schema/default_type_error.rb +0 -15
  392. data/lib/graphql/schema/member/accepts_definition.rb +0 -152
  393. data/lib/graphql/schema/member/cached_graphql_definition.rb +0 -26
  394. data/lib/graphql/schema/member/instrumentation.rb +0 -132
  395. data/lib/graphql/schema/middleware_chain.rb +0 -82
  396. data/lib/graphql/schema/possible_types.rb +0 -39
  397. data/lib/graphql/schema/rescue_middleware.rb +0 -60
  398. data/lib/graphql/schema/timeout_middleware.rb +0 -86
  399. data/lib/graphql/schema/traversal.rb +0 -228
  400. data/lib/graphql/schema/validation.rb +0 -303
  401. data/lib/graphql/static_validation/default_visitor.rb +0 -15
  402. data/lib/graphql/static_validation/no_validate_visitor.rb +0 -10
  403. data/lib/graphql/string_type.rb +0 -2
  404. data/lib/graphql/subscriptions/subscription_root.rb +0 -66
  405. data/lib/graphql/tracing/skylight_tracing.rb +0 -62
  406. data/lib/graphql/types/relay/base_field.rb +0 -22
  407. data/lib/graphql/types/relay/base_interface.rb +0 -29
  408. data/lib/graphql/types/relay/base_object.rb +0 -26
  409. data/lib/graphql/types/relay/node_field.rb +0 -43
  410. data/lib/graphql/types/relay/nodes_field.rb +0 -45
  411. data/lib/graphql/union_type.rb +0 -128
  412. data/lib/graphql/upgrader/member.rb +0 -936
  413. 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::Schema::FindInheritedValue::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_given, &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,46 @@ 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
471
- end
611
+ if (arg_values = context[:current_arguments])
612
+ # ^^ that's provided by the interpreter at runtime, and includes info about whether the default value was used or not.
613
+ using_arg_values = true
614
+ arg_values = arg_values.argument_values
615
+ else
616
+ arg_values = args
617
+ using_arg_values = false
472
618
  end
473
- true
474
- end
475
- end
619
+ if args.size > 0
620
+ args = context.warden.arguments(self)
621
+ args.each do |arg|
622
+ arg_key = arg.keyword
623
+ if arg_values.key?(arg_key)
624
+ arg_value = arg_values[arg_key]
625
+ if using_arg_values
626
+ if arg_value.default_used?
627
+ # pass -- no auth required for default used
628
+ next
629
+ else
630
+ application_arg_value = arg_value.value
631
+ if application_arg_value.is_a?(GraphQL::Execution::Interpreter::Arguments)
632
+ application_arg_value.keyword_arguments
633
+ end
634
+ end
635
+ else
636
+ application_arg_value = arg_value
637
+ end
476
638
 
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)
639
+ if !arg.authorized?(object, application_arg_value, context)
640
+ return false
641
+ end
496
642
  end
497
- else
498
- public_send_field(after_obj, ruby_args, ctx)
499
643
  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
644
  end
645
+ true
504
646
  end
505
647
  end
506
648
 
@@ -509,92 +651,114 @@ module GraphQL
509
651
  # @param object [GraphQL::Schema::Object] An instance of some type class, wrapping an application object
510
652
  # @param args [Hash] A symbol-keyed hash of Ruby keyword arguments. (Empty if no args)
511
653
  # @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
654
+ def resolve(object, args, query_ctx)
655
+ # Unwrap the GraphQL object to get the application object.
656
+ application_object = object.object
657
+ method_receiver = nil
658
+ method_to_call = nil
659
+ method_args = nil
660
+
661
+ Schema::Validator.validate!(validators, application_object, query_ctx, args)
662
+
663
+ query_ctx.schema.after_lazy(self.authorized?(application_object, args, query_ctx)) do |is_authorized|
664
+ if is_authorized
665
+ with_extensions(object, args, query_ctx) do |obj, ruby_kwargs|
666
+ method_args = ruby_kwargs
667
+ if @resolver_class
668
+ if obj.is_a?(GraphQL::Schema::Object)
669
+ obj = obj.object
527
670
  end
528
- @resolver_class.new(object: resolver_obj, context: ctx, field: self)
529
- else
530
- extended_obj
671
+ obj = @resolver_class.new(object: obj, context: query_ctx, field: self)
531
672
  end
532
673
 
533
- if field_receiver.respond_to?(@resolver_method)
674
+ inner_object = obj.object
675
+
676
+ if !NOT_CONFIGURED.equal?(@hash_key)
677
+ hash_value = if inner_object.is_a?(Hash)
678
+ inner_object.key?(@hash_key) ? inner_object[@hash_key] : inner_object[@hash_key_str]
679
+ elsif inner_object.respond_to?(:[])
680
+ inner_object[@hash_key]
681
+ else
682
+ nil
683
+ end
684
+ if hash_value == false
685
+ hash_value
686
+ else
687
+ hash_value || (@fallback_value != :not_given ? @fallback_value : nil)
688
+ end
689
+ elsif obj.respond_to?(resolver_method)
690
+ method_to_call = resolver_method
691
+ method_receiver = obj
534
692
  # Call the method with kwargs, if there are any
535
- if extended_args.any?
536
- field_receiver.public_send(@resolver_method, **extended_args)
693
+ if ruby_kwargs.any?
694
+ obj.public_send(resolver_method, **ruby_kwargs)
695
+ else
696
+ obj.public_send(resolver_method)
697
+ end
698
+ elsif inner_object.is_a?(Hash)
699
+ if @dig_keys
700
+ inner_object.dig(*@dig_keys)
701
+ elsif inner_object.key?(@method_sym)
702
+ inner_object[@method_sym]
703
+ elsif inner_object.key?(@method_str)
704
+ inner_object[@method_str]
705
+ elsif @fallback_value != :not_given
706
+ @fallback_value
707
+ else
708
+ nil
709
+ end
710
+ elsif inner_object.respond_to?(@method_sym)
711
+ method_to_call = @method_sym
712
+ method_receiver = obj.object
713
+ if ruby_kwargs.any?
714
+ inner_object.public_send(@method_sym, **ruby_kwargs)
537
715
  else
538
- field_receiver.public_send(@resolver_method)
716
+ inner_object.public_send(@method_sym)
539
717
  end
718
+ elsif @fallback_value != :not_given
719
+ @fallback_value
540
720
  else
541
- resolve_field_method(field_receiver, extended_args, ctx)
721
+ raise <<-ERR
722
+ Failed to implement #{@owner.graphql_name}.#{@name}, tried:
723
+
724
+ - `#{obj.class}##{resolver_method}`, which did not exist
725
+ - `#{inner_object.class}##{@method_sym}`, which did not exist
726
+ - Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{inner_object}`, but it wasn't a Hash
727
+
728
+ To implement this field, define one of the methods above (and check for typos), or supply a `fallback_value`.
729
+ ERR
542
730
  end
543
731
  end
544
732
  else
545
- err = GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: ctx, field: self)
546
- ctx.schema.unauthorized_field(err)
733
+ raise GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: query_ctx, field: self)
547
734
  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
735
  end
736
+ rescue GraphQL::UnauthorizedFieldError => err
737
+ err.field ||= self
738
+ begin
739
+ query_ctx.schema.unauthorized_field(err)
740
+ rescue GraphQL::ExecutionError => err
741
+ err
742
+ end
743
+ rescue GraphQL::UnauthorizedError => err
744
+ begin
745
+ query_ctx.schema.unauthorized_object(err)
746
+ rescue GraphQL::ExecutionError => err
747
+ err
748
+ end
749
+ rescue ArgumentError
750
+ if method_receiver && method_to_call
751
+ assert_satisfactory_implementation(method_receiver, method_to_call, method_args)
752
+ end
753
+ # if the line above doesn't raise, re-raise
754
+ raise
554
755
  rescue GraphQL::ExecutionError => err
555
756
  err
556
757
  end
557
758
 
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
759
  # @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
760
  def fetch_extra(extra_name, ctx)
597
- if extra_name != :path && respond_to?(extra_name)
761
+ if extra_name != :path && extra_name != :ast_node && respond_to?(extra_name)
598
762
  self.public_send(extra_name)
599
763
  elsif ctx.respond_to?(extra_name)
600
764
  ctx.public_send(extra_name)
@@ -605,55 +769,43 @@ module GraphQL
605
769
 
606
770
  private
607
771
 
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])
772
+ def assert_satisfactory_implementation(receiver, method_name, ruby_kwargs)
773
+ method_defn = receiver.method(method_name)
774
+ unsatisfied_ruby_kwargs = ruby_kwargs.dup
775
+ unsatisfied_method_params = []
776
+ encountered_keyrest = false
777
+ method_defn.parameters.each do |(param_type, param_name)|
778
+ case param_type
779
+ when :key
780
+ unsatisfied_ruby_kwargs.delete(param_name)
781
+ when :keyreq
782
+ if unsatisfied_ruby_kwargs.key?(param_name)
783
+ unsatisfied_ruby_kwargs.delete(param_name)
784
+ else
785
+ 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
786
  end
787
+ when :keyrest
788
+ encountered_keyrest = true
789
+ when :req
790
+ 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."
791
+ when :opt, :rest
792
+ # This is fine, although it will never be present
626
793
  end
794
+ end
627
795
 
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
796
+ if encountered_keyrest
797
+ unsatisfied_ruby_kwargs.clear
635
798
  end
636
- end
637
799
 
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
800
+ if unsatisfied_ruby_kwargs.any? || unsatisfied_method_params.any?
801
+ raise FieldImplementationFailed.new, <<-ERR
802
+ Failed to call `#{method_name.inspect}` on #{receiver.inspect} because the Ruby method params were incompatible with the GraphQL arguments:
647
803
 
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
804
+ #{ unsatisfied_ruby_kwargs
805
+ .map { |key, value| "- `#{key}: #{value}` was given by GraphQL but not defined in the Ruby method. Add `#{key}:` to the method parameters." }
806
+ .concat(unsatisfied_method_params)
807
+ .join("\n") }
808
+ ERR
657
809
  end
658
810
  end
659
811
 
@@ -664,32 +816,52 @@ module GraphQL
664
816
  if @extensions.empty?
665
817
  yield(obj, args)
666
818
  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)
819
+ # This is a hack to get the _last_ value for extended obj and args,
820
+ # in case one of the extensions doesn't `yield`.
821
+ # (There's another implementation that uses multiple-return, but I'm wary of the perf cost of the extra arrays)
822
+ extended = { args: args, obj: obj, memos: nil, added_extras: nil }
823
+ value = run_extensions_before_resolve(obj, args, ctx, extended) do |obj, args|
824
+ if (added_extras = extended[:added_extras])
825
+ args = args.dup
826
+ added_extras.each { |e| args.delete(e) }
827
+ end
828
+ yield(obj, args)
674
829
  end
675
830
 
831
+ extended_obj = extended[:obj]
832
+ extended_args = extended[:args]
833
+ memos = extended[:memos] || EMPTY_HASH
834
+
676
835
  ctx.schema.after_lazy(value) do |resolved_value|
677
- @extensions.each_with_index do |ext, idx|
836
+ idx = 0
837
+ @extensions.each do |ext|
678
838
  memo = memos[idx]
679
839
  # TODO after_lazy?
680
- resolved_value = ext.after_resolve(object: original_obj, arguments: original_args, context: ctx, value: resolved_value, memo: memo)
840
+ resolved_value = ext.after_resolve(object: extended_obj, arguments: extended_args, context: ctx, value: resolved_value, memo: memo)
841
+ idx += 1
681
842
  end
682
843
  resolved_value
683
844
  end
684
845
  end
685
846
  end
686
847
 
687
- def run_extensions_before_resolve(memos, obj, args, ctx, idx: 0)
848
+ def run_extensions_before_resolve(obj, args, ctx, extended, idx: 0)
688
849
  extension = @extensions[idx]
689
850
  if extension
690
851
  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) }
852
+ if memo
853
+ memos = extended[:memos] ||= {}
854
+ memos[idx] = memo
855
+ end
856
+
857
+ if (extras = extension.added_extras)
858
+ ae = extended[:added_extras] ||= []
859
+ ae.concat(extras)
860
+ end
861
+
862
+ extended[:obj] = extended_obj
863
+ extended[:args] = extended_args
864
+ run_extensions_before_resolve(extended_obj, extended_args, ctx, extended, idx: idx + 1) { |o, a| yield(o, a) }
693
865
  end
694
866
  else
695
867
  yield(obj, args)