graphql 1.12.12 → 2.4.8

Sign up to get free protection for your applications and to get access to all the features.
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"