graphql 1.12.12 → 2.4.8

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