graphql 1.9.18 → 1.13.24

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 (353) 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/analyze_query.rb +7 -0
  49. data/lib/graphql/analysis/ast/field_usage.rb +29 -2
  50. data/lib/graphql/analysis/ast/query_complexity.rb +174 -67
  51. data/lib/graphql/analysis/ast/visitor.rb +16 -7
  52. data/lib/graphql/analysis/ast.rb +21 -11
  53. data/lib/graphql/argument.rb +8 -36
  54. data/lib/graphql/backtrace/inspect_result.rb +0 -1
  55. data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
  56. data/lib/graphql/backtrace/table.rb +44 -5
  57. data/lib/graphql/backtrace/traced_error.rb +0 -1
  58. data/lib/graphql/backtrace/tracer.rb +40 -9
  59. data/lib/graphql/backtrace.rb +28 -19
  60. data/lib/graphql/backwards_compatibility.rb +2 -1
  61. data/lib/graphql/base_type.rb +10 -4
  62. data/lib/graphql/boolean_type.rb +1 -1
  63. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +2 -2
  64. data/lib/graphql/compatibility/execution_specification.rb +1 -0
  65. data/lib/graphql/compatibility/lazy_execution_specification.rb +2 -0
  66. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +5 -9
  67. data/lib/graphql/compatibility/query_parser_specification.rb +2 -0
  68. data/lib/graphql/compatibility/schema_parser_specification.rb +2 -0
  69. data/lib/graphql/dataloader/null_dataloader.rb +22 -0
  70. data/lib/graphql/dataloader/request.rb +19 -0
  71. data/lib/graphql/dataloader/request_all.rb +19 -0
  72. data/lib/graphql/dataloader/source.rb +155 -0
  73. data/lib/graphql/dataloader.rb +308 -0
  74. data/lib/graphql/date_encoding_error.rb +16 -0
  75. data/lib/graphql/define/assign_enum_value.rb +1 -1
  76. data/lib/graphql/define/assign_global_id_field.rb +2 -2
  77. data/lib/graphql/define/assign_object_field.rb +1 -1
  78. data/lib/graphql/define/defined_object_proxy.rb +5 -8
  79. data/lib/graphql/define/instance_definable.rb +60 -110
  80. data/lib/graphql/define/type_definer.rb +5 -5
  81. data/lib/graphql/deprecated_dsl.rb +18 -5
  82. data/lib/graphql/deprecation.rb +9 -0
  83. data/lib/graphql/directive/deprecated_directive.rb +1 -12
  84. data/lib/graphql/directive/include_directive.rb +1 -1
  85. data/lib/graphql/directive/skip_directive.rb +1 -1
  86. data/lib/graphql/directive.rb +9 -6
  87. data/lib/graphql/enum_type.rb +14 -74
  88. data/lib/graphql/execution/directive_checks.rb +2 -2
  89. data/lib/graphql/execution/errors.rb +110 -8
  90. data/lib/graphql/execution/execute.rb +8 -1
  91. data/lib/graphql/execution/instrumentation.rb +1 -1
  92. data/lib/graphql/execution/interpreter/argument_value.rb +28 -0
  93. data/lib/graphql/execution/interpreter/arguments.rb +88 -0
  94. data/lib/graphql/execution/interpreter/arguments_cache.rb +105 -0
  95. data/lib/graphql/execution/interpreter/handles_raw_value.rb +18 -0
  96. data/lib/graphql/execution/interpreter/resolve.rb +37 -25
  97. data/lib/graphql/execution/interpreter/runtime.rb +721 -386
  98. data/lib/graphql/execution/interpreter.rb +42 -19
  99. data/lib/graphql/execution/lazy/lazy_method_map.rb +4 -0
  100. data/lib/graphql/execution/lazy.rb +5 -1
  101. data/lib/graphql/execution/lookahead.rb +39 -114
  102. data/lib/graphql/execution/multiplex.rb +50 -25
  103. data/lib/graphql/field.rb +15 -119
  104. data/lib/graphql/filter.rb +1 -1
  105. data/lib/graphql/float_type.rb +1 -1
  106. data/lib/graphql/function.rb +5 -30
  107. data/lib/graphql/id_type.rb +1 -1
  108. data/lib/graphql/input_object_type.rb +9 -25
  109. data/lib/graphql/int_type.rb +1 -1
  110. data/lib/graphql/integer_decoding_error.rb +17 -0
  111. data/lib/graphql/integer_encoding_error.rb +18 -2
  112. data/lib/graphql/interface_type.rb +10 -24
  113. data/lib/graphql/internal_representation/document.rb +2 -2
  114. data/lib/graphql/internal_representation/rewrite.rb +1 -1
  115. data/lib/graphql/internal_representation/scope.rb +2 -2
  116. data/lib/graphql/internal_representation/visit.rb +2 -2
  117. data/lib/graphql/introspection/base_object.rb +2 -5
  118. data/lib/graphql/introspection/directive_location_enum.rb +2 -2
  119. data/lib/graphql/introspection/directive_type.rb +12 -6
  120. data/lib/graphql/introspection/entry_points.rb +9 -9
  121. data/lib/graphql/introspection/enum_value_type.rb +2 -2
  122. data/lib/graphql/introspection/field_type.rb +9 -5
  123. data/lib/graphql/introspection/input_value_type.rb +41 -11
  124. data/lib/graphql/introspection/introspection_query.rb +6 -92
  125. data/lib/graphql/introspection/schema_type.rb +12 -12
  126. data/lib/graphql/introspection/type_type.rb +27 -17
  127. data/lib/graphql/introspection.rb +99 -0
  128. data/lib/graphql/invalid_null_error.rb +18 -0
  129. data/lib/graphql/language/block_string.rb +20 -5
  130. data/lib/graphql/language/cache.rb +37 -0
  131. data/lib/graphql/language/definition_slice.rb +21 -10
  132. data/lib/graphql/language/document_from_schema_definition.rb +116 -63
  133. data/lib/graphql/language/lexer.rb +53 -27
  134. data/lib/graphql/language/lexer.rl +5 -3
  135. data/lib/graphql/language/nodes.rb +67 -93
  136. data/lib/graphql/language/parser.rb +929 -896
  137. data/lib/graphql/language/parser.y +125 -102
  138. data/lib/graphql/language/printer.rb +11 -2
  139. data/lib/graphql/language/sanitized_printer.rb +222 -0
  140. data/lib/graphql/language/token.rb +0 -4
  141. data/lib/graphql/language/visitor.rb +2 -2
  142. data/lib/graphql/language.rb +3 -1
  143. data/lib/graphql/name_validator.rb +2 -7
  144. data/lib/graphql/non_null_type.rb +0 -10
  145. data/lib/graphql/object_type.rb +47 -58
  146. data/lib/graphql/pagination/active_record_relation_connection.rb +85 -0
  147. data/lib/graphql/pagination/array_connection.rb +77 -0
  148. data/lib/graphql/pagination/connection.rb +226 -0
  149. data/lib/graphql/pagination/connections.rb +160 -0
  150. data/lib/graphql/pagination/mongoid_relation_connection.rb +25 -0
  151. data/lib/graphql/pagination/relation_connection.rb +226 -0
  152. data/lib/graphql/pagination/sequel_dataset_connection.rb +28 -0
  153. data/lib/graphql/pagination.rb +6 -0
  154. data/lib/graphql/parse_error.rb +0 -1
  155. data/lib/graphql/query/arguments.rb +6 -4
  156. data/lib/graphql/query/arguments_cache.rb +1 -2
  157. data/lib/graphql/query/context.rb +52 -7
  158. data/lib/graphql/query/executor.rb +0 -1
  159. data/lib/graphql/query/fingerprint.rb +26 -0
  160. data/lib/graphql/query/input_validation_result.rb +32 -6
  161. data/lib/graphql/query/literal_input.rb +31 -11
  162. data/lib/graphql/query/null_context.rb +24 -8
  163. data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
  164. data/lib/graphql/query/serial_execution.rb +1 -0
  165. data/lib/graphql/query/validation_pipeline.rb +6 -4
  166. data/lib/graphql/query/variable_validation_error.rb +3 -3
  167. data/lib/graphql/query/variables.rb +50 -10
  168. data/lib/graphql/query.rb +77 -18
  169. data/lib/graphql/railtie.rb +9 -1
  170. data/lib/graphql/rake_task/validate.rb +3 -0
  171. data/lib/graphql/rake_task.rb +12 -9
  172. data/lib/graphql/relay/array_connection.rb +10 -12
  173. data/lib/graphql/relay/base_connection.rb +30 -13
  174. data/lib/graphql/relay/connection_instrumentation.rb +4 -4
  175. data/lib/graphql/relay/connection_type.rb +18 -4
  176. data/lib/graphql/relay/edge_type.rb +1 -0
  177. data/lib/graphql/relay/edges_instrumentation.rb +1 -2
  178. data/lib/graphql/relay/global_id_resolve.rb +1 -2
  179. data/lib/graphql/relay/mutation.rb +3 -87
  180. data/lib/graphql/relay/node.rb +3 -0
  181. data/lib/graphql/relay/page_info.rb +1 -1
  182. data/lib/graphql/relay/range_add.rb +27 -9
  183. data/lib/graphql/relay/relation_connection.rb +8 -10
  184. data/lib/graphql/relay/type_extensions.rb +2 -0
  185. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  186. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  187. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  188. data/lib/graphql/rubocop.rb +4 -0
  189. data/lib/graphql/scalar_type.rb +18 -60
  190. data/lib/graphql/schema/addition.rb +247 -0
  191. data/lib/graphql/schema/argument.rb +274 -18
  192. data/lib/graphql/schema/base_64_encoder.rb +2 -0
  193. data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +1 -1
  194. data/lib/graphql/schema/build_from_definition/resolve_map.rb +13 -5
  195. data/lib/graphql/schema/build_from_definition.rb +320 -219
  196. data/lib/graphql/schema/built_in_types.rb +5 -5
  197. data/lib/graphql/schema/default_type_error.rb +2 -0
  198. data/lib/graphql/schema/directive/deprecated.rb +18 -0
  199. data/lib/graphql/schema/directive/feature.rb +1 -1
  200. data/lib/graphql/schema/directive/flagged.rb +57 -0
  201. data/lib/graphql/schema/directive/include.rb +2 -2
  202. data/lib/graphql/schema/directive/skip.rb +2 -2
  203. data/lib/graphql/schema/directive/transform.rb +14 -2
  204. data/lib/graphql/schema/directive.rb +130 -6
  205. data/lib/graphql/schema/enum.rb +121 -12
  206. data/lib/graphql/schema/enum_value.rb +24 -7
  207. data/lib/graphql/schema/field/connection_extension.rb +46 -20
  208. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  209. data/lib/graphql/schema/field.rb +465 -181
  210. data/lib/graphql/schema/field_extension.rb +89 -2
  211. data/lib/graphql/schema/find_inherited_value.rb +17 -1
  212. data/lib/graphql/schema/finder.rb +16 -14
  213. data/lib/graphql/schema/input_object.rb +172 -37
  214. data/lib/graphql/schema/interface.rb +39 -25
  215. data/lib/graphql/schema/introspection_system.rb +106 -38
  216. data/lib/graphql/schema/late_bound_type.rb +3 -2
  217. data/lib/graphql/schema/list.rb +65 -1
  218. data/lib/graphql/schema/loader.rb +145 -102
  219. data/lib/graphql/schema/member/accepts_definition.rb +15 -3
  220. data/lib/graphql/schema/member/base_dsl_methods.rb +34 -28
  221. data/lib/graphql/schema/member/build_type.rb +19 -8
  222. data/lib/graphql/schema/member/cached_graphql_definition.rb +34 -2
  223. data/lib/graphql/schema/member/has_arguments.rb +206 -13
  224. data/lib/graphql/schema/member/has_ast_node.rb +20 -0
  225. data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
  226. data/lib/graphql/schema/member/has_directives.rb +98 -0
  227. data/lib/graphql/schema/member/has_fields.rb +97 -32
  228. data/lib/graphql/schema/member/has_interfaces.rb +100 -0
  229. data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
  230. data/lib/graphql/schema/member/has_validators.rb +31 -0
  231. data/lib/graphql/schema/member/instrumentation.rb +0 -1
  232. data/lib/graphql/schema/member/type_system_helpers.rb +3 -3
  233. data/lib/graphql/schema/member/validates_input.rb +33 -0
  234. data/lib/graphql/schema/member.rb +11 -0
  235. data/lib/graphql/schema/middleware_chain.rb +1 -1
  236. data/lib/graphql/schema/mutation.rb +4 -0
  237. data/lib/graphql/schema/non_null.rb +37 -1
  238. data/lib/graphql/schema/object.rb +51 -38
  239. data/lib/graphql/schema/possible_types.rb +9 -4
  240. data/lib/graphql/schema/printer.rb +16 -35
  241. data/lib/graphql/schema/relay_classic_mutation.rb +40 -4
  242. data/lib/graphql/schema/resolver/has_payload_type.rb +34 -4
  243. data/lib/graphql/schema/resolver.rb +133 -79
  244. data/lib/graphql/schema/scalar.rb +43 -3
  245. data/lib/graphql/schema/subscription.rb +57 -21
  246. data/lib/graphql/schema/timeout.rb +29 -15
  247. data/lib/graphql/schema/timeout_middleware.rb +3 -1
  248. data/lib/graphql/schema/traversal.rb +2 -2
  249. data/lib/graphql/schema/type_expression.rb +21 -13
  250. data/lib/graphql/schema/type_membership.rb +19 -5
  251. data/lib/graphql/schema/union.rb +44 -3
  252. data/lib/graphql/schema/unique_within_type.rb +1 -2
  253. data/lib/graphql/schema/validation.rb +14 -4
  254. data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
  255. data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
  256. data/lib/graphql/schema/validator/exclusion_validator.rb +33 -0
  257. data/lib/graphql/schema/validator/format_validator.rb +48 -0
  258. data/lib/graphql/schema/validator/inclusion_validator.rb +35 -0
  259. data/lib/graphql/schema/validator/length_validator.rb +59 -0
  260. data/lib/graphql/schema/validator/numericality_validator.rb +82 -0
  261. data/lib/graphql/schema/validator/required_validator.rb +82 -0
  262. data/lib/graphql/schema/validator.rb +171 -0
  263. data/lib/graphql/schema/warden.rb +193 -34
  264. data/lib/graphql/schema.rb +882 -247
  265. data/lib/graphql/static_validation/all_rules.rb +2 -0
  266. data/lib/graphql/static_validation/base_visitor.rb +17 -10
  267. data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
  268. data/lib/graphql/static_validation/error.rb +3 -1
  269. data/lib/graphql/static_validation/literal_validator.rb +51 -26
  270. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +45 -83
  271. data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +22 -6
  272. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +35 -26
  273. data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +4 -2
  274. data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -1
  275. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -2
  276. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
  277. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -5
  278. data/lib/graphql/static_validation/rules/fields_will_merge.rb +94 -51
  279. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
  280. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  281. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
  282. data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
  283. data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
  284. data/lib/graphql/static_validation/rules/query_root_exists.rb +17 -0
  285. data/lib/graphql/static_validation/rules/query_root_exists_error.rb +26 -0
  286. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -2
  287. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +9 -10
  288. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
  289. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +12 -13
  290. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +19 -14
  291. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  292. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +5 -3
  293. data/lib/graphql/static_validation/type_stack.rb +2 -2
  294. data/lib/graphql/static_validation/validation_context.rb +13 -3
  295. data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
  296. data/lib/graphql/static_validation/validator.rb +43 -9
  297. data/lib/graphql/static_validation.rb +1 -0
  298. data/lib/graphql/string_encoding_error.rb +13 -3
  299. data/lib/graphql/string_type.rb +1 -1
  300. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +123 -22
  301. data/lib/graphql/subscriptions/broadcast_analyzer.rb +81 -0
  302. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +21 -0
  303. data/lib/graphql/subscriptions/event.rb +84 -30
  304. data/lib/graphql/subscriptions/instrumentation.rb +10 -6
  305. data/lib/graphql/subscriptions/serialize.rb +53 -6
  306. data/lib/graphql/subscriptions/subscription_root.rb +15 -5
  307. data/lib/graphql/subscriptions.rb +117 -49
  308. data/lib/graphql/tracing/active_support_notifications_tracing.rb +8 -17
  309. data/lib/graphql/tracing/appoptics_tracing.rb +173 -0
  310. data/lib/graphql/tracing/appsignal_tracing.rb +23 -0
  311. data/lib/graphql/tracing/data_dog_tracing.rb +32 -15
  312. data/lib/graphql/tracing/new_relic_tracing.rb +9 -12
  313. data/lib/graphql/tracing/notifications_tracing.rb +59 -0
  314. data/lib/graphql/tracing/platform_tracing.rb +66 -10
  315. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
  316. data/lib/graphql/tracing/prometheus_tracing.rb +8 -0
  317. data/lib/graphql/tracing/scout_tracing.rb +19 -0
  318. data/lib/graphql/tracing/skylight_tracing.rb +9 -1
  319. data/lib/graphql/tracing/statsd_tracing.rb +42 -0
  320. data/lib/graphql/tracing.rb +15 -35
  321. data/lib/graphql/types/big_int.rb +5 -1
  322. data/lib/graphql/types/int.rb +10 -3
  323. data/lib/graphql/types/iso_8601_date.rb +16 -8
  324. data/lib/graphql/types/iso_8601_date_time.rb +32 -10
  325. data/lib/graphql/types/relay/base_connection.rb +6 -88
  326. data/lib/graphql/types/relay/base_edge.rb +2 -34
  327. data/lib/graphql/types/relay/connection_behaviors.rb +174 -0
  328. data/lib/graphql/types/relay/default_relay.rb +31 -0
  329. data/lib/graphql/types/relay/edge_behaviors.rb +64 -0
  330. data/lib/graphql/types/relay/has_node_field.rb +41 -0
  331. data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
  332. data/lib/graphql/types/relay/node.rb +2 -4
  333. data/lib/graphql/types/relay/node_behaviors.rb +15 -0
  334. data/lib/graphql/types/relay/node_field.rb +3 -22
  335. data/lib/graphql/types/relay/nodes_field.rb +16 -18
  336. data/lib/graphql/types/relay/page_info.rb +2 -14
  337. data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
  338. data/lib/graphql/types/relay.rb +11 -3
  339. data/lib/graphql/types/string.rb +8 -2
  340. data/lib/graphql/unauthorized_error.rb +2 -2
  341. data/lib/graphql/union_type.rb +5 -25
  342. data/lib/graphql/unresolved_type_error.rb +2 -2
  343. data/lib/graphql/upgrader/member.rb +1 -0
  344. data/lib/graphql/upgrader/schema.rb +1 -0
  345. data/lib/graphql/version.rb +1 -1
  346. data/lib/graphql.rb +87 -31
  347. data/readme.md +3 -6
  348. metadata +126 -124
  349. data/lib/graphql/execution/interpreter/hash_response.rb +0 -46
  350. data/lib/graphql/literal_validation_error.rb +0 -6
  351. data/lib/graphql/types/relay/base_field.rb +0 -22
  352. data/lib/graphql/types/relay/base_interface.rb +0 -29
  353. data/lib/graphql/types/relay/base_object.rb +0 -26
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require "graphql/schema/addition"
2
3
  require "graphql/schema/base_64_encoder"
3
4
  require "graphql/schema/catchall_middleware"
4
5
  require "graphql/schema/default_parse_error"
@@ -21,6 +22,7 @@ require "graphql/schema/validation"
21
22
  require "graphql/schema/warden"
22
23
  require "graphql/schema/build_from_definition"
23
24
 
25
+ require "graphql/schema/validator"
24
26
  require "graphql/schema/member"
25
27
  require "graphql/schema/wrapper"
26
28
  require "graphql/schema/list"
@@ -36,9 +38,11 @@ require "graphql/schema/scalar"
36
38
  require "graphql/schema/object"
37
39
  require "graphql/schema/union"
38
40
  require "graphql/schema/directive"
41
+ require "graphql/schema/directive/deprecated"
39
42
  require "graphql/schema/directive/include"
40
43
  require "graphql/schema/directive/skip"
41
44
  require "graphql/schema/directive/feature"
45
+ require "graphql/schema/directive/flagged"
42
46
  require "graphql/schema/directive/transform"
43
47
  require "graphql/schema/type_membership"
44
48
 
@@ -55,7 +59,6 @@ module GraphQL
55
59
  # - types for exposing your application
56
60
  # - query analyzers for assessing incoming queries (including max depth & max complexity restrictions)
57
61
  # - execution strategies for running incoming queries
58
- # - middleware for interacting with execution
59
62
  #
60
63
  # Schemas start with root types, {Schema#query}, {Schema#mutation} and {Schema#subscription}.
61
64
  # The schema will traverse the tree of fields & types, using those as starting points.
@@ -67,14 +70,10 @@ module GraphQL
67
70
  # Schemas can specify how queries should be executed against them.
68
71
  # `query_execution_strategy`, `mutation_execution_strategy` and `subscription_execution_strategy`
69
72
  # each apply to corresponding root types.
70
- #
71
- # A schema accepts a `Relay::GlobalNodeIdentification` instance for use with Relay IDs.
72
- #
73
+ # #
73
74
  # @example defining a schema
74
- # MySchema = GraphQL::Schema.define do
75
+ # class MySchema < GraphQL::Schema
75
76
  # query QueryType
76
- # middleware PermissionMiddleware
77
- # rescue_from(ActiveRecord::RecordNotFound) { "Not found" }
78
77
  # # If types are only connected by way of interfaces, they must be added here
79
78
  # orphan_types ImageType, AudioType
80
79
  # end
@@ -82,12 +81,89 @@ module GraphQL
82
81
  class Schema
83
82
  extend Forwardable
84
83
  extend GraphQL::Schema::Member::AcceptsDefinition
84
+ extend GraphQL::Schema::Member::HasAstNode
85
85
  include GraphQL::Define::InstanceDefinable
86
+ extend GraphQL::Define::InstanceDefinable::DeprecatedDefine
86
87
  extend GraphQL::Schema::FindInheritedValue
87
88
 
88
- accepts_definitions \
89
+ class DuplicateTypeNamesError < GraphQL::Error
90
+ def initialize(type_name:, first_definition:, second_definition:, path:)
91
+ super("Multiple definitions for `#{type_name}`. Previously found #{first_definition.inspect} (#{first_definition.class}), then found #{second_definition.inspect} (#{second_definition.class}) at #{path.join(".")}")
92
+ end
93
+ end
94
+
95
+ class DuplicateNamesError < GraphQL::Error; end
96
+
97
+ class UnresolvedLateBoundTypeError < GraphQL::Error
98
+ attr_reader :type
99
+ def initialize(type:)
100
+ @type = type
101
+ super("Late bound type was never found: #{type.inspect}")
102
+ end
103
+ end
104
+
105
+ module LazyHandlingMethods
106
+ # Call the given block at the right time, either:
107
+ # - Right away, if `value` is not registered with `lazy_resolve`
108
+ # - After resolving `value`, if it's registered with `lazy_resolve` (eg, `Promise`)
109
+ # @api private
110
+ def after_lazy(value, &block)
111
+ if lazy?(value)
112
+ GraphQL::Execution::Lazy.new do
113
+ result = sync_lazy(value)
114
+ # The returned result might also be lazy, so check it, too
115
+ after_lazy(result, &block)
116
+ end
117
+ else
118
+ yield(value) if block_given?
119
+ end
120
+ end
121
+
122
+ # Override this method to handle lazy objects in a custom way.
123
+ # @param value [Object] an instance of a class registered with {.lazy_resolve}
124
+ # @return [Object] A GraphQL-ready (non-lazy) object
125
+ # @api private
126
+ def sync_lazy(value)
127
+ lazy_method = lazy_method_name(value)
128
+ if lazy_method
129
+ synced_value = value.public_send(lazy_method)
130
+ sync_lazy(synced_value)
131
+ else
132
+ value
133
+ end
134
+ end
135
+
136
+ # @return [Symbol, nil] The method name to lazily resolve `obj`, or nil if `obj`'s class wasn't registered with {#lazy_resolve}.
137
+ def lazy_method_name(obj)
138
+ lazy_methods.get(obj)
139
+ end
140
+
141
+ # @return [Boolean] True if this object should be lazily resolved
142
+ def lazy?(obj)
143
+ !!lazy_method_name(obj)
144
+ end
145
+
146
+ # Return a lazy if any of `maybe_lazies` are lazy,
147
+ # otherwise, call the block eagerly and return the result.
148
+ # @param maybe_lazies [Array]
149
+ # @api private
150
+ def after_any_lazies(maybe_lazies)
151
+ if maybe_lazies.any? { |l| lazy?(l) }
152
+ GraphQL::Execution::Lazy.all(maybe_lazies).then do |result|
153
+ yield result
154
+ end
155
+ else
156
+ yield maybe_lazies
157
+ end
158
+ end
159
+ end
160
+
161
+ include LazyHandlingMethods
162
+ extend LazyHandlingMethods
163
+
164
+ deprecated_accepts_definitions \
89
165
  :query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
90
- :max_depth, :max_complexity, :default_max_page_size,
166
+ :validate_timeout, :validate_max_errors, :max_depth, :max_complexity, :default_max_page_size,
91
167
  :orphan_types, :resolve_type, :type_error, :parse_error,
92
168
  :error_bubbling,
93
169
  :raise_definition_error,
@@ -101,7 +177,7 @@ module GraphQL
101
177
  disable_introspection_entry_points: ->(schema) { schema.disable_introspection_entry_points = true },
102
178
  disable_schema_introspection_entry_point: ->(schema) { schema.disable_schema_introspection_entry_point = true },
103
179
  disable_type_introspection_entry_point: ->(schema) { schema.disable_type_introspection_entry_point = true },
104
- directives: ->(schema, directives) { schema.directives = directives.reduce({}) { |m, d| m[d.name] = d; m } },
180
+ directives: ->(schema, directives) { schema.directives = directives.reduce({}) { |m, d| m[d.graphql_name] = d; m } },
105
181
  directive: ->(schema, directive) { schema.directives[directive.graphql_name] = directive },
106
182
  instrument: ->(schema, type, instrumenter, after_built_ins: false) {
107
183
  if type == :field && after_built_ins
@@ -111,7 +187,7 @@ module GraphQL
111
187
  },
112
188
  query_analyzer: ->(schema, analyzer) {
113
189
  if analyzer == GraphQL::Authorization::Analyzer
114
- warn("The Authorization query analyzer is deprecated. Authorizing at query runtime is generally a better idea.")
190
+ GraphQL::Deprecation.warn("The Authorization query analyzer is deprecated. Authorizing at query runtime is generally a better idea.")
115
191
  end
116
192
  schema.query_analyzers << analyzer
117
193
  },
@@ -126,7 +202,7 @@ module GraphQL
126
202
  attr_accessor \
127
203
  :query, :mutation, :subscription,
128
204
  :query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
129
- :max_depth, :max_complexity, :default_max_page_size,
205
+ :validate_timeout, :validate_max_errors, :max_depth, :max_complexity, :default_max_page_size,
130
206
  :orphan_types, :directives,
131
207
  :query_analyzers, :multiplex_analyzers, :instrumenters, :lazy_methods,
132
208
  :cursor_encoder,
@@ -156,12 +232,24 @@ module GraphQL
156
232
  # [Boolean] True if this object disables the introspection entry point fields
157
233
  attr_accessor :disable_introspection_entry_points
158
234
 
235
+ def disable_introspection_entry_points?
236
+ !!@disable_introspection_entry_points
237
+ end
238
+
159
239
  # [Boolean] True if this object disables the __schema introspection entry point field
160
240
  attr_accessor :disable_schema_introspection_entry_point
161
241
 
242
+ def disable_schema_introspection_entry_point?
243
+ !!@disable_schema_introspection_entry_point
244
+ end
245
+
162
246
  # [Boolean] True if this object disables the __type introspection entry point field
163
247
  attr_accessor :disable_type_introspection_entry_point
164
248
 
249
+ def disable_type_introspection_entry_point?
250
+ !!@disable_type_introspection_entry_point
251
+ end
252
+
165
253
  class << self
166
254
  attr_writer :default_execution_strategy
167
255
  end
@@ -175,8 +263,6 @@ module GraphQL
175
263
  attr_reader :tracers
176
264
 
177
265
  DYNAMIC_FIELDS = ["__type", "__typename", "__schema"].freeze
178
- EMPTY_ARRAY = [].freeze
179
- EMPTY_HASH = {}.freeze
180
266
 
181
267
  attr_reader :static_validator, :object_from_id_proc, :id_from_object_proc, :resolve_type_proc
182
268
 
@@ -184,7 +270,10 @@ module GraphQL
184
270
  @tracers = []
185
271
  @definition_error = nil
186
272
  @orphan_types = []
187
- @directives = self.class.default_directives
273
+ @directives = {}
274
+ self.class.default_directives.each do |name, dir|
275
+ @directives[name] = dir.graphql_definition
276
+ end
188
277
  @static_validator = GraphQL::StaticValidation::Validator.new(schema: self)
189
278
  @middleware = MiddlewareChain.new(final_step: GraphQL::Execution::Execute::FieldResolveStep)
190
279
  @query_analyzers = []
@@ -198,11 +287,11 @@ module GraphQL
198
287
  @lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
199
288
  @lazy_methods.set(GraphQL::Execution::Lazy, :value)
200
289
  @cursor_encoder = Base64Encoder
201
- # Default to the built-in execution strategy:
290
+ # For schema instances, default to legacy runtime modules
202
291
  @analysis_engine = GraphQL::Analysis
203
- @query_execution_strategy = self.class.default_execution_strategy
204
- @mutation_execution_strategy = self.class.default_execution_strategy
205
- @subscription_execution_strategy = self.class.default_execution_strategy
292
+ @query_execution_strategy = GraphQL::Execution::Execute
293
+ @mutation_execution_strategy = GraphQL::Execution::Execute
294
+ @subscription_execution_strategy = GraphQL::Execution::Execute
206
295
  @default_mask = GraphQL::Schema::NullMask
207
296
  @rebuilding_artifacts = false
208
297
  @context_class = GraphQL::Query::Context
@@ -217,12 +306,11 @@ module GraphQL
217
306
 
218
307
  # @return [Boolean] True if using the new {GraphQL::Execution::Interpreter}
219
308
  def interpreter?
220
- @interpreter
309
+ query_execution_strategy == GraphQL::Execution::Interpreter &&
310
+ mutation_execution_strategy == GraphQL::Execution::Interpreter &&
311
+ subscription_execution_strategy == GraphQL::Execution::Interpreter
221
312
  end
222
313
 
223
- # @api private
224
- attr_writer :interpreter
225
-
226
314
  def inspect
227
315
  "#<#{self.class.name} ...>"
228
316
  end
@@ -270,24 +358,7 @@ module GraphQL
270
358
  # For forwards-compatibility with Schema classes
271
359
  alias :graphql_definition :itself
272
360
 
273
- # Validate a query string according to this schema.
274
- # @param string_or_document [String, GraphQL::Language::Nodes::Document]
275
- # @return [Array<GraphQL::StaticValidation::Error >]
276
- def validate(string_or_document, rules: nil, context: nil)
277
- doc = if string_or_document.is_a?(String)
278
- GraphQL.parse(string_or_document)
279
- else
280
- string_or_document
281
- end
282
- query = GraphQL::Query.new(self, document: doc, context: context)
283
- validator_opts = { schema: self }
284
- rules && (validator_opts[:rules] = rules)
285
- validator = GraphQL::StaticValidation::Validator.new(**validator_opts)
286
- res = validator.validate(query)
287
- res[:errors]
288
- end
289
-
290
- def define(**kwargs, &block)
361
+ def deprecated_define(**kwargs, &block)
291
362
  super
292
363
  ensure_defined
293
364
  # Assert that all necessary configs are present:
@@ -335,6 +406,10 @@ module GraphQL
335
406
  end
336
407
  end
337
408
 
409
+ def get_type(type_name)
410
+ @types[type_name]
411
+ end
412
+
338
413
  # @api private
339
414
  def introspection_system
340
415
  @introspection_system ||= begin
@@ -346,9 +421,13 @@ module GraphQL
346
421
  # Returns a list of Arguments and Fields referencing a certain type
347
422
  # @param type_name [String]
348
423
  # @return [Hash]
349
- def references_to(type_name)
424
+ def references_to(type_name = nil)
350
425
  rebuild_artifacts unless defined?(@type_reference_map)
351
- @type_reference_map.fetch(type_name, [])
426
+ if type_name
427
+ @type_reference_map.fetch(type_name, [])
428
+ else
429
+ @type_reference_map
430
+ end
352
431
  end
353
432
 
354
433
  # Returns a list of Union types in which a type is a member
@@ -423,11 +502,11 @@ module GraphQL
423
502
  # @param field_name [String]
424
503
  # @return [GraphQL::Field, nil] The field named `field_name` on `parent_type`
425
504
  # @see [GraphQL::Schema::Warden] Restricted access to members of a schema
426
- def get_field(parent_type, field_name)
505
+ def get_field(parent_type, field_name, _context = GraphQL::Query::NullContext)
427
506
  with_definition_error_check do
428
507
  parent_type_name = case parent_type
429
- when GraphQL::BaseType
430
- parent_type.name
508
+ when GraphQL::BaseType, Class, Module
509
+ parent_type.graphql_name
431
510
  when String
432
511
  parent_type
433
512
  else
@@ -453,8 +532,8 @@ module GraphQL
453
532
  @instrumented_field_map[type.graphql_name]
454
533
  end
455
534
 
456
- def type_from_ast(ast_node)
457
- GraphQL::Schema::TypeExpression.build_type(self.types, ast_node)
535
+ def type_from_ast(ast_node, context:)
536
+ GraphQL::Schema::TypeExpression.build_type(self, ast_node)
458
537
  end
459
538
 
460
539
  # @see [GraphQL::Schema::Warden] Restricted access to members of a schema
@@ -462,11 +541,20 @@ module GraphQL
462
541
  # @param context [GraphQL::Query::Context] The context for the current query
463
542
  # @return [Array<GraphQL::ObjectType>] types which belong to `type_defn` in this schema
464
543
  def possible_types(type_defn, context = GraphQL::Query::NullContext)
465
- @possible_types ||= GraphQL::Schema::PossibleTypes.new(self)
466
- @possible_types.possible_types(type_defn, context)
544
+ if context == GraphQL::Query::NullContext
545
+ @possible_types ||= GraphQL::Schema::PossibleTypes.new(self)
546
+ @possible_types.possible_types(type_defn, context)
547
+ else
548
+ # Use the incoming context to cache this instance --
549
+ # if it were cached on the schema, we'd have a memory leak
550
+ # https://github.com/rmosolgo/graphql-ruby/issues/2878
551
+ ns = context.namespace(:possible_types)
552
+ per_query_possible_types = ns[:possible_types] ||= GraphQL::Schema::PossibleTypes.new(self)
553
+ per_query_possible_types.possible_types(type_defn, context)
554
+ end
467
555
  end
468
556
 
469
- # @see [GraphQL::Schema::Warden] Resticted access to root types
557
+ # @see [GraphQL::Schema::Warden] Restricted access to root types
470
558
  # @return [GraphQL::ObjectType, nil]
471
559
  def root_type_for_operation(operation)
472
560
  case operation
@@ -607,9 +695,41 @@ module GraphQL
607
695
 
608
696
  # Can't delegate to `class`
609
697
  alias :_schema_class :class
610
- def_delegators :_schema_class, :visible?, :accessible?, :authorized?, :unauthorized_object, :unauthorized_field, :inaccessible_fields
698
+ def_delegators :_schema_class, :unauthorized_object, :unauthorized_field, :inaccessible_fields
611
699
  def_delegators :_schema_class, :directive
612
700
  def_delegators :_schema_class, :error_handler
701
+ def_delegators :_schema_class, :validate
702
+
703
+
704
+ # Given this schema member, find the class-based definition object
705
+ # whose `method_name` should be treated as an application hook
706
+ # @see {.visible?}
707
+ # @see {.accessible?}
708
+ def call_on_type_class(member, method_name, context, default:)
709
+ member = if member.respond_to?(:type_class)
710
+ member.type_class
711
+ else
712
+ member
713
+ end
714
+
715
+ if member.respond_to?(:relay_node_type) && (t = member.relay_node_type)
716
+ member = t
717
+ end
718
+
719
+ if member.respond_to?(method_name)
720
+ member.public_send(method_name, context)
721
+ else
722
+ default
723
+ end
724
+ end
725
+
726
+ def visible?(member, context)
727
+ call_on_type_class(member, :visible?, context, default: true)
728
+ end
729
+
730
+ def accessible?(member, context)
731
+ call_on_type_class(member, :accessible?, context, default: true)
732
+ end
613
733
 
614
734
  # A function to call when {#execute} receives an invalid query string
615
735
  #
@@ -655,30 +775,30 @@ module GraphQL
655
775
  # @param definition_or_path [String] A schema definition string, or a path to a file containing the definition
656
776
  # @param default_resolve [<#call(type, field, obj, args, ctx)>] A callable for handling field resolution
657
777
  # @param parser [Object] An object for handling definition string parsing (must respond to `parse`)
658
- # @return [GraphQL::Schema] the schema described by `document`
659
- def self.from_definition(definition_or_path, default_resolve: BuildFromDefinition::DefaultResolve, parser: BuildFromDefinition::DefaultParser)
778
+ # @param using [Hash] Plugins to attach to the created schema with `use(key, value)`
779
+ # @return [Class] the schema described by `document`
780
+ def self.from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.default_parser, using: {})
660
781
  # If the file ends in `.graphql`, treat it like a filepath
661
- definition = if definition_or_path.end_with?(".graphql")
662
- File.read(definition_or_path)
782
+ if definition_or_path.end_with?(".graphql")
783
+ GraphQL::Schema::BuildFromDefinition.from_definition_path(
784
+ definition_or_path,
785
+ default_resolve: default_resolve,
786
+ parser: parser,
787
+ using: using,
788
+ )
663
789
  else
664
- definition_or_path
790
+ GraphQL::Schema::BuildFromDefinition.from_definition(
791
+ definition_or_path,
792
+ default_resolve: default_resolve,
793
+ parser: parser,
794
+ using: using,
795
+ )
665
796
  end
666
- GraphQL::Schema::BuildFromDefinition.from_definition(definition, default_resolve: default_resolve, parser: parser)
667
797
  end
668
798
 
669
799
  # Error that is raised when [#Schema#from_definition] is passed an invalid schema definition string.
670
800
  class InvalidDocumentError < Error; end;
671
801
 
672
- # @return [Symbol, nil] The method name to lazily resolve `obj`, or nil if `obj`'s class wasn't registered wtih {#lazy_resolve}.
673
- def lazy_method_name(obj)
674
- @lazy_methods.get(obj)
675
- end
676
-
677
- # @return [Boolean] True if this object should be lazily resolved
678
- def lazy?(obj)
679
- !!lazy_method_name(obj)
680
- end
681
-
682
802
  # Return the GraphQL IDL for the schema
683
803
  # @param context [Hash]
684
804
  # @param only [<#call(member, ctx)>]
@@ -703,7 +823,7 @@ module GraphQL
703
823
  # @param except [<#call(member, ctx)>]
704
824
  # @return [Hash] GraphQL result
705
825
  def as_json(only: nil, except: nil, context: {})
706
- execute(Introspection::INTROSPECTION_QUERY, only: only, except: except, context: context).to_h
826
+ execute(Introspection.query(include_deprecated_args: true), only: only, except: except, context: context).to_h
707
827
  end
708
828
 
709
829
  # Returns the JSON response of {Introspection::INTROSPECTION_QUERY}.
@@ -713,65 +833,138 @@ module GraphQL
713
833
  JSON.pretty_generate(as_json(*args))
714
834
  end
715
835
 
836
+ def new_connections?
837
+ !!connections
838
+ end
839
+
840
+ attr_accessor :connections
841
+
716
842
  class << self
717
843
  extend Forwardable
718
844
  # For compatibility, these methods all:
719
845
  # - Cause the Schema instance to be created, if it hasn't been created yet
720
846
  # - Delegate to that instance
721
847
  # Eventually, the methods will be moved into this class, removing the need for the singleton.
722
- def_delegators :graphql_definition,
723
- # Schema structure
724
- :as_json, :to_json, :to_document, :to_definition, :ast_node,
848
+ def_delegators :deprecated_graphql_definition,
725
849
  # Execution
726
- :execute, :multiplex,
727
- :static_validator, :introspection_system,
728
- :query_analyzers, :tracers, :instrumenters,
729
850
  :execution_strategy_for_operation,
730
- :validate, :multiplex_analyzers, :lazy?, :lazy_method_name, :after_lazy, :sync_lazy,
731
851
  # Configuration
732
- :analysis_engine, :analysis_engine=, :using_ast_analysis?, :interpreter?,
733
- :max_complexity=, :max_depth=,
734
- :error_bubbling=,
735
- :metadata,
736
- :default_mask,
737
- :default_filter, :redefine,
852
+ :metadata, :redefine,
738
853
  :id_from_object_proc, :object_from_id_proc,
739
854
  :id_from_object=, :object_from_id=,
740
- :remove_handler,
741
- # Members
742
- :types, :get_fields, :find,
743
- :root_type_for_operation,
744
- :subscriptions,
745
- :union_memberships,
746
- :get_field, :root_types, :references_to, :type_from_ast,
747
- :possible_types,
748
- :disable_introspection_entry_points=,
749
- :disable_schema_introspection_entry_point=,
750
- :disable_type_introspection_entry_point=
855
+ :remove_handler
751
856
 
752
- def graphql_definition
753
- @graphql_definition ||= to_graphql
857
+ def deprecated_graphql_definition
858
+ graphql_definition(silence_deprecation_warning: true)
754
859
  end
755
860
 
756
- def use(plugin, **options)
757
- own_plugins << [plugin, options]
861
+ # @return [GraphQL::Subscriptions]
862
+ attr_accessor :subscriptions
863
+
864
+ # Returns the JSON response of {Introspection::INTROSPECTION_QUERY}.
865
+ # @see {#as_json}
866
+ # @return [String]
867
+ def to_json(**args)
868
+ JSON.pretty_generate(as_json(**args))
869
+ end
870
+
871
+ # Return the Hash response of {Introspection::INTROSPECTION_QUERY}.
872
+ # @param context [Hash]
873
+ # @param only [<#call(member, ctx)>]
874
+ # @param except [<#call(member, ctx)>]
875
+ # @return [Hash] GraphQL result
876
+ def as_json(only: nil, except: nil, context: {})
877
+ execute(Introspection.query(include_deprecated_args: true), only: only, except: except, context: context).to_h
878
+ end
879
+
880
+ # Return the GraphQL IDL for the schema
881
+ # @param context [Hash]
882
+ # @param only [<#call(member, ctx)>]
883
+ # @param except [<#call(member, ctx)>]
884
+ # @return [String]
885
+ def to_definition(only: nil, except: nil, context: {})
886
+ GraphQL::Schema::Printer.print_schema(self, only: only, except: except, context: context)
887
+ end
888
+
889
+ # Return the GraphQL::Language::Document IDL AST for the schema
890
+ # @return [GraphQL::Language::Document]
891
+ def to_document
892
+ GraphQL::Language::DocumentFromSchemaDefinition.new(self).document
893
+ end
894
+
895
+ # @return [String, nil]
896
+ def description(new_description = nil)
897
+ if new_description
898
+ @description = new_description
899
+ elsif defined?(@description)
900
+ @description
901
+ else
902
+ find_inherited_value(:description, nil)
903
+ end
904
+ end
905
+
906
+ def find(path)
907
+ if !@finder
908
+ @find_cache = {}
909
+ @finder ||= GraphQL::Schema::Finder.new(self)
910
+ end
911
+ @find_cache[path] ||= @finder.find(path)
912
+ end
913
+
914
+ def graphql_definition(silence_deprecation_warning: false)
915
+ @graphql_definition ||= begin
916
+ unless silence_deprecation_warning
917
+ message = "Legacy `.graphql_definition` objects are deprecated and will be removed in GraphQL-Ruby 2.0. Use a class-based definition instead."
918
+ caller_message = "\n\nCalled on #{self.inspect} from:\n #{caller(1, 25).map { |l| " #{l}" }.join("\n")}"
919
+ GraphQL::Deprecation.warn(message + caller_message)
920
+ end
921
+ to_graphql(silence_deprecation_warning: silence_deprecation_warning)
922
+ end
923
+ end
924
+
925
+ def default_filter
926
+ GraphQL::Filter.new(except: default_mask)
927
+ end
928
+
929
+ def default_mask(new_mask = nil)
930
+ if new_mask
931
+ @own_default_mask = new_mask
932
+ else
933
+ @own_default_mask || find_inherited_value(:default_mask, Schema::NullMask)
934
+ end
935
+ end
936
+
937
+ def static_validator
938
+ GraphQL::StaticValidation::Validator.new(schema: self)
939
+ end
940
+
941
+ def use(plugin, **kwargs)
942
+ if kwargs.any?
943
+ plugin.use(self, **kwargs)
944
+ else
945
+ plugin.use(self)
946
+ end
947
+ own_plugins << [plugin, kwargs]
758
948
  end
759
949
 
760
950
  def plugins
761
951
  find_inherited_value(:plugins, EMPTY_ARRAY) + own_plugins
762
952
  end
763
953
 
954
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
764
955
  def to_graphql
765
956
  schema_defn = self.new
766
957
  schema_defn.raise_definition_error = true
767
- schema_defn.query = query
768
- schema_defn.mutation = mutation
769
- schema_defn.subscription = subscription
958
+ schema_defn.query = query && query.graphql_definition(silence_deprecation_warning: true)
959
+ schema_defn.mutation = mutation && mutation.graphql_definition(silence_deprecation_warning: true)
960
+ schema_defn.subscription = subscription && subscription.graphql_definition(silence_deprecation_warning: true)
961
+ schema_defn.validate_timeout = validate_timeout
962
+ schema_defn.validate_max_errors = validate_max_errors
770
963
  schema_defn.max_complexity = max_complexity
771
964
  schema_defn.error_bubbling = error_bubbling
772
965
  schema_defn.max_depth = max_depth
773
966
  schema_defn.default_max_page_size = default_max_page_size
774
- schema_defn.orphan_types = orphan_types
967
+ schema_defn.orphan_types = orphan_types.map { |t| t.graphql_definition(silence_deprecation_warning: true) }
775
968
  schema_defn.disable_introspection_entry_points = disable_introspection_entry_points?
776
969
  schema_defn.disable_schema_introspection_entry_point = disable_schema_introspection_entry_point?
777
970
  schema_defn.disable_type_introspection_entry_point = disable_type_introspection_entry_point?
@@ -788,81 +981,319 @@ module GraphQL
788
981
  schema_defn.cursor_encoder = cursor_encoder
789
982
  schema_defn.tracers.concat(tracers)
790
983
  schema_defn.query_analyzers.concat(query_analyzers)
984
+ schema_defn.analysis_engine = analysis_engine
791
985
 
792
986
  schema_defn.middleware.concat(all_middleware)
793
987
  schema_defn.multiplex_analyzers.concat(multiplex_analyzers)
794
988
  schema_defn.query_execution_strategy = query_execution_strategy
795
989
  schema_defn.mutation_execution_strategy = mutation_execution_strategy
796
990
  schema_defn.subscription_execution_strategy = subscription_execution_strategy
797
- all_instrumenters.each do |step, insts|
991
+ schema_defn.default_mask = default_mask
992
+ instrumenters.each do |step, insts|
798
993
  insts.each do |inst|
799
994
  schema_defn.instrumenters[step] << inst
800
995
  end
801
996
  end
802
- lazy_classes.each do |lazy_class, value_method|
997
+
998
+ lazy_methods.each do |lazy_class, value_method|
803
999
  schema_defn.lazy_methods.set(lazy_class, value_method)
804
1000
  end
805
- rescues.each do |err_class, handler|
1001
+
1002
+ error_handler.each_rescue do |err_class, handler|
806
1003
  schema_defn.rescue_from(err_class, &handler)
807
1004
  end
808
1005
 
809
- if plugins.any?
810
- schema_plugins = plugins
811
- # TODO don't depend on .define
812
- schema_defn = schema_defn.redefine do
813
- schema_plugins.each do |plugin, options|
814
- if options.any?
815
- use(plugin, **options)
1006
+ schema_defn.subscriptions ||= self.subscriptions
1007
+
1008
+ if !schema_defn.interpreter?
1009
+ schema_defn.instrumenters[:query] << GraphQL::Schema::Member::Instrumentation
1010
+ end
1011
+
1012
+ if new_connections?
1013
+ schema_defn.connections = self.connections
1014
+ end
1015
+
1016
+ schema_defn.send(:rebuild_artifacts)
1017
+
1018
+ schema_defn
1019
+ end
1020
+
1021
+ # Build a map of `{ name => type }` and return it
1022
+ # @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
1023
+ # @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
1024
+ def types(context = GraphQL::Query::NullContext)
1025
+ all_types = non_introspection_types.merge(introspection_system.types)
1026
+ visible_types = {}
1027
+ all_types.each do |k, v|
1028
+ visible_types[k] =if v.is_a?(Array)
1029
+ visible_t = nil
1030
+ v.each do |t|
1031
+ if t.visible?(context)
1032
+ if visible_t.nil?
1033
+ visible_t = t
1034
+ else
1035
+ raise DuplicateNamesError, "Found two visible type definitions for `#{k}`: #{visible_t.inspect}, #{t.inspect}"
1036
+ end
1037
+ end
1038
+ end
1039
+ visible_t
1040
+ else
1041
+ v
1042
+ end
1043
+ end
1044
+ visible_types
1045
+ end
1046
+
1047
+ # @param type_name [String]
1048
+ # @return [Module, nil] A type, or nil if there's no type called `type_name`
1049
+ def get_type(type_name, context = GraphQL::Query::NullContext)
1050
+ local_entry = own_types[type_name]
1051
+ type_defn = case local_entry
1052
+ when nil
1053
+ nil
1054
+ when Array
1055
+ visible_t = nil
1056
+ warden = Warden.from_context(context)
1057
+ local_entry.each do |t|
1058
+ if warden.visible_type?(t, context)
1059
+ if visible_t.nil?
1060
+ visible_t = t
816
1061
  else
817
- use(plugin)
1062
+ raise DuplicateNamesError, "Found two visible type definitions for `#{type_name}`: #{visible_t.inspect}, #{t.inspect}"
818
1063
  end
819
1064
  end
820
1065
  end
1066
+ visible_t
1067
+ when Module
1068
+ local_entry
1069
+ else
1070
+ raise "Invariant: unexpected own_types[#{type_name.inspect}]: #{local_entry.inspect}"
821
1071
  end
822
- # Do this after `plugins` since Interpreter is a plugin
823
- if schema_defn.query_execution_strategy != GraphQL::Execution::Interpreter
824
- schema_defn.instrumenters[:query] << GraphQL::Schema::Member::Instrumentation
1072
+
1073
+ type_defn ||
1074
+ introspection_system.types[type_name] || # todo context-specific introspection?
1075
+ (superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context) : nil)
1076
+ end
1077
+
1078
+ # @api private
1079
+ attr_writer :connections
1080
+
1081
+ # @return [GraphQL::Pagination::Connections] if installed
1082
+ def connections
1083
+ if defined?(@connections)
1084
+ @connections
1085
+ else
1086
+ inherited_connections = find_inherited_value(:connections, nil)
1087
+ # This schema is part of an inheritance chain which is using new connections,
1088
+ # make a new instance, so we don't pollute the upstream one.
1089
+ if inherited_connections
1090
+ @connections = Pagination::Connections.new(schema: self)
1091
+ else
1092
+ nil
1093
+ end
825
1094
  end
826
- schema_defn.send(:rebuild_artifacts)
1095
+ end
827
1096
 
828
- schema_defn
1097
+ def new_connections?
1098
+ !!connections
829
1099
  end
830
1100
 
831
1101
  def query(new_query_object = nil)
832
1102
  if new_query_object
833
- @query_object = new_query_object
1103
+ if @query_object
1104
+ raise GraphQL::Error, "Second definition of `query(...)` (#{new_query_object.inspect}) is invalid, already configured with #{@query_object.inspect}"
1105
+ else
1106
+ @query_object = new_query_object
1107
+ add_type_and_traverse(new_query_object, root: true)
1108
+ nil
1109
+ end
834
1110
  else
835
- query_object = @query_object || find_inherited_value(:query)
836
- query_object.respond_to?(:graphql_definition) ? query_object.graphql_definition : query_object
1111
+ @query_object || find_inherited_value(:query)
837
1112
  end
838
1113
  end
839
1114
 
840
1115
  def mutation(new_mutation_object = nil)
841
1116
  if new_mutation_object
842
- @mutation_object = new_mutation_object
1117
+ if @mutation_object
1118
+ raise GraphQL::Error, "Second definition of `mutation(...)` (#{new_mutation_object.inspect}) is invalid, already configured with #{@mutation_object.inspect}"
1119
+ else
1120
+ @mutation_object = new_mutation_object
1121
+ add_type_and_traverse(new_mutation_object, root: true)
1122
+ nil
1123
+ end
843
1124
  else
844
- mutation_object = @mutation_object || find_inherited_value(:mutation)
845
- mutation_object.respond_to?(:graphql_definition) ? mutation_object.graphql_definition : mutation_object
1125
+ @mutation_object || find_inherited_value(:mutation)
846
1126
  end
847
1127
  end
848
1128
 
849
1129
  def subscription(new_subscription_object = nil)
850
1130
  if new_subscription_object
851
- @subscription_object = new_subscription_object
1131
+ if @subscription_object
1132
+ raise GraphQL::Error, "Second definition of `subscription(...)` (#{new_subscription_object.inspect}) is invalid, already configured with #{@subscription_object.inspect}"
1133
+ else
1134
+ @subscription_object = new_subscription_object
1135
+ add_subscription_extension_if_necessary
1136
+ add_type_and_traverse(new_subscription_object, root: true)
1137
+ nil
1138
+ end
1139
+ else
1140
+ @subscription_object || find_inherited_value(:subscription)
1141
+ end
1142
+ end
1143
+
1144
+ # @see [GraphQL::Schema::Warden] Restricted access to root types
1145
+ # @return [GraphQL::ObjectType, nil]
1146
+ def root_type_for_operation(operation)
1147
+ case operation
1148
+ when "query"
1149
+ query
1150
+ when "mutation"
1151
+ mutation
1152
+ when "subscription"
1153
+ subscription
1154
+ else
1155
+ raise ArgumentError, "unknown operation type: #{operation}"
1156
+ end
1157
+ end
1158
+
1159
+ def root_types
1160
+ @root_types
1161
+ end
1162
+
1163
+ # @param type [Module] The type definition whose possible types you want to see
1164
+ # @return [Hash<String, Module>] All possible types, if no `type` is given.
1165
+ # @return [Array<Module>] Possible types for `type`, if it's given.
1166
+ def possible_types(type = nil, context = GraphQL::Query::NullContext)
1167
+ if type
1168
+ # TODO duck-typing `.possible_types` would probably be nicer here
1169
+ if type.kind.union?
1170
+ type.possible_types(context: context)
1171
+ else
1172
+ stored_possible_types = own_possible_types[type.graphql_name]
1173
+ visible_possible_types = if stored_possible_types && type.kind.interface?
1174
+ stored_possible_types.select do |possible_type|
1175
+ # Use `.graphql_name` comparison to match legacy vs class-based types.
1176
+ # When we don't need to support legacy `.define` types, use `.include?(type)` instead.
1177
+ possible_type.interfaces(context).any? { |interface| interface.graphql_name == type.graphql_name }
1178
+ end
1179
+ else
1180
+ stored_possible_types
1181
+ end
1182
+ visible_possible_types ||
1183
+ introspection_system.possible_types[type.graphql_name] ||
1184
+ (
1185
+ superclass.respond_to?(:possible_types) ?
1186
+ superclass.possible_types(type, context) :
1187
+ EMPTY_ARRAY
1188
+ )
1189
+ end
1190
+ else
1191
+ find_inherited_value(:possible_types, EMPTY_HASH)
1192
+ .merge(own_possible_types)
1193
+ .merge(introspection_system.possible_types)
1194
+ end
1195
+ end
1196
+
1197
+ def union_memberships(type = nil)
1198
+ if type
1199
+ own_um = own_union_memberships.fetch(type.graphql_name, EMPTY_ARRAY)
1200
+ inherited_um = find_inherited_value(:union_memberships, EMPTY_HASH).fetch(type.graphql_name, EMPTY_ARRAY)
1201
+ own_um + inherited_um
1202
+ else
1203
+ joined_um = own_union_memberships.dup
1204
+ find_inherited_value(:union_memberhips, EMPTY_HASH).each do |k, v|
1205
+ um = joined_um[k] ||= []
1206
+ um.concat(v)
1207
+ end
1208
+ joined_um
1209
+ end
1210
+ end
1211
+
1212
+ # @api private
1213
+ # @see GraphQL::Dataloader
1214
+ def dataloader_class
1215
+ @dataloader_class || GraphQL::Dataloader::NullDataloader
1216
+ end
1217
+
1218
+ attr_writer :dataloader_class
1219
+
1220
+ def references_to(to_type = nil, from: nil)
1221
+ @own_references_to ||= Hash.new { |h, k| h[k] = [] }
1222
+ if to_type
1223
+ if !to_type.is_a?(String)
1224
+ to_type = to_type.graphql_name
1225
+ end
1226
+
1227
+ if from
1228
+ @own_references_to[to_type] << from
1229
+ else
1230
+ own_refs = @own_references_to[to_type]
1231
+ inherited_refs = find_inherited_value(:references_to, EMPTY_HASH)[to_type] || EMPTY_ARRAY
1232
+ own_refs + inherited_refs
1233
+ end
1234
+ else
1235
+ # `@own_references_to` can be quite large for big schemas,
1236
+ # and generally speaking, we won't inherit any values.
1237
+ # So optimize the most common case -- don't create a duplicate Hash.
1238
+ inherited_value = find_inherited_value(:references_to, EMPTY_HASH)
1239
+ if inherited_value.any?
1240
+ inherited_value.merge(@own_references_to)
1241
+ else
1242
+ @own_references_to
1243
+ end
1244
+ end
1245
+ end
1246
+
1247
+ def type_from_ast(ast_node, context: nil)
1248
+ type_owner = context ? context.warden : self
1249
+ GraphQL::Schema::TypeExpression.build_type(type_owner, ast_node)
1250
+ end
1251
+
1252
+ def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext)
1253
+ parent_type = case type_or_name
1254
+ when LateBoundType
1255
+ get_type(type_or_name.name, context)
1256
+ when String
1257
+ get_type(type_or_name, context)
1258
+ when Module
1259
+ type_or_name
852
1260
  else
853
- subscription_object = @subscription_object || find_inherited_value(:subscription)
854
- subscription_object.respond_to?(:graphql_definition) ? subscription_object.graphql_definition : subscription_object
1261
+ raise GraphQL::InvariantError, "Unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})"
1262
+ end
1263
+
1264
+ if parent_type.kind.fields? && (field = parent_type.get_field(field_name, context))
1265
+ field
1266
+ elsif parent_type == query && (entry_point_field = introspection_system.entry_point(name: field_name))
1267
+ entry_point_field
1268
+ elsif (dynamic_field = introspection_system.dynamic_field(name: field_name))
1269
+ dynamic_field
1270
+ else
1271
+ nil
855
1272
  end
856
1273
  end
857
1274
 
1275
+ def get_fields(type, context = GraphQL::Query::NullContext)
1276
+ type.fields(context)
1277
+ end
1278
+
858
1279
  def introspection(new_introspection_namespace = nil)
859
1280
  if new_introspection_namespace
860
1281
  @introspection = new_introspection_namespace
1282
+ # reset this cached value:
1283
+ @introspection_system = nil
861
1284
  else
862
1285
  @introspection || find_inherited_value(:introspection)
863
1286
  end
864
1287
  end
865
1288
 
1289
+ def introspection_system
1290
+ if !@introspection_system
1291
+ @introspection_system = Schema::IntrospectionSystem.new(self)
1292
+ @introspection_system.resolve_late_bindings
1293
+ end
1294
+ @introspection_system
1295
+ end
1296
+
866
1297
  def cursor_encoder(new_encoder = nil)
867
1298
  if new_encoder
868
1299
  @cursor_encoder = new_encoder
@@ -902,14 +1333,77 @@ module GraphQL
902
1333
  end
903
1334
  end
904
1335
 
1336
+ attr_writer :validate_timeout
1337
+
1338
+ def validate_timeout(new_validate_timeout = nil)
1339
+ if new_validate_timeout
1340
+ @validate_timeout = new_validate_timeout
1341
+ elsif defined?(@validate_timeout)
1342
+ @validate_timeout
1343
+ else
1344
+ find_inherited_value(:validate_timeout)
1345
+ end
1346
+ end
1347
+
1348
+ # Validate a query string according to this schema.
1349
+ # @param string_or_document [String, GraphQL::Language::Nodes::Document]
1350
+ # @return [Array<GraphQL::StaticValidation::Error >]
1351
+ def validate(string_or_document, rules: nil, context: nil)
1352
+ doc = if string_or_document.is_a?(String)
1353
+ GraphQL.parse(string_or_document)
1354
+ else
1355
+ string_or_document
1356
+ end
1357
+ query = GraphQL::Query.new(self, document: doc, context: context)
1358
+ validator_opts = { schema: self }
1359
+ rules && (validator_opts[:rules] = rules)
1360
+ validator = GraphQL::StaticValidation::Validator.new(**validator_opts)
1361
+ res = validator.validate(query, timeout: validate_timeout, max_errors: validate_max_errors)
1362
+ res[:errors]
1363
+ end
1364
+
1365
+ attr_writer :validate_max_errors
1366
+
1367
+ def validate_max_errors(new_validate_max_errors = nil)
1368
+ if new_validate_max_errors
1369
+ @validate_max_errors = new_validate_max_errors
1370
+ elsif defined?(@validate_max_errors)
1371
+ @validate_max_errors
1372
+ else
1373
+ find_inherited_value(:validate_max_errors)
1374
+ end
1375
+ end
1376
+
1377
+ attr_writer :max_complexity
1378
+
905
1379
  def max_complexity(max_complexity = nil)
906
1380
  if max_complexity
907
1381
  @max_complexity = max_complexity
1382
+ elsif defined?(@max_complexity)
1383
+ @max_complexity
908
1384
  else
909
- @max_complexity || find_inherited_value(:max_complexity)
1385
+ find_inherited_value(:max_complexity)
910
1386
  end
911
1387
  end
912
1388
 
1389
+ attr_writer :analysis_engine
1390
+
1391
+ def analysis_engine
1392
+ @analysis_engine || find_inherited_value(:analysis_engine, self.default_analysis_engine)
1393
+ end
1394
+
1395
+ def using_ast_analysis?
1396
+ analysis_engine == GraphQL::Analysis::AST
1397
+ end
1398
+
1399
+ def interpreter?
1400
+ query_execution_strategy == GraphQL::Execution::Interpreter &&
1401
+ mutation_execution_strategy == GraphQL::Execution::Interpreter &&
1402
+ subscription_execution_strategy == GraphQL::Execution::Interpreter
1403
+ end
1404
+
1405
+ attr_writer :interpreter
1406
+
913
1407
  def error_bubbling(new_error_bubbling = nil)
914
1408
  if !new_error_bubbling.nil?
915
1409
  @error_bubbling = new_error_bubbling
@@ -918,24 +1412,36 @@ module GraphQL
918
1412
  end
919
1413
  end
920
1414
 
1415
+ attr_writer :error_bubbling
1416
+
1417
+ attr_writer :max_depth
1418
+
921
1419
  def max_depth(new_max_depth = nil)
922
1420
  if new_max_depth
923
1421
  @max_depth = new_max_depth
1422
+ elsif defined?(@max_depth)
1423
+ @max_depth
924
1424
  else
925
- @max_depth || find_inherited_value(:max_depth)
1425
+ find_inherited_value(:max_depth)
926
1426
  end
927
1427
  end
928
1428
 
929
1429
  def disable_introspection_entry_points
930
1430
  @disable_introspection_entry_points = true
1431
+ # TODO: this clears the cache made in `def types`. But this is not a great solution.
1432
+ @introspection_system = nil
931
1433
  end
932
1434
 
933
1435
  def disable_schema_introspection_entry_point
934
1436
  @disable_schema_introspection_entry_point = true
1437
+ # TODO: this clears the cache made in `def types`. But this is not a great solution.
1438
+ @introspection_system = nil
935
1439
  end
936
1440
 
937
1441
  def disable_type_introspection_entry_point
938
1442
  @disable_type_introspection_entry_point = true
1443
+ # TODO: this clears the cache made in `def types`. But this is not a great solution.
1444
+ @introspection_system = nil
939
1445
  end
940
1446
 
941
1447
  def disable_introspection_entry_points?
@@ -964,6 +1470,8 @@ module GraphQL
964
1470
 
965
1471
  def orphan_types(*new_orphan_types)
966
1472
  if new_orphan_types.any?
1473
+ new_orphan_types = new_orphan_types.flatten
1474
+ add_type_and_traverse(new_orphan_types, root: false)
967
1475
  own_orphan_types.concat(new_orphan_types.flatten)
968
1476
  end
969
1477
 
@@ -974,7 +1482,15 @@ module GraphQL
974
1482
  if superclass <= GraphQL::Schema
975
1483
  superclass.default_execution_strategy
976
1484
  else
977
- @default_execution_strategy ||= GraphQL::Execution::Execute
1485
+ @default_execution_strategy ||= GraphQL::Execution::Interpreter
1486
+ end
1487
+ end
1488
+
1489
+ def default_analysis_engine
1490
+ if superclass <= GraphQL::Schema
1491
+ superclass.default_analysis_engine
1492
+ else
1493
+ @default_analysis_engine ||= GraphQL::Analysis::AST
978
1494
  end
979
1495
  end
980
1496
 
@@ -988,12 +1504,31 @@ module GraphQL
988
1504
 
989
1505
  def rescue_from(*err_classes, &handler_block)
990
1506
  err_classes.each do |err_class|
991
- own_rescues[err_class] = handler_block
1507
+ error_handler.rescue_from(err_class, handler_block)
992
1508
  end
993
1509
  end
994
1510
 
995
- def rescues
996
- find_inherited_value(:rescues, EMPTY_HASH).merge(own_rescues)
1511
+ # rubocop:disable Lint/DuplicateMethods
1512
+ module ResolveTypeWithType
1513
+ def resolve_type(type, obj, ctx)
1514
+ first_resolved_type, resolved_value = if type.is_a?(Module) && type.respond_to?(:resolve_type)
1515
+ type.resolve_type(obj, ctx)
1516
+ else
1517
+ super
1518
+ end
1519
+
1520
+ after_lazy(first_resolved_type) do |resolved_type|
1521
+ if resolved_type.nil? || (resolved_type.is_a?(Module) && resolved_type.respond_to?(:kind)) || resolved_type.is_a?(GraphQL::BaseType)
1522
+ if resolved_value
1523
+ [resolved_type, resolved_value]
1524
+ else
1525
+ resolved_type
1526
+ end
1527
+ else
1528
+ raise ".resolve_type should return a type definition, but got #{resolved_type.inspect} (#{resolved_type.class}) from `resolve_type(#{type}, #{obj}, #{ctx})`"
1529
+ end
1530
+ end
1531
+ end
997
1532
  end
998
1533
 
999
1534
  def resolve_type(type, obj, ctx)
@@ -1003,6 +1538,15 @@ module GraphQL
1003
1538
  raise GraphQL::RequiredImplementationMissingError, "#{self.name}.resolve_type(type, obj, ctx) must be implemented to use Union types or Interface types (tried to resolve: #{type.name})"
1004
1539
  end
1005
1540
  end
1541
+ # rubocop:enable Lint/DuplicateMethods
1542
+
1543
+ def inherited(child_class)
1544
+ if self == GraphQL::Schema
1545
+ child_class.directives(default_directives.values)
1546
+ end
1547
+ child_class.singleton_class.prepend(ResolveTypeWithType)
1548
+ super
1549
+ end
1006
1550
 
1007
1551
  def object_from_id(node_id, ctx)
1008
1552
  raise GraphQL::RequiredImplementationMissingError, "#{self.name}.object_from_id(node_id, ctx) must be implemented to load by ID (tried to load from id `#{node_id}`)"
@@ -1012,12 +1556,12 @@ module GraphQL
1012
1556
  raise GraphQL::RequiredImplementationMissingError, "#{self.name}.id_from_object(object, type, ctx) must be implemented to create global ids (tried to create an id for `#{object.inspect}`)"
1013
1557
  end
1014
1558
 
1015
- def visible?(member, context)
1016
- call_on_type_class(member, :visible?, context, default: true)
1559
+ def visible?(member, ctx)
1560
+ member.type_class.visible?(ctx)
1017
1561
  end
1018
1562
 
1019
- def accessible?(member, context)
1020
- call_on_type_class(member, :accessible?, context, default: true)
1563
+ def accessible?(member, ctx)
1564
+ member.type_class.accessible?(ctx)
1021
1565
  end
1022
1566
 
1023
1567
  # This hook is called when a client tries to access one or more
@@ -1071,18 +1615,30 @@ module GraphQL
1071
1615
  DefaultTypeError.call(type_err, ctx)
1072
1616
  end
1073
1617
 
1074
- attr_writer :error_handler
1618
+ # A function to call when {#execute} receives an invalid query string
1619
+ #
1620
+ # The default is to add the error to `context.errors`
1621
+ # @param err [GraphQL::ParseError] The error encountered during parsing
1622
+ # @param ctx [GraphQL::Query::Context] The context for the query where the error occurred
1623
+ # @return void
1624
+ def parse_error(parse_err, ctx)
1625
+ ctx.errors.push(parse_err)
1626
+ end
1075
1627
 
1076
- # @return [GraphQL::Execution::Errors, Class<GraphQL::Execution::Errors::NullErrorHandler>]
1628
+ # @return [GraphQL::Execution::Errors]
1077
1629
  def error_handler
1078
- @error_handler ||= GraphQL::Execution::Errors::NullErrorHandler
1630
+ @error_handler ||= GraphQL::Execution::Errors.new(self)
1079
1631
  end
1080
1632
 
1081
1633
  def lazy_resolve(lazy_class, value_method)
1082
- lazy_classes[lazy_class] = value_method
1634
+ lazy_methods.set(lazy_class, value_method)
1083
1635
  end
1084
1636
 
1085
1637
  def instrument(instrument_step, instrumenter, options = {})
1638
+ if instrument_step == :field
1639
+ GraphQL::Deprecation.warn "Field instrumentation (#{instrumenter.inspect}) will be removed in GraphQL-Ruby 2.0, please upgrade to field extensions: https://graphql-ruby.org/type_definitions/field_extensions.html"
1640
+ end
1641
+
1086
1642
  step = if instrument_step == :field && options[:after_built_ins]
1087
1643
  :field_after_built_ins
1088
1644
  else
@@ -1092,24 +1648,29 @@ module GraphQL
1092
1648
  own_instrumenters[step] << instrumenter
1093
1649
  end
1094
1650
 
1095
- def directives(new_directives = nil)
1096
- if new_directives
1097
- new_directives.each {|d| directive(d) }
1651
+ # Add several directives at once
1652
+ # @param new_directives [Class]
1653
+ def directives(*new_directives)
1654
+ if new_directives.any?
1655
+ new_directives.flatten.each { |d| directive(d) }
1098
1656
  end
1099
1657
 
1100
1658
  find_inherited_value(:directives, default_directives).merge(own_directives)
1101
1659
  end
1102
1660
 
1661
+ # Attach a single directive to this schema
1662
+ # @param new_directive [Class]
1663
+ # @return void
1103
1664
  def directive(new_directive)
1104
- own_directives[new_directive.graphql_name] = new_directive
1665
+ add_type_and_traverse(new_directive, root: false)
1105
1666
  end
1106
1667
 
1107
1668
  def default_directives
1108
- {
1109
- "include" => GraphQL::Directive::IncludeDirective,
1110
- "skip" => GraphQL::Directive::SkipDirective,
1111
- "deprecated" => GraphQL::Directive::DeprecatedDirective,
1112
- }
1669
+ @default_directives ||= {
1670
+ "include" => GraphQL::Schema::Directive::Include,
1671
+ "skip" => GraphQL::Schema::Directive::Skip,
1672
+ "deprecated" => GraphQL::Schema::Directive::Deprecated,
1673
+ }.freeze
1113
1674
  end
1114
1675
 
1115
1676
  def tracer(new_tracer)
@@ -1122,7 +1683,7 @@ module GraphQL
1122
1683
 
1123
1684
  def query_analyzer(new_analyzer)
1124
1685
  if new_analyzer == GraphQL::Authorization::Analyzer
1125
- warn("The Authorization query analyzer is deprecated. Authorizing at query runtime is generally a better idea.")
1686
+ GraphQL::Deprecation.warn("The Authorization query analyzer is deprecated. Authorizing at query runtime is generally a better idea.")
1126
1687
  end
1127
1688
  own_query_analyzers << new_analyzer
1128
1689
  end
@@ -1133,9 +1694,11 @@ module GraphQL
1133
1694
 
1134
1695
  def middleware(new_middleware = nil)
1135
1696
  if new_middleware
1697
+ GraphQL::Deprecation.warn "Middleware will be removed in GraphQL-Ruby 2.0, please upgrade to Field Extensions: https://graphql-ruby.org/type_definitions/field_extensions.html"
1136
1698
  own_middleware << new_middleware
1137
1699
  else
1138
- graphql_definition.middleware
1700
+ # TODO make sure this is cached when running a query
1701
+ MiddlewareChain.new(steps: all_middleware, final_step: GraphQL::Execution::Execute::FieldResolveStep)
1139
1702
  end
1140
1703
  end
1141
1704
 
@@ -1147,33 +1710,184 @@ module GraphQL
1147
1710
  find_inherited_value(:multiplex_analyzers, EMPTY_ARRAY) + own_multiplex_analyzers
1148
1711
  end
1149
1712
 
1713
+ def sanitized_printer(new_sanitized_printer = nil)
1714
+ if new_sanitized_printer
1715
+ @own_sanitized_printer = new_sanitized_printer
1716
+ else
1717
+ @own_sanitized_printer || GraphQL::Language::SanitizedPrinter
1718
+ end
1719
+ end
1720
+
1721
+ # Execute a query on itself.
1722
+ # @see {Query#initialize} for arguments.
1723
+ # @return [Hash] query result, ready to be serialized as JSON
1724
+ def execute(query_str = nil, **kwargs)
1725
+ if query_str
1726
+ kwargs[:query] = query_str
1727
+ end
1728
+ # Some of the query context _should_ be passed to the multiplex, too
1729
+ multiplex_context = if (ctx = kwargs[:context])
1730
+ {
1731
+ backtrace: ctx[:backtrace],
1732
+ tracers: ctx[:tracers],
1733
+ dataloader: ctx[:dataloader],
1734
+ }
1735
+ else
1736
+ {}
1737
+ end
1738
+ # Since we're running one query, don't run a multiplex-level complexity analyzer
1739
+ all_results = multiplex([kwargs], max_complexity: nil, context: multiplex_context)
1740
+ all_results[0]
1741
+ end
1742
+
1743
+ # Execute several queries on itself, concurrently.
1744
+ #
1745
+ # @example Run several queries at once
1746
+ # context = { ... }
1747
+ # queries = [
1748
+ # { query: params[:query_1], variables: params[:variables_1], context: context },
1749
+ # { query: params[:query_2], variables: params[:variables_2], context: context },
1750
+ # ]
1751
+ # results = MySchema.multiplex(queries)
1752
+ # render json: {
1753
+ # result_1: results[0],
1754
+ # result_2: results[1],
1755
+ # }
1756
+ #
1757
+ # @see {Query#initialize} for query keyword arguments
1758
+ # @see {Execution::Multiplex#run_queries} for multiplex keyword arguments
1759
+ # @param queries [Array<Hash>] Keyword arguments for each query
1760
+ # @param context [Hash] Multiplex-level context
1761
+ # @return [Array<Hash>] One result for each query in the input
1762
+ def multiplex(queries, **kwargs)
1763
+ schema = if interpreter?
1764
+ self
1765
+ else
1766
+ graphql_definition
1767
+ end
1768
+ GraphQL::Execution::Multiplex.run_all(schema, queries, **kwargs)
1769
+ end
1770
+
1771
+ def instrumenters
1772
+ inherited_instrumenters = find_inherited_value(:instrumenters) || Hash.new { |h,k| h[k] = [] }
1773
+ inherited_instrumenters.merge(own_instrumenters) do |_step, inherited, own|
1774
+ inherited + own
1775
+ end
1776
+ end
1777
+
1778
+ # @api private
1779
+ def add_subscription_extension_if_necessary
1780
+ if interpreter? && !defined?(@subscription_extension_added) && subscription && self.subscriptions
1781
+ @subscription_extension_added = true
1782
+ if subscription.singleton_class.ancestors.include?(Subscriptions::SubscriptionRoot)
1783
+ GraphQL::Deprecation.warn("`extend Subscriptions::SubscriptionRoot` is no longer required; you may remove it from #{self}'s `subscription` root type (#{subscription}).")
1784
+ else
1785
+ subscription.all_field_definitions.each do |field|
1786
+ field.extension(Subscriptions::DefaultSubscriptionResolveExtension)
1787
+ end
1788
+ end
1789
+ end
1790
+ end
1791
+
1792
+ def query_stack_error(query, err)
1793
+ query.context.errors.push(GraphQL::ExecutionError.new("This query is too large to execute."))
1794
+ end
1795
+
1150
1796
  private
1151
1797
 
1152
- def lazy_classes
1153
- @lazy_classes ||= {}
1798
+ # @param t [Module, Array<Module>]
1799
+ # @return [void]
1800
+ def add_type_and_traverse(t, root:)
1801
+ if root
1802
+ @root_types ||= []
1803
+ @root_types << t
1804
+ end
1805
+ new_types = Array(t)
1806
+ addition = Schema::Addition.new(schema: self, own_types: own_types, new_types: new_types)
1807
+ addition.types.each do |name, types_entry| # rubocop:disable Development/ContextIsPassedCop -- build-time, not query-time
1808
+ if (prev_entry = own_types[name])
1809
+ prev_entries = case prev_entry
1810
+ when Array
1811
+ prev_entry
1812
+ when Module
1813
+ own_types[name] = [prev_entry]
1814
+ else
1815
+ raise "Invariant: unexpected prev_entry at #{name.inspect} when adding #{t.inspect}"
1816
+ end
1817
+
1818
+ case types_entry
1819
+ when Array
1820
+ prev_entries.concat(types_entry)
1821
+ prev_entries.uniq! # in case any are being re-visited
1822
+ when Module
1823
+ if !prev_entries.include?(types_entry)
1824
+ prev_entries << types_entry
1825
+ end
1826
+ else
1827
+ raise "Invariant: unexpected types_entry at #{name} when adding #{t.inspect}"
1828
+ end
1829
+ else
1830
+ if types_entry.is_a?(Array)
1831
+ types_entry.uniq!
1832
+ end
1833
+ own_types[name] = types_entry
1834
+ end
1835
+ end
1836
+
1837
+ own_possible_types.merge!(addition.possible_types) { |key, old_val, new_val| old_val + new_val }
1838
+ own_union_memberships.merge!(addition.union_memberships)
1839
+
1840
+ addition.references.each { |thing, pointers|
1841
+ pointers.each { |pointer| references_to(thing, from: pointer) }
1842
+ }
1843
+
1844
+ addition.directives.each { |dir_class| own_directives[dir_class.graphql_name] = dir_class }
1845
+
1846
+ addition.arguments_with_default_values.each do |arg|
1847
+ arg.validate_default_value
1848
+ end
1154
1849
  end
1155
1850
 
1156
- def own_plugins
1157
- @own_plugins ||= []
1851
+ def lazy_methods
1852
+ if !defined?(@lazy_methods)
1853
+ if inherited_map = find_inherited_value(:lazy_methods)
1854
+ # this isn't _completely_ inherited :S (Things added after `dup` won't work)
1855
+ @lazy_methods = inherited_map.dup
1856
+ else
1857
+ @lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
1858
+ @lazy_methods.set(GraphQL::Execution::Lazy, :value)
1859
+ @lazy_methods.set(GraphQL::Dataloader::Request, :load)
1860
+ end
1861
+ end
1862
+ @lazy_methods
1863
+ end
1864
+
1865
+ def own_types
1866
+ @own_types ||= {}
1158
1867
  end
1159
1868
 
1160
- def own_rescues
1161
- @own_rescues ||= {}
1869
+ def non_introspection_types
1870
+ find_inherited_value(:non_introspection_types, EMPTY_HASH).merge(own_types)
1871
+ end
1872
+
1873
+ def own_plugins
1874
+ @own_plugins ||= []
1162
1875
  end
1163
1876
 
1164
1877
  def own_orphan_types
1165
1878
  @own_orphan_types ||= []
1166
1879
  end
1167
1880
 
1168
- def own_directives
1169
- @own_directives ||= {}
1881
+ def own_possible_types
1882
+ @own_possible_types ||= {}
1170
1883
  end
1171
1884
 
1172
- def all_instrumenters
1173
- inherited_instrumenters = find_inherited_value(:all_instrumenters) || Hash.new { |h,k| h[k] = [] }
1174
- inherited_instrumenters.merge(own_instrumenters) do |_step, inherited, own|
1175
- inherited + own
1176
- end
1885
+ def own_union_memberships
1886
+ @own_union_memberships ||= {}
1887
+ end
1888
+
1889
+ def own_directives
1890
+ @own_directives ||= {}
1177
1891
  end
1178
1892
 
1179
1893
  def own_instrumenters
@@ -1199,93 +1913,14 @@ module GraphQL
1199
1913
  def own_multiplex_analyzers
1200
1914
  @own_multiplex_analyzers ||= []
1201
1915
  end
1202
-
1203
- # Given this schema member, find the class-based definition object
1204
- # whose `method_name` should be treated as an application hook
1205
- # @see {.visible?}
1206
- # @see {.accessible?}
1207
- # @see {.authorized?}
1208
- def call_on_type_class(member, method_name, *args, default:)
1209
- member = if member.respond_to?(:metadata) && member.metadata
1210
- member.metadata[:type_class] || member
1211
- else
1212
- member
1213
- end
1214
-
1215
- if member.respond_to?(:relay_node_type) && (t = member.relay_node_type)
1216
- member = t
1217
- end
1218
-
1219
- if member.respond_to?(method_name)
1220
- member.public_send(method_name, *args)
1221
- else
1222
- default
1223
- end
1224
- end
1225
- end
1226
-
1227
-
1228
- def self.inherited(child_class)
1229
- child_class.singleton_class.class_eval do
1230
- prepend(MethodWrappers)
1231
- end
1232
1916
  end
1233
1917
 
1234
- module MethodWrappers
1235
- # Wrap the user-provided resolve-type in a correctness check
1236
- def resolve_type(type, obj, ctx = :__undefined__)
1237
- graphql_definition.check_resolved_type(type, obj, ctx) do |ok_type, ok_obj, ok_ctx|
1238
- super(ok_type, ok_obj, ok_ctx)
1239
- end
1240
- end
1918
+ def dataloader_class
1919
+ self.class.dataloader_class
1241
1920
  end
1242
1921
 
1243
- # Call the given block at the right time, either:
1244
- # - Right away, if `value` is not registered with `lazy_resolve`
1245
- # - After resolving `value`, if it's registered with `lazy_resolve` (eg, `Promise`)
1246
- # @api private
1247
- def after_lazy(value)
1248
- if lazy?(value)
1249
- GraphQL::Execution::Lazy.new do
1250
- result = sync_lazy(value)
1251
- # The returned result might also be lazy, so check it, too
1252
- after_lazy(result) do |final_result|
1253
- yield(final_result) if block_given?
1254
- end
1255
- end
1256
- else
1257
- yield(value) if block_given?
1258
- end
1259
- end
1260
-
1261
- # Override this method to handle lazy objects in a custom way.
1262
- # @param value [Object] an instance of a class registered with {.lazy_resolve}
1263
- # @param ctx [GraphQL::Query::Context] the context for this query
1264
- # @return [Object] A GraphQL-ready (non-lazy) object
1265
- def self.sync_lazy(value)
1266
- if block_given?
1267
- # This was already hit by the instance, just give it back
1268
- yield(value)
1269
- else
1270
- # This was called directly on the class, hit the instance
1271
- # which has the lazy method map
1272
- self.graphql_definition.sync_lazy(value)
1273
- end
1274
- end
1275
-
1276
- # @see Schema.sync_lazy for a hook to override
1277
- # @api private
1278
- def sync_lazy(value)
1279
- self.class.sync_lazy(value) { |v|
1280
- lazy_method = lazy_method_name(v)
1281
- if lazy_method
1282
- synced_value = value.public_send(lazy_method)
1283
- sync_lazy(synced_value)
1284
- else
1285
- v
1286
- end
1287
- }
1288
- end
1922
+ # Install these here so that subclasses will also install it.
1923
+ use(GraphQL::Pagination::Connections)
1289
1924
 
1290
1925
  protected
1291
1926