graphql 2.6.9 → 2.6.14

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.
Files changed (382) hide show
  1. checksums.yaml +4 -4
  2. data/.rdoc_options +4 -0
  3. data/lib/generators/graphql/core.rb +1 -0
  4. data/lib/generators/graphql/detailed_trace_generator.rb +1 -0
  5. data/lib/generators/graphql/enum_generator.rb +1 -0
  6. data/lib/generators/graphql/field_extractor.rb +1 -0
  7. data/lib/generators/graphql/input_generator.rb +1 -0
  8. data/lib/generators/graphql/install/mutation_root_generator.rb +1 -0
  9. data/lib/generators/graphql/install_generator.rb +1 -0
  10. data/lib/generators/graphql/interface_generator.rb +1 -0
  11. data/lib/generators/graphql/loader_generator.rb +8 -2
  12. data/lib/generators/graphql/mutation_create_generator.rb +8 -2
  13. data/lib/generators/graphql/mutation_delete_generator.rb +8 -2
  14. data/lib/generators/graphql/mutation_generator.rb +8 -2
  15. data/lib/generators/graphql/mutation_update_generator.rb +8 -2
  16. data/lib/generators/graphql/object_generator.rb +1 -0
  17. data/lib/generators/graphql/orm_mutations_base.rb +8 -2
  18. data/lib/generators/graphql/relay.rb +1 -0
  19. data/lib/generators/graphql/relay_generator.rb +1 -0
  20. data/lib/generators/graphql/scalar_generator.rb +1 -0
  21. data/lib/generators/graphql/type_generator.rb +38 -8
  22. data/lib/generators/graphql/union_generator.rb +1 -0
  23. data/lib/graphql/analysis/analyzer.rb +35 -9
  24. data/lib/graphql/analysis/field_usage.rb +1 -0
  25. data/lib/graphql/analysis/max_query_complexity.rb +3 -2
  26. data/lib/graphql/analysis/max_query_depth.rb +1 -0
  27. data/lib/graphql/analysis/query_complexity.rb +57 -20
  28. data/lib/graphql/analysis/query_depth.rb +17 -12
  29. data/lib/graphql/analysis/visitor.rb +79 -16
  30. data/lib/graphql/analysis.rb +23 -6
  31. data/lib/graphql/analysis_error.rb +1 -0
  32. data/lib/graphql/autoload.rb +30 -7
  33. data/lib/graphql/backtrace/table.rb +21 -5
  34. data/lib/graphql/backtrace/traced_error.rb +8 -3
  35. data/lib/graphql/backtrace.rb +14 -9
  36. data/lib/graphql/coercion_error.rb +1 -0
  37. data/lib/graphql/current.rb +44 -19
  38. data/lib/graphql/dashboard/application_controller.rb +1 -0
  39. data/lib/graphql/dashboard/detailed_traces.rb +1 -0
  40. data/lib/graphql/dashboard/installable.rb +1 -0
  41. data/lib/graphql/dashboard/landings_controller.rb +1 -0
  42. data/lib/graphql/dashboard/limiters.rb +1 -0
  43. data/lib/graphql/dashboard/operation_store.rb +1 -0
  44. data/lib/graphql/dashboard/statics_controller.rb +1 -0
  45. data/lib/graphql/dashboard/subscriptions.rb +1 -0
  46. data/lib/graphql/dashboard.rb +36 -22
  47. data/lib/graphql/dataloader/active_record_association_source.rb +1 -0
  48. data/lib/graphql/dataloader/active_record_source.rb +1 -0
  49. data/lib/graphql/dataloader/async_dataloader.rb +45 -16
  50. data/lib/graphql/dataloader/null_dataloader.rb +4 -3
  51. data/lib/graphql/dataloader/request.rb +8 -2
  52. data/lib/graphql/dataloader/request_all.rb +8 -2
  53. data/lib/graphql/dataloader/source.rb +127 -35
  54. data/lib/graphql/dataloader.rb +98 -44
  55. data/lib/graphql/date_encoding_error.rb +2 -1
  56. data/lib/graphql/dig.rb +12 -3
  57. data/lib/graphql/duration_encoding_error.rb +2 -1
  58. data/lib/graphql/execution/directive_checks.rb +8 -3
  59. data/lib/graphql/execution/errors.rb +20 -6
  60. data/lib/graphql/execution/field_resolve_step.rb +288 -168
  61. data/lib/graphql/execution/finalize.rb +12 -6
  62. data/lib/graphql/execution/input_values.rb +5 -2
  63. data/lib/graphql/execution/interpreter/argument_value.rb +17 -5
  64. data/lib/graphql/execution/interpreter/arguments.rb +24 -9
  65. data/lib/graphql/execution/interpreter/arguments_cache.rb +2 -1
  66. data/lib/graphql/execution/interpreter/execution_errors.rb +1 -0
  67. data/lib/graphql/execution/interpreter/handles_raw_value.rb +1 -0
  68. data/lib/graphql/execution/interpreter/resolve.rb +18 -8
  69. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +4 -1
  70. data/lib/graphql/execution/interpreter/runtime.rb +72 -25
  71. data/lib/graphql/execution/interpreter.rb +18 -7
  72. data/lib/graphql/execution/lazy/lazy_method_map.rb +21 -8
  73. data/lib/graphql/execution/lazy.rb +34 -10
  74. data/lib/graphql/execution/load_argument_step.rb +1 -0
  75. data/lib/graphql/execution/lookahead.rb +137 -54
  76. data/lib/graphql/execution/multiplex.rb +4 -4
  77. data/lib/graphql/execution/next.rb +7 -0
  78. data/lib/graphql/execution/prepare_object_step.rb +38 -13
  79. data/lib/graphql/execution/resolve_type_step.rb +2 -1
  80. data/lib/graphql/execution/runner.rb +32 -15
  81. data/lib/graphql/execution/selections_step.rb +3 -2
  82. data/lib/graphql/execution.rb +2 -2
  83. data/lib/graphql/execution_error.rb +19 -10
  84. data/lib/graphql/float_decoding_error.rb +14 -0
  85. data/lib/graphql/float_encoding_error.rb +33 -0
  86. data/lib/graphql/integer_decoding_error.rb +2 -1
  87. data/lib/graphql/integer_encoding_error.rb +8 -3
  88. data/lib/graphql/introspection/base_object.rb +3 -1
  89. data/lib/graphql/introspection/directive_location_enum.rb +1 -0
  90. data/lib/graphql/introspection/directive_type.rb +1 -0
  91. data/lib/graphql/introspection/dynamic_fields.rb +1 -0
  92. data/lib/graphql/introspection/entry_points.rb +1 -0
  93. data/lib/graphql/introspection/enum_value_type.rb +1 -0
  94. data/lib/graphql/introspection/field_type.rb +1 -0
  95. data/lib/graphql/introspection/input_value_type.rb +1 -0
  96. data/lib/graphql/introspection/introspection_query.rb +1 -0
  97. data/lib/graphql/introspection/schema_type.rb +1 -0
  98. data/lib/graphql/introspection/type_kind_enum.rb +1 -0
  99. data/lib/graphql/introspection/type_type.rb +1 -0
  100. data/lib/graphql/introspection.rb +1 -0
  101. data/lib/graphql/invalid_name_error.rb +1 -0
  102. data/lib/graphql/invalid_null_error.rb +19 -6
  103. data/lib/graphql/language/block_string.rb +2 -1
  104. data/lib/graphql/language/cache.rb +10 -4
  105. data/lib/graphql/language/comment.rb +1 -0
  106. data/lib/graphql/language/definition_slice.rb +1 -0
  107. data/lib/graphql/language/document_from_schema_definition.rb +9 -10
  108. data/lib/graphql/language/generation.rb +23 -9
  109. data/lib/graphql/language/lexer.rb +16 -8
  110. data/lib/graphql/language/nodes.rb +2094 -419
  111. data/lib/graphql/language/parser.rb +29 -18
  112. data/lib/graphql/language/printer.rb +31 -16
  113. data/lib/graphql/language/sanitized_printer.rb +16 -5
  114. data/lib/graphql/language/static_visitor.rb +19 -4
  115. data/lib/graphql/language/visitor.rb +51 -29
  116. data/lib/graphql/language.rb +15 -9
  117. data/lib/graphql/load_application_object_failed_error.rb +14 -5
  118. data/lib/graphql/name_validator.rb +1 -0
  119. data/lib/graphql/pagination/active_record_relation_connection.rb +1 -0
  120. data/lib/graphql/pagination/array_connection.rb +24 -6
  121. data/lib/graphql/pagination/connection.rb +178 -48
  122. data/lib/graphql/pagination/connections.rb +21 -14
  123. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -0
  124. data/lib/graphql/pagination/relation_connection.rb +164 -27
  125. data/lib/graphql/pagination/sequel_dataset_connection.rb +1 -0
  126. data/lib/graphql/pagination.rb +1 -0
  127. data/lib/graphql/parse_error.rb +1 -0
  128. data/lib/graphql/query/context/scoped_context.rb +2 -1
  129. data/lib/graphql/query/context.rb +106 -35
  130. data/lib/graphql/query/fingerprint.rb +15 -7
  131. data/lib/graphql/query/input_validation_result.rb +1 -0
  132. data/lib/graphql/query/null_context.rb +1 -0
  133. data/lib/graphql/query/partial.rb +28 -16
  134. data/lib/graphql/query/result.rb +15 -5
  135. data/lib/graphql/query/validation_pipeline.rb +16 -6
  136. data/lib/graphql/query/variable_validation_error.rb +17 -1
  137. data/lib/graphql/query/variables.rb +9 -2
  138. data/lib/graphql/query.rb +195 -50
  139. data/lib/graphql/railtie.rb +7 -3
  140. data/lib/graphql/rake_task/validate.rb +72 -17
  141. data/lib/graphql/rake_task.rb +55 -23
  142. data/lib/graphql/relay/range_add.rb +32 -21
  143. data/lib/graphql/relay.rb +1 -0
  144. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -0
  145. data/lib/graphql/rubocop/graphql/default_null_true.rb +9 -6
  146. data/lib/graphql/rubocop/graphql/default_required_true.rb +9 -6
  147. data/lib/graphql/rubocop/graphql/field_type_in_block.rb +12 -8
  148. data/lib/graphql/rubocop/graphql/root_types_in_block.rb +9 -5
  149. data/lib/graphql/rubocop.rb +1 -0
  150. data/lib/graphql/runtime_error.rb +1 -0
  151. data/lib/graphql/runtime_type_error.rb +1 -0
  152. data/lib/graphql/schema/addition.rb +69 -50
  153. data/lib/graphql/schema/always_visible.rb +1 -0
  154. data/lib/graphql/schema/argument.rb +279 -45
  155. data/lib/graphql/schema/base_64_encoder.rb +2 -2
  156. data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +1 -0
  157. data/lib/graphql/schema/build_from_definition/resolve_map.rb +2 -2
  158. data/lib/graphql/schema/build_from_definition.rb +507 -178
  159. data/lib/graphql/schema/built_in_types.rb +1 -0
  160. data/lib/graphql/schema/directive/deprecated.rb +1 -0
  161. data/lib/graphql/schema/directive/feature.rb +39 -22
  162. data/lib/graphql/schema/directive/flagged.rb +6 -1
  163. data/lib/graphql/schema/directive/include.rb +1 -0
  164. data/lib/graphql/schema/directive/one_of.rb +1 -0
  165. data/lib/graphql/schema/directive/skip.rb +1 -0
  166. data/lib/graphql/schema/directive/specified_by.rb +1 -0
  167. data/lib/graphql/schema/directive/transform.rb +17 -8
  168. data/lib/graphql/schema/directive.rb +33 -7
  169. data/lib/graphql/schema/enum.rb +99 -38
  170. data/lib/graphql/schema/enum_value.rb +21 -13
  171. data/lib/graphql/schema/field/connection_extension.rb +10 -2
  172. data/lib/graphql/schema/field/scope_extension.rb +1 -0
  173. data/lib/graphql/schema/field.rb +622 -110
  174. data/lib/graphql/schema/field_extension.rb +89 -34
  175. data/lib/graphql/schema/find_inherited_value.rb +5 -3
  176. data/lib/graphql/schema/finder.rb +22 -8
  177. data/lib/graphql/schema/has_single_input_argument.rb +39 -6
  178. data/lib/graphql/schema/input_object.rb +40 -13
  179. data/lib/graphql/schema/interface.rb +33 -17
  180. data/lib/graphql/schema/introspection_system.rb +7 -3
  181. data/lib/graphql/schema/late_bound_type.rb +2 -2
  182. data/lib/graphql/schema/list.rb +15 -4
  183. data/lib/graphql/schema/loader.rb +23 -12
  184. data/lib/graphql/schema/member/base_dsl_methods.rb +58 -12
  185. data/lib/graphql/schema/member/build_type.rb +21 -7
  186. data/lib/graphql/schema/member/graphql_type_names.rb +10 -6
  187. data/lib/graphql/schema/member/has_arguments.rb +112 -49
  188. data/lib/graphql/schema/member/has_ast_node.rb +1 -0
  189. data/lib/graphql/schema/member/has_authorization.rb +5 -0
  190. data/lib/graphql/schema/member/has_dataloader.rb +91 -34
  191. data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -2
  192. data/lib/graphql/schema/member/has_directives.rb +45 -11
  193. data/lib/graphql/schema/member/has_fields.rb +151 -70
  194. data/lib/graphql/schema/member/has_interfaces.rb +8 -2
  195. data/lib/graphql/schema/member/has_path.rb +14 -5
  196. data/lib/graphql/schema/member/has_unresolved_type_error.rb +1 -0
  197. data/lib/graphql/schema/member/has_validators.rb +19 -4
  198. data/lib/graphql/schema/member/relay_shortcuts.rb +10 -2
  199. data/lib/graphql/schema/member/scoped.rb +14 -3
  200. data/lib/graphql/schema/member/type_system_helpers.rb +36 -6
  201. data/lib/graphql/schema/member/validates_input.rb +1 -0
  202. data/lib/graphql/schema/member.rb +9 -2
  203. data/lib/graphql/schema/mutation.rb +50 -46
  204. data/lib/graphql/schema/non_null.rb +21 -5
  205. data/lib/graphql/schema/object.rb +44 -15
  206. data/lib/graphql/schema/printer.rb +52 -31
  207. data/lib/graphql/schema/ractor_shareable.rb +6 -2
  208. data/lib/graphql/schema/relay_classic_mutation.rb +3 -3
  209. data/lib/graphql/schema/resolver/has_payload_type.rb +29 -7
  210. data/lib/graphql/schema/resolver.rb +156 -45
  211. data/lib/graphql/schema/scalar.rb +1 -0
  212. data/lib/graphql/schema/subscription.rb +67 -28
  213. data/lib/graphql/schema/timeout.rb +65 -25
  214. data/lib/graphql/schema/type_expression.rb +14 -5
  215. data/lib/graphql/schema/type_membership.rb +29 -10
  216. data/lib/graphql/schema/union.rb +4 -2
  217. data/lib/graphql/schema/unique_within_type.rb +24 -5
  218. data/lib/graphql/schema/validator/all_validator.rb +17 -9
  219. data/lib/graphql/schema/validator/allow_blank_validator.rb +8 -2
  220. data/lib/graphql/schema/validator/allow_null_validator.rb +8 -2
  221. data/lib/graphql/schema/validator/exclusion_validator.rb +14 -5
  222. data/lib/graphql/schema/validator/format_validator.rb +21 -10
  223. data/lib/graphql/schema/validator/inclusion_validator.rb +15 -6
  224. data/lib/graphql/schema/validator/length_validator.rb +23 -12
  225. data/lib/graphql/schema/validator/numericality_validator.rb +29 -16
  226. data/lib/graphql/schema/validator/required_validator.rb +45 -31
  227. data/lib/graphql/schema/validator.rb +89 -25
  228. data/lib/graphql/schema/visibility/migration.rb +14 -7
  229. data/lib/graphql/schema/visibility/profile.rb +73 -19
  230. data/lib/graphql/schema/visibility/visit.rb +12 -10
  231. data/lib/graphql/schema/visibility.rb +24 -20
  232. data/lib/graphql/schema/warden.rb +117 -31
  233. data/lib/graphql/schema/wrapper.rb +4 -1
  234. data/lib/graphql/schema.rb +1052 -304
  235. data/lib/graphql/static_validation/all_rules.rb +2 -1
  236. data/lib/graphql/static_validation/base_visitor.rb +59 -9
  237. data/lib/graphql/static_validation/definition_dependencies.rb +27 -7
  238. data/lib/graphql/static_validation/error.rb +3 -0
  239. data/lib/graphql/static_validation/interpreter_visitor.rb +1 -0
  240. data/lib/graphql/static_validation/literal_validator.rb +1 -0
  241. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +4 -0
  242. data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +1 -0
  243. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +3 -0
  244. data/lib/graphql/static_validation/rules/argument_names_are_unique_error.rb +1 -0
  245. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -0
  246. data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +1 -0
  247. data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -0
  248. data/lib/graphql/static_validation/rules/directives_are_defined_error.rb +1 -0
  249. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +3 -0
  250. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations_error.rb +1 -0
  251. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +3 -0
  252. data/lib/graphql/static_validation/rules/fields_are_defined_on_type_error.rb +1 -0
  253. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +9 -1
  254. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections_error.rb +1 -0
  255. data/lib/graphql/static_validation/rules/fields_will_merge.rb +355 -157
  256. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +14 -2
  257. data/lib/graphql/static_validation/rules/fragment_names_are_unique.rb +3 -0
  258. data/lib/graphql/static_validation/rules/fragment_names_are_unique_error.rb +1 -0
  259. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -0
  260. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible_error.rb +1 -0
  261. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +3 -0
  262. data/lib/graphql/static_validation/rules/fragment_types_exist_error.rb +1 -0
  263. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +3 -0
  264. data/lib/graphql/static_validation/rules/fragments_are_finite_error.rb +1 -0
  265. data/lib/graphql/static_validation/rules/fragments_are_named.rb +3 -0
  266. data/lib/graphql/static_validation/rules/fragments_are_named_error.rb +1 -0
  267. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +3 -0
  268. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types_error.rb +1 -0
  269. data/lib/graphql/static_validation/rules/fragments_are_used.rb +3 -0
  270. data/lib/graphql/static_validation/rules/fragments_are_used_error.rb +1 -0
  271. data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +3 -0
  272. data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +1 -0
  273. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +3 -0
  274. data/lib/graphql/static_validation/rules/mutation_root_exists_error.rb +1 -0
  275. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +3 -0
  276. data/lib/graphql/static_validation/rules/no_definitions_are_present_error.rb +1 -0
  277. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +1 -0
  278. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +4 -0
  279. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +1 -0
  280. data/lib/graphql/static_validation/rules/operation_names_are_valid.rb +3 -0
  281. data/lib/graphql/static_validation/rules/operation_names_are_valid_error.rb +1 -0
  282. data/lib/graphql/static_validation/rules/query_root_exists.rb +3 -0
  283. data/lib/graphql/static_validation/rules/query_root_exists_error.rb +1 -0
  284. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -1
  285. data/lib/graphql/static_validation/rules/required_arguments_are_present_error.rb +1 -0
  286. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -0
  287. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present_error.rb +1 -0
  288. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +43 -5
  289. data/lib/graphql/static_validation/rules/subscription_root_exists_error.rb +1 -0
  290. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +3 -0
  291. data/lib/graphql/static_validation/rules/unique_directives_per_location_error.rb +1 -0
  292. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +3 -0
  293. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed_error.rb +1 -0
  294. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +3 -0
  295. data/lib/graphql/static_validation/rules/variable_names_are_unique_error.rb +1 -0
  296. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +207 -51
  297. data/lib/graphql/static_validation/rules/variable_usages_are_allowed_error.rb +1 -0
  298. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +3 -0
  299. data/lib/graphql/static_validation/rules/variables_are_input_types_error.rb +1 -0
  300. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +6 -3
  301. data/lib/graphql/static_validation/rules/variables_are_used_and_defined_error.rb +1 -0
  302. data/lib/graphql/static_validation/validation_context.rb +3 -2
  303. data/lib/graphql/static_validation/validation_timeout_error.rb +1 -0
  304. data/lib/graphql/static_validation/validator.rb +41 -16
  305. data/lib/graphql/static_validation.rb +1 -0
  306. data/lib/graphql/string_encoding_error.rb +1 -0
  307. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +85 -65
  308. data/lib/graphql/subscriptions/broadcast_analyzer.rb +10 -4
  309. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +1 -0
  310. data/lib/graphql/subscriptions/event.rb +25 -6
  311. data/lib/graphql/subscriptions/serialize.rb +89 -23
  312. data/lib/graphql/subscriptions.rb +172 -46
  313. data/lib/graphql/testing/helpers.rb +17 -4
  314. data/lib/graphql/testing/mock_action_cable.rb +55 -33
  315. data/lib/graphql/testing.rb +1 -0
  316. data/lib/graphql/tracing/active_support_notifications_trace.rb +17 -9
  317. data/lib/graphql/tracing/active_support_notifications_tracing.rb +2 -1
  318. data/lib/graphql/tracing/appoptics_trace.rb +2 -1
  319. data/lib/graphql/tracing/appoptics_tracing.rb +2 -1
  320. data/lib/graphql/tracing/appsignal_trace.rb +16 -7
  321. data/lib/graphql/tracing/appsignal_tracing.rb +7 -3
  322. data/lib/graphql/tracing/call_legacy_tracers.rb +1 -0
  323. data/lib/graphql/tracing/data_dog_trace.rb +17 -6
  324. data/lib/graphql/tracing/data_dog_tracing.rb +10 -3
  325. data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +1 -0
  326. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +1 -0
  327. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +1 -0
  328. data/lib/graphql/tracing/detailed_trace.rb +105 -42
  329. data/lib/graphql/tracing/legacy_hooks_trace.rb +5 -0
  330. data/lib/graphql/tracing/legacy_trace.rb +1 -0
  331. data/lib/graphql/tracing/monitor_trace.rb +17 -11
  332. data/lib/graphql/tracing/new_relic_trace.rb +17 -8
  333. data/lib/graphql/tracing/new_relic_tracing.rb +7 -3
  334. data/lib/graphql/tracing/notifications_trace.rb +11 -8
  335. data/lib/graphql/tracing/notifications_tracing.rb +23 -7
  336. data/lib/graphql/tracing/null_trace.rb +1 -0
  337. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +1 -0
  338. data/lib/graphql/tracing/perfetto_trace.rb +41 -19
  339. data/lib/graphql/tracing/platform_trace.rb +3 -1
  340. data/lib/graphql/tracing/platform_tracing.rb +16 -7
  341. data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +1 -0
  342. data/lib/graphql/tracing/prometheus_trace.rb +22 -13
  343. data/lib/graphql/tracing/prometheus_tracing.rb +1 -0
  344. data/lib/graphql/tracing/scout_trace.rb +10 -4
  345. data/lib/graphql/tracing/scout_tracing.rb +7 -3
  346. data/lib/graphql/tracing/sentry_trace.rb +25 -6
  347. data/lib/graphql/tracing/statsd_trace.rb +12 -6
  348. data/lib/graphql/tracing/statsd_tracing.rb +7 -1
  349. data/lib/graphql/tracing/trace.rb +231 -58
  350. data/lib/graphql/tracing.rb +25 -9
  351. data/lib/graphql/type_kinds.rb +2 -1
  352. data/lib/graphql/types/big_int.rb +1 -0
  353. data/lib/graphql/types/boolean.rb +1 -0
  354. data/lib/graphql/types/float.rb +19 -4
  355. data/lib/graphql/types/id.rb +1 -0
  356. data/lib/graphql/types/int.rb +2 -1
  357. data/lib/graphql/types/iso_8601_date.rb +21 -4
  358. data/lib/graphql/types/iso_8601_date_time.rb +33 -6
  359. data/lib/graphql/types/iso_8601_duration.rb +42 -9
  360. data/lib/graphql/types/json.rb +1 -0
  361. data/lib/graphql/types/relay/base_connection.rb +38 -32
  362. data/lib/graphql/types/relay/base_edge.rb +15 -9
  363. data/lib/graphql/types/relay/connection_behaviors.rb +19 -3
  364. data/lib/graphql/types/relay/edge_behaviors.rb +17 -4
  365. data/lib/graphql/types/relay/has_node_field.rb +6 -1
  366. data/lib/graphql/types/relay/has_nodes_field.rb +6 -1
  367. data/lib/graphql/types/relay/node.rb +1 -0
  368. data/lib/graphql/types/relay/node_behaviors.rb +5 -1
  369. data/lib/graphql/types/relay/page_info.rb +1 -0
  370. data/lib/graphql/types/relay/page_info_behaviors.rb +1 -0
  371. data/lib/graphql/types/relay.rb +4 -3
  372. data/lib/graphql/types/string.rb +1 -0
  373. data/lib/graphql/types.rb +1 -0
  374. data/lib/graphql/unauthorized_enum_value_error.rb +4 -1
  375. data/lib/graphql/unauthorized_error.rb +11 -4
  376. data/lib/graphql/unauthorized_field_error.rb +4 -1
  377. data/lib/graphql/unresolved_type_error.rb +16 -5
  378. data/lib/graphql/version.rb +2 -1
  379. data/lib/graphql.rb +39 -9
  380. data/readme.md +1 -1
  381. metadata +91 -5
  382. data/.yardopts +0 -5
@@ -1,23 +1,24 @@
1
1
  # frozen_string_literal: true
2
+ # typed: true
2
3
  require "graphql/schema/build_from_definition/resolve_map"
3
4
 
4
5
  module GraphQL
5
6
  class Schema
6
7
  module BuildFromDefinition
7
8
  class << self
8
- # @see {Schema.from_definition}
9
- def from_definition(schema_superclass, definition_string, parser: GraphQL.default_parser, **kwargs)
9
+ # See [Schema.from_definition](rdoc-ref:Schema.from_definition)
10
+ def from_definition(schema_superclass, definition_string, default_resolve:, parser: GraphQL.default_parser, using: {}, base_types: {}, relay: false)
10
11
  if defined?(parser::SchemaParser)
11
- parser = parser::SchemaParser
12
+ parser = parser.const_get(:SchemaParser)
12
13
  end
13
- from_document(schema_superclass, parser.parse(definition_string), **kwargs)
14
+ from_document(schema_superclass, parser.parse(definition_string), default_resolve: default_resolve, using: using, base_types: base_types, relay: relay)
14
15
  end
15
16
 
16
- def from_definition_path(schema_superclass, definition_path, parser: GraphQL.default_parser, **kwargs)
17
+ def from_definition_path(schema_superclass, definition_path, default_resolve:, parser: GraphQL.default_parser, using: {}, base_types: {}, relay: false)
17
18
  if defined?(parser::SchemaParser)
18
- parser = parser::SchemaParser
19
+ parser = parser.const_get(:SchemaParser)
19
20
  end
20
- from_document(schema_superclass, parser.parse_file(definition_path), **kwargs)
21
+ from_document(schema_superclass, parser.parse_file(definition_path), default_resolve: default_resolve, using: using, base_types: base_types, relay: relay)
21
22
  end
22
23
 
23
24
  def from_document(schema_superclass, document, default_resolve:, using: {}, base_types: {}, relay: false)
@@ -25,8 +26,9 @@ module GraphQL
25
26
  end
26
27
  end
27
28
 
28
- # @api private
29
- module Builder
29
+ #--
30
+ # @requires_ancestor: Kernel
31
+ module Builder # :nodoc:
30
32
  include GraphQL::EmptyObjects
31
33
  extend self
32
34
 
@@ -46,21 +48,61 @@ module GraphQL
46
48
  default_resolve = ResolveMap.new(default_resolve)
47
49
  end
48
50
 
49
- schema_defns = document.definitions.select { |d| d.is_a?(GraphQL::Language::Nodes::SchemaDefinition) }
51
+ definitions = document.definitions #: Array[untyped]
52
+ schema_defns = definitions.select { |d| d.is_a?(GraphQL::Language::Nodes::SchemaDefinition) }
50
53
  if schema_defns.size > 1
51
54
  raise InvalidDocumentError.new('Must provide only one schema definition.')
52
55
  end
53
- schema_definition = schema_defns.first
56
+ schema_definition = schema_defns.first #: GraphQL::Language::Nodes::SchemaDefinition?
54
57
  types = {}
55
58
  directives = schema_superclass.directives.dup
56
- type_resolver = build_resolve_type(types, directives, ->(type_name) { types[type_name] ||= Schema::LateBoundType.new(type_name)})
57
- # Make a different type resolver because we need to coerce directive arguments
58
- # _while_ building the schema.
59
- # It will dig for a type if it encounters a custom type. This could be a problem if there are cycles.
59
+ # Directive applications are queued while types are built, then applied once every type is complete,
60
+ # because applying a directive coerces its arguments, which requires fully-built input types.
61
+ queued_directives = []
62
+ definitions_by_name = {}
63
+ # Classes (or modules, for interfaces) made before any type is built, so that references to them don't need late-bound types
64
+ type_classes = {}
65
+ interface_definitions = {}
66
+ definitions.each do |d|
67
+ if d.respond_to?(:name)
68
+ definitions_by_name[d.name] ||= d
69
+ if !type_classes.key?(d.name)
70
+ base_type = case d
71
+ when GraphQL::Language::Nodes::ObjectTypeDefinition then base_types[:object]
72
+ when GraphQL::Language::Nodes::EnumTypeDefinition then base_types[:enum]
73
+ when GraphQL::Language::Nodes::InputObjectTypeDefinition then base_types[:input_object]
74
+ when GraphQL::Language::Nodes::UnionTypeDefinition then base_types[:union]
75
+ when GraphQL::Language::Nodes::ScalarTypeDefinition then base_types[:scalar]
76
+ when GraphQL::Language::Nodes::InterfaceTypeDefinition
77
+ interface_definitions[d.name] = d
78
+ type_classes[d.name] = new_interface_module(d.name, base_types[:interface])
79
+ nil
80
+ end
81
+ if base_type
82
+ type_classes[d.name] = Class.new(base_type) {
83
+ #: self as singleton(GraphQL::Schema::Member)
84
+ graphql_name(d.name)
85
+ }
86
+ end
87
+ end
88
+ end
89
+ end
90
+ type_resolver = TypeResolver.new(types, directives, queued_directives, type_classes) { |type_name|
91
+ types[type_name] ||= if (type_class = type_classes[type_name])
92
+ type_class
93
+ elsif !definitions_by_name.key?(type_name) && (built_in_type = GraphQL::Schema::BUILT_IN_TYPES[type_name])
94
+ built_in_type
95
+ else
96
+ Schema::LateBoundType.new(type_name)
97
+ end
98
+ }
99
+ # Make a different type resolver because directive argument types must be real classes (not late-bound)
100
+ # when queued directives are applied. It builds types eagerly. (Each type registers itself in `types`
101
+ # before building its members, so cycles between input objects terminate.)
60
102
  directive_type_resolver = nil
61
- directive_type_resolver = build_resolve_type(types, directives, ->(type_name) {
103
+ directive_type_resolver = TypeResolver.new(types, directives, queued_directives, type_classes) { |type_name|
62
104
  types[type_name] ||= begin
63
- defn = document.definitions.find { |d| d.respond_to?(:name) && d.name == type_name }
105
+ defn = definitions_by_name[type_name]
64
106
  if defn
65
107
  build_definition_from_node(defn, directive_type_resolver, default_resolve, base_types)
66
108
  elsif (built_in_defn = GraphQL::Schema::BUILT_IN_TYPES[type_name])
@@ -69,10 +111,22 @@ module GraphQL
69
111
  raise "No definition for #{type_name.inspect} found in schema document or built-in types. Add a definition for it or remove it."
70
112
  end
71
113
  end
72
- })
114
+ }
115
+
116
+ build_interface = ->(interface_name, resolver) {
117
+ if (interface_definition = interface_definitions.delete(interface_name)) && type_classes[interface_name]
118
+ check_transitive_interfaces(interface_definition, definitions_by_name)
119
+ build_interface_type(interface_definition, resolver, base_types[:interface])
120
+ end
121
+ }
122
+ type_resolver.build_interface = build_interface
123
+ directive_type_resolver.build_interface = build_interface
124
+ type_links = TypeLinks.new
125
+ type_resolver.links = type_links
126
+ directive_type_resolver.links = type_links
73
127
 
74
128
  directives.merge!(GraphQL::Schema.default_directives)
75
- document.definitions.each do |definition|
129
+ definitions.each do |definition|
76
130
  if definition.is_a?(GraphQL::Language::Nodes::DirectiveDefinition)
77
131
  directives[definition.name] = build_directive(definition, directive_type_resolver)
78
132
  end
@@ -81,9 +135,8 @@ module GraphQL
81
135
  # In case any directives referenced built-in types for their arguments:
82
136
  replace_late_bound_types_with_built_in(types)
83
137
 
84
- schema_extensions = nil
85
- definitions_by_name = nil
86
- document.definitions.each do |definition|
138
+ schema_extensions = nil #: Array[untyped]?
139
+ definitions.each do |definition|
87
140
  case definition
88
141
  when GraphQL::Language::Nodes::SchemaDefinition, GraphQL::Language::Nodes::DirectiveDefinition
89
142
  nil # already handled
@@ -99,25 +152,11 @@ module GraphQL
99
152
  else
100
153
  # It's possible that this was already loaded by the directives
101
154
  prev_type = types[definition.name]
102
- if prev_type.nil? || prev_type.is_a?(Schema::LateBoundType)
155
+ if prev_type.nil? || prev_type.is_a?(Schema::LateBoundType) || type_classes[definition.name]
103
156
  if definition.is_a?(GraphQL::Language::Nodes::ObjectTypeDefinition) || definition.is_a?(Language::Nodes::InterfaceTypeDefinition)
104
- interface_names = definition.interfaces.map(&:name)
105
- if !interface_names.empty?
106
- definitions_by_name ||= document.definitions.each_with_object({}) do |d, by_name|
107
- by_name[d.name] ||= d if d.respond_to?(:name)
108
- end
109
- transitive_names = interface_names.map { |n| definitions_by_name[n]&.interfaces&.map(&:name) }
110
- transitive_names.flatten!
111
- transitive_names.compact!
112
- else
113
- transitive_names = interface_names
114
- end
115
- if !(missing_transitive_interfaces = transitive_names - interface_names).empty?
116
- raise GraphQL::Schema::InvalidDocumentError, "type #{definition.name} is missing one or more transitive interface names: #{missing_transitive_interfaces.join(", ")}. Add them to the type's `implements` list and try again."
117
- end
157
+ check_transitive_interfaces(definition, definitions_by_name)
118
158
  end
119
-
120
- types[definition.name] = build_definition_from_node(definition, type_resolver, default_resolve, base_types)
159
+ build_definition_from_node(definition, type_resolver, default_resolve, base_types)
121
160
  end
122
161
  end
123
162
  end
@@ -185,15 +224,25 @@ module GraphQL
185
224
  end
186
225
  end
187
226
 
227
+ # All types are built now, so directive arguments can be coerced
228
+ apply_queued_directives(queued_directives)
229
+
188
230
  builder = self
189
231
 
190
232
  found_types = types.values
191
- object_types = found_types.select { |t| t.respond_to?(:kind) && t.kind.object? }
233
+ object_types = found_types.select { |t| t.is_a?(Class) && t < GraphQL::Schema::Object }
192
234
  schema_class = Class.new(schema_superclass) do
235
+ #: self as singleton(GraphQL::Schema)
193
236
  begin
194
237
  # Add these first so that there's some chance of resolving late-bound types
195
- add_type_and_traverse(found_types, root: false)
196
- orphan_types(object_types)
238
+ if !builder.add_linked_types(self, found_types, type_links, directives, base_types)
239
+ add_type_and_traverse(found_types, root: false)
240
+ end
241
+ # Without visibility, this would only re-traverse types that were just added
242
+ # (and `own_orphan_types` is cleared below), so it can be skipped.
243
+ if use_visibility_profile? || visibility
244
+ orphan_types(object_types)
245
+ end
197
246
  query query_root_type
198
247
  mutation mutation_root_type
199
248
  subscription subscription_root_type
@@ -228,6 +277,7 @@ module GraphQL
228
277
  if schema_definition
229
278
  ast_node(schema_definition)
230
279
  builder.build_directives(self, schema_definition, type_resolver)
280
+ builder.apply_queued_directives(queued_directives)
231
281
  end
232
282
 
233
283
  using.each do |plugin, options|
@@ -245,7 +295,7 @@ module GraphQL
245
295
  attr_accessor :definition_default_resolve
246
296
  end
247
297
 
248
- self.definition_default_resolve = default_resolve
298
+ @definition_default_resolve = default_resolve
249
299
 
250
300
  def definition_default_resolve
251
301
  self.class.definition_default_resolve
@@ -262,10 +312,133 @@ module GraphQL
262
312
  build_directives(schema_class, ext, type_resolver)
263
313
  end
264
314
  end
315
+ apply_queued_directives(queued_directives)
316
+
317
+ directives.each_value do |dir_class|
318
+ if dir_class.respond_to?(:coerced_arguments_cache=) && dir_class.coerced_arguments_cache
319
+ dir_class.coerced_arguments_cache = nil
320
+ end
321
+ end
265
322
 
266
323
  schema_class
267
324
  end
268
325
 
326
+ # References between types in this document, recorded while they're built.
327
+ class TypeLinks
328
+ def initialize
329
+ @references = {}.compare_by_identity
330
+ @arguments = []
331
+ @complete = true
332
+ end
333
+
334
+ # Hash of type => Array of the fields and arguments that use it (no duplicates, since each one is added once)
335
+ attr_reader :references
336
+
337
+ attr_reader :arguments
338
+
339
+ # True if everything was recorded, so the generic traversal isn't needed
340
+ def complete?
341
+ @complete
342
+ end
343
+
344
+ def incomplete!
345
+ @complete = false
346
+ end
347
+
348
+ def add_argument(argument, argument_type)
349
+ @arguments << argument
350
+ add_reference(argument, argument_type)
351
+ end
352
+
353
+ def add_reference(member, member_type)
354
+ member_type = member_type.unwrap
355
+ if member_type.is_a?(Schema::LateBoundType)
356
+ # The generic traversal will raise an error for this
357
+ incomplete!
358
+ else
359
+ (@references[member_type] ||= []) << member
360
+ end
361
+ end
362
+ end
363
+
364
+ # Like `schema_class.add_type_and_traverse(found_types, root: false)`, but it uses the links recorded while types were built
365
+ # instead of a traversal. (Directive classes are added later, with `directives(...)`.)
366
+ # Returns false if the traversal is required, for example, when base types have their own fields or interfaces.
367
+ def add_linked_types(schema_class, found_types, type_links, directives, base_types)
368
+ if !type_links.complete? ||
369
+ schema_class.use_visibility_profile? ||
370
+ # A redefined `@deprecated` isn't the one used by `deprecation_reason:`, so the traversal would find another directive.
371
+ !directives["deprecated"].equal?(GraphQL::Schema::Directive::Deprecated) ||
372
+ !base_types_without_members?(base_types)
373
+ return false
374
+ end
375
+
376
+ own_types = schema_class.send(:own_types)
377
+ found_types.each do |type|
378
+ if !(Module === type)
379
+ return false
380
+ end
381
+ type = type #: as GraphQL::Schema::Member::type_definition
382
+ if (Class === type && GraphQL::Schema::Directive > type) ||
383
+ ((prev_type = own_types[type.graphql_name]) && !prev_type.equal?(type))
384
+ return false
385
+ end
386
+ end
387
+
388
+ # Like the traversal, check each argument's type now (it raises an error if the type is invalid)
389
+ type_links.arguments.each(&:type)
390
+
391
+ possible_types = {}.compare_by_identity
392
+ possible_types_sets = {}.compare_by_identity
393
+ union_memberships = {}
394
+ found_types.each do |type|
395
+ own_types[type.graphql_name] = type
396
+ if Class === type
397
+ if GraphQL::Schema::Object > type
398
+ (possible_types[type] ||= []) << type
399
+ object_type = type #: as singleton(GraphQL::Schema::Object)
400
+ object_type.interface_type_memberships.each do |type_membership|
401
+ interface_type = type_membership.abstract_type
402
+ if (possible_types_sets[interface_type] ||= Set.new).add?(type)
403
+ (possible_types[interface_type] ||= []) << type
404
+ end
405
+ end
406
+ elsif GraphQL::Schema::Union > type
407
+ union_type = type #: as singleton(GraphQL::Schema::Union)
408
+ member_types = possible_types[type] = union_type.all_possible_types
409
+ member_types.each do |member_type|
410
+ (union_memberships[member_type.graphql_name] ||= []) << type
411
+ end
412
+ end
413
+ end
414
+ end
415
+
416
+ schema_class.send(:own_possible_types).merge!(possible_types) { |key, old_val, new_val| old_val + new_val }
417
+ schema_class.send(:own_union_memberships).merge!(union_memberships)
418
+ own_references_to = schema_class.send(:own_references_to)
419
+ type_links.references.each do |thing, new_refs|
420
+ prev_refs = own_references_to[thing]
421
+ own_references_to[thing] = prev_refs ? (prev_refs + (new_refs - prev_refs)) : new_refs
422
+ end
423
+ type_links.arguments.each do |argument|
424
+ if argument.default_value?
425
+ argument.validate_default_value
426
+ end
427
+ end
428
+ true
429
+ end
430
+
431
+ def base_types_without_members?(base_types)
432
+ object = base_types[:object]
433
+ interface = base_types[:interface]
434
+ object.all_field_definitions.empty? && object.interface_type_memberships.empty? && object.directives.empty? &&
435
+ interface.all_field_definitions.empty? && interface.interface_type_memberships.empty? && interface.directives.empty? && interface.orphan_types.empty? &&
436
+ base_types[:input_object].all_argument_definitions.empty? && base_types[:input_object].directives.empty? &&
437
+ base_types[:union].all_possible_types.empty? && base_types[:union].directives.empty? &&
438
+ base_types[:enum].all_enum_value_definitions.empty? && base_types[:enum].directives.empty? &&
439
+ base_types[:scalar].directives.empty?
440
+ end
441
+
269
442
  NullResolveType = ->(type, obj, ctx) {
270
443
  raise(GraphQL::RequiredImplementationMissingError, "Generated Schema cannot use Interface or Union types for execution. Implement resolve_type on your resolver.")
271
444
  }
@@ -293,7 +466,13 @@ module GraphQL
293
466
  # with their actual definitions.
294
467
  #
295
468
  # (Schema definitions are allowed to reference those built-ins without redefining them.)
296
- # @return void
469
+ #
470
+ # **Returns**
471
+ #
472
+ # - `Object` — void
473
+ #
474
+ # :call-seq:
475
+ # replace_late_bound_types_with_built_in(types) -> Object
297
476
  def replace_late_bound_types_with_built_in(types)
298
477
  GraphQL::Schema::BUILT_IN_TYPES.each do |scalar_name, built_in_scalar|
299
478
  existing_type = types[scalar_name]
@@ -303,46 +482,54 @@ module GraphQL
303
482
  end
304
483
  end
305
484
 
485
+ # Apply `ast_node`'s directives to `definition`. If any of those directives has arguments,
486
+ # they're queued for {apply_queued_directives} because coercing arguments requires fully-built types.
487
+ # Directives without arguments are applied right away, since some (like `@oneOf`) affect how the rest of the type is built.
306
488
  def build_directives(definition, ast_node, type_resolver)
307
- dirs = prepare_directives(ast_node, type_resolver)
308
- dirs.each do |(dir_class, options)|
309
- if definition.respond_to?(:schema_directive)
310
- # it's a schema
311
- definition.schema_directive(dir_class, **options)
312
- else
313
- definition.directive(dir_class, **options)
314
- end
489
+ return if ast_node.directives.empty?
490
+
491
+ if ast_node.directives.all? { |dir_node| dir_node.name == "deprecated" || type_resolver.directive_without_arguments?(dir_node.name) }
492
+ apply_directives(definition, ast_node, type_resolver)
493
+ else
494
+ type_resolver.queued_directives << [definition, ast_node, type_resolver]
315
495
  end
316
496
  end
317
497
 
318
- def prepare_directives(ast_node, type_resolver)
319
- dirs = []
498
+ def apply_queued_directives(queued_directives)
499
+ until queued_directives.empty?
500
+ apply_directives(*queued_directives.shift)
501
+ end
502
+ end
503
+
504
+ def apply_directives(definition, ast_node, type_resolver)
505
+ is_schema = Class === definition && GraphQL::Schema >= definition
320
506
  ast_node.directives.each do |dir_node|
321
- if dir_node.name == "deprecated"
322
- # This is handled using `deprecation_reason`
323
- next
507
+ # This is handled using `deprecation_reason`
508
+ next if dir_node.name == "deprecated"
509
+ dir_class = type_resolver.call(dir_node.name)
510
+ if dir_class.nil?
511
+ raise ArgumentError, "No definition for @#{dir_node.name} #{ast_node.respond_to?(:name) ? "on #{ast_node.name} " : ""}at #{ast_node.line}:#{ast_node.col}"
512
+ end
513
+ options = args_to_kwargs(dir_class, dir_node, type_resolver)
514
+ if is_schema
515
+ schema_class = definition #: as singleton(GraphQL::Schema)
516
+ schema_class.schema_directive(dir_class, **options)
324
517
  else
325
- dir_class = type_resolver.call(dir_node.name)
326
- if dir_class.nil?
327
- raise ArgumentError, "No definition for @#{dir_node.name} #{ast_node.respond_to?(:name) ? "on #{ast_node.name} " : ""}at #{ast_node.line}:#{ast_node.col}"
328
- end
329
- options = args_to_kwargs(dir_class, dir_node)
330
- dirs << [dir_class, options]
518
+ definition.directive(dir_class, **options)
331
519
  end
332
520
  end
333
- dirs
334
521
  end
335
522
 
336
- def args_to_kwargs(arg_owner, node)
523
+ def args_to_kwargs(arg_owner, node, type_resolver)
337
524
  if node.respond_to?(:arguments)
338
525
  kwargs = {}
339
526
  node.arguments.each do |arg_node|
340
- arg_defn = arg_owner.get_argument(arg_node.name)
341
- kwargs[arg_defn.keyword] = args_to_kwargs(arg_defn.type.unwrap, arg_node.value)
527
+ arg_defn = type_resolver.get_argument(arg_owner, arg_node.name)
528
+ kwargs[arg_defn.keyword] = args_to_kwargs(arg_defn.type.unwrap, arg_node.value, type_resolver)
342
529
  end
343
530
  kwargs
344
531
  elsif node.is_a?(Array)
345
- node.map { |n| args_to_kwargs(arg_owner, n) }
532
+ node.map { |n| args_to_kwargs(arg_owner, n, type_resolver) }
346
533
  elsif node.is_a?(Language::Nodes::Enum)
347
534
  node.name
348
535
  else
@@ -352,53 +539,56 @@ module GraphQL
352
539
  end
353
540
 
354
541
  def build_enum_type(enum_type_definition, type_resolver, base_type)
355
- builder = self
356
- Class.new(base_type) do
357
- graphql_name(enum_type_definition.name)
358
- builder.build_directives(self, enum_type_definition, type_resolver)
359
- description(enum_type_definition.description)
360
- ast_node(enum_type_definition)
361
- builder.build_values(self, enum_type_definition.values, type_resolver)
362
- end
542
+ type_class = type_resolver.type_class(enum_type_definition.name, base_type) #: singleton(GraphQL::Schema::Enum)
543
+ type_resolver.register(type_class)
544
+ build_directives(type_class, enum_type_definition, type_resolver)
545
+ type_class.description(enum_type_definition.description)
546
+ type_class.ast_node(enum_type_definition)
547
+ build_values(type_class, enum_type_definition.values, type_resolver)
548
+ type_class
363
549
  end
364
550
 
365
551
  def build_values(type_class, enum_value_definitions, type_resolver)
366
552
  enum_value_definitions.each do |enum_value_definition|
367
- type_class.value(enum_value_definition.name,
553
+ enum_value = type_class.value(enum_value_definition.name,
368
554
  value: enum_value_definition.name,
369
555
  deprecation_reason: build_deprecation_reason(enum_value_definition.directives),
370
556
  description: enum_value_definition.description,
371
- directives: prepare_directives(enum_value_definition, type_resolver),
372
557
  ast_node: enum_value_definition,
373
558
  )
559
+ build_directives(enum_value, enum_value_definition, type_resolver)
374
560
  end
375
561
  end
376
562
 
377
563
  def build_deprecation_reason(directives)
378
- deprecated_directive = directives.find{ |d| d.name == 'deprecated' }
379
- return unless deprecated_directive
380
-
381
- reason = deprecated_directive.arguments.find{ |a| a.name == 'reason' }
382
- return GraphQL::Schema::Directive::DEFAULT_DEPRECATION_REASON unless reason
383
-
384
- reason.value
564
+ return if directives.empty?
565
+ directives.each do |d|
566
+ if d.name == "deprecated"
567
+ d.arguments.each do |a|
568
+ if a.name == "reason"
569
+ return a.value
570
+ end
571
+ end
572
+ return GraphQL::Schema::Directive::DEFAULT_DEPRECATION_REASON
573
+ end
574
+ end
575
+ nil
385
576
  end
386
577
 
387
578
  def build_scalar_type(scalar_type_definition, type_resolver, base_type, default_resolve:)
388
- builder = self
389
- Class.new(base_type) do
390
- graphql_name(scalar_type_definition.name)
391
- description(scalar_type_definition.description)
392
- ast_node(scalar_type_definition)
393
- builder.build_directives(self, scalar_type_definition, type_resolver)
394
-
395
- if default_resolve.respond_to?(:coerce_input)
396
- # Put these method definitions in another method to avoid retaining `type_resolve`
397
- # from this method's bindiing
398
- builder.build_scalar_type_coerce_method(self, :coerce_input, default_resolve)
399
- builder.build_scalar_type_coerce_method(self, :coerce_result, default_resolve)
400
- end
579
+ type_class = type_resolver.type_class(scalar_type_definition.name, base_type) #: singleton(GraphQL::Schema::Scalar)
580
+ type_resolver.register(type_class)
581
+ type_class.description(scalar_type_definition.description)
582
+ type_class.ast_node(scalar_type_definition)
583
+ build_directives(type_class, scalar_type_definition, type_resolver)
584
+
585
+ if default_resolve.respond_to?(:coerce_input)
586
+ # Put these method definitions in another method to avoid retaining `type_resolve`
587
+ # from this method's bindiing
588
+ build_scalar_type_coerce_method(type_class, :coerce_input, default_resolve)
589
+ build_scalar_type_coerce_method(type_class, :coerce_result, default_resolve)
401
590
  end
591
+ type_class
402
592
  end
403
593
 
404
594
  def build_scalar_type_coerce_method(scalar_class, method_name, default_definition_resolve)
@@ -408,44 +598,68 @@ module GraphQL
408
598
  end
409
599
 
410
600
  def build_union_type(union_type_definition, type_resolver, base_type)
411
- builder = self
412
- Class.new(base_type) do
413
- graphql_name(union_type_definition.name)
414
- description(union_type_definition.description)
415
- possible_types(*union_type_definition.types.map { |type_name| type_resolver.call(type_name) })
416
- ast_node(union_type_definition)
417
- builder.build_directives(self, union_type_definition, type_resolver)
418
- end
601
+ type_class = type_resolver.type_class(union_type_definition.name, base_type) #: singleton(GraphQL::Schema::Union)
602
+ type_resolver.register(type_class)
603
+ type_class.description(union_type_definition.description)
604
+ type_class.possible_types(*union_type_definition.types.map { |type_name| type_resolver.call(type_name) })
605
+ type_class.ast_node(union_type_definition)
606
+ build_directives(type_class, union_type_definition, type_resolver)
607
+ type_class
419
608
  end
420
609
 
421
610
  def build_object_type(object_type_definition, type_resolver, base_type)
422
- builder = self
423
-
424
- Class.new(base_type) do
425
- graphql_name(object_type_definition.name)
426
- description(object_type_definition.description)
427
- ast_node(object_type_definition)
428
- builder.build_directives(self, object_type_definition, type_resolver)
429
- builder.build_interfaces(self, object_type_definition.interfaces, type_resolver)
430
- builder.build_fields(self, object_type_definition.fields, type_resolver, default_resolve: true)
431
- end
611
+ type_class = type_resolver.type_class(object_type_definition.name, base_type) #: singleton(GraphQL::Schema::Object)
612
+ type_resolver.register(type_class)
613
+ type_class.description(object_type_definition.description)
614
+ type_class.ast_node(object_type_definition)
615
+ build_directives(type_class, object_type_definition, type_resolver)
616
+ build_interfaces(type_class, object_type_definition.interfaces, type_resolver)
617
+ build_fields(type_class, object_type_definition.fields, type_resolver, default_resolve: true)
618
+ type_class
432
619
  end
433
620
 
434
621
  def build_interfaces(type_class, interface_names, type_resolver)
435
622
  interface_names.each do |interface_name|
623
+ # Implementing an interface copies some of its configuration, so it must be built first
624
+ type_resolver.build_interface_now(interface_name.name)
436
625
  type_class.implements(type_resolver.call(interface_name))
437
626
  end
438
627
  end
439
628
 
440
- def build_input_object_type(input_object_type_definition, type_resolver, base_type)
441
- builder = self
442
- Class.new(base_type) do
443
- graphql_name(input_object_type_definition.name)
444
- description(input_object_type_definition.description)
445
- ast_node(input_object_type_definition)
446
- builder.build_directives(self, input_object_type_definition, type_resolver)
447
- builder.build_arguments(self, input_object_type_definition.fields, type_resolver)
629
+ def check_transitive_interfaces(definition, definitions_by_name)
630
+ interface_names = definition.interfaces.map(&:name)
631
+ if !interface_names.empty?
632
+ transitive_names = interface_names.map { |n|
633
+ defn = definitions_by_name[n]
634
+ # Other kinds of types don't have `interfaces`; `implements` raises a better error for them later.
635
+ defn.is_a?(Language::Nodes::InterfaceTypeDefinition) ? defn.interfaces.map(&:name) : nil
636
+ }
637
+ transitive_names.flatten!
638
+ transitive_names.compact!
639
+ else
640
+ transitive_names = interface_names
448
641
  end
642
+ if !(missing_transitive_interfaces = transitive_names - interface_names).empty?
643
+ raise GraphQL::Schema::InvalidDocumentError, "type #{definition.name} is missing one or more transitive interface names: #{missing_transitive_interfaces.join(", ")}. Add them to the type's `implements` list and try again."
644
+ end
645
+ end
646
+
647
+ def new_interface_module(name, base_type)
648
+ Module.new {
649
+ #: self as GraphQL::Schema::Member::interface_definition
650
+ include base_type
651
+ graphql_name(name)
652
+ }
653
+ end
654
+
655
+ def build_input_object_type(input_object_type_definition, type_resolver, base_type)
656
+ type_class = type_resolver.type_class(input_object_type_definition.name, base_type) #: singleton(GraphQL::Schema::InputObject)
657
+ type_resolver.register(type_class)
658
+ type_class.description(input_object_type_definition.description)
659
+ type_class.ast_node(input_object_type_definition)
660
+ build_directives(type_class, input_object_type_definition, type_resolver)
661
+ build_arguments(type_class, input_object_type_definition.fields, type_resolver)
662
+ type_class
449
663
  end
450
664
 
451
665
  def build_default_value(default_value)
@@ -465,7 +679,8 @@ module GraphQL
465
679
 
466
680
  def build_arguments(type_class, arguments, type_resolver)
467
681
  builder = self
468
-
682
+ # Directive arguments are linked when directives are added to the schema
683
+ links = (Class === type_class && GraphQL::Schema::Directive > type_class) ? nil : type_resolver.links
469
684
  arguments.each do |argument_defn|
470
685
  default_value_kwargs = if !argument_defn.default_value.nil?
471
686
  { default_value: builder.build_default_value(argument_defn.default_value) }
@@ -473,17 +688,19 @@ module GraphQL
473
688
  EMPTY_HASH
474
689
  end
475
690
 
476
- type_class.argument(
691
+ argument_type = type_resolver.call(argument_defn.type)
692
+ argument = type_class.argument(
477
693
  argument_defn.name,
478
- type: type_resolver.call(argument_defn.type),
694
+ type: argument_type,
479
695
  required: false,
480
696
  description: argument_defn.description,
481
697
  deprecation_reason: builder.build_deprecation_reason(argument_defn.directives),
482
698
  ast_node: argument_defn,
483
699
  camelize: false,
484
- directives: prepare_directives(argument_defn, type_resolver),
485
700
  **default_value_kwargs
486
701
  )
702
+ links&.add_argument(argument, argument_type)
703
+ builder.build_directives(argument, argument_defn, type_resolver)
487
704
  end
488
705
  end
489
706
 
@@ -496,87 +713,199 @@ module GraphQL
496
713
  locations(*directive_definition.locations.map { |location| location.name.to_sym })
497
714
  ast_node(directive_definition)
498
715
  builder.build_arguments(self, directive_definition.arguments, type_resolver)
716
+ self.coerced_arguments_cache = {}
499
717
  end
500
718
  end
501
719
 
502
720
  def build_interface_type(interface_type_definition, type_resolver, base_type)
503
- builder = self
504
- Module.new do
505
- include base_type
506
- graphql_name(interface_type_definition.name)
507
- description(interface_type_definition.description)
508
- builder.build_interfaces(self, interface_type_definition.interfaces, type_resolver)
509
- ast_node(interface_type_definition)
510
- builder.build_directives(self, interface_type_definition, type_resolver)
511
-
512
- builder.build_fields(self, interface_type_definition.fields, type_resolver, default_resolve: nil)
513
- end
721
+ type_module = type_resolver.interface_module(interface_type_definition.name, base_type) #: GraphQL::Schema::Member::interface_definition
722
+ type_resolver.register(type_module)
723
+ type_module.description(interface_type_definition.description)
724
+ build_interfaces(type_module, interface_type_definition.interfaces, type_resolver)
725
+ type_module.ast_node(interface_type_definition)
726
+ build_directives(type_module, interface_type_definition, type_resolver)
727
+ build_fields(type_module, interface_type_definition.fields, type_resolver, default_resolve: nil)
728
+ type_module
514
729
  end
515
730
 
516
731
  def build_fields(owner, field_definitions, type_resolver, default_resolve:)
517
732
  builder = self
518
-
733
+ # If `owner.field` isn't customized, make fields directly. It's the same, but faster.
734
+ field_class = owner.method(:field).owner.equal?(GraphQL::Schema::Member::HasFields) ? owner.field_class : nil
735
+ resolve_method_names = type_resolver.field_resolve_methods.method_names
736
+ links = type_resolver.links
737
+ # Custom field classes might add arguments (for example, with extensions), which the links wouldn't include
738
+ if !field_class.equal?(GraphQL::Schema::Field)
739
+ links.incomplete!
740
+ end
519
741
  field_definitions.each do |field_definition|
520
- resolve_method_name = -"resolve_field_#{field_definition.name}"
521
- schema_field_defn = owner.field(
522
- field_definition.name,
523
- description: field_definition.description,
524
- type: type_resolver.call(field_definition.type),
525
- null: true,
526
- connection_extension: nil,
527
- deprecation_reason: build_deprecation_reason(field_definition.directives),
528
- ast_node: field_definition,
529
- method_conflict_warning: false,
530
- camelize: false,
531
- directives: prepare_directives(field_definition, type_resolver),
532
- resolver_method: resolve_method_name,
533
- resolve_batch: resolve_method_name,
534
- )
742
+ resolve_method_name = resolve_method_names[field_definition.name]
743
+ if field_class
744
+ schema_field_defn = field_class.new(
745
+ name: field_definition.name,
746
+ owner: owner,
747
+ description: field_definition.description,
748
+ type: (field_type = type_resolver.call(field_definition.type)),
749
+ null: true,
750
+ connection_extension: nil,
751
+ deprecation_reason: build_deprecation_reason(field_definition.directives),
752
+ ast_node: field_definition,
753
+ method_conflict_warning: false,
754
+ camelize: false,
755
+ resolver_method: resolve_method_name,
756
+ resolve_batch: resolve_method_name,
757
+ )
758
+ owner.add_field(schema_field_defn)
759
+ else
760
+ schema_field_defn = owner.field(
761
+ field_definition.name,
762
+ description: field_definition.description,
763
+ type: (field_type = type_resolver.call(field_definition.type)),
764
+ null: true,
765
+ connection_extension: nil,
766
+ deprecation_reason: build_deprecation_reason(field_definition.directives),
767
+ ast_node: field_definition,
768
+ method_conflict_warning: false,
769
+ camelize: false,
770
+ resolver_method: resolve_method_name,
771
+ resolve_batch: resolve_method_name,
772
+ )
773
+ end
535
774
 
775
+ links.add_reference(schema_field_defn, field_type)
776
+ builder.build_directives(schema_field_defn, field_definition, type_resolver)
536
777
  builder.build_arguments(schema_field_defn, field_definition.arguments, type_resolver)
537
778
 
538
779
  # Don't do this for interfaces
539
780
  if default_resolve
540
- define_field_resolve_method(owner, resolve_method_name, field_definition.name)
781
+ define_field_resolve_method(owner, resolve_method_name, field_definition.name, type_resolver)
541
782
  end
542
783
  end
543
784
  end
544
785
 
545
- def define_field_resolve_method(owner, method_name, field_name)
546
- owner.define_method(method_name) { |**args|
547
- field_instance = context.types.field(owner, field_name)
548
- context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context)
549
- }
550
- owner.define_singleton_method(method_name) { |objects, context, **args|
551
- field_instance = context.types.field(owner, field_name)
552
- objects.map do |object|
553
- context.schema.definition_default_resolve.call(self, field_instance, object, args, context)
554
- end
555
- }
786
+ def define_field_resolve_method(owner, method_name, field_name, type_resolver)
787
+ resolve_methods = type_resolver.field_resolve_methods
788
+ resolve_methods.field_names[method_name.to_sym] = field_name
789
+ owner.define_method(method_name, &resolve_methods.instance_method_proc)
790
+ owner.define_singleton_method(method_name, &resolve_methods.singleton_method_proc)
791
+ end
792
+
793
+ # Every field's resolve methods share these procs. They use the method's name to find the field.
794
+ class FieldResolveMethods
795
+ def initialize
796
+ @method_names = Hash.new { |h, field_name| h[field_name] = -"resolve_field_#{field_name}" }
797
+ field_names = @field_names = {}
798
+ @instance_method_proc = proc { |**args|
799
+ #: self as GraphQL::Schema::Object
800
+ owner = self.class
801
+ field_instance = context.types.field(owner, field_names[__method__])
802
+ context.schema.definition_default_resolve.call(owner, field_instance, object, args, context)
803
+ }
804
+ @singleton_method_proc = proc { |objects, context, **args|
805
+ field_instance = context.types.field(self, field_names[__method__])
806
+ objects.map do |object|
807
+ context.schema.definition_default_resolve.call(self, field_instance, object, args, context)
808
+ end
809
+ }
810
+ end
811
+
812
+ # Field names by resolve method name
813
+ attr_reader :field_names
814
+
815
+ # Resolve method names by field name
816
+ attr_reader :method_names
817
+
818
+ attr_reader :instance_method_proc, :singleton_method_proc
556
819
  end
557
820
 
558
- def build_resolve_type(lookup_hash, directives, missing_type_handler)
559
- resolve_type_proc = nil
560
- resolve_type_proc = ->(ast_node) {
821
+ class TypeResolver
822
+ def initialize(lookup_hash, directives, queued_directives, type_classes, &missing_type_handler)
823
+ @lookup_hash = lookup_hash
824
+ @directives = directives
825
+ @queued_directives = queued_directives
826
+ @type_classes = type_classes
827
+ @missing_type_handler = missing_type_handler
828
+ end
829
+
830
+ # The class made ahead of time for `type_name` (returned once, to be built), or a new subclass of `base_type`
831
+ def type_class(type_name, base_type)
832
+ @type_classes.delete(type_name) || Class.new(base_type) {
833
+ #: self as singleton(GraphQL::Schema::Member)
834
+ graphql_name(type_name)
835
+ }
836
+ end
837
+
838
+ def interface_module(type_name, base_type)
839
+ @type_classes.delete(type_name) || Builder.new_interface_module(type_name, base_type)
840
+ end
841
+
842
+ attr_writer :build_interface
843
+
844
+ attr_accessor :links
845
+
846
+ def build_interface_now(interface_name)
847
+ @build_interface&.call(interface_name, self)
848
+ end
849
+
850
+ # Directive applications waiting for {Builder#apply_queued_directives}
851
+ attr_reader :queued_directives
852
+
853
+ # Like `owner.get_argument(argument_name)`, but cached. Directives are applied after types are built,
854
+ # so their arguments don't change while this cache is used.
855
+ def get_argument(owner, argument_name)
856
+ @arguments_by_owner ||= {}.compare_by_identity
857
+ owner_arguments = @arguments_by_owner[owner] ||= {}
858
+ if owner_arguments.key?(argument_name)
859
+ owner_arguments[argument_name]
860
+ else
861
+ owner_arguments[argument_name] = owner.get_argument(argument_name)
862
+ end
863
+ end
864
+
865
+ def field_resolve_methods
866
+ @field_resolve_methods ||= FieldResolveMethods.new
867
+ end
868
+
869
+ # Add `type` to the lookup hash. Types call this before building their members, so that references back to them (including cycles) find this type.
870
+ def register(type)
871
+ @lookup_hash[type.graphql_name] = type
872
+ end
873
+
874
+ # The already-defined directive class named `name`, or `nil` if there isn't one yet
875
+ def directive_class(name)
876
+ @directives[name]
877
+ end
878
+
879
+ def directive_without_arguments?(name)
880
+ @directives_without_arguments ||= {}
881
+ if @directives_without_arguments.key?(name)
882
+ @directives_without_arguments[name]
883
+ else
884
+ dir_class = @directives[name]
885
+ # Don't cache unknown directives, they might be defined later
886
+ dir_class ? (@directives_without_arguments[name] = dir_class.all_argument_definitions.empty?) : false
887
+ end
888
+ end
889
+
890
+ def call(ast_node)
561
891
  case ast_node
562
892
  when GraphQL::Language::Nodes::TypeName
563
893
  type_name = ast_node.name
564
- if lookup_hash.key?(type_name)
565
- lookup_hash[type_name]
894
+ if @lookup_hash.key?(type_name)
895
+ @lookup_hash[type_name]
566
896
  else
567
- missing_type_handler.call(type_name)
897
+ @missing_type_handler.call(type_name)
568
898
  end
569
899
  when GraphQL::Language::Nodes::NonNullType
570
- resolve_type_proc.call(ast_node.of_type).to_non_null_type
900
+ call(ast_node.of_type).to_non_null_type
571
901
  when GraphQL::Language::Nodes::ListType
572
- resolve_type_proc.call(ast_node.of_type).to_list_type
902
+ call(ast_node.of_type).to_list_type
573
903
  when String
574
- directives[ast_node] ||= missing_type_handler.call(ast_node)
904
+ @directives[ast_node] ||= @missing_type_handler.call(ast_node)
575
905
  else
576
906
  raise "Unexpected ast_node: #{ast_node.inspect}"
577
907
  end
578
- }
579
- resolve_type_proc
908
+ end
580
909
  end
581
910
  end
582
911