graphql 1.9.21 → 2.0.0

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