graphql 1.12.12 → 2.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of graphql might be problematic. Click here for more details.

Files changed (428) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +3 -8
  3. data/lib/generators/graphql/enum_generator.rb +4 -10
  4. data/lib/generators/graphql/field_extractor.rb +31 -0
  5. data/lib/generators/graphql/input_generator.rb +50 -0
  6. data/lib/generators/graphql/install/mutation_root_generator.rb +34 -0
  7. data/lib/generators/graphql/{templates → install/templates}/base_mutation.erb +2 -0
  8. data/lib/generators/graphql/{templates → install/templates}/mutation_type.erb +2 -0
  9. data/lib/generators/graphql/install_generator.rb +60 -4
  10. data/lib/generators/graphql/interface_generator.rb +7 -7
  11. data/lib/generators/graphql/mutation_create_generator.rb +22 -0
  12. data/lib/generators/graphql/mutation_delete_generator.rb +22 -0
  13. data/lib/generators/graphql/mutation_generator.rb +5 -30
  14. data/lib/generators/graphql/mutation_update_generator.rb +22 -0
  15. data/lib/generators/graphql/object_generator.rb +10 -38
  16. data/lib/generators/graphql/orm_mutations_base.rb +40 -0
  17. data/lib/generators/graphql/relay.rb +23 -12
  18. data/lib/generators/graphql/scalar_generator.rb +4 -2
  19. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  20. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  21. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  22. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  23. data/lib/generators/graphql/templates/base_field.erb +2 -0
  24. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  25. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  26. data/lib/generators/graphql/templates/base_object.erb +2 -0
  27. data/lib/generators/graphql/templates/base_resolver.erb +8 -0
  28. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  29. data/lib/generators/graphql/templates/base_union.erb +2 -0
  30. data/lib/generators/graphql/templates/enum.erb +5 -1
  31. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  32. data/lib/generators/graphql/templates/input.erb +9 -0
  33. data/lib/generators/graphql/templates/interface.erb +4 -2
  34. data/lib/generators/graphql/templates/loader.erb +2 -0
  35. data/lib/generators/graphql/templates/mutation.erb +3 -1
  36. data/lib/generators/graphql/templates/mutation_create.erb +20 -0
  37. data/lib/generators/graphql/templates/mutation_delete.erb +20 -0
  38. data/lib/generators/graphql/templates/mutation_update.erb +21 -0
  39. data/lib/generators/graphql/templates/node_type.erb +2 -0
  40. data/lib/generators/graphql/templates/object.erb +4 -2
  41. data/lib/generators/graphql/templates/query_type.erb +2 -0
  42. data/lib/generators/graphql/templates/scalar.erb +3 -1
  43. data/lib/generators/graphql/templates/schema.erb +22 -2
  44. data/lib/generators/graphql/templates/union.erb +4 -2
  45. data/lib/generators/graphql/type_generator.rb +46 -10
  46. data/lib/generators/graphql/union_generator.rb +5 -5
  47. data/lib/graphql/analysis/analyzer.rb +89 -0
  48. data/lib/graphql/analysis/field_usage.rb +65 -28
  49. data/lib/graphql/analysis/max_query_complexity.rb +11 -17
  50. data/lib/graphql/analysis/max_query_depth.rb +13 -19
  51. data/lib/graphql/analysis/query_complexity.rb +156 -61
  52. data/lib/graphql/analysis/query_depth.rb +38 -23
  53. data/lib/graphql/analysis/visitor.rb +283 -0
  54. data/lib/graphql/analysis.rb +90 -6
  55. data/lib/graphql/autoload.rb +38 -0
  56. data/lib/graphql/backtrace/inspect_result.rb +0 -12
  57. data/lib/graphql/backtrace/table.rb +4 -22
  58. data/lib/graphql/backtrace/trace.rb +93 -0
  59. data/lib/graphql/backtrace/tracer.rb +8 -6
  60. data/lib/graphql/backtrace.rb +3 -8
  61. data/lib/graphql/coercion_error.rb +1 -9
  62. data/lib/graphql/current.rb +52 -0
  63. data/lib/graphql/dataloader/async_dataloader.rb +89 -0
  64. data/lib/graphql/dataloader/null_dataloader.rb +4 -2
  65. data/lib/graphql/dataloader/request.rb +5 -0
  66. data/lib/graphql/dataloader/source.rb +125 -33
  67. data/lib/graphql/dataloader.rb +193 -143
  68. data/lib/graphql/date_encoding_error.rb +16 -0
  69. data/lib/graphql/dig.rb +1 -1
  70. data/lib/graphql/duration_encoding_error.rb +16 -0
  71. data/lib/graphql/execution/errors.rb +12 -81
  72. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  73. data/lib/graphql/execution/interpreter/arguments.rb +2 -2
  74. data/lib/graphql/execution/interpreter/arguments_cache.rb +33 -36
  75. data/lib/graphql/execution/interpreter/resolve.rb +38 -4
  76. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +175 -0
  77. data/lib/graphql/execution/interpreter/runtime.rb +447 -403
  78. data/lib/graphql/execution/interpreter.rb +126 -80
  79. data/lib/graphql/execution/lazy.rb +11 -21
  80. data/lib/graphql/execution/lookahead.rb +133 -55
  81. data/lib/graphql/execution/multiplex.rb +4 -172
  82. data/lib/graphql/execution.rb +11 -4
  83. data/lib/graphql/integer_encoding_error.rb +18 -2
  84. data/lib/graphql/introspection/directive_location_enum.rb +2 -2
  85. data/lib/graphql/introspection/directive_type.rb +6 -4
  86. data/lib/graphql/introspection/dynamic_fields.rb +3 -8
  87. data/lib/graphql/introspection/entry_points.rb +11 -18
  88. data/lib/graphql/introspection/enum_value_type.rb +2 -2
  89. data/lib/graphql/introspection/field_type.rb +4 -4
  90. data/lib/graphql/introspection/input_value_type.rb +10 -4
  91. data/lib/graphql/introspection/schema_type.rb +17 -15
  92. data/lib/graphql/introspection/type_type.rb +29 -16
  93. data/lib/graphql/introspection.rb +6 -2
  94. data/lib/graphql/invalid_null_error.rb +1 -1
  95. data/lib/graphql/language/block_string.rb +37 -25
  96. data/lib/graphql/language/cache.rb +13 -0
  97. data/lib/graphql/language/comment.rb +18 -0
  98. data/lib/graphql/language/definition_slice.rb +1 -1
  99. data/lib/graphql/language/document_from_schema_definition.rb +122 -81
  100. data/lib/graphql/language/lexer.rb +364 -1467
  101. data/lib/graphql/language/nodes.rb +197 -106
  102. data/lib/graphql/language/parser.rb +799 -1920
  103. data/lib/graphql/language/printer.rb +372 -160
  104. data/lib/graphql/language/sanitized_printer.rb +25 -27
  105. data/lib/graphql/language/static_visitor.rb +167 -0
  106. data/lib/graphql/language/visitor.rb +188 -141
  107. data/lib/graphql/language.rb +62 -1
  108. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  109. data/lib/graphql/name_validator.rb +0 -4
  110. data/lib/graphql/pagination/active_record_relation_connection.rb +37 -8
  111. data/lib/graphql/pagination/array_connection.rb +8 -6
  112. data/lib/graphql/pagination/connection.rb +61 -7
  113. data/lib/graphql/pagination/connections.rb +22 -23
  114. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  115. data/lib/graphql/pagination/relation_connection.rb +60 -28
  116. data/lib/graphql/query/context/scoped_context.rb +101 -0
  117. data/lib/graphql/query/context.rb +146 -222
  118. data/lib/graphql/query/input_validation_result.rb +10 -1
  119. data/lib/graphql/query/null_context.rb +15 -32
  120. data/lib/graphql/query/validation_pipeline.rb +15 -39
  121. data/lib/graphql/query/variable_validation_error.rb +3 -3
  122. data/lib/graphql/query/variables.rb +35 -17
  123. data/lib/graphql/query.rb +149 -82
  124. data/lib/graphql/railtie.rb +15 -109
  125. data/lib/graphql/rake_task/validate.rb +1 -1
  126. data/lib/graphql/rake_task.rb +30 -11
  127. data/lib/graphql/relay/range_add.rb +9 -16
  128. data/lib/graphql/relay.rb +0 -15
  129. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  130. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  131. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  132. data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
  133. data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
  134. data/lib/graphql/rubocop.rb +6 -0
  135. data/lib/graphql/schema/addition.rb +98 -54
  136. data/lib/graphql/schema/always_visible.rb +14 -0
  137. data/lib/graphql/schema/argument.rb +179 -82
  138. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  139. data/lib/graphql/schema/build_from_definition.rb +77 -39
  140. data/lib/graphql/schema/directive/feature.rb +1 -1
  141. data/lib/graphql/schema/directive/flagged.rb +4 -4
  142. data/lib/graphql/schema/directive/include.rb +1 -1
  143. data/lib/graphql/schema/directive/one_of.rb +24 -0
  144. data/lib/graphql/schema/directive/skip.rb +1 -1
  145. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  146. data/lib/graphql/schema/directive/transform.rb +2 -2
  147. data/lib/graphql/schema/directive.rb +36 -22
  148. data/lib/graphql/schema/enum.rb +158 -63
  149. data/lib/graphql/schema/enum_value.rb +12 -21
  150. data/lib/graphql/schema/field/connection_extension.rb +7 -17
  151. data/lib/graphql/schema/field/scope_extension.rb +8 -1
  152. data/lib/graphql/schema/field.rb +521 -359
  153. data/lib/graphql/schema/field_extension.rb +86 -2
  154. data/lib/graphql/schema/find_inherited_value.rb +3 -7
  155. data/lib/graphql/schema/finder.rb +5 -5
  156. data/lib/graphql/schema/has_single_input_argument.rb +160 -0
  157. data/lib/graphql/schema/input_object.rb +148 -99
  158. data/lib/graphql/schema/interface.rb +41 -64
  159. data/lib/graphql/schema/introspection_system.rb +12 -26
  160. data/lib/graphql/schema/late_bound_type.rb +12 -2
  161. data/lib/graphql/schema/list.rb +18 -7
  162. data/lib/graphql/schema/loader.rb +6 -5
  163. data/lib/graphql/schema/member/base_dsl_methods.rb +32 -18
  164. data/lib/graphql/schema/member/build_type.rb +16 -13
  165. data/lib/graphql/schema/member/has_arguments.rb +270 -86
  166. data/lib/graphql/schema/member/has_ast_node.rb +12 -0
  167. data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
  168. data/lib/graphql/schema/member/has_directives.rb +81 -61
  169. data/lib/graphql/schema/member/has_fields.rb +169 -31
  170. data/lib/graphql/schema/member/has_interfaces.rb +143 -0
  171. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  172. data/lib/graphql/schema/member/has_validators.rb +32 -6
  173. data/lib/graphql/schema/member/relay_shortcuts.rb +47 -2
  174. data/lib/graphql/schema/member/scoped.rb +19 -0
  175. data/lib/graphql/schema/member/type_system_helpers.rb +16 -0
  176. data/lib/graphql/schema/member/validates_input.rb +6 -6
  177. data/lib/graphql/schema/member.rb +1 -6
  178. data/lib/graphql/schema/mutation.rb +7 -9
  179. data/lib/graphql/schema/non_null.rb +7 -7
  180. data/lib/graphql/schema/object.rb +38 -119
  181. data/lib/graphql/schema/printer.rb +24 -25
  182. data/lib/graphql/schema/relay_classic_mutation.rb +13 -91
  183. data/lib/graphql/schema/resolver/has_payload_type.rb +46 -11
  184. data/lib/graphql/schema/resolver.rb +118 -115
  185. data/lib/graphql/schema/scalar.rb +20 -21
  186. data/lib/graphql/schema/subscription.rb +95 -21
  187. data/lib/graphql/schema/timeout.rb +25 -29
  188. data/lib/graphql/schema/type_expression.rb +2 -2
  189. data/lib/graphql/schema/type_membership.rb +21 -4
  190. data/lib/graphql/schema/union.rb +16 -16
  191. data/lib/graphql/schema/unique_within_type.rb +1 -1
  192. data/lib/graphql/schema/validator/all_validator.rb +62 -0
  193. data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
  194. data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
  195. data/lib/graphql/schema/validator/exclusion_validator.rb +3 -1
  196. data/lib/graphql/schema/validator/format_validator.rb +4 -5
  197. data/lib/graphql/schema/validator/inclusion_validator.rb +3 -1
  198. data/lib/graphql/schema/validator/length_validator.rb +5 -3
  199. data/lib/graphql/schema/validator/numericality_validator.rb +13 -2
  200. data/lib/graphql/schema/validator/required_validator.rb +56 -18
  201. data/lib/graphql/schema/validator.rb +38 -28
  202. data/lib/graphql/schema/visibility/migration.rb +188 -0
  203. data/lib/graphql/schema/visibility/profile.rb +359 -0
  204. data/lib/graphql/schema/visibility/visit.rb +190 -0
  205. data/lib/graphql/schema/visibility.rb +294 -0
  206. data/lib/graphql/schema/warden.rb +423 -134
  207. data/lib/graphql/schema/wrapper.rb +0 -5
  208. data/lib/graphql/schema.rb +1015 -1057
  209. data/lib/graphql/static_validation/all_rules.rb +3 -1
  210. data/lib/graphql/static_validation/base_visitor.rb +15 -28
  211. data/lib/graphql/static_validation/definition_dependencies.rb +7 -2
  212. data/lib/graphql/static_validation/error.rb +3 -1
  213. data/lib/graphql/static_validation/literal_validator.rb +24 -7
  214. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  215. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  216. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +4 -3
  217. data/lib/graphql/static_validation/rules/directives_are_defined.rb +13 -7
  218. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +15 -13
  219. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +12 -2
  220. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +13 -5
  221. data/lib/graphql/static_validation/rules/fields_will_merge.rb +62 -35
  222. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
  223. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  224. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +12 -2
  225. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
  226. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  227. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  228. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  229. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +66 -0
  230. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -0
  231. data/lib/graphql/static_validation/rules/query_root_exists.rb +17 -0
  232. data/lib/graphql/static_validation/rules/query_root_exists_error.rb +26 -0
  233. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +7 -5
  234. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
  235. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
  236. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +14 -8
  237. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  238. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  239. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +14 -8
  240. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +11 -2
  241. data/lib/graphql/static_validation/validation_context.rb +32 -6
  242. data/lib/graphql/static_validation/validator.rb +11 -27
  243. data/lib/graphql/static_validation.rb +0 -3
  244. data/lib/graphql/string_encoding_error.rb +13 -3
  245. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +49 -11
  246. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  247. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +40 -1
  248. data/lib/graphql/subscriptions/event.rb +87 -38
  249. data/lib/graphql/subscriptions/serialize.rb +27 -3
  250. data/lib/graphql/subscriptions.rb +63 -49
  251. data/lib/graphql/testing/helpers.rb +155 -0
  252. data/lib/graphql/testing.rb +2 -0
  253. data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
  254. data/lib/graphql/tracing/active_support_notifications_tracing.rb +6 -20
  255. data/lib/graphql/tracing/appoptics_trace.rb +253 -0
  256. data/lib/graphql/tracing/appoptics_tracing.rb +4 -2
  257. data/lib/graphql/tracing/appsignal_trace.rb +79 -0
  258. data/lib/graphql/tracing/appsignal_tracing.rb +17 -0
  259. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  260. data/lib/graphql/tracing/data_dog_trace.rb +185 -0
  261. data/lib/graphql/tracing/data_dog_tracing.rb +27 -15
  262. data/lib/graphql/{execution/instrumentation.rb → tracing/legacy_hooks_trace.rb} +11 -28
  263. data/lib/graphql/tracing/legacy_trace.rb +12 -0
  264. data/lib/graphql/tracing/new_relic_trace.rb +77 -0
  265. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  266. data/lib/graphql/tracing/notifications_trace.rb +45 -0
  267. data/lib/graphql/tracing/notifications_tracing.rb +61 -0
  268. data/lib/graphql/tracing/null_trace.rb +9 -0
  269. data/lib/graphql/tracing/platform_trace.rb +118 -0
  270. data/lib/graphql/tracing/platform_tracing.rb +46 -49
  271. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +6 -2
  272. data/lib/graphql/tracing/prometheus_trace.rb +94 -0
  273. data/lib/graphql/tracing/prometheus_tracing.rb +5 -3
  274. data/lib/graphql/tracing/scout_trace.rb +74 -0
  275. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  276. data/lib/graphql/tracing/sentry_trace.rb +114 -0
  277. data/lib/graphql/tracing/statsd_trace.rb +58 -0
  278. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  279. data/lib/graphql/tracing/trace.rb +79 -0
  280. data/lib/graphql/tracing.rb +29 -52
  281. data/lib/graphql/type_kinds.rb +7 -4
  282. data/lib/graphql/types/big_int.rb +5 -1
  283. data/lib/graphql/types/int.rb +1 -1
  284. data/lib/graphql/types/iso_8601_date.rb +17 -6
  285. data/lib/graphql/types/iso_8601_date_time.rb +12 -1
  286. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  287. data/lib/graphql/types/relay/base_connection.rb +16 -6
  288. data/lib/graphql/types/relay/connection_behaviors.rb +92 -32
  289. data/lib/graphql/types/relay/edge_behaviors.rb +46 -7
  290. data/lib/graphql/types/relay/has_node_field.rb +2 -2
  291. data/lib/graphql/types/relay/has_nodes_field.rb +2 -2
  292. data/lib/graphql/types/relay/node_behaviors.rb +12 -2
  293. data/lib/graphql/types/relay/page_info_behaviors.rb +11 -2
  294. data/lib/graphql/types/relay.rb +0 -3
  295. data/lib/graphql/types/string.rb +2 -2
  296. data/lib/graphql/types.rb +18 -10
  297. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  298. data/lib/graphql/unauthorized_error.rb +1 -1
  299. data/lib/graphql/version.rb +1 -1
  300. data/lib/graphql.rb +82 -137
  301. data/readme.md +13 -6
  302. metadata +127 -186
  303. data/lib/graphql/analysis/analyze_query.rb +0 -98
  304. data/lib/graphql/analysis/ast/analyzer.rb +0 -84
  305. data/lib/graphql/analysis/ast/field_usage.rb +0 -28
  306. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -23
  307. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  308. data/lib/graphql/analysis/ast/query_complexity.rb +0 -234
  309. data/lib/graphql/analysis/ast/query_depth.rb +0 -56
  310. data/lib/graphql/analysis/ast/visitor.rb +0 -268
  311. data/lib/graphql/analysis/ast.rb +0 -91
  312. data/lib/graphql/analysis/reducer_state.rb +0 -48
  313. data/lib/graphql/argument.rb +0 -131
  314. data/lib/graphql/authorization.rb +0 -82
  315. data/lib/graphql/backtrace/legacy_tracer.rb +0 -56
  316. data/lib/graphql/backwards_compatibility.rb +0 -61
  317. data/lib/graphql/base_type.rb +0 -230
  318. data/lib/graphql/boolean_type.rb +0 -2
  319. data/lib/graphql/compatibility/execution_specification/counter_schema.rb +0 -53
  320. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +0 -200
  321. data/lib/graphql/compatibility/execution_specification.rb +0 -436
  322. data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +0 -111
  323. data/lib/graphql/compatibility/lazy_execution_specification.rb +0 -215
  324. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +0 -87
  325. data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +0 -79
  326. data/lib/graphql/compatibility/query_parser_specification.rb +0 -266
  327. data/lib/graphql/compatibility/schema_parser_specification.rb +0 -682
  328. data/lib/graphql/compatibility.rb +0 -5
  329. data/lib/graphql/define/assign_argument.rb +0 -12
  330. data/lib/graphql/define/assign_connection.rb +0 -13
  331. data/lib/graphql/define/assign_enum_value.rb +0 -18
  332. data/lib/graphql/define/assign_global_id_field.rb +0 -11
  333. data/lib/graphql/define/assign_mutation_function.rb +0 -34
  334. data/lib/graphql/define/assign_object_field.rb +0 -42
  335. data/lib/graphql/define/defined_object_proxy.rb +0 -53
  336. data/lib/graphql/define/instance_definable.rb +0 -240
  337. data/lib/graphql/define/no_definition_error.rb +0 -7
  338. data/lib/graphql/define/non_null_with_bang.rb +0 -16
  339. data/lib/graphql/define/type_definer.rb +0 -31
  340. data/lib/graphql/define.rb +0 -31
  341. data/lib/graphql/deprecated_dsl.rb +0 -47
  342. data/lib/graphql/deprecation.rb +0 -13
  343. data/lib/graphql/directive/deprecated_directive.rb +0 -2
  344. data/lib/graphql/directive/include_directive.rb +0 -2
  345. data/lib/graphql/directive/skip_directive.rb +0 -2
  346. data/lib/graphql/directive.rb +0 -111
  347. data/lib/graphql/enum_type.rb +0 -129
  348. data/lib/graphql/execution/execute.rb +0 -333
  349. data/lib/graphql/execution/flatten.rb +0 -40
  350. data/lib/graphql/execution/lazy/resolve.rb +0 -91
  351. data/lib/graphql/execution/typecast.rb +0 -50
  352. data/lib/graphql/field/resolve.rb +0 -59
  353. data/lib/graphql/field.rb +0 -226
  354. data/lib/graphql/filter.rb +0 -53
  355. data/lib/graphql/float_type.rb +0 -2
  356. data/lib/graphql/function.rb +0 -128
  357. data/lib/graphql/id_type.rb +0 -2
  358. data/lib/graphql/input_object_type.rb +0 -138
  359. data/lib/graphql/int_type.rb +0 -2
  360. data/lib/graphql/interface_type.rb +0 -72
  361. data/lib/graphql/internal_representation/document.rb +0 -27
  362. data/lib/graphql/internal_representation/node.rb +0 -206
  363. data/lib/graphql/internal_representation/print.rb +0 -51
  364. data/lib/graphql/internal_representation/rewrite.rb +0 -184
  365. data/lib/graphql/internal_representation/scope.rb +0 -88
  366. data/lib/graphql/internal_representation/visit.rb +0 -36
  367. data/lib/graphql/internal_representation.rb +0 -7
  368. data/lib/graphql/language/lexer.rl +0 -262
  369. data/lib/graphql/language/parser.y +0 -543
  370. data/lib/graphql/language/token.rb +0 -38
  371. data/lib/graphql/list_type.rb +0 -80
  372. data/lib/graphql/non_null_type.rb +0 -71
  373. data/lib/graphql/object_type.rb +0 -130
  374. data/lib/graphql/query/arguments.rb +0 -189
  375. data/lib/graphql/query/arguments_cache.rb +0 -24
  376. data/lib/graphql/query/executor.rb +0 -52
  377. data/lib/graphql/query/literal_input.rb +0 -136
  378. data/lib/graphql/query/serial_execution/field_resolution.rb +0 -92
  379. data/lib/graphql/query/serial_execution/operation_resolution.rb +0 -19
  380. data/lib/graphql/query/serial_execution/selection_resolution.rb +0 -23
  381. data/lib/graphql/query/serial_execution/value_resolution.rb +0 -87
  382. data/lib/graphql/query/serial_execution.rb +0 -40
  383. data/lib/graphql/relay/array_connection.rb +0 -83
  384. data/lib/graphql/relay/base_connection.rb +0 -189
  385. data/lib/graphql/relay/connection_instrumentation.rb +0 -54
  386. data/lib/graphql/relay/connection_resolve.rb +0 -43
  387. data/lib/graphql/relay/connection_type.rb +0 -41
  388. data/lib/graphql/relay/edge.rb +0 -27
  389. data/lib/graphql/relay/edge_type.rb +0 -19
  390. data/lib/graphql/relay/edges_instrumentation.rb +0 -40
  391. data/lib/graphql/relay/global_id_resolve.rb +0 -18
  392. data/lib/graphql/relay/mongo_relation_connection.rb +0 -50
  393. data/lib/graphql/relay/mutation/instrumentation.rb +0 -23
  394. data/lib/graphql/relay/mutation/resolve.rb +0 -56
  395. data/lib/graphql/relay/mutation/result.rb +0 -38
  396. data/lib/graphql/relay/mutation.rb +0 -106
  397. data/lib/graphql/relay/node.rb +0 -39
  398. data/lib/graphql/relay/page_info.rb +0 -7
  399. data/lib/graphql/relay/relation_connection.rb +0 -188
  400. data/lib/graphql/relay/type_extensions.rb +0 -32
  401. data/lib/graphql/scalar_type.rb +0 -91
  402. data/lib/graphql/schema/base_64_bp.rb +0 -26
  403. data/lib/graphql/schema/catchall_middleware.rb +0 -35
  404. data/lib/graphql/schema/default_parse_error.rb +0 -10
  405. data/lib/graphql/schema/default_type_error.rb +0 -17
  406. data/lib/graphql/schema/invalid_type_error.rb +0 -7
  407. data/lib/graphql/schema/member/accepts_definition.rb +0 -152
  408. data/lib/graphql/schema/member/cached_graphql_definition.rb +0 -31
  409. data/lib/graphql/schema/member/instrumentation.rb +0 -131
  410. data/lib/graphql/schema/middleware_chain.rb +0 -82
  411. data/lib/graphql/schema/possible_types.rb +0 -44
  412. data/lib/graphql/schema/rescue_middleware.rb +0 -60
  413. data/lib/graphql/schema/timeout_middleware.rb +0 -88
  414. data/lib/graphql/schema/traversal.rb +0 -228
  415. data/lib/graphql/schema/validation.rb +0 -313
  416. data/lib/graphql/static_validation/default_visitor.rb +0 -15
  417. data/lib/graphql/static_validation/no_validate_visitor.rb +0 -10
  418. data/lib/graphql/static_validation/type_stack.rb +0 -216
  419. data/lib/graphql/string_type.rb +0 -2
  420. data/lib/graphql/subscriptions/instrumentation.rb +0 -79
  421. data/lib/graphql/subscriptions/subscription_root.rb +0 -76
  422. data/lib/graphql/tracing/skylight_tracing.rb +0 -70
  423. data/lib/graphql/types/relay/default_relay.rb +0 -27
  424. data/lib/graphql/types/relay/node_field.rb +0 -25
  425. data/lib/graphql/types/relay/nodes_field.rb +0 -27
  426. data/lib/graphql/union_type.rb +0 -115
  427. data/lib/graphql/upgrader/member.rb +0 -937
  428. data/lib/graphql/upgrader/schema.rb +0 -38
@@ -1,24 +1,16 @@
1
1
  # frozen_string_literal: true
2
+ require "logger"
2
3
  require "graphql/schema/addition"
4
+ require "graphql/schema/always_visible"
3
5
  require "graphql/schema/base_64_encoder"
4
- require "graphql/schema/catchall_middleware"
5
- require "graphql/schema/default_parse_error"
6
- require "graphql/schema/default_type_error"
7
6
  require "graphql/schema/find_inherited_value"
8
7
  require "graphql/schema/finder"
9
- require "graphql/schema/invalid_type_error"
10
8
  require "graphql/schema/introspection_system"
11
9
  require "graphql/schema/late_bound_type"
12
- require "graphql/schema/middleware_chain"
13
10
  require "graphql/schema/null_mask"
14
- require "graphql/schema/possible_types"
15
- require "graphql/schema/rescue_middleware"
16
11
  require "graphql/schema/timeout"
17
- require "graphql/schema/timeout_middleware"
18
- require "graphql/schema/traversal"
19
12
  require "graphql/schema/type_expression"
20
13
  require "graphql/schema/unique_within_type"
21
- require "graphql/schema/validation"
22
14
  require "graphql/schema/warden"
23
15
  require "graphql/schema/build_from_definition"
24
16
 
@@ -40,16 +32,20 @@ require "graphql/schema/union"
40
32
  require "graphql/schema/directive"
41
33
  require "graphql/schema/directive/deprecated"
42
34
  require "graphql/schema/directive/include"
35
+ require "graphql/schema/directive/one_of"
43
36
  require "graphql/schema/directive/skip"
44
37
  require "graphql/schema/directive/feature"
45
38
  require "graphql/schema/directive/flagged"
46
39
  require "graphql/schema/directive/transform"
40
+ require "graphql/schema/directive/specified_by"
47
41
  require "graphql/schema/type_membership"
48
42
 
49
43
  require "graphql/schema/resolver"
50
44
  require "graphql/schema/mutation"
45
+ require "graphql/schema/has_single_input_argument"
51
46
  require "graphql/schema/relay_classic_mutation"
52
47
  require "graphql/schema/subscription"
48
+ require "graphql/schema/visibility"
53
49
 
54
50
  module GraphQL
55
51
  # A GraphQL schema which may be queried with {GraphQL::Query}.
@@ -67,10 +63,6 @@ module GraphQL
67
63
  # Schemas can restrict large incoming queries with `max_depth` and `max_complexity` configurations.
68
64
  # (These configurations can be overridden by specific calls to {Schema#execute})
69
65
  #
70
- # Schemas can specify how queries should be executed against them.
71
- # `query_execution_strategy`, `mutation_execution_strategy` and `subscription_execution_strategy`
72
- # each apply to corresponding root types.
73
- # #
74
66
  # @example defining a schema
75
67
  # class MySchema < GraphQL::Schema
76
68
  # query QueryType
@@ -79,16 +71,19 @@ module GraphQL
79
71
  # end
80
72
  #
81
73
  class Schema
82
- extend Forwardable
83
- extend GraphQL::Schema::Member::AcceptsDefinition
84
74
  extend GraphQL::Schema::Member::HasAstNode
85
- include GraphQL::Define::InstanceDefinable
86
- extend GraphQL::Define::InstanceDefinable::DeprecatedDefine
87
75
  extend GraphQL::Schema::FindInheritedValue
76
+ extend Autoload
88
77
 
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(".")}")
78
+ autoload :BUILT_IN_TYPES, "graphql/schema/built_in_types"
79
+
80
+ class DuplicateNamesError < GraphQL::Error
81
+ attr_reader :duplicated_name
82
+ def initialize(duplicated_name:, duplicated_definition_1:, duplicated_definition_2:)
83
+ @duplicated_name = duplicated_name
84
+ super(
85
+ "Found two visible definitions for `#{duplicated_name}`: #{duplicated_definition_1}, #{duplicated_definition_2}"
86
+ )
92
87
  end
93
88
  end
94
89
 
@@ -100,760 +95,165 @@ module GraphQL
100
95
  end
101
96
  end
102
97
 
103
- module LazyHandlingMethods
104
- # Call the given block at the right time, either:
105
- # - Right away, if `value` is not registered with `lazy_resolve`
106
- # - After resolving `value`, if it's registered with `lazy_resolve` (eg, `Promise`)
107
- # @api private
108
- def after_lazy(value, &block)
109
- if lazy?(value)
110
- GraphQL::Execution::Lazy.new do
111
- result = sync_lazy(value)
112
- # The returned result might also be lazy, so check it, too
113
- after_lazy(result, &block)
114
- end
98
+ # Error that is raised when [#Schema#from_definition] is passed an invalid schema definition string.
99
+ class InvalidDocumentError < Error; end;
100
+
101
+ class << self
102
+ # Create schema with the result of an introspection query.
103
+ # @param introspection_result [Hash] A response from {GraphQL::Introspection::INTROSPECTION_QUERY}
104
+ # @return [Class<GraphQL::Schema>] the schema described by `input`
105
+ def from_introspection(introspection_result)
106
+ GraphQL::Schema::Loader.load(introspection_result)
107
+ end
108
+
109
+ # Create schema from an IDL schema or file containing an IDL definition.
110
+ # @param definition_or_path [String] A schema definition string, or a path to a file containing the definition
111
+ # @param default_resolve [<#call(type, field, obj, args, ctx)>] A callable for handling field resolution
112
+ # @param parser [Object] An object for handling definition string parsing (must respond to `parse`)
113
+ # @param using [Hash] Plugins to attach to the created schema with `use(key, value)`
114
+ # @return [Class] the schema described by `document`
115
+ def from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.default_parser, using: {})
116
+ # If the file ends in `.graphql` or `.graphqls`, treat it like a filepath
117
+ if definition_or_path.end_with?(".graphql") || definition_or_path.end_with?(".graphqls")
118
+ GraphQL::Schema::BuildFromDefinition.from_definition_path(
119
+ self,
120
+ definition_or_path,
121
+ default_resolve: default_resolve,
122
+ parser: parser,
123
+ using: using,
124
+ )
115
125
  else
116
- yield(value) if block_given?
126
+ GraphQL::Schema::BuildFromDefinition.from_definition(
127
+ self,
128
+ definition_or_path,
129
+ default_resolve: default_resolve,
130
+ parser: parser,
131
+ using: using,
132
+ )
117
133
  end
118
134
  end
119
135
 
120
- # Override this method to handle lazy objects in a custom way.
121
- # @param value [Object] an instance of a class registered with {.lazy_resolve}
122
- # @return [Object] A GraphQL-ready (non-lazy) object
123
- # @api private
124
- def sync_lazy(value)
125
- lazy_method = lazy_method_name(value)
126
- if lazy_method
127
- synced_value = value.public_send(lazy_method)
128
- sync_lazy(synced_value)
129
- else
130
- value
131
- end
136
+ def deprecated_graphql_definition
137
+ graphql_definition(silence_deprecation_warning: true)
132
138
  end
133
139
 
134
- # @return [Symbol, nil] The method name to lazily resolve `obj`, or nil if `obj`'s class wasn't registered with {#lazy_resolve}.
135
- def lazy_method_name(obj)
136
- lazy_methods.get(obj)
140
+ # @return [GraphQL::Subscriptions]
141
+ def subscriptions(inherited: true)
142
+ defined?(@subscriptions) ? @subscriptions : (inherited ? find_inherited_value(:subscriptions, nil) : nil)
137
143
  end
138
144
 
139
- # @return [Boolean] True if this object should be lazily resolved
140
- def lazy?(obj)
141
- !!lazy_method_name(obj)
145
+ def subscriptions=(new_implementation)
146
+ @subscriptions = new_implementation
142
147
  end
143
148
 
144
- # Return a lazy if any of `maybe_lazies` are lazy,
145
- # otherwise, call the block eagerly and return the result.
146
- # @param maybe_lazies [Array]
147
- # @api private
148
- def after_any_lazies(maybe_lazies)
149
- if maybe_lazies.any? { |l| lazy?(l) }
150
- GraphQL::Execution::Lazy.all(maybe_lazies).then do |result|
151
- yield result
152
- end
149
+ # @param new_mode [Symbol] If configured, this will be used when `context: { trace_mode: ... }` isn't set.
150
+ def default_trace_mode(new_mode = nil)
151
+ if new_mode
152
+ @default_trace_mode = new_mode
153
+ elsif defined?(@default_trace_mode)
154
+ @default_trace_mode
155
+ elsif superclass.respond_to?(:default_trace_mode)
156
+ superclass.default_trace_mode
153
157
  else
154
- yield maybe_lazies
158
+ :default
155
159
  end
156
160
  end
157
- end
158
-
159
- include LazyHandlingMethods
160
- extend LazyHandlingMethods
161
-
162
- accepts_definitions \
163
- :query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
164
- :validate_timeout, :max_depth, :max_complexity, :default_max_page_size,
165
- :orphan_types, :resolve_type, :type_error, :parse_error,
166
- :error_bubbling,
167
- :raise_definition_error,
168
- :object_from_id, :id_from_object,
169
- :default_mask,
170
- :cursor_encoder,
171
- # If these are given as classes, normalize them. Accept `nil` when building from string.
172
- query: ->(schema, t) { schema.query = t.respond_to?(:graphql_definition) ? t.graphql_definition : t },
173
- mutation: ->(schema, t) { schema.mutation = t.respond_to?(:graphql_definition) ? t.graphql_definition : t },
174
- subscription: ->(schema, t) { schema.subscription = t.respond_to?(:graphql_definition) ? t.graphql_definition : t },
175
- disable_introspection_entry_points: ->(schema) { schema.disable_introspection_entry_points = true },
176
- disable_schema_introspection_entry_point: ->(schema) { schema.disable_schema_introspection_entry_point = true },
177
- disable_type_introspection_entry_point: ->(schema) { schema.disable_type_introspection_entry_point = true },
178
- directives: ->(schema, directives) { schema.directives = directives.reduce({}) { |m, d| m[d.graphql_name] = d; m } },
179
- directive: ->(schema, directive) { schema.directives[directive.graphql_name] = directive },
180
- instrument: ->(schema, type, instrumenter, after_built_ins: false) {
181
- if type == :field && after_built_ins
182
- type = :field_after_built_ins
183
- end
184
- schema.instrumenters[type] << instrumenter
185
- },
186
- query_analyzer: ->(schema, analyzer) {
187
- if analyzer == GraphQL::Authorization::Analyzer
188
- GraphQL::Deprecation.warn("The Authorization query analyzer is deprecated. Authorizing at query runtime is generally a better idea.")
189
- end
190
- schema.query_analyzers << analyzer
191
- },
192
- multiplex_analyzer: ->(schema, analyzer) { schema.multiplex_analyzers << analyzer },
193
- middleware: ->(schema, middleware) { schema.middleware << middleware },
194
- lazy_resolve: ->(schema, lazy_class, lazy_value_method) { schema.lazy_methods.set(lazy_class, lazy_value_method) },
195
- rescue_from: ->(schema, err_class, &block) { schema.rescue_from(err_class, &block) },
196
- tracer: ->(schema, tracer) { schema.tracers.push(tracer) }
197
-
198
- ensure_defined :introspection_system
199
-
200
- attr_accessor \
201
- :query, :mutation, :subscription,
202
- :query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
203
- :validate_timeout, :max_depth, :max_complexity, :default_max_page_size,
204
- :orphan_types, :directives,
205
- :query_analyzers, :multiplex_analyzers, :instrumenters, :lazy_methods,
206
- :cursor_encoder,
207
- :ast_node,
208
- :raise_definition_error,
209
- :introspection_namespace,
210
- :analysis_engine
211
-
212
- # [Boolean] True if this object bubbles validation errors up from a field into its parent InputObject, if there is one.
213
- attr_accessor :error_bubbling
214
-
215
- # Single, long-lived instance of the provided subscriptions class, if there is one.
216
- # @return [GraphQL::Subscriptions]
217
- attr_accessor :subscriptions
218
-
219
- # @return [MiddlewareChain] MiddlewareChain which is applied to fields during execution
220
- attr_accessor :middleware
221
-
222
- # @return [<#call(member, ctx)>] A callable for filtering members of the schema
223
- # @see {Query.new} for query-specific filters with `except:`
224
- attr_accessor :default_mask
225
-
226
- # @see {GraphQL::Query::Context} The parent class of these classes
227
- # @return [Class] Instantiated for each query
228
- attr_accessor :context_class
229
-
230
- # [Boolean] True if this object disables the introspection entry point fields
231
- attr_accessor :disable_introspection_entry_points
232
-
233
- def disable_introspection_entry_points?
234
- !!@disable_introspection_entry_points
235
- end
236
-
237
- # [Boolean] True if this object disables the __schema introspection entry point field
238
- attr_accessor :disable_schema_introspection_entry_point
239
-
240
- def disable_schema_introspection_entry_point?
241
- !!@disable_schema_introspection_entry_point
242
- end
243
-
244
- # [Boolean] True if this object disables the __type introspection entry point field
245
- attr_accessor :disable_type_introspection_entry_point
246
-
247
- def disable_type_introspection_entry_point?
248
- !!@disable_type_introspection_entry_point
249
- end
250
-
251
- class << self
252
- attr_writer :default_execution_strategy
253
- end
254
-
255
- def default_filter
256
- GraphQL::Filter.new(except: default_mask)
257
- end
258
-
259
- # @return [Array<#trace(key, data)>] Tracers applied to every query
260
- # @see {Query#tracers} for query-specific tracers
261
- attr_reader :tracers
262
-
263
- DYNAMIC_FIELDS = ["__type", "__typename", "__schema"].freeze
264
-
265
- attr_reader :static_validator, :object_from_id_proc, :id_from_object_proc, :resolve_type_proc
266
-
267
- def initialize
268
- @tracers = []
269
- @definition_error = nil
270
- @orphan_types = []
271
- @directives = {}
272
- self.class.default_directives.each do |name, dir|
273
- @directives[name] = dir.graphql_definition
274
- end
275
- @static_validator = GraphQL::StaticValidation::Validator.new(schema: self)
276
- @middleware = MiddlewareChain.new(final_step: GraphQL::Execution::Execute::FieldResolveStep)
277
- @query_analyzers = []
278
- @multiplex_analyzers = []
279
- @resolve_type_proc = nil
280
- @object_from_id_proc = nil
281
- @id_from_object_proc = nil
282
- @type_error_proc = DefaultTypeError
283
- @parse_error_proc = DefaultParseError
284
- @instrumenters = Hash.new { |h, k| h[k] = [] }
285
- @lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
286
- @lazy_methods.set(GraphQL::Execution::Lazy, :value)
287
- @cursor_encoder = Base64Encoder
288
- # For schema instances, default to legacy runtime modules
289
- @analysis_engine = GraphQL::Analysis
290
- @query_execution_strategy = GraphQL::Execution::Execute
291
- @mutation_execution_strategy = GraphQL::Execution::Execute
292
- @subscription_execution_strategy = GraphQL::Execution::Execute
293
- @default_mask = GraphQL::Schema::NullMask
294
- @rebuilding_artifacts = false
295
- @context_class = GraphQL::Query::Context
296
- @introspection_namespace = nil
297
- @introspection_system = nil
298
- @interpreter = false
299
- @error_bubbling = false
300
- @disable_introspection_entry_points = false
301
- @disable_schema_introspection_entry_point = false
302
- @disable_type_introspection_entry_point = false
303
- end
304
-
305
- # @return [Boolean] True if using the new {GraphQL::Execution::Interpreter}
306
- def interpreter?
307
- query_execution_strategy == GraphQL::Execution::Interpreter &&
308
- mutation_execution_strategy == GraphQL::Execution::Interpreter &&
309
- subscription_execution_strategy == GraphQL::Execution::Interpreter
310
- end
311
-
312
- def inspect
313
- "#<#{self.class.name} ...>"
314
- end
315
-
316
- def initialize_copy(other)
317
- super
318
- @orphan_types = other.orphan_types.dup
319
- @directives = other.directives.dup
320
- @static_validator = GraphQL::StaticValidation::Validator.new(schema: self)
321
- @middleware = other.middleware.dup
322
- @query_analyzers = other.query_analyzers.dup
323
- @multiplex_analyzers = other.multiplex_analyzers.dup
324
- @tracers = other.tracers.dup
325
- @possible_types = GraphQL::Schema::PossibleTypes.new(self)
326
-
327
- @lazy_methods = other.lazy_methods.dup
328
-
329
- @instrumenters = Hash.new { |h, k| h[k] = [] }
330
- other.instrumenters.each do |key, insts|
331
- @instrumenters[key].concat(insts)
332
- end
333
-
334
- if other.rescues?
335
- @rescue_middleware = other.rescue_middleware
336
- end
337
-
338
- # This will be rebuilt when it's requested
339
- # or during a later `define` call
340
- @types = nil
341
- @introspection_system = nil
342
- end
343
-
344
- def rescue_from(*args, &block)
345
- rescue_middleware.rescue_from(*args, &block)
346
- end
347
-
348
- def remove_handler(*args, &block)
349
- rescue_middleware.remove_handler(*args, &block)
350
- end
351
-
352
- def using_ast_analysis?
353
- @analysis_engine == GraphQL::Analysis::AST
354
- end
355
-
356
- # For forwards-compatibility with Schema classes
357
- alias :graphql_definition :itself
358
-
359
- def deprecated_define(**kwargs, &block)
360
- super
361
- ensure_defined
362
- # Assert that all necessary configs are present:
363
- validation_error = Validation.validate(self)
364
- validation_error && raise(GraphQL::RequiredImplementationMissingError, validation_error)
365
- rebuild_artifacts
366
-
367
- @definition_error = nil
368
- nil
369
- rescue StandardError => err
370
- if @raise_definition_error || err.is_a?(CyclicalDefinitionError) || err.is_a?(GraphQL::RequiredImplementationMissingError)
371
- raise
372
- else
373
- # Raise this error _later_ to avoid messing with Rails constant loading
374
- @definition_error = err
375
- end
376
- nil
377
- end
378
-
379
- # Attach `instrumenter` to this schema for instrumenting events of `instrumentation_type`.
380
- # @param instrumentation_type [Symbol]
381
- # @param instrumenter
382
- # @return [void]
383
- def instrument(instrumentation_type, instrumenter)
384
- @instrumenters[instrumentation_type] << instrumenter
385
- if instrumentation_type == :field
386
- rebuild_artifacts
387
- end
388
- end
389
-
390
- # @return [Array<GraphQL::BaseType>] The root types of this schema
391
- def root_types
392
- @root_types ||= begin
393
- rebuild_artifacts
394
- @root_types
395
- end
396
- end
397
-
398
- # @see [GraphQL::Schema::Warden] Restricted access to members of a schema
399
- # @return [GraphQL::Schema::TypeMap] `{ name => type }` pairs of types in this schema
400
- def types
401
- @types ||= begin
402
- rebuild_artifacts
403
- @types
404
- end
405
- end
406
161
 
407
- def get_type(type_name)
408
- @types[type_name]
409
- end
410
-
411
- # @api private
412
- def introspection_system
413
- @introspection_system ||= begin
414
- rebuild_artifacts
415
- @introspection_system
416
- end
417
- end
418
-
419
- # Returns a list of Arguments and Fields referencing a certain type
420
- # @param type_name [String]
421
- # @return [Hash]
422
- def references_to(type_name = nil)
423
- rebuild_artifacts unless defined?(@type_reference_map)
424
- if type_name
425
- @type_reference_map.fetch(type_name, [])
426
- else
427
- @type_reference_map
428
- end
429
- end
430
-
431
- # Returns a list of Union types in which a type is a member
432
- # @param type [GraphQL::ObjectType]
433
- # @return [Array<GraphQL::UnionType>] list of union types of which the type is a member
434
- def union_memberships(type)
435
- rebuild_artifacts unless defined?(@union_memberships)
436
- @union_memberships.fetch(type.name, [])
437
- end
438
-
439
- # Execute a query on itself. Raises an error if the schema definition is invalid.
440
- # @see {Query#initialize} for arguments.
441
- # @return [Hash] query result, ready to be serialized as JSON
442
- def execute(query_str = nil, **kwargs)
443
- if query_str
444
- kwargs[:query] = query_str
445
- end
446
- # Some of the query context _should_ be passed to the multiplex, too
447
- multiplex_context = if (ctx = kwargs[:context])
448
- {
449
- backtrace: ctx[:backtrace],
450
- tracers: ctx[:tracers],
451
- }
452
- else
453
- {}
162
+ def trace_class(new_class = nil)
163
+ if new_class
164
+ # If any modules were already added for `:default`,
165
+ # re-apply them here
166
+ mods = trace_modules_for(:default)
167
+ mods.each { |mod| new_class.include(mod) }
168
+ new_class.include(DefaultTraceClass)
169
+ trace_mode(:default, new_class)
170
+ backtrace_class = Class.new(new_class)
171
+ backtrace_class.include(GraphQL::Backtrace::Trace)
172
+ trace_mode(:default_backtrace, backtrace_class)
173
+ end
174
+ trace_class_for(:default, build: true)
454
175
  end
455
- # Since we're running one query, don't run a multiplex-level complexity analyzer
456
- all_results = multiplex([kwargs], max_complexity: nil, context: multiplex_context)
457
- all_results[0]
458
- end
459
176
 
460
- # Execute several queries on itself. Raises an error if the schema definition is invalid.
461
- # @example Run several queries at once
462
- # context = { ... }
463
- # queries = [
464
- # { query: params[:query_1], variables: params[:variables_1], context: context },
465
- # { query: params[:query_2], variables: params[:variables_2], context: context },
466
- # ]
467
- # results = MySchema.multiplex(queries)
468
- # render json: {
469
- # result_1: results[0],
470
- # result_2: results[1],
471
- # }
472
- #
473
- # @see {Query#initialize} for query keyword arguments
474
- # @see {Execution::Multiplex#run_queries} for multiplex keyword arguments
475
- # @param queries [Array<Hash>] Keyword arguments for each query
476
- # @param context [Hash] Multiplex-level context
477
- # @return [Array<Hash>] One result for each query in the input
478
- def multiplex(queries, **kwargs)
479
- with_definition_error_check {
480
- GraphQL::Execution::Multiplex.run_all(self, queries, **kwargs)
481
- }
482
- end
483
-
484
- # Search for a schema member using a string path
485
- # @example Finding a Field
486
- # Schema.find("Ensemble.musicians")
487
- #
488
- # @see {GraphQL::Schema::Finder} for more examples
489
- # @param path [String] A dot-separated path to the member
490
- # @raise [Schema::Finder::MemberNotFoundError] if path could not be found
491
- # @return [GraphQL::BaseType, GraphQL::Field, GraphQL::Argument, GraphQL::Directive] A GraphQL Schema Member
492
- def find(path)
493
- rebuild_artifacts unless defined?(@finder)
494
- @find_cache[path] ||= @finder.find(path)
495
- end
496
-
497
- # Resolve field named `field_name` for type `parent_type`.
498
- # Handles dynamic fields `__typename`, `__type` and `__schema`, too
499
- # @param parent_type [String, GraphQL::BaseType]
500
- # @param field_name [String]
501
- # @return [GraphQL::Field, nil] The field named `field_name` on `parent_type`
502
- # @see [GraphQL::Schema::Warden] Restricted access to members of a schema
503
- def get_field(parent_type, field_name)
504
- with_definition_error_check do
505
- parent_type_name = case parent_type
506
- when GraphQL::BaseType, Class, Module
507
- parent_type.graphql_name
508
- when String
509
- parent_type
510
- else
511
- raise "Unexpected parent_type: #{parent_type}"
512
- end
513
-
514
- defined_field = @instrumented_field_map[parent_type_name][field_name]
515
- if defined_field
516
- defined_field
517
- elsif parent_type == query && (entry_point_field = introspection_system.entry_point(name: field_name))
518
- entry_point_field
519
- elsif (dynamic_field = introspection_system.dynamic_field(name: field_name))
520
- dynamic_field
177
+ # @return [Class] Return the trace class to use for this mode, looking one up on the superclass if this Schema doesn't have one defined.
178
+ def trace_class_for(mode, build: false)
179
+ if (trace_class = own_trace_modes[mode])
180
+ trace_class
181
+ elsif superclass.respond_to?(:trace_class_for) && (trace_class = superclass.trace_class_for(mode, build: false))
182
+ trace_class
183
+ elsif build
184
+ own_trace_modes[mode] = build_trace_mode(mode)
521
185
  else
522
186
  nil
523
187
  end
524
188
  end
525
- end
526
-
527
- # Fields for this type, after instrumentation is applied
528
- # @return [Hash<String, GraphQL::Field>]
529
- def get_fields(type)
530
- @instrumented_field_map[type.graphql_name]
531
- end
532
-
533
- def type_from_ast(ast_node, context:)
534
- GraphQL::Schema::TypeExpression.build_type(self, ast_node)
535
- end
536
-
537
- # @see [GraphQL::Schema::Warden] Restricted access to members of a schema
538
- # @param type_defn [GraphQL::InterfaceType, GraphQL::UnionType] the type whose members you want to retrieve
539
- # @param context [GraphQL::Query::Context] The context for the current query
540
- # @return [Array<GraphQL::ObjectType>] types which belong to `type_defn` in this schema
541
- def possible_types(type_defn, context = GraphQL::Query::NullContext)
542
- if context == GraphQL::Query::NullContext
543
- @possible_types ||= GraphQL::Schema::PossibleTypes.new(self)
544
- @possible_types.possible_types(type_defn, context)
545
- else
546
- # Use the incoming context to cache this instance --
547
- # if it were cached on the schema, we'd have a memory leak
548
- # https://github.com/rmosolgo/graphql-ruby/issues/2878
549
- ns = context.namespace(:possible_types)
550
- per_query_possible_types = ns[:possible_types] ||= GraphQL::Schema::PossibleTypes.new(self)
551
- per_query_possible_types.possible_types(type_defn, context)
552
- end
553
- end
554
-
555
- # @see [GraphQL::Schema::Warden] Resticted access to root types
556
- # @return [GraphQL::ObjectType, nil]
557
- def root_type_for_operation(operation)
558
- case operation
559
- when "query"
560
- query
561
- when "mutation"
562
- mutation
563
- when "subscription"
564
- subscription
565
- else
566
- raise ArgumentError, "unknown operation type: #{operation}"
567
- end
568
- end
569
-
570
- def execution_strategy_for_operation(operation)
571
- case operation
572
- when "query"
573
- query_execution_strategy
574
- when "mutation"
575
- mutation_execution_strategy
576
- when "subscription"
577
- subscription_execution_strategy
578
- else
579
- raise ArgumentError, "unknown operation type: #{operation}"
580
- end
581
- end
582
-
583
- # Determine the GraphQL type for a given object.
584
- # This is required for unions and interfaces (including Relay's `Node` interface)
585
- # @see [GraphQL::Schema::Warden] Restricted access to members of a schema
586
- # @param type [GraphQL::UnionType, GraphQL:InterfaceType] the abstract type which is being resolved
587
- # @param object [Any] An application object which GraphQL is currently resolving on
588
- # @param ctx [GraphQL::Query::Context] The context for the current query
589
- # @return [GraphQL::ObjectType] The type for exposing `object` in GraphQL
590
- def resolve_type(type, object, ctx = :__undefined__)
591
- check_resolved_type(type, object, ctx) do |ok_type, ok_object, ok_ctx|
592
- if @resolve_type_proc.nil?
593
- raise(GraphQL::RequiredImplementationMissingError, "Can't determine GraphQL type for: #{ok_object.inspect}, define `resolve_type (type, obj, ctx) -> { ... }` inside `Schema.define`.")
594
- end
595
- @resolve_type_proc.call(ok_type, ok_object, ok_ctx)
596
- end
597
- end
598
-
599
- # This is a compatibility hack so that instance-level and class-level
600
- # methods can get correctness checks without calling one another
601
- # @api private
602
- def check_resolved_type(type, object, ctx = :__undefined__)
603
- if ctx == :__undefined__
604
- # Old method signature
605
- ctx = object
606
- object = type
607
- type = nil
608
- end
609
-
610
- if object.is_a?(GraphQL::Schema::Object)
611
- object = object.object
612
- end
613
189
 
614
- if type.respond_to?(:graphql_definition)
615
- type = type.graphql_definition
190
+ # Configure `trace_class` to be used whenever `context: { trace_mode: mode_name }` is requested.
191
+ # {default_trace_mode} is used when no `trace_mode: ...` is requested.
192
+ #
193
+ # When a `trace_class` is added this way, it will _not_ receive other modules added with `trace_with(...)`
194
+ # unless `trace_mode` is explicitly given. (This class will not receive any default trace modules.)
195
+ #
196
+ # Subclasses of the schema will use `trace_class` as a base class for this mode and those
197
+ # subclass also will _not_ receive default tracing modules.
198
+ #
199
+ # @param mode_name [Symbol]
200
+ # @param trace_class [Class] subclass of GraphQL::Tracing::Trace
201
+ # @return void
202
+ def trace_mode(mode_name, trace_class)
203
+ own_trace_modes[mode_name] = trace_class
204
+ nil
616
205
  end
617
206
 
618
- # Prefer a type-local function; fall back to the schema-level function
619
- type_proc = type && type.resolve_type_proc
620
- type_result = if type_proc
621
- type_proc.call(object, ctx)
622
- else
623
- yield(type, object, ctx)
207
+ def own_trace_modes
208
+ @own_trace_modes ||= {}
624
209
  end
625
210
 
626
- if type_result.nil?
627
- nil
628
- else
629
- after_lazy(type_result) do |resolved_type_result|
630
- if resolved_type_result.respond_to?(:graphql_definition)
631
- resolved_type_result = resolved_type_result.graphql_definition
211
+ def build_trace_mode(mode)
212
+ case mode
213
+ when :default
214
+ # Use the superclass's default mode if it has one, or else start an inheritance chain at the built-in base class.
215
+ base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode, build: true)) || GraphQL::Tracing::Trace
216
+ const_set(:DefaultTrace, Class.new(base_class) do
217
+ include DefaultTraceClass
218
+ end)
219
+ when :default_backtrace
220
+ schema_base_class = trace_class_for(:default, build: true)
221
+ const_set(:DefaultTraceBacktrace, Class.new(schema_base_class) do
222
+ include(GraphQL::Backtrace::Trace)
223
+ end)
224
+ else
225
+ # First, see if the superclass has a custom-defined class for this.
226
+ # Then, if it doesn't, use this class's default trace
227
+ base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode)) || trace_class_for(:default, build: true)
228
+ # Prepare the default trace class if it hasn't been initialized yet
229
+ base_class ||= (own_trace_modes[:default] = build_trace_mode(:default))
230
+ mods = trace_modules_for(mode)
231
+ if base_class < DefaultTraceClass
232
+ mods = trace_modules_for(:default) + mods
632
233
  end
633
- if !resolved_type_result.is_a?(GraphQL::BaseType)
634
- type_str = "#{resolved_type_result} (#{resolved_type_result.class.name})"
635
- raise "resolve_type(#{object}) returned #{type_str}, but it should return a GraphQL type"
636
- else
637
- resolved_type_result
234
+ # Copy the existing default options into this mode's options
235
+ default_options = trace_options_for(:default)
236
+ add_trace_options_for(mode, default_options)
237
+
238
+ Class.new(base_class) do
239
+ !mods.empty? && include(*mods)
638
240
  end
639
241
  end
640
242
  end
641
- end
642
-
643
- def resolve_type=(new_resolve_type_proc)
644
- callable = GraphQL::BackwardsCompatibility.wrap_arity(new_resolve_type_proc, from: 2, to: 3, last: true, name: "Schema#resolve_type(type, obj, ctx)")
645
- @resolve_type_proc = callable
646
- end
647
-
648
- # Fetch an application object by its unique id
649
- # @param id [String] A unique identifier, provided previously by this GraphQL schema
650
- # @param ctx [GraphQL::Query::Context] The context for the current query
651
- # @return [Any] The application object identified by `id`
652
- def object_from_id(id, ctx)
653
- if @object_from_id_proc.nil?
654
- raise(GraphQL::RequiredImplementationMissingError, "Can't fetch an object for id \"#{id}\" because the schema's `object_from_id (id, ctx) -> { ... }` function is not defined")
655
- else
656
- @object_from_id_proc.call(id, ctx)
657
- end
658
- end
659
-
660
- # @param new_proc [#call] A new callable for fetching objects by ID
661
- def object_from_id=(new_proc)
662
- @object_from_id_proc = new_proc
663
- end
664
-
665
- # When we encounter a type error during query execution, we call this hook.
666
- #
667
- # You can use this hook to write a log entry,
668
- # add a {GraphQL::ExecutionError} to the response (with `ctx.add_error`)
669
- # or raise an exception and halt query execution.
670
- #
671
- # @example A `nil` is encountered by a non-null field
672
- # type_error ->(err, query_ctx) {
673
- # err.is_a?(GraphQL::InvalidNullError) # => true
674
- # }
675
- #
676
- # @example An object doesn't resolve to one of a {UnionType}'s members
677
- # type_error ->(err, query_ctx) {
678
- # err.is_a?(GraphQL::UnresolvedTypeError) # => true
679
- # }
680
- #
681
- # @see {DefaultTypeError} is the default behavior.
682
- # @param err [GraphQL::TypeError] The error encountered during execution
683
- # @param ctx [GraphQL::Query::Context] The context for the field where the error occurred
684
- # @return void
685
- def type_error(err, ctx)
686
- @type_error_proc.call(err, ctx)
687
- end
688
-
689
- # @param new_proc [#call] A new callable for handling type errors during execution
690
- def type_error=(new_proc)
691
- @type_error_proc = new_proc
692
- end
693
-
694
- # Can't delegate to `class`
695
- alias :_schema_class :class
696
- def_delegators :_schema_class, :unauthorized_object, :unauthorized_field, :inaccessible_fields
697
- def_delegators :_schema_class, :directive
698
- def_delegators :_schema_class, :error_handler
699
- def_delegators :_schema_class, :validate
700
-
701
-
702
- # Given this schema member, find the class-based definition object
703
- # whose `method_name` should be treated as an application hook
704
- # @see {.visible?}
705
- # @see {.accessible?}
706
- def call_on_type_class(member, method_name, context, default:)
707
- member = if member.respond_to?(:type_class)
708
- member.type_class
709
- else
710
- member
711
- end
712
-
713
- if member.respond_to?(:relay_node_type) && (t = member.relay_node_type)
714
- member = t
715
- end
716
243
 
717
- if member.respond_to?(method_name)
718
- member.public_send(method_name, context)
719
- else
720
- default
244
+ def own_trace_modules
245
+ @own_trace_modules ||= Hash.new { |h, k| h[k] = [] }
721
246
  end
722
- end
723
247
 
724
- def visible?(member, context)
725
- call_on_type_class(member, :visible?, context, default: true)
726
- end
727
-
728
- def accessible?(member, context)
729
- call_on_type_class(member, :accessible?, context, default: true)
730
- end
731
-
732
- # A function to call when {#execute} receives an invalid query string
733
- #
734
- # @see {DefaultParseError} is the default behavior.
735
- # @param err [GraphQL::ParseError] The error encountered during parsing
736
- # @param ctx [GraphQL::Query::Context] The context for the query where the error occurred
737
- # @return void
738
- def parse_error(err, ctx)
739
- @parse_error_proc.call(err, ctx)
740
- end
741
-
742
- # @param new_proc [#call] A new callable for handling parse errors during execution
743
- def parse_error=(new_proc)
744
- @parse_error_proc = new_proc
745
- end
746
-
747
- # Get a unique identifier from this object
748
- # @param object [Any] An application object
749
- # @param type [GraphQL::BaseType] The current type definition
750
- # @param ctx [GraphQL::Query::Context] the context for the current query
751
- # @return [String] a unique identifier for `object` which clients can use to refetch it
752
- def id_from_object(object, type, ctx)
753
- if @id_from_object_proc.nil?
754
- raise(GraphQL::RequiredImplementationMissingError, "Can't generate an ID for #{object.inspect} of type #{type}, schema's `id_from_object` must be defined")
755
- else
756
- @id_from_object_proc.call(object, type, ctx)
757
- end
758
- end
759
-
760
- # @param new_proc [#call] A new callable for generating unique IDs
761
- def id_from_object=(new_proc)
762
- @id_from_object_proc = new_proc
763
- end
764
-
765
- # Create schema with the result of an introspection query.
766
- # @param introspection_result [Hash] A response from {GraphQL::Introspection::INTROSPECTION_QUERY}
767
- # @return [GraphQL::Schema] the schema described by `input`
768
- def self.from_introspection(introspection_result)
769
- GraphQL::Schema::Loader.load(introspection_result)
770
- end
771
-
772
- # Create schema from an IDL schema or file containing an IDL definition.
773
- # @param definition_or_path [String] A schema definition string, or a path to a file containing the definition
774
- # @param default_resolve [<#call(type, field, obj, args, ctx)>] A callable for handling field resolution
775
- # @param parser [Object] An object for handling definition string parsing (must respond to `parse`)
776
- # @param using [Hash] Plugins to attach to the created schema with `use(key, value)`
777
- # @return [Class] the schema described by `document`
778
- def self.from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.default_parser, using: {})
779
- # If the file ends in `.graphql`, treat it like a filepath
780
- if definition_or_path.end_with?(".graphql")
781
- GraphQL::Schema::BuildFromDefinition.from_definition_path(
782
- definition_or_path,
783
- default_resolve: default_resolve,
784
- parser: parser,
785
- using: using,
786
- )
787
- else
788
- GraphQL::Schema::BuildFromDefinition.from_definition(
789
- definition_or_path,
790
- default_resolve: default_resolve,
791
- parser: parser,
792
- using: using,
793
- )
248
+ # @return [Array<Module>] Modules added for tracing in `trace_mode`, including inherited ones
249
+ def trace_modules_for(trace_mode)
250
+ modules = own_trace_modules[trace_mode]
251
+ if superclass.respond_to?(:trace_modules_for)
252
+ modules += superclass.trace_modules_for(trace_mode)
253
+ end
254
+ modules
794
255
  end
795
- end
796
256
 
797
- # Error that is raised when [#Schema#from_definition] is passed an invalid schema definition string.
798
- class InvalidDocumentError < Error; end;
799
-
800
- # Return the GraphQL IDL for the schema
801
- # @param context [Hash]
802
- # @param only [<#call(member, ctx)>]
803
- # @param except [<#call(member, ctx)>]
804
- # @return [String]
805
- def to_definition(only: nil, except: nil, context: {})
806
- GraphQL::Schema::Printer.print_schema(self, only: only, except: except, context: context)
807
- end
808
-
809
- # Return the GraphQL::Language::Document IDL AST for the schema
810
- # @param context [Hash]
811
- # @param only [<#call(member, ctx)>]
812
- # @param except [<#call(member, ctx)>]
813
- # @return [GraphQL::Language::Document]
814
- def to_document(only: nil, except: nil, context: {})
815
- GraphQL::Language::DocumentFromSchemaDefinition.new(self, only: only, except: except, context: context).document
816
- end
817
-
818
- # Return the Hash response of {Introspection::INTROSPECTION_QUERY}.
819
- # @param context [Hash]
820
- # @param only [<#call(member, ctx)>]
821
- # @param except [<#call(member, ctx)>]
822
- # @return [Hash] GraphQL result
823
- def as_json(only: nil, except: nil, context: {})
824
- execute(Introspection.query(include_deprecated_args: true), only: only, except: except, context: context).to_h
825
- end
826
-
827
- # Returns the JSON response of {Introspection::INTROSPECTION_QUERY}.
828
- # @see {#as_json}
829
- # @return [String]
830
- def to_json(*args)
831
- JSON.pretty_generate(as_json(*args))
832
- end
833
-
834
- def new_connections?
835
- !!connections
836
- end
837
-
838
- attr_accessor :connections
839
-
840
- class << self
841
- extend Forwardable
842
- # For compatibility, these methods all:
843
- # - Cause the Schema instance to be created, if it hasn't been created yet
844
- # - Delegate to that instance
845
- # Eventually, the methods will be moved into this class, removing the need for the singleton.
846
- def_delegators :graphql_definition,
847
- # Execution
848
- :execution_strategy_for_operation,
849
- # Configuration
850
- :metadata, :redefine,
851
- :id_from_object_proc, :object_from_id_proc,
852
- :id_from_object=, :object_from_id=,
853
- :remove_handler
854
-
855
- # @return [GraphQL::Subscriptions]
856
- attr_accessor :subscriptions
857
257
 
858
258
  # Returns the JSON response of {Introspection::INTROSPECTION_QUERY}.
859
259
  # @see {#as_json}
@@ -866,18 +266,29 @@ module GraphQL
866
266
  # @param context [Hash]
867
267
  # @param only [<#call(member, ctx)>]
868
268
  # @param except [<#call(member, ctx)>]
269
+ # @param include_deprecated_args [Boolean] If true, deprecated arguments will be included in the JSON response
270
+ # @param include_schema_description [Boolean] If true, the schema's description will be queried and included in the response
271
+ # @param include_is_repeatable [Boolean] If true, `isRepeatable: true|false` will be included with the schema's directives
272
+ # @param include_specified_by_url [Boolean] If true, scalar types' `specifiedByUrl:` will be included in the response
273
+ # @param include_is_one_of [Boolean] If true, `isOneOf: true|false` will be included with input objects
869
274
  # @return [Hash] GraphQL result
870
- def as_json(only: nil, except: nil, context: {})
871
- execute(Introspection.query(include_deprecated_args: true), only: only, except: except, context: context).to_h
275
+ def as_json(context: {}, include_deprecated_args: true, include_schema_description: false, include_is_repeatable: false, include_specified_by_url: false, include_is_one_of: false)
276
+ introspection_query = Introspection.query(
277
+ include_deprecated_args: include_deprecated_args,
278
+ include_schema_description: include_schema_description,
279
+ include_is_repeatable: include_is_repeatable,
280
+ include_is_one_of: include_is_one_of,
281
+ include_specified_by_url: include_specified_by_url,
282
+ )
283
+
284
+ execute(introspection_query, context: context).to_h
872
285
  end
873
286
 
874
287
  # Return the GraphQL IDL for the schema
875
288
  # @param context [Hash]
876
- # @param only [<#call(member, ctx)>]
877
- # @param except [<#call(member, ctx)>]
878
289
  # @return [String]
879
- def to_definition(only: nil, except: nil, context: {})
880
- GraphQL::Schema::Printer.print_schema(self, only: only, except: except, context: context)
290
+ def to_definition(context: {})
291
+ GraphQL::Schema::Printer.print_schema(self, context: context)
881
292
  end
882
293
 
883
294
  # Return the GraphQL::Language::Document IDL AST for the schema
@@ -886,6 +297,17 @@ module GraphQL
886
297
  GraphQL::Language::DocumentFromSchemaDefinition.new(self).document
887
298
  end
888
299
 
300
+ # @return [String, nil]
301
+ def description(new_description = nil)
302
+ if new_description
303
+ @description = new_description
304
+ elsif defined?(@description)
305
+ @description
306
+ else
307
+ find_inherited_value(:description, nil)
308
+ end
309
+ end
310
+
889
311
  def find(path)
890
312
  if !@finder
891
313
  @find_cache = {}
@@ -894,28 +316,15 @@ module GraphQL
894
316
  @find_cache[path] ||= @finder.find(path)
895
317
  end
896
318
 
897
- def graphql_definition
898
- @graphql_definition ||= to_graphql
899
- end
900
-
901
- def default_filter
902
- GraphQL::Filter.new(except: default_mask)
903
- end
904
-
905
- def default_mask(new_mask = nil)
906
- if new_mask
907
- @own_default_mask = new_mask
908
- else
909
- @own_default_mask || find_inherited_value(:default_mask, Schema::NullMask)
910
- end
911
- end
912
-
913
319
  def static_validator
914
320
  GraphQL::StaticValidation::Validator.new(schema: self)
915
321
  end
916
322
 
323
+ # Add `plugin` to this schema
324
+ # @param plugin [#use] A Schema plugin
325
+ # @return void
917
326
  def use(plugin, **kwargs)
918
- if kwargs.any?
327
+ if !kwargs.empty?
919
328
  plugin.use(self, **kwargs)
920
329
  else
921
330
  plugin.use(self)
@@ -927,84 +336,83 @@ module GraphQL
927
336
  find_inherited_value(:plugins, EMPTY_ARRAY) + own_plugins
928
337
  end
929
338
 
930
- def to_graphql
931
- schema_defn = self.new
932
- schema_defn.raise_definition_error = true
933
- schema_defn.query = query && query.graphql_definition
934
- schema_defn.mutation = mutation && mutation.graphql_definition
935
- schema_defn.subscription = subscription && subscription.graphql_definition
936
- schema_defn.validate_timeout = validate_timeout
937
- schema_defn.max_complexity = max_complexity
938
- schema_defn.error_bubbling = error_bubbling
939
- schema_defn.max_depth = max_depth
940
- schema_defn.default_max_page_size = default_max_page_size
941
- schema_defn.orphan_types = orphan_types.map(&:graphql_definition)
942
- schema_defn.disable_introspection_entry_points = disable_introspection_entry_points?
943
- schema_defn.disable_schema_introspection_entry_point = disable_schema_introspection_entry_point?
944
- schema_defn.disable_type_introspection_entry_point = disable_type_introspection_entry_point?
945
-
946
- prepped_dirs = {}
947
- directives.each { |k, v| prepped_dirs[k] = v.graphql_definition}
948
- schema_defn.directives = prepped_dirs
949
- schema_defn.introspection_namespace = introspection
950
- schema_defn.resolve_type = method(:resolve_type)
951
- schema_defn.object_from_id = method(:object_from_id)
952
- schema_defn.id_from_object = method(:id_from_object)
953
- schema_defn.type_error = method(:type_error)
954
- schema_defn.context_class = context_class
955
- schema_defn.cursor_encoder = cursor_encoder
956
- schema_defn.tracers.concat(tracers)
957
- schema_defn.query_analyzers.concat(query_analyzers)
958
- schema_defn.analysis_engine = analysis_engine
959
-
960
- schema_defn.middleware.concat(all_middleware)
961
- schema_defn.multiplex_analyzers.concat(multiplex_analyzers)
962
- schema_defn.query_execution_strategy = query_execution_strategy
963
- schema_defn.mutation_execution_strategy = mutation_execution_strategy
964
- schema_defn.subscription_execution_strategy = subscription_execution_strategy
965
- schema_defn.default_mask = default_mask
966
- instrumenters.each do |step, insts|
967
- insts.each do |inst|
968
- schema_defn.instrumenters[step] << inst
969
- end
970
- end
971
-
972
- lazy_methods.each do |lazy_class, value_method|
973
- schema_defn.lazy_methods.set(lazy_class, value_method)
339
+ # Build a map of `{ name => type }` and return it
340
+ # @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
341
+ # @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
342
+ def types(context = GraphQL::Query::NullContext.instance)
343
+ if use_visibility_profile?
344
+ types = Visibility::Profile.from_context(context, self)
345
+ return types.all_types_h
974
346
  end
975
-
976
- error_handler.each_rescue do |err_class, handler|
977
- schema_defn.rescue_from(err_class, &handler)
347
+ all_types = non_introspection_types.merge(introspection_system.types)
348
+ visible_types = {}
349
+ all_types.each do |k, v|
350
+ visible_types[k] =if v.is_a?(Array)
351
+ visible_t = nil
352
+ v.each do |t|
353
+ if t.visible?(context)
354
+ if visible_t.nil?
355
+ visible_t = t
356
+ else
357
+ raise DuplicateNamesError.new(
358
+ duplicated_name: k, duplicated_definition_1: visible_t.inspect, duplicated_definition_2: t.inspect
359
+ )
360
+ end
361
+ end
362
+ end
363
+ visible_t
364
+ else
365
+ v
366
+ end
978
367
  end
368
+ visible_types
369
+ end
979
370
 
980
- schema_defn.subscriptions ||= self.subscriptions
981
-
982
- if !schema_defn.interpreter?
983
- schema_defn.instrumenters[:query] << GraphQL::Schema::Member::Instrumentation
371
+ # @param type_name [String]
372
+ # @param context [GraphQL::Query::Context] Used for filtering definitions at query-time
373
+ # @param use_visibility_profile Private, for migration to {Schema::Visibility}
374
+ # @return [Module, nil] A type, or nil if there's no type called `type_name`
375
+ def get_type(type_name, context = GraphQL::Query::NullContext.instance, use_visibility_profile = use_visibility_profile?)
376
+ if use_visibility_profile
377
+ return Visibility::Profile.from_context(context, self).type(type_name)
984
378
  end
985
-
986
- if new_connections?
987
- schema_defn.connections = self.connections
379
+ local_entry = own_types[type_name]
380
+ type_defn = case local_entry
381
+ when nil
382
+ nil
383
+ when Array
384
+ if context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile)
385
+ local_entry
386
+ else
387
+ visible_t = nil
388
+ warden = Warden.from_context(context)
389
+ local_entry.each do |t|
390
+ if warden.visible_type?(t, context)
391
+ if visible_t.nil?
392
+ visible_t = t
393
+ else
394
+ raise DuplicateNamesError.new(
395
+ duplicated_name: type_name, duplicated_definition_1: visible_t.inspect, duplicated_definition_2: t.inspect
396
+ )
397
+ end
398
+ end
399
+ end
400
+ visible_t
401
+ end
402
+ when Module
403
+ local_entry
404
+ else
405
+ raise "Invariant: unexpected own_types[#{type_name.inspect}]: #{local_entry.inspect}"
988
406
  end
989
407
 
990
- schema_defn.send(:rebuild_artifacts)
991
-
992
- schema_defn
408
+ type_defn ||
409
+ introspection_system.types[type_name] || # todo context-specific introspection?
410
+ (superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context, use_visibility_profile) : nil)
993
411
  end
994
412
 
995
- # Build a map of `{ name => type }` and return it
996
- # @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
997
- # @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
998
- def types
999
- non_introspection_types.merge(introspection_system.types)
1000
- end
1001
-
1002
- # @param type_name [String]
1003
- # @return [Module, nil] A type, or nil if there's no type called `type_name`
1004
- def get_type(type_name)
1005
- own_types[type_name] ||
1006
- introspection_system.types[type_name] ||
1007
- find_inherited_value(:types, EMPTY_HASH)[type_name]
413
+ # @return [Boolean] Does this schema have _any_ definition for a type named `type_name`, regardless of visibility?
414
+ def has_defined_type?(type_name)
415
+ own_types.key?(type_name) || introspection_system.types.key?(type_name) || (superclass.respond_to?(:has_defined_type?) ? superclass.has_defined_type?(type_name) : false)
1008
416
  end
1009
417
 
1010
418
  # @api private
@@ -1026,55 +434,127 @@ module GraphQL
1026
434
  end
1027
435
  end
1028
436
 
1029
- def new_connections?
1030
- !!connections
1031
- end
1032
-
1033
- def query(new_query_object = nil)
1034
- if new_query_object
437
+ # Get or set the root `query { ... }` object for this schema.
438
+ #
439
+ # @example Using `Types::Query` as the entry-point
440
+ # query { Types::Query }
441
+ #
442
+ # @param new_query_object [Class<GraphQL::Schema::Object>] The root type to use for queries
443
+ # @param lazy_load_block If a block is given, then it will be called when GraphQL-Ruby needs the root query type.
444
+ # @return [Class<GraphQL::Schema::Object>, nil] The configured query root type, if there is one.
445
+ def query(new_query_object = nil, &lazy_load_block)
446
+ if new_query_object || block_given?
1035
447
  if @query_object
1036
- raise GraphQL::Error, "Second definition of `query(...)` (#{new_query_object.inspect}) is invalid, already configured with #{@query_object.inspect}"
448
+ dup_defn = new_query_object || yield
449
+ raise GraphQL::Error, "Second definition of `query(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@query_object.inspect}"
450
+ elsif use_visibility_profile?
451
+ if block_given?
452
+ if visibility.preload?
453
+ @query_object = lazy_load_block.call
454
+ self.visibility.query_configured(@query_object)
455
+ else
456
+ @query_object = lazy_load_block
457
+ end
458
+ else
459
+ @query_object = new_query_object
460
+ self.visibility.query_configured(@query_object)
461
+ end
1037
462
  else
1038
- @query_object = new_query_object
1039
- add_type_and_traverse(new_query_object, root: true)
1040
- nil
463
+ @query_object = new_query_object || lazy_load_block.call
464
+ add_type_and_traverse(@query_object, root: true)
1041
465
  end
466
+ nil
467
+ elsif @query_object.is_a?(Proc)
468
+ @query_object = @query_object.call
469
+ self.visibility&.query_configured(@query_object)
470
+ @query_object
1042
471
  else
1043
472
  @query_object || find_inherited_value(:query)
1044
473
  end
1045
474
  end
1046
475
 
1047
- def mutation(new_mutation_object = nil)
1048
- if new_mutation_object
476
+ # Get or set the root `mutation { ... }` object for this schema.
477
+ #
478
+ # @example Using `Types::Mutation` as the entry-point
479
+ # mutation { Types::Mutation }
480
+ #
481
+ # @param new_mutation_object [Class<GraphQL::Schema::Object>] The root type to use for mutations
482
+ # @param lazy_load_block If a block is given, then it will be called when GraphQL-Ruby needs the root mutation type.
483
+ # @return [Class<GraphQL::Schema::Object>, nil] The configured mutation root type, if there is one.
484
+ def mutation(new_mutation_object = nil, &lazy_load_block)
485
+ if new_mutation_object || block_given?
1049
486
  if @mutation_object
1050
- raise GraphQL::Error, "Second definition of `mutation(...)` (#{new_mutation_object.inspect}) is invalid, already configured with #{@mutation_object.inspect}"
487
+ dup_defn = new_mutation_object || yield
488
+ raise GraphQL::Error, "Second definition of `mutation(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@mutation_object.inspect}"
489
+ elsif use_visibility_profile?
490
+ if block_given?
491
+ if visibility.preload?
492
+ @mutation_object = lazy_load_block.call
493
+ self.visibility.mutation_configured(@mutation_object)
494
+ else
495
+ @mutation_object = lazy_load_block
496
+ end
497
+ else
498
+ @mutation_object = new_mutation_object
499
+ self.visibility.mutation_configured(@mutation_object)
500
+ end
1051
501
  else
1052
- @mutation_object = new_mutation_object
1053
- add_type_and_traverse(new_mutation_object, root: true)
1054
- nil
502
+ @mutation_object = new_mutation_object || lazy_load_block.call
503
+ add_type_and_traverse(@mutation_object, root: true)
1055
504
  end
505
+ nil
506
+ elsif @mutation_object.is_a?(Proc)
507
+ @mutation_object = @mutation_object.call
508
+ self.visibility&.mutation_configured(@mutation_object)
509
+ @mutation_object
1056
510
  else
1057
511
  @mutation_object || find_inherited_value(:mutation)
1058
512
  end
1059
513
  end
1060
514
 
1061
- def subscription(new_subscription_object = nil)
1062
- if new_subscription_object
515
+ # Get or set the root `subscription { ... }` object for this schema.
516
+ #
517
+ # @example Using `Types::Subscription` as the entry-point
518
+ # subscription { Types::Subscription }
519
+ #
520
+ # @param new_subscription_object [Class<GraphQL::Schema::Object>] The root type to use for subscriptions
521
+ # @param lazy_load_block If a block is given, then it will be called when GraphQL-Ruby needs the root subscription type.
522
+ # @return [Class<GraphQL::Schema::Object>, nil] The configured subscription root type, if there is one.
523
+ def subscription(new_subscription_object = nil, &lazy_load_block)
524
+ if new_subscription_object || block_given?
1063
525
  if @subscription_object
1064
- raise GraphQL::Error, "Second definition of `subscription(...)` (#{new_subscription_object.inspect}) is invalid, already configured with #{@subscription_object.inspect}"
526
+ dup_defn = new_subscription_object || yield
527
+ raise GraphQL::Error, "Second definition of `subscription(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@subscription_object.inspect}"
528
+ elsif use_visibility_profile?
529
+ if block_given?
530
+ if visibility.preload?
531
+ @subscription_object = lazy_load_block.call
532
+ visibility.subscription_configured(@subscription_object)
533
+ else
534
+ @subscription_object = lazy_load_block
535
+ end
536
+ else
537
+ @subscription_object = new_subscription_object
538
+ self.visibility.subscription_configured(@subscription_object)
539
+ end
540
+ add_subscription_extension_if_necessary
1065
541
  else
1066
- @subscription_object = new_subscription_object
542
+ @subscription_object = new_subscription_object || lazy_load_block.call
1067
543
  add_subscription_extension_if_necessary
1068
- add_type_and_traverse(new_subscription_object, root: true)
1069
- nil
544
+ add_type_and_traverse(@subscription_object, root: true)
1070
545
  end
546
+ nil
547
+ elsif @subscription_object.is_a?(Proc)
548
+ @subscription_object = @subscription_object.call
549
+ add_subscription_extension_if_necessary
550
+ self.visibility.subscription_configured(@subscription_object)
551
+ @subscription_object
1071
552
  else
1072
553
  @subscription_object || find_inherited_value(:subscription)
1073
554
  end
1074
555
  end
1075
556
 
1076
- # @see [GraphQL::Schema::Warden] Resticted access to root types
1077
- # @return [GraphQL::ObjectType, nil]
557
+ # @api private
1078
558
  def root_type_for_operation(operation)
1079
559
  case operation
1080
560
  when "query"
@@ -1084,38 +564,90 @@ module GraphQL
1084
564
  when "subscription"
1085
565
  subscription
1086
566
  else
1087
- raise ArgumentError, "unknown operation type: #{operation}"
567
+ raise ArgumentError, "unknown operation type: #{operation}"
568
+ end
569
+ end
570
+
571
+ # @return [Array<Class>] The root types (query, mutation, subscription) defined for this schema
572
+ def root_types
573
+ if use_visibility_profile?
574
+ [query, mutation, subscription].compact
575
+ else
576
+ @root_types
577
+ end
578
+ end
579
+
580
+ # @api private
581
+ def warden_class
582
+ if defined?(@warden_class)
583
+ @warden_class
584
+ elsif superclass.respond_to?(:warden_class)
585
+ superclass.warden_class
586
+ else
587
+ GraphQL::Schema::Warden
588
+ end
589
+ end
590
+
591
+ # @api private
592
+ attr_writer :warden_class
593
+
594
+ # @api private
595
+ def visibility_profile_class
596
+ if defined?(@visibility_profile_class)
597
+ @visibility_profile_class
598
+ elsif superclass.respond_to?(:visibility_profile_class)
599
+ superclass.visibility_profile_class
600
+ else
601
+ GraphQL::Schema::Visibility::Profile
602
+ end
603
+ end
604
+
605
+ # @api private
606
+ attr_writer :visibility_profile_class, :use_visibility_profile
607
+ # @api private
608
+ attr_accessor :visibility
609
+ # @api private
610
+ def use_visibility_profile?
611
+ if defined?(@use_visibility_profile)
612
+ @use_visibility_profile
613
+ elsif superclass.respond_to?(:use_visibility_profile?)
614
+ superclass.use_visibility_profile?
615
+ else
616
+ false
1088
617
  end
1089
618
  end
1090
619
 
1091
- def root_types
1092
- @root_types
1093
- end
1094
-
1095
620
  # @param type [Module] The type definition whose possible types you want to see
621
+ # @param context [GraphQL::Query::Context] used for filtering visible possible types at runtime
622
+ # @param use_visibility_profile Private, for migration to {Schema::Visibility}
1096
623
  # @return [Hash<String, Module>] All possible types, if no `type` is given.
1097
624
  # @return [Array<Module>] Possible types for `type`, if it's given.
1098
- def possible_types(type = nil, context = GraphQL::Query::NullContext)
625
+ def possible_types(type = nil, context = GraphQL::Query::NullContext.instance, use_visibility_profile = use_visibility_profile?)
626
+ if use_visibility_profile
627
+ if type
628
+ return Visibility::Profile.from_context(context, self).possible_types(type)
629
+ else
630
+ raise "Schema.possible_types is not implemented for `use_visibility_profile?`"
631
+ end
632
+ end
1099
633
  if type
1100
634
  # TODO duck-typing `.possible_types` would probably be nicer here
1101
635
  if type.kind.union?
1102
636
  type.possible_types(context: context)
1103
637
  else
1104
- stored_possible_types = own_possible_types[type.graphql_name]
638
+ stored_possible_types = own_possible_types[type]
1105
639
  visible_possible_types = if stored_possible_types && type.kind.interface?
1106
640
  stored_possible_types.select do |possible_type|
1107
- # Use `.graphql_name` comparison to match legacy vs class-based types.
1108
- # When we don't need to support legacy `.define` types, use `.include?(type)` instead.
1109
- possible_type.interfaces(context).any? { |interface| interface.graphql_name == type.graphql_name }
641
+ possible_type.interfaces(context).include?(type)
1110
642
  end
1111
643
  else
1112
644
  stored_possible_types
1113
645
  end
1114
646
  visible_possible_types ||
1115
- introspection_system.possible_types[type.graphql_name] ||
647
+ introspection_system.possible_types[type] ||
1116
648
  (
1117
649
  superclass.respond_to?(:possible_types) ?
1118
- superclass.possible_types(type, context) :
650
+ superclass.possible_types(type, context, use_visibility_profile) :
1119
651
  EMPTY_ARRAY
1120
652
  )
1121
653
  end
@@ -1150,50 +682,43 @@ module GraphQL
1150
682
  attr_writer :dataloader_class
1151
683
 
1152
684
  def references_to(to_type = nil, from: nil)
1153
- @own_references_to ||= Hash.new { |h, k| h[k] = [] }
1154
685
  if to_type
1155
- if !to_type.is_a?(String)
1156
- to_type = to_type.graphql_name
1157
- end
1158
-
1159
686
  if from
1160
- @own_references_to[to_type] << from
687
+ refs = own_references_to[to_type] ||= []
688
+ refs << from
1161
689
  else
1162
- own_refs = @own_references_to[to_type]
1163
- inherited_refs = find_inherited_value(:references_to, EMPTY_HASH)[to_type] || EMPTY_ARRAY
1164
- own_refs + inherited_refs
690
+ get_references_to(to_type) || EMPTY_ARRAY
1165
691
  end
1166
692
  else
1167
693
  # `@own_references_to` can be quite large for big schemas,
1168
694
  # and generally speaking, we won't inherit any values.
1169
695
  # So optimize the most common case -- don't create a duplicate Hash.
1170
696
  inherited_value = find_inherited_value(:references_to, EMPTY_HASH)
1171
- if inherited_value.any?
1172
- inherited_value.merge(@own_references_to)
697
+ if !inherited_value.empty?
698
+ inherited_value.merge(own_references_to)
1173
699
  else
1174
- @own_references_to
700
+ own_references_to
1175
701
  end
1176
702
  end
1177
703
  end
1178
704
 
1179
- def type_from_ast(ast_node, context: nil)
1180
- type_owner = context ? context.warden : self
1181
- GraphQL::Schema::TypeExpression.build_type(type_owner, ast_node)
705
+ def type_from_ast(ast_node, context: self.query_class.new(self, "{ __typename }").context)
706
+ GraphQL::Schema::TypeExpression.build_type(context.query.types, ast_node)
1182
707
  end
1183
708
 
1184
- def get_field(type_or_name, field_name)
709
+ def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext.instance)
1185
710
  parent_type = case type_or_name
1186
711
  when LateBoundType
1187
- get_type(type_or_name.name)
712
+ get_type(type_or_name.name, context)
1188
713
  when String
1189
- get_type(type_or_name)
714
+ get_type(type_or_name, context)
1190
715
  when Module
1191
716
  type_or_name
1192
717
  else
1193
- raise ArgumentError, "unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})"
718
+ raise GraphQL::InvariantError, "Unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})"
1194
719
  end
1195
720
 
1196
- if parent_type.kind.fields? && (field = parent_type.get_field(field_name))
721
+ if parent_type.kind.fields? && (field = parent_type.get_field(field_name, context))
1197
722
  field
1198
723
  elsif parent_type == query && (entry_point_field = introspection_system.entry_point(name: field_name))
1199
724
  entry_point_field
@@ -1204,24 +729,31 @@ module GraphQL
1204
729
  end
1205
730
  end
1206
731
 
1207
- def get_fields(type)
1208
- type.fields
732
+ def get_fields(type, context = GraphQL::Query::NullContext.instance)
733
+ type.fields(context)
1209
734
  end
1210
735
 
736
+ # Pass a custom introspection module here to use it for this schema.
737
+ # @param new_introspection_namespace [Module] If given, use this module for custom introspection on the schema
738
+ # @return [Module, nil] The configured namespace, if there is one
1211
739
  def introspection(new_introspection_namespace = nil)
1212
740
  if new_introspection_namespace
1213
741
  @introspection = new_introspection_namespace
1214
742
  # reset this cached value:
1215
743
  @introspection_system = nil
744
+ introspection_system
745
+ @introspection
1216
746
  else
1217
747
  @introspection || find_inherited_value(:introspection)
1218
748
  end
1219
749
  end
1220
750
 
751
+ # @return [Schema::IntrospectionSystem] Based on {introspection}
1221
752
  def introspection_system
1222
753
  if !@introspection_system
1223
754
  @introspection_system = Schema::IntrospectionSystem.new(self)
1224
755
  @introspection_system.resolve_late_bindings
756
+ self.visibility&.introspection_system_configured(@introspection_system)
1225
757
  end
1226
758
  @introspection_system
1227
759
  end
@@ -1241,27 +773,58 @@ module GraphQL
1241
773
  end
1242
774
  end
1243
775
 
1244
- def query_execution_strategy(new_query_execution_strategy = nil)
776
+ # A limit on the number of tokens to accept on incoming query strings.
777
+ # Use this to prevent parsing maliciously-large query strings.
778
+ # @return [nil, Integer]
779
+ def max_query_string_tokens(new_max_tokens = NOT_CONFIGURED)
780
+ if NOT_CONFIGURED.equal?(new_max_tokens)
781
+ defined?(@max_query_string_tokens) ? @max_query_string_tokens : find_inherited_value(:max_query_string_tokens)
782
+ else
783
+ @max_query_string_tokens = new_max_tokens
784
+ end
785
+ end
786
+
787
+ def default_page_size(new_default_page_size = nil)
788
+ if new_default_page_size
789
+ @default_page_size = new_default_page_size
790
+ else
791
+ @default_page_size || find_inherited_value(:default_page_size)
792
+ end
793
+ end
794
+
795
+ def query_execution_strategy(new_query_execution_strategy = nil, deprecation_warning: true)
796
+ if deprecation_warning
797
+ warn "GraphQL::Schema.query_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead."
798
+ warn " #{caller(1, 1).first}"
799
+ end
1245
800
  if new_query_execution_strategy
1246
801
  @query_execution_strategy = new_query_execution_strategy
1247
802
  else
1248
- @query_execution_strategy || find_inherited_value(:query_execution_strategy, self.default_execution_strategy)
803
+ @query_execution_strategy || (superclass.respond_to?(:query_execution_strategy) ? superclass.query_execution_strategy(deprecation_warning: false) : self.default_execution_strategy)
1249
804
  end
1250
805
  end
1251
806
 
1252
- def mutation_execution_strategy(new_mutation_execution_strategy = nil)
807
+ def mutation_execution_strategy(new_mutation_execution_strategy = nil, deprecation_warning: true)
808
+ if deprecation_warning
809
+ warn "GraphQL::Schema.mutation_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead."
810
+ warn " #{caller(1, 1).first}"
811
+ end
1253
812
  if new_mutation_execution_strategy
1254
813
  @mutation_execution_strategy = new_mutation_execution_strategy
1255
814
  else
1256
- @mutation_execution_strategy || find_inherited_value(:mutation_execution_strategy, self.default_execution_strategy)
815
+ @mutation_execution_strategy || (superclass.respond_to?(:mutation_execution_strategy) ? superclass.mutation_execution_strategy(deprecation_warning: false) : self.default_execution_strategy)
1257
816
  end
1258
817
  end
1259
818
 
1260
- def subscription_execution_strategy(new_subscription_execution_strategy = nil)
819
+ def subscription_execution_strategy(new_subscription_execution_strategy = nil, deprecation_warning: true)
820
+ if deprecation_warning
821
+ warn "GraphQL::Schema.subscription_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead."
822
+ warn " #{caller(1, 1).first}"
823
+ end
1261
824
  if new_subscription_execution_strategy
1262
825
  @subscription_execution_strategy = new_subscription_execution_strategy
1263
826
  else
1264
- @subscription_execution_strategy || find_inherited_value(:subscription_execution_strategy, self.default_execution_strategy)
827
+ @subscription_execution_strategy || (superclass.respond_to?(:subscription_execution_strategy) ? superclass.subscription_execution_strategy(deprecation_warning: false) : self.default_execution_strategy)
1265
828
  end
1266
829
  end
1267
830
 
@@ -1286,19 +849,39 @@ module GraphQL
1286
849
  else
1287
850
  string_or_document
1288
851
  end
1289
- query = GraphQL::Query.new(self, document: doc, context: context)
852
+ query = query_class.new(self, document: doc, context: context)
1290
853
  validator_opts = { schema: self }
1291
854
  rules && (validator_opts[:rules] = rules)
1292
855
  validator = GraphQL::StaticValidation::Validator.new(**validator_opts)
1293
- res = validator.validate(query, timeout: validate_timeout)
856
+ res = validator.validate(query, timeout: validate_timeout, max_errors: validate_max_errors)
1294
857
  res[:errors]
1295
858
  end
1296
859
 
860
+ # @param new_query_class [Class<GraphQL::Query>] A subclass to use when executing queries
861
+ def query_class(new_query_class = NOT_CONFIGURED)
862
+ if NOT_CONFIGURED.equal?(new_query_class)
863
+ @query_class || (superclass.respond_to?(:query_class) ? superclass.query_class : GraphQL::Query)
864
+ else
865
+ @query_class = new_query_class
866
+ end
867
+ end
868
+
869
+ attr_writer :validate_max_errors
870
+
871
+ def validate_max_errors(new_validate_max_errors = NOT_CONFIGURED)
872
+ if NOT_CONFIGURED.equal?(new_validate_max_errors)
873
+ defined?(@validate_max_errors) ? @validate_max_errors : find_inherited_value(:validate_max_errors)
874
+ else
875
+ @validate_max_errors = new_validate_max_errors
876
+ end
877
+ end
878
+
1297
879
  attr_writer :max_complexity
1298
880
 
1299
- def max_complexity(max_complexity = nil)
881
+ def max_complexity(max_complexity = nil, count_introspection_fields: true)
1300
882
  if max_complexity
1301
883
  @max_complexity = max_complexity
884
+ @max_complexity_count_introspection_fields = count_introspection_fields
1302
885
  elsif defined?(@max_complexity)
1303
886
  @max_complexity
1304
887
  else
@@ -1306,26 +889,23 @@ module GraphQL
1306
889
  end
1307
890
  end
1308
891
 
892
+ def max_complexity_count_introspection_fields
893
+ if defined?(@max_complexity_count_introspection_fields)
894
+ @max_complexity_count_introspection_fields
895
+ else
896
+ find_inherited_value(:max_complexity_count_introspection_fields, true)
897
+ end
898
+ end
899
+
1309
900
  attr_writer :analysis_engine
1310
901
 
1311
902
  def analysis_engine
1312
903
  @analysis_engine || find_inherited_value(:analysis_engine, self.default_analysis_engine)
1313
904
  end
1314
905
 
1315
- def using_ast_analysis?
1316
- analysis_engine == GraphQL::Analysis::AST
1317
- end
1318
-
1319
- def interpreter?
1320
- query_execution_strategy == GraphQL::Execution::Interpreter &&
1321
- mutation_execution_strategy == GraphQL::Execution::Interpreter &&
1322
- subscription_execution_strategy == GraphQL::Execution::Interpreter
1323
- end
1324
-
1325
- attr_writer :interpreter
1326
-
1327
906
  def error_bubbling(new_error_bubbling = nil)
1328
907
  if !new_error_bubbling.nil?
908
+ warn("error_bubbling(#{new_error_bubbling.inspect}) is deprecated; the default value of `false` will be the only option in GraphQL-Ruby 3.0")
1329
909
  @error_bubbling = new_error_bubbling
1330
910
  else
1331
911
  @error_bubbling.nil? ? find_inherited_value(:error_bubbling) : @error_bubbling
@@ -1336,9 +916,10 @@ module GraphQL
1336
916
 
1337
917
  attr_writer :max_depth
1338
918
 
1339
- def max_depth(new_max_depth = nil)
919
+ def max_depth(new_max_depth = nil, count_introspection_fields: true)
1340
920
  if new_max_depth
1341
921
  @max_depth = new_max_depth
922
+ @count_introspection_fields = count_introspection_fields
1342
923
  elsif defined?(@max_depth)
1343
924
  @max_depth
1344
925
  else
@@ -1346,6 +927,14 @@ module GraphQL
1346
927
  end
1347
928
  end
1348
929
 
930
+ def count_introspection_fields
931
+ if defined?(@count_introspection_fields)
932
+ @count_introspection_fields
933
+ else
934
+ find_inherited_value(:count_introspection_fields, true)
935
+ end
936
+ end
937
+
1349
938
  def disable_introspection_entry_points
1350
939
  @disable_introspection_entry_points = true
1351
940
  # TODO: this clears the cache made in `def types`. But this is not a great solution.
@@ -1388,15 +977,62 @@ module GraphQL
1388
977
  end
1389
978
  end
1390
979
 
980
+ # @param new_extra_types [Module] Type definitions to include in printing and introspection, even though they aren't referenced in the schema
981
+ # @return [Array<Module>] Type definitions added to this schema
982
+ def extra_types(*new_extra_types)
983
+ if !new_extra_types.empty?
984
+ new_extra_types = new_extra_types.flatten
985
+ @own_extra_types ||= []
986
+ @own_extra_types.concat(new_extra_types)
987
+ end
988
+ inherited_et = find_inherited_value(:extra_types, nil)
989
+ if inherited_et
990
+ if @own_extra_types
991
+ inherited_et + @own_extra_types
992
+ else
993
+ inherited_et
994
+ end
995
+ else
996
+ @own_extra_types || EMPTY_ARRAY
997
+ end
998
+ end
999
+
1000
+ # Tell the schema about these types so that they can be registered as implementations of interfaces in the schema.
1001
+ #
1002
+ # This method must be used when an object type is connected to the schema as an interface implementor but
1003
+ # not as a return type of a field. In that case, if the object type isn't registered here, GraphQL-Ruby won't be able to find it.
1004
+ #
1005
+ # @param new_orphan_types [Array<Class<GraphQL::Schema::Object>>] Object types to register as implementations of interfaces in the schema.
1006
+ # @return [Array<Class<GraphQL::Schema::Object>>] All previously-registered orphan types for this schema
1391
1007
  def orphan_types(*new_orphan_types)
1392
- if new_orphan_types.any?
1008
+ if !new_orphan_types.empty?
1393
1009
  new_orphan_types = new_orphan_types.flatten
1394
- add_type_and_traverse(new_orphan_types, root: false)
1395
- @orphan_types = new_orphan_types
1010
+ non_object_types = new_orphan_types.reject { |ot| ot.is_a?(Class) && ot < GraphQL::Schema::Object }
1011
+ if !non_object_types.empty?
1012
+ raise ArgumentError, <<~ERR
1013
+ Only object type classes should be added as `orphan_types(...)`.
1014
+
1015
+ - Remove these no-op types from `orphan_types`: #{non_object_types.map { |t| "#{t.inspect} (#{t.kind.name})"}.join(", ")}
1016
+ - See https://graphql-ruby.org/type_definitions/interfaces.html#orphan-types
1017
+
1018
+ To add other types to your schema, you might want `extra_types`: https://graphql-ruby.org/schema/definition.html#extra-types
1019
+ ERR
1020
+ end
1021
+ add_type_and_traverse(new_orphan_types, root: false) unless use_visibility_profile?
1396
1022
  own_orphan_types.concat(new_orphan_types.flatten)
1023
+ self.visibility&.orphan_types_configured(new_orphan_types)
1397
1024
  end
1398
1025
 
1399
- find_inherited_value(:orphan_types, EMPTY_ARRAY) + own_orphan_types
1026
+ inherited_ot = find_inherited_value(:orphan_types, nil)
1027
+ if inherited_ot
1028
+ if !own_orphan_types.empty?
1029
+ inherited_ot + own_orphan_types
1030
+ else
1031
+ inherited_ot
1032
+ end
1033
+ else
1034
+ own_orphan_types
1035
+ end
1400
1036
  end
1401
1037
 
1402
1038
  def default_execution_strategy
@@ -1415,6 +1051,29 @@ module GraphQL
1415
1051
  end
1416
1052
  end
1417
1053
 
1054
+
1055
+ # @param new_default_logger [#log] Something to use for logging messages
1056
+ def default_logger(new_default_logger = NOT_CONFIGURED)
1057
+ if NOT_CONFIGURED.equal?(new_default_logger)
1058
+ if defined?(@default_logger)
1059
+ @default_logger
1060
+ elsif superclass.respond_to?(:default_logger)
1061
+ superclass.default_logger
1062
+ elsif defined?(Rails) && Rails.respond_to?(:logger) && (rails_logger = Rails.logger)
1063
+ rails_logger
1064
+ else
1065
+ def_logger = Logger.new($stdout)
1066
+ def_logger.info! # It doesn't output debug info by default
1067
+ def_logger
1068
+ end
1069
+ elsif new_default_logger == nil
1070
+ @default_logger = Logger.new(IO::NULL)
1071
+ else
1072
+ @default_logger = new_default_logger
1073
+ end
1074
+ end
1075
+
1076
+ # @param new_context_class [Class<GraphQL::Query::Context>] A subclass to use when executing queries
1418
1077
  def context_class(new_context_class = nil)
1419
1078
  if new_context_class
1420
1079
  @context_class = new_context_class
@@ -1423,28 +1082,79 @@ module GraphQL
1423
1082
  end
1424
1083
  end
1425
1084
 
1085
+ # Register a handler for errors raised during execution. The handlers can return a new value or raise a new error.
1086
+ #
1087
+ # @example Handling "not found" with a client-facing error
1088
+ # rescue_from(ActiveRecord::NotFound) { raise GraphQL::ExecutionError, "An object could not be found" }
1089
+ #
1090
+ # @param err_classes [Array<StandardError>] Classes which should be rescued by `handler_block`
1091
+ # @param handler_block The code to run when one of those errors is raised during execution
1092
+ # @yieldparam error [StandardError] An instance of one of the configured `err_classes`
1093
+ # @yieldparam object [Object] The current application object in the query when the error was raised
1094
+ # @yieldparam arguments [GraphQL::Query::Arguments] The current field arguments when the error was raised
1095
+ # @yieldparam context [GraphQL::Query::Context] The context for the currently-running operation
1096
+ # @yieldreturn [Object] Some object to use in the place where this error was raised
1097
+ # @raise [GraphQL::ExecutionError] In the handler, raise to add a client-facing error to the response
1098
+ # @raise [StandardError] In the handler, raise to crash the query with a developer-facing error
1426
1099
  def rescue_from(*err_classes, &handler_block)
1427
1100
  err_classes.each do |err_class|
1428
- error_handler.rescue_from(err_class, handler_block)
1101
+ Execution::Errors.register_rescue_from(err_class, error_handlers[:subclass_handlers], handler_block)
1102
+ end
1103
+ end
1104
+
1105
+ NEW_HANDLER_HASH = ->(h, k) {
1106
+ h[k] = {
1107
+ class: k,
1108
+ handler: nil,
1109
+ subclass_handlers: Hash.new(&NEW_HANDLER_HASH),
1110
+ }
1111
+ }
1112
+
1113
+ def error_handlers
1114
+ @error_handlers ||= {
1115
+ class: nil,
1116
+ handler: nil,
1117
+ subclass_handlers: Hash.new(&NEW_HANDLER_HASH),
1118
+ }
1119
+ end
1120
+
1121
+ # @api private
1122
+ def handle_or_reraise(context, err)
1123
+ handler = Execution::Errors.find_handler_for(self, err.class)
1124
+ if handler
1125
+ obj = context[:current_object]
1126
+ args = context[:current_arguments]
1127
+ args = args && args.respond_to?(:keyword_arguments) ? args.keyword_arguments : nil
1128
+ field = context[:current_field]
1129
+ if obj.is_a?(GraphQL::Schema::Object)
1130
+ obj = obj.object
1131
+ end
1132
+ handler[:handler].call(err, obj, args, context, field)
1133
+ else
1134
+ raise err
1429
1135
  end
1430
1136
  end
1431
1137
 
1432
1138
  # rubocop:disable Lint/DuplicateMethods
1433
1139
  module ResolveTypeWithType
1434
1140
  def resolve_type(type, obj, ctx)
1435
- first_resolved_type, resolved_value = if type.is_a?(Module) && type.respond_to?(:resolve_type)
1141
+ maybe_lazy_resolve_type_result = if type.is_a?(Module) && type.respond_to?(:resolve_type)
1436
1142
  type.resolve_type(obj, ctx)
1437
1143
  else
1438
1144
  super
1439
1145
  end
1440
1146
 
1441
- after_lazy(first_resolved_type) do |resolved_type|
1442
- if resolved_type.nil? || (resolved_type.is_a?(Module) && resolved_type.respond_to?(:kind)) || resolved_type.is_a?(GraphQL::BaseType)
1443
- if resolved_value
1444
- [resolved_type, resolved_value]
1445
- else
1446
- resolved_type
1447
- end
1147
+ after_lazy(maybe_lazy_resolve_type_result) do |resolve_type_result|
1148
+ if resolve_type_result.is_a?(Array) && resolve_type_result.size == 2
1149
+ resolved_type = resolve_type_result[0]
1150
+ resolved_value = resolve_type_result[1]
1151
+ else
1152
+ resolved_type = resolve_type_result
1153
+ resolved_value = obj
1154
+ end
1155
+
1156
+ if resolved_type.nil? || (resolved_type.is_a?(Module) && resolved_type.respond_to?(:kind))
1157
+ [resolved_type, resolved_value]
1448
1158
  else
1449
1159
  raise ".resolve_type should return a type definition, but got #{resolved_type.inspect} (#{resolved_type.class}) from `resolve_type(#{type}, #{obj}, #{ctx})`"
1450
1160
  end
@@ -1452,51 +1162,96 @@ module GraphQL
1452
1162
  end
1453
1163
  end
1454
1164
 
1455
- def resolve_type(type, obj, ctx)
1456
- if type.kind.object?
1457
- type
1458
- else
1459
- 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})"
1460
- end
1165
+ # GraphQL-Ruby calls this method during execution when it needs the application to determine the type to use for an object.
1166
+ #
1167
+ # Usually, this object was returned from a field whose return type is an {GraphQL::Schema::Interface} or a {GraphQL::Schema::Union}.
1168
+ # But this method is called in other cases, too -- for example, when {GraphQL::Schema::Argument.loads} cases an object to be directly loaded from the database.
1169
+ #
1170
+ # @example Returning a GraphQL type based on the object's class name
1171
+ # class MySchema < GraphQL::Schema
1172
+ # def resolve_type(_abs_type, object, _context)
1173
+ # graphql_type_name = "Types::#{object.class.name}Type"
1174
+ # graphql_type_name.constantize # If this raises a NameError, then come implement special cases in this method
1175
+ # end
1176
+ # end
1177
+ # @param abstract_type [Class, Module, nil] The Interface or Union type which is being resolved, if there is one
1178
+ # @param application_object [Object] The object returned from a field whose type must be determined
1179
+ # @param context [GraphQL::Query::Context] The query context for the currently-executing query
1180
+ # @return [Class<GraphQL::Schema::Object] The Object type definition to use for `obj`
1181
+ def resolve_type(abstract_type, application_object, context)
1182
+ raise GraphQL::RequiredImplementationMissingError, "#{self.name}.resolve_type(abstract_type, application_object, context) must be implemented to use Union types, Interface types, or `loads:` (tried to resolve: #{abstract_type.name})"
1461
1183
  end
1462
1184
  # rubocop:enable Lint/DuplicateMethods
1463
1185
 
1464
1186
  def inherited(child_class)
1465
1187
  if self == GraphQL::Schema
1466
1188
  child_class.directives(default_directives.values)
1189
+ child_class.extend(SubclassGetReferencesTo)
1190
+ end
1191
+ # Make sure the child class has these built out, so that
1192
+ # subclasses can be modified by later calls to `trace_with`
1193
+ own_trace_modes.each do |name, _class|
1194
+ child_class.own_trace_modes[name] = child_class.build_trace_mode(name)
1467
1195
  end
1468
1196
  child_class.singleton_class.prepend(ResolveTypeWithType)
1469
- super
1470
- end
1471
1197
 
1472
- def object_from_id(node_id, ctx)
1473
- 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}`)"
1198
+ if use_visibility_profile?
1199
+ vis = self.visibility
1200
+ child_class.visibility = vis.dup_for(child_class)
1201
+ end
1202
+ super
1474
1203
  end
1475
1204
 
1476
- def id_from_object(object, type, ctx)
1477
- 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}`)"
1205
+ # Fetch an object based on an incoming ID and the current context. This method should return an object
1206
+ # from your application, or return `nil` if there is no object or the object shouldn't be available to this operation.
1207
+ #
1208
+ # @example Fetching an object with Rails's GlobalID
1209
+ # def self.object_from_id(object_id, _context)
1210
+ # GlobalID.find(global_id)
1211
+ # # TODO: use `context[:current_user]` to determine if this object is authorized.
1212
+ # end
1213
+ # @param object_id [String] The ID to fetch an object for. This may be client-provided (as in `node(id: ...)` or `loads:`) or previously stored by the schema (eg, by the `ObjectCache`)
1214
+ # @param context [GraphQL::Query::Context] The context for the currently-executing operation
1215
+ # @return [Object, nil] The application which `object_id` references, or `nil` if there is no object or the current operation shouldn't have access to the object
1216
+ # @see id_from_object which produces these IDs
1217
+ def object_from_id(object_id, context)
1218
+ raise GraphQL::RequiredImplementationMissingError, "#{self.name}.object_from_id(object_id, context) must be implemented to load by ID (tried to load from id `#{object_id}`)"
1219
+ end
1220
+
1221
+ # Return a stable ID string for `object` so that it can be refetched later, using {.object_from_id}.
1222
+ #
1223
+ # {GlobalID}(https://github.com/rails/globalid) and {SQIDs}(https://sqids.org/ruby) can both be used to create IDs.
1224
+ #
1225
+ # @example Using Rails's GlobalID to generate IDs
1226
+ # def self.id_from_object(application_object, graphql_type, context)
1227
+ # application_object.to_gid_param
1228
+ # end
1229
+ #
1230
+ # @param application_object [Object] Some object encountered by GraphQL-Ruby while running a query
1231
+ # @param graphql_type [Class, Module] The type that GraphQL-Ruby is using for `application_object` during this query
1232
+ # @param context [GraphQL::Query::Context] The context for the operation that is currently running
1233
+ # @return [String] A stable identifier which can be passed to {.object_from_id} later to re-fetch `application_object`
1234
+ def id_from_object(application_object, graphql_type, context)
1235
+ raise GraphQL::RequiredImplementationMissingError, "#{self.name}.id_from_object(application_object, graphql_type, context) must be implemented to create global ids (tried to create an id for `#{application_object.inspect}`)"
1478
1236
  end
1479
1237
 
1480
1238
  def visible?(member, ctx)
1481
- member.type_class.visible?(ctx)
1239
+ member.visible?(ctx)
1482
1240
  end
1483
1241
 
1484
- def accessible?(member, ctx)
1485
- member.type_class.accessible?(ctx)
1242
+ def schema_directive(dir_class, **options)
1243
+ @own_schema_directives ||= []
1244
+ Member::HasDirectives.add_directive(self, @own_schema_directives, dir_class, options)
1486
1245
  end
1487
1246
 
1488
- # This hook is called when a client tries to access one or more
1489
- # fields that fail the `accessible?` check.
1490
- #
1491
- # By default, an error is added to the response. Override this hook to
1492
- # track metrics or return a different error to the client.
1493
- #
1494
- # @param error [InaccessibleFieldsError] The analysis error for this check
1495
- # @return [AnalysisError, nil] Return an error to skip the query
1496
- def inaccessible_fields(error)
1497
- error
1247
+ def schema_directives
1248
+ Member::HasDirectives.get_directives(self, @own_schema_directives, :schema_directives)
1498
1249
  end
1499
1250
 
1251
+ # Called when a type is needed by name at runtime
1252
+ def load_type(type_name, ctx)
1253
+ get_type(type_name, ctx)
1254
+ end
1500
1255
  # This hook is called when an object fails an `authorized?` check.
1501
1256
  # You might report to your bug tracker here, so you can correct
1502
1257
  # the field resolvers not to return unauthorized objects.
@@ -1532,58 +1287,75 @@ module GraphQL
1532
1287
  unauthorized_object(unauthorized_error)
1533
1288
  end
1534
1289
 
1535
- def type_error(type_err, ctx)
1536
- DefaultTypeError.call(type_err, ctx)
1290
+ # Called at runtime when GraphQL-Ruby encounters a mismatch between the application behavior
1291
+ # and the GraphQL type system.
1292
+ #
1293
+ # The default implementation of this method is to follow the GraphQL specification,
1294
+ # but you can override this to report errors to your bug tracker or customize error handling.
1295
+ # @param type_error [GraphQL::Error] several specific error classes are passed here, see the default implementation for details
1296
+ # @param context [GraphQL::Query::Context] the context for the currently-running operation
1297
+ # @return [void]
1298
+ # @raise [GraphQL::ExecutionError] to return this error to the client
1299
+ # @raise [GraphQL::Error] to crash the query and raise a developer-facing error
1300
+ def type_error(type_error, ctx)
1301
+ case type_error
1302
+ when GraphQL::InvalidNullError
1303
+ ctx.errors << type_error
1304
+ when GraphQL::UnresolvedTypeError, GraphQL::StringEncodingError, GraphQL::IntegerEncodingError
1305
+ raise type_error
1306
+ when GraphQL::IntegerDecodingError
1307
+ nil
1308
+ end
1537
1309
  end
1538
1310
 
1539
1311
  # A function to call when {#execute} receives an invalid query string
1540
1312
  #
1541
1313
  # The default is to add the error to `context.errors`
1542
- # @param err [GraphQL::ParseError] The error encountered during parsing
1314
+ # @param parse_err [GraphQL::ParseError] The error encountered during parsing
1543
1315
  # @param ctx [GraphQL::Query::Context] The context for the query where the error occurred
1544
1316
  # @return void
1545
1317
  def parse_error(parse_err, ctx)
1546
1318
  ctx.errors.push(parse_err)
1547
1319
  end
1548
1320
 
1549
- # @return [GraphQL::Execution::Errors]
1550
- def error_handler
1551
- @error_handler ||= GraphQL::Execution::Errors.new(self)
1552
- end
1553
-
1554
1321
  def lazy_resolve(lazy_class, value_method)
1555
1322
  lazy_methods.set(lazy_class, value_method)
1556
1323
  end
1557
1324
 
1558
1325
  def instrument(instrument_step, instrumenter, options = {})
1559
- if instrument_step == :field
1560
- 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"
1561
- end
1562
-
1563
- step = if instrument_step == :field && options[:after_built_ins]
1564
- :field_after_built_ins
1565
- else
1566
- instrument_step
1567
- end
1326
+ warn <<~WARN
1327
+ Schema.instrument is deprecated, use `trace_with` instead: https://graphql-ruby.org/queries/tracing.html"
1328
+ (From `#{self}.instrument(#{instrument_step}, #{instrumenter})` at #{caller(1, 1).first})
1568
1329
 
1569
- own_instrumenters[step] << instrumenter
1330
+ WARN
1331
+ trace_with(Tracing::LegacyHooksTrace)
1332
+ own_instrumenters[instrument_step] << instrumenter
1570
1333
  end
1571
1334
 
1572
1335
  # Add several directives at once
1573
1336
  # @param new_directives [Class]
1574
1337
  def directives(*new_directives)
1575
- if new_directives.any?
1338
+ if !new_directives.empty?
1576
1339
  new_directives.flatten.each { |d| directive(d) }
1577
1340
  end
1578
1341
 
1579
- find_inherited_value(:directives, default_directives).merge(own_directives)
1342
+ inherited_dirs = find_inherited_value(:directives, default_directives)
1343
+ if !own_directives.empty?
1344
+ inherited_dirs.merge(own_directives)
1345
+ else
1346
+ inherited_dirs
1347
+ end
1580
1348
  end
1581
1349
 
1582
1350
  # Attach a single directive to this schema
1583
1351
  # @param new_directive [Class]
1584
1352
  # @return void
1585
1353
  def directive(new_directive)
1586
- add_type_and_traverse(new_directive, root: false)
1354
+ if use_visibility_profile?
1355
+ own_directives[new_directive.graphql_name] = new_directive
1356
+ else
1357
+ add_type_and_traverse(new_directive, root: false)
1358
+ end
1587
1359
  end
1588
1360
 
1589
1361
  def default_directives
@@ -1591,10 +1363,21 @@ module GraphQL
1591
1363
  "include" => GraphQL::Schema::Directive::Include,
1592
1364
  "skip" => GraphQL::Schema::Directive::Skip,
1593
1365
  "deprecated" => GraphQL::Schema::Directive::Deprecated,
1366
+ "oneOf" => GraphQL::Schema::Directive::OneOf,
1367
+ "specifiedBy" => GraphQL::Schema::Directive::SpecifiedBy,
1594
1368
  }.freeze
1595
1369
  end
1596
1370
 
1597
- def tracer(new_tracer)
1371
+ def tracer(new_tracer, silence_deprecation_warning: false)
1372
+ if !silence_deprecation_warning
1373
+ warn("`Schema.tracer(#{new_tracer.inspect})` is deprecated; use module-based `trace_with` instead. See: https://graphql-ruby.org/queries/tracing.html")
1374
+ warn " #{caller(1, 1).first}"
1375
+ end
1376
+ default_trace = trace_class_for(:default, build: true)
1377
+ if default_trace.nil? || !(default_trace < GraphQL::Tracing::CallLegacyTracers)
1378
+ trace_with(GraphQL::Tracing::CallLegacyTracers)
1379
+ end
1380
+
1598
1381
  own_tracers << new_tracer
1599
1382
  end
1600
1383
 
@@ -1602,27 +1385,103 @@ module GraphQL
1602
1385
  find_inherited_value(:tracers, EMPTY_ARRAY) + own_tracers
1603
1386
  end
1604
1387
 
1605
- def query_analyzer(new_analyzer)
1606
- if new_analyzer == GraphQL::Authorization::Analyzer
1607
- GraphQL::Deprecation.warn("The Authorization query analyzer is deprecated. Authorizing at query runtime is generally a better idea.")
1388
+ # Mix `trace_mod` into this schema's `Trace` class so that its methods
1389
+ # will be called at runtime.
1390
+ #
1391
+ # @param trace_mod [Module] A module that implements tracing methods
1392
+ # @param mode [Symbol] Trace module will only be used for this trade mode
1393
+ # @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
1394
+ # @return [void]
1395
+ # @see GraphQL::Tracing::Trace for available tracing methods
1396
+ def trace_with(trace_mod, mode: :default, **options)
1397
+ if mode.is_a?(Array)
1398
+ mode.each { |m| trace_with(trace_mod, mode: m, **options) }
1399
+ else
1400
+ tc = own_trace_modes[mode] ||= build_trace_mode(mode)
1401
+ tc.include(trace_mod)
1402
+ own_trace_modules[mode] << trace_mod
1403
+ add_trace_options_for(mode, options)
1404
+ if mode == :default
1405
+ # This module is being added as a default tracer. If any other mode classes
1406
+ # have already been created, but get their default behavior from a superclass,
1407
+ # Then mix this into this schema's subclass.
1408
+ # (But don't mix it into mode classes that aren't default-based.)
1409
+ own_trace_modes.each do |other_mode_name, other_mode_class|
1410
+ if other_mode_class < DefaultTraceClass
1411
+ # Don't add it back to the inheritance tree if it's already there
1412
+ if !(other_mode_class < trace_mod)
1413
+ other_mode_class.include(trace_mod)
1414
+ end
1415
+ # Add any options so they'll be available
1416
+ add_trace_options_for(other_mode_name, options)
1417
+ end
1418
+ end
1419
+ end
1608
1420
  end
1609
- own_query_analyzers << new_analyzer
1421
+ nil
1610
1422
  end
1611
1423
 
1612
- def query_analyzers
1613
- find_inherited_value(:query_analyzers, EMPTY_ARRAY) + own_query_analyzers
1424
+ # The options hash for this trace mode
1425
+ # @return [Hash]
1426
+ def trace_options_for(mode)
1427
+ @trace_options_for_mode ||= {}
1428
+ @trace_options_for_mode[mode] ||= begin
1429
+ # It may be time to create an options hash for a mode that wasn't registered yet.
1430
+ # Mix in the default options in that case.
1431
+ default_options = mode == :default ? EMPTY_HASH : trace_options_for(:default)
1432
+ # Make sure this returns a new object so that other hashes aren't modified later
1433
+ if superclass.respond_to?(:trace_options_for)
1434
+ superclass.trace_options_for(mode).merge(default_options)
1435
+ else
1436
+ default_options.dup
1437
+ end
1438
+ end
1614
1439
  end
1615
1440
 
1616
- def middleware(new_middleware = nil)
1617
- if new_middleware
1618
- 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"
1619
- own_middleware << new_middleware
1441
+ # Create a trace instance which will include the trace modules specified for the optional mode.
1442
+ #
1443
+ # If no `mode:` is given, then {default_trace_mode} will be used.
1444
+ #
1445
+ # @param mode [Symbol] Trace modules for this trade mode will be included
1446
+ # @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
1447
+ # @return [Tracing::Trace]
1448
+ def new_trace(mode: nil, **options)
1449
+ target = options[:query] || options[:multiplex]
1450
+ mode ||= target && target.context[:trace_mode]
1451
+
1452
+ trace_mode = if mode
1453
+ mode
1454
+ elsif target && target.context[:backtrace]
1455
+ if default_trace_mode != :default
1456
+ raise ArgumentError, "Can't use `context[:backtrace]` with a custom default trace mode (`#{dm.inspect}`)"
1457
+ else
1458
+ own_trace_modes[:default_backtrace] ||= build_trace_mode(:default_backtrace)
1459
+ options_trace_mode = :default
1460
+ :default_backtrace
1461
+ end
1620
1462
  else
1621
- # TODO make sure this is cached when running a query
1622
- MiddlewareChain.new(steps: all_middleware, final_step: GraphQL::Execution::Execute::FieldResolveStep)
1463
+ default_trace_mode
1623
1464
  end
1465
+
1466
+ options_trace_mode ||= trace_mode
1467
+ base_trace_options = trace_options_for(options_trace_mode)
1468
+ trace_options = base_trace_options.merge(options)
1469
+ trace_class_for_mode = trace_class_for(trace_mode, build: true)
1470
+ trace_class_for_mode.new(**trace_options)
1471
+ end
1472
+
1473
+ # @param new_analyzer [Class<GraphQL::Analysis::Analyzer>] An analyzer to run on queries to this schema
1474
+ # @see GraphQL::Analysis the analysis system
1475
+ def query_analyzer(new_analyzer)
1476
+ own_query_analyzers << new_analyzer
1477
+ end
1478
+
1479
+ def query_analyzers
1480
+ find_inherited_value(:query_analyzers, EMPTY_ARRAY) + own_query_analyzers
1624
1481
  end
1625
1482
 
1483
+ # @param new_analyzer [Class<GraphQL::Analysis::Analyzer>] An analyzer to run on multiplexes to this schema
1484
+ # @see GraphQL::Analysis the analysis system
1626
1485
  def multiplex_analyzer(new_analyzer)
1627
1486
  own_multiplex_analyzers << new_analyzer
1628
1487
  end
@@ -1631,9 +1490,17 @@ module GraphQL
1631
1490
  find_inherited_value(:multiplex_analyzers, EMPTY_ARRAY) + own_multiplex_analyzers
1632
1491
  end
1633
1492
 
1493
+ def sanitized_printer(new_sanitized_printer = nil)
1494
+ if new_sanitized_printer
1495
+ @own_sanitized_printer = new_sanitized_printer
1496
+ else
1497
+ @own_sanitized_printer || GraphQL::Language::SanitizedPrinter
1498
+ end
1499
+ end
1500
+
1634
1501
  # Execute a query on itself.
1635
1502
  # @see {Query#initialize} for arguments.
1636
- # @return [Hash] query result, ready to be serialized as JSON
1503
+ # @return [GraphQL::Query::Result] query result, ready to be serialized as JSON
1637
1504
  def execute(query_str = nil, **kwargs)
1638
1505
  if query_str
1639
1506
  kwargs[:query] = query_str
@@ -1643,6 +1510,9 @@ module GraphQL
1643
1510
  {
1644
1511
  backtrace: ctx[:backtrace],
1645
1512
  tracers: ctx[:tracers],
1513
+ trace: ctx[:trace],
1514
+ dataloader: ctx[:dataloader],
1515
+ trace_mode: ctx[:trace_mode],
1646
1516
  }
1647
1517
  else
1648
1518
  {}
@@ -1667,17 +1537,12 @@ module GraphQL
1667
1537
  # }
1668
1538
  #
1669
1539
  # @see {Query#initialize} for query keyword arguments
1670
- # @see {Execution::Multiplex#run_queries} for multiplex keyword arguments
1540
+ # @see {Execution::Multiplex#run_all} for multiplex keyword arguments
1671
1541
  # @param queries [Array<Hash>] Keyword arguments for each query
1672
1542
  # @param context [Hash] Multiplex-level context
1673
- # @return [Array<Hash>] One result for each query in the input
1543
+ # @return [Array<GraphQL::Query::Result>] One result for each query in the input
1674
1544
  def multiplex(queries, **kwargs)
1675
- schema = if interpreter?
1676
- self
1677
- else
1678
- graphql_definition
1679
- end
1680
- GraphQL::Execution::Multiplex.run_all(schema, queries, **kwargs)
1545
+ GraphQL::Execution::Interpreter.run_all(self, queries, **kwargs)
1681
1546
  end
1682
1547
 
1683
1548
  def instrumenters
@@ -1689,24 +1554,111 @@ module GraphQL
1689
1554
 
1690
1555
  # @api private
1691
1556
  def add_subscription_extension_if_necessary
1692
- if interpreter? && !defined?(@subscription_extension_added) && subscription && self.subscriptions
1557
+ # TODO: when there's a proper API for extending root types, migrat this to use it.
1558
+ if !defined?(@subscription_extension_added) && @subscription_object.is_a?(Class) && self.subscriptions
1693
1559
  @subscription_extension_added = true
1694
- if subscription.singleton_class.ancestors.include?(Subscriptions::SubscriptionRoot)
1695
- GraphQL::Deprecation.warn("`extend Subscriptions::SubscriptionRoot` is no longer required; you may remove it from #{self}'s `subscription` root type (#{subscription}).")
1696
- else
1697
- subscription.fields.each do |name, field|
1560
+ subscription.all_field_definitions.each do |field|
1561
+ if !field.extensions.any? { |ext| ext.is_a?(Subscriptions::DefaultSubscriptionResolveExtension) }
1698
1562
  field.extension(Subscriptions::DefaultSubscriptionResolveExtension)
1699
1563
  end
1700
1564
  end
1701
1565
  end
1702
1566
  end
1703
1567
 
1568
+ # Called when execution encounters a `SystemStackError`. By default, it adds a client-facing error to the response.
1569
+ # You could modify this method to report this error to your bug tracker.
1570
+ # @param query [GraphQL::Query]
1571
+ # @param err [SystemStackError]
1572
+ # @return [void]
1704
1573
  def query_stack_error(query, err)
1705
1574
  query.context.errors.push(GraphQL::ExecutionError.new("This query is too large to execute."))
1706
1575
  end
1707
1576
 
1577
+ # Call the given block at the right time, either:
1578
+ # - Right away, if `value` is not registered with `lazy_resolve`
1579
+ # - After resolving `value`, if it's registered with `lazy_resolve` (eg, `Promise`)
1580
+ # @api private
1581
+ def after_lazy(value, &block)
1582
+ if lazy?(value)
1583
+ GraphQL::Execution::Lazy.new do
1584
+ result = sync_lazy(value)
1585
+ # The returned result might also be lazy, so check it, too
1586
+ after_lazy(result, &block)
1587
+ end
1588
+ else
1589
+ yield(value) if block_given?
1590
+ end
1591
+ end
1592
+
1593
+ # Override this method to handle lazy objects in a custom way.
1594
+ # @param value [Object] an instance of a class registered with {.lazy_resolve}
1595
+ # @return [Object] A GraphQL-ready (non-lazy) object
1596
+ # @api private
1597
+ def sync_lazy(value)
1598
+ lazy_method = lazy_method_name(value)
1599
+ if lazy_method
1600
+ synced_value = value.public_send(lazy_method)
1601
+ sync_lazy(synced_value)
1602
+ else
1603
+ value
1604
+ end
1605
+ end
1606
+
1607
+ # @return [Symbol, nil] The method name to lazily resolve `obj`, or nil if `obj`'s class wasn't registered with {#lazy_resolve}.
1608
+ def lazy_method_name(obj)
1609
+ lazy_methods.get(obj)
1610
+ end
1611
+
1612
+ # @return [Boolean] True if this object should be lazily resolved
1613
+ def lazy?(obj)
1614
+ !!lazy_method_name(obj)
1615
+ end
1616
+
1617
+ # Return a lazy if any of `maybe_lazies` are lazy,
1618
+ # otherwise, call the block eagerly and return the result.
1619
+ # @param maybe_lazies [Array]
1620
+ # @api private
1621
+ def after_any_lazies(maybe_lazies)
1622
+ if maybe_lazies.any? { |l| lazy?(l) }
1623
+ GraphQL::Execution::Lazy.all(maybe_lazies).then do |result|
1624
+ yield result
1625
+ end
1626
+ else
1627
+ yield maybe_lazies
1628
+ end
1629
+ end
1630
+
1631
+ # Returns `DidYouMean` if it's defined.
1632
+ # Override this to return `nil` if you don't want to use `DidYouMean`
1633
+ def did_you_mean(new_dym = NOT_CONFIGURED)
1634
+ if NOT_CONFIGURED.equal?(new_dym)
1635
+ if defined?(@did_you_mean)
1636
+ @did_you_mean
1637
+ else
1638
+ find_inherited_value(:did_you_mean, defined?(DidYouMean) ? DidYouMean : nil)
1639
+ end
1640
+ else
1641
+ @did_you_mean = new_dym
1642
+ end
1643
+ end
1644
+
1708
1645
  private
1709
1646
 
1647
+ def add_trace_options_for(mode, new_options)
1648
+ if mode == :default
1649
+ own_trace_modes.each do |mode_name, t_class|
1650
+ if t_class <= DefaultTraceClass
1651
+ t_opts = trace_options_for(mode_name)
1652
+ t_opts.merge!(new_options)
1653
+ end
1654
+ end
1655
+ else
1656
+ t_opts = trace_options_for(mode)
1657
+ t_opts.merge!(new_options)
1658
+ end
1659
+ nil
1660
+ end
1661
+
1710
1662
  # @param t [Module, Array<Module>]
1711
1663
  # @return [void]
1712
1664
  def add_type_and_traverse(t, root:)
@@ -1716,12 +1668,42 @@ module GraphQL
1716
1668
  end
1717
1669
  new_types = Array(t)
1718
1670
  addition = Schema::Addition.new(schema: self, own_types: own_types, new_types: new_types)
1719
- own_types.merge!(addition.types)
1671
+ addition.types.each do |name, types_entry| # rubocop:disable Development/ContextIsPassedCop -- build-time, not query-time
1672
+ if (prev_entry = own_types[name])
1673
+ prev_entries = case prev_entry
1674
+ when Array
1675
+ prev_entry
1676
+ when Module
1677
+ own_types[name] = [prev_entry]
1678
+ else
1679
+ raise "Invariant: unexpected prev_entry at #{name.inspect} when adding #{t.inspect}"
1680
+ end
1681
+
1682
+ case types_entry
1683
+ when Array
1684
+ prev_entries.concat(types_entry)
1685
+ prev_entries.uniq! # in case any are being re-visited
1686
+ when Module
1687
+ if !prev_entries.include?(types_entry)
1688
+ prev_entries << types_entry
1689
+ end
1690
+ else
1691
+ raise "Invariant: unexpected types_entry at #{name} when adding #{t.inspect}"
1692
+ end
1693
+ else
1694
+ if types_entry.is_a?(Array)
1695
+ types_entry.uniq!
1696
+ end
1697
+ own_types[name] = types_entry
1698
+ end
1699
+ end
1700
+
1720
1701
  own_possible_types.merge!(addition.possible_types) { |key, old_val, new_val| old_val + new_val }
1721
1702
  own_union_memberships.merge!(addition.union_memberships)
1722
1703
 
1723
1704
  addition.references.each { |thing, pointers|
1724
- pointers.each { |pointer| references_to(thing, from: pointer) }
1705
+ prev_refs = own_references_to[thing] || []
1706
+ own_references_to[thing] = prev_refs | pointers.to_a
1725
1707
  }
1726
1708
 
1727
1709
  addition.directives.each { |dir_class| own_directives[dir_class.graphql_name] = dir_class }
@@ -1739,7 +1721,7 @@ module GraphQL
1739
1721
  else
1740
1722
  @lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
1741
1723
  @lazy_methods.set(GraphQL::Execution::Lazy, :value)
1742
- @lazy_methods.set(GraphQL::Dataloader::Request, :load)
1724
+ @lazy_methods.set(GraphQL::Dataloader::Request, :load_with_deprecation_warning)
1743
1725
  end
1744
1726
  end
1745
1727
  @lazy_methods
@@ -1749,6 +1731,10 @@ module GraphQL
1749
1731
  @own_types ||= {}
1750
1732
  end
1751
1733
 
1734
+ def own_references_to
1735
+ @own_references_to ||= {}.compare_by_identity
1736
+ end
1737
+
1752
1738
  def non_introspection_types
1753
1739
  find_inherited_value(:non_introspection_types, EMPTY_HASH).merge(own_types)
1754
1740
  end
@@ -1762,7 +1748,7 @@ module GraphQL
1762
1748
  end
1763
1749
 
1764
1750
  def own_possible_types
1765
- @own_possible_types ||= {}
1751
+ @own_possible_types ||= {}.compare_by_identity
1766
1752
  end
1767
1753
 
1768
1754
  def own_union_memberships
@@ -1785,68 +1771,40 @@ module GraphQL
1785
1771
  @defined_query_analyzers ||= []
1786
1772
  end
1787
1773
 
1788
- def all_middleware
1789
- find_inherited_value(:all_middleware, EMPTY_ARRAY) + own_middleware
1790
- end
1791
-
1792
- def own_middleware
1793
- @own_middleware ||= []
1794
- end
1795
-
1796
1774
  def own_multiplex_analyzers
1797
1775
  @own_multiplex_analyzers ||= []
1798
1776
  end
1799
- end
1800
-
1801
- def dataloader_class
1802
- self.class.dataloader_class
1803
- end
1804
-
1805
- # Install these here so that subclasses will also install it.
1806
- use(GraphQL::Pagination::Connections)
1807
-
1808
- protected
1809
-
1810
- def rescues?
1811
- !!@rescue_middleware
1812
- end
1813
1777
 
1814
- # Lazily create a middleware and add it to the schema
1815
- # (Don't add it if it's not used)
1816
- def rescue_middleware
1817
- @rescue_middleware ||= GraphQL::Schema::RescueMiddleware.new.tap { |m| middleware.insert(0, m) }
1778
+ # This is overridden in subclasses to check the inheritance chain
1779
+ def get_references_to(type_defn)
1780
+ own_references_to[type_defn]
1781
+ end
1818
1782
  end
1819
1783
 
1820
- private
1821
-
1822
- def rebuild_artifacts
1823
- if @rebuilding_artifacts
1824
- raise CyclicalDefinitionError, "Part of the schema build process re-triggered the schema build process, causing an infinite loop. Avoid using Schema#types, Schema#possible_types, and Schema#get_field during schema build."
1825
- else
1826
- @rebuilding_artifacts = true
1827
- @introspection_system = Schema::IntrospectionSystem.new(self)
1828
- traversal = Traversal.new(self)
1829
- @types = traversal.type_map
1830
- @root_types = [query, mutation, subscription]
1831
- @instrumented_field_map = traversal.instrumented_field_map
1832
- @type_reference_map = traversal.type_reference_map
1833
- @union_memberships = traversal.union_memberships
1834
- @find_cache = {}
1835
- @finder = Finder.new(self)
1836
- end
1837
- ensure
1838
- @rebuilding_artifacts = false
1784
+ module SubclassGetReferencesTo
1785
+ def get_references_to(type_defn)
1786
+ own_refs = own_references_to[type_defn]
1787
+ inherited_refs = superclass.references_to(type_defn)
1788
+ if inherited_refs&.any?
1789
+ if own_refs&.any?
1790
+ own_refs + inherited_refs
1791
+ else
1792
+ inherited_refs
1793
+ end
1794
+ else
1795
+ own_refs
1796
+ end
1797
+ end
1839
1798
  end
1840
1799
 
1841
- class CyclicalDefinitionError < GraphQL::Error
1842
- end
1800
+ # Install these here so that subclasses will also install it.
1801
+ self.connections = GraphQL::Pagination::Connections.new(schema: self)
1843
1802
 
1844
- def with_definition_error_check
1845
- if @definition_error
1846
- raise @definition_error
1847
- else
1848
- yield
1849
- end
1803
+ # @api private
1804
+ module DefaultTraceClass
1850
1805
  end
1851
1806
  end
1852
1807
  end
1808
+
1809
+ require "graphql/schema/loader"
1810
+ require "graphql/schema/printer"