graphql 2.3.7 → 2.4.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (152) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install_generator.rb +46 -0
  3. data/lib/generators/graphql/orm_mutations_base.rb +1 -1
  4. data/lib/generators/graphql/templates/base_resolver.erb +2 -0
  5. data/lib/generators/graphql/type_generator.rb +1 -1
  6. data/lib/graphql/analysis/field_usage.rb +1 -1
  7. data/lib/graphql/analysis/query_complexity.rb +3 -3
  8. data/lib/graphql/analysis/visitor.rb +8 -7
  9. data/lib/graphql/analysis.rb +4 -4
  10. data/lib/graphql/autoload.rb +38 -0
  11. data/lib/graphql/current.rb +52 -0
  12. data/lib/graphql/dataloader/async_dataloader.rb +7 -6
  13. data/lib/graphql/dataloader/source.rb +7 -4
  14. data/lib/graphql/dataloader.rb +40 -19
  15. data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
  16. data/lib/graphql/execution/interpreter/resolve.rb +13 -9
  17. data/lib/graphql/execution/interpreter/runtime.rb +35 -31
  18. data/lib/graphql/execution/interpreter.rb +9 -5
  19. data/lib/graphql/execution/lookahead.rb +18 -11
  20. data/lib/graphql/introspection/directive_type.rb +1 -1
  21. data/lib/graphql/introspection/entry_points.rb +2 -2
  22. data/lib/graphql/introspection/field_type.rb +1 -1
  23. data/lib/graphql/introspection/schema_type.rb +6 -11
  24. data/lib/graphql/introspection/type_type.rb +5 -5
  25. data/lib/graphql/invalid_null_error.rb +1 -1
  26. data/lib/graphql/language/cache.rb +13 -0
  27. data/lib/graphql/language/comment.rb +18 -0
  28. data/lib/graphql/language/document_from_schema_definition.rb +62 -34
  29. data/lib/graphql/language/lexer.rb +18 -15
  30. data/lib/graphql/language/nodes.rb +24 -16
  31. data/lib/graphql/language/parser.rb +14 -1
  32. data/lib/graphql/language/printer.rb +31 -15
  33. data/lib/graphql/language/sanitized_printer.rb +1 -1
  34. data/lib/graphql/language.rb +6 -6
  35. data/lib/graphql/pagination/connection.rb +1 -1
  36. data/lib/graphql/query/context/scoped_context.rb +1 -1
  37. data/lib/graphql/query/context.rb +13 -6
  38. data/lib/graphql/query/null_context.rb +3 -5
  39. data/lib/graphql/query/variable_validation_error.rb +1 -1
  40. data/lib/graphql/query.rb +72 -18
  41. data/lib/graphql/railtie.rb +7 -0
  42. data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
  43. data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
  44. data/lib/graphql/rubocop.rb +2 -0
  45. data/lib/graphql/schema/addition.rb +2 -1
  46. data/lib/graphql/schema/always_visible.rb +6 -2
  47. data/lib/graphql/schema/argument.rb +14 -1
  48. data/lib/graphql/schema/build_from_definition.rb +9 -1
  49. data/lib/graphql/schema/directive/flagged.rb +2 -2
  50. data/lib/graphql/schema/directive.rb +1 -1
  51. data/lib/graphql/schema/enum.rb +71 -23
  52. data/lib/graphql/schema/enum_value.rb +10 -2
  53. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  54. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  55. data/lib/graphql/schema/field.rb +102 -47
  56. data/lib/graphql/schema/field_extension.rb +1 -1
  57. data/lib/graphql/schema/has_single_input_argument.rb +5 -2
  58. data/lib/graphql/schema/input_object.rb +90 -39
  59. data/lib/graphql/schema/interface.rb +22 -5
  60. data/lib/graphql/schema/introspection_system.rb +5 -16
  61. data/lib/graphql/schema/loader.rb +1 -1
  62. data/lib/graphql/schema/member/base_dsl_methods.rb +15 -0
  63. data/lib/graphql/schema/member/has_arguments.rb +36 -23
  64. data/lib/graphql/schema/member/has_directives.rb +3 -3
  65. data/lib/graphql/schema/member/has_fields.rb +26 -6
  66. data/lib/graphql/schema/member/has_interfaces.rb +4 -4
  67. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  68. data/lib/graphql/schema/member/has_validators.rb +1 -1
  69. data/lib/graphql/schema/object.rb +8 -0
  70. data/lib/graphql/schema/printer.rb +1 -0
  71. data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
  72. data/lib/graphql/schema/resolver.rb +12 -14
  73. data/lib/graphql/schema/subscription.rb +52 -6
  74. data/lib/graphql/schema/type_expression.rb +2 -2
  75. data/lib/graphql/schema/union.rb +1 -1
  76. data/lib/graphql/schema/validator/all_validator.rb +62 -0
  77. data/lib/graphql/schema/validator/required_validator.rb +28 -4
  78. data/lib/graphql/schema/validator.rb +3 -1
  79. data/lib/graphql/schema/visibility/migration.rb +188 -0
  80. data/lib/graphql/schema/visibility/profile.rb +359 -0
  81. data/lib/graphql/schema/visibility/visit.rb +190 -0
  82. data/lib/graphql/schema/visibility.rb +294 -0
  83. data/lib/graphql/schema/warden.rb +179 -16
  84. data/lib/graphql/schema.rb +348 -94
  85. data/lib/graphql/static_validation/base_visitor.rb +6 -5
  86. data/lib/graphql/static_validation/literal_validator.rb +4 -4
  87. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  88. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  89. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -2
  90. data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
  91. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
  92. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +12 -2
  93. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +2 -2
  94. data/lib/graphql/static_validation/rules/fields_will_merge.rb +8 -7
  95. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  96. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +12 -2
  97. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  98. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  99. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  100. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  101. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -4
  102. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +3 -3
  103. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
  104. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
  105. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  106. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  107. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
  108. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +11 -2
  109. data/lib/graphql/static_validation/validation_context.rb +18 -2
  110. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +3 -2
  111. data/lib/graphql/subscriptions/broadcast_analyzer.rb +10 -4
  112. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
  113. data/lib/graphql/subscriptions/event.rb +13 -2
  114. data/lib/graphql/subscriptions/serialize.rb +2 -0
  115. data/lib/graphql/subscriptions.rb +6 -4
  116. data/lib/graphql/testing/helpers.rb +10 -6
  117. data/lib/graphql/tracing/active_support_notifications_trace.rb +1 -1
  118. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  119. data/lib/graphql/tracing/appoptics_trace.rb +2 -0
  120. data/lib/graphql/tracing/appoptics_tracing.rb +2 -0
  121. data/lib/graphql/tracing/appsignal_trace.rb +2 -0
  122. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  123. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  124. data/lib/graphql/tracing/data_dog_trace.rb +2 -0
  125. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  126. data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
  127. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  128. data/lib/graphql/tracing/new_relic_trace.rb +2 -0
  129. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  130. data/lib/graphql/tracing/notifications_trace.rb +2 -2
  131. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  132. data/lib/graphql/tracing/null_trace.rb +9 -0
  133. data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
  134. data/lib/graphql/tracing/prometheus_trace.rb +5 -0
  135. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  136. data/lib/graphql/tracing/scout_trace.rb +2 -0
  137. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  138. data/lib/graphql/tracing/sentry_trace.rb +2 -0
  139. data/lib/graphql/tracing/statsd_trace.rb +2 -0
  140. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  141. data/lib/graphql/tracing/trace.rb +3 -0
  142. data/lib/graphql/tracing.rb +28 -30
  143. data/lib/graphql/types/relay/connection_behaviors.rb +12 -2
  144. data/lib/graphql/types/relay/edge_behaviors.rb +11 -1
  145. data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
  146. data/lib/graphql/types.rb +18 -11
  147. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  148. data/lib/graphql/version.rb +1 -1
  149. data/lib/graphql.rb +53 -45
  150. metadata +33 -8
  151. data/lib/graphql/language/token.rb +0 -34
  152. data/lib/graphql/schema/invalid_type_error.rb +0 -7
@@ -5,7 +5,6 @@ require "graphql/schema/always_visible"
5
5
  require "graphql/schema/base_64_encoder"
6
6
  require "graphql/schema/find_inherited_value"
7
7
  require "graphql/schema/finder"
8
- require "graphql/schema/invalid_type_error"
9
8
  require "graphql/schema/introspection_system"
10
9
  require "graphql/schema/late_bound_type"
11
10
  require "graphql/schema/null_mask"
@@ -46,6 +45,7 @@ require "graphql/schema/mutation"
46
45
  require "graphql/schema/has_single_input_argument"
47
46
  require "graphql/schema/relay_classic_mutation"
48
47
  require "graphql/schema/subscription"
48
+ require "graphql/schema/visibility"
49
49
 
50
50
  module GraphQL
51
51
  # A GraphQL schema which may be queried with {GraphQL::Query}.
@@ -73,6 +73,9 @@ module GraphQL
73
73
  class Schema
74
74
  extend GraphQL::Schema::Member::HasAstNode
75
75
  extend GraphQL::Schema::FindInheritedValue
76
+ extend Autoload
77
+
78
+ autoload :BUILT_IN_TYPES, "graphql/schema/built_in_types"
76
79
 
77
80
  class DuplicateNamesError < GraphQL::Error
78
81
  attr_reader :duplicated_name
@@ -162,6 +165,7 @@ module GraphQL
162
165
  # re-apply them here
163
166
  mods = trace_modules_for(:default)
164
167
  mods.each { |mod| new_class.include(mod) }
168
+ new_class.include(DefaultTraceClass)
165
169
  trace_mode(:default, new_class)
166
170
  backtrace_class = Class.new(new_class)
167
171
  backtrace_class.include(GraphQL::Backtrace::Trace)
@@ -204,24 +208,19 @@ module GraphQL
204
208
  @own_trace_modes ||= {}
205
209
  end
206
210
 
207
- module DefaultTraceClass
208
- end
209
-
210
- private_constant :DefaultTraceClass
211
-
212
211
  def build_trace_mode(mode)
213
212
  case mode
214
213
  when :default
215
214
  # Use the superclass's default mode if it has one, or else start an inheritance chain at the built-in base class.
216
- base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode)) || GraphQL::Tracing::Trace
217
- Class.new(base_class) do
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
218
217
  include DefaultTraceClass
219
- end
218
+ end)
220
219
  when :default_backtrace
221
220
  schema_base_class = trace_class_for(:default, build: true)
222
- Class.new(schema_base_class) do
221
+ const_set(:DefaultTraceBacktrace, Class.new(schema_base_class) do
223
222
  include(GraphQL::Backtrace::Trace)
224
- end
223
+ end)
225
224
  else
226
225
  # First, see if the superclass has a custom-defined class for this.
227
226
  # Then, if it doesn't, use this class's default trace
@@ -237,7 +236,7 @@ module GraphQL
237
236
  add_trace_options_for(mode, default_options)
238
237
 
239
238
  Class.new(base_class) do
240
- mods.any? && include(*mods)
239
+ !mods.empty? && include(*mods)
241
240
  end
242
241
  end
243
242
  end
@@ -321,8 +320,11 @@ module GraphQL
321
320
  GraphQL::StaticValidation::Validator.new(schema: self)
322
321
  end
323
322
 
323
+ # Add `plugin` to this schema
324
+ # @param plugin [#use] A Schema plugin
325
+ # @return void
324
326
  def use(plugin, **kwargs)
325
- if kwargs.any?
327
+ if !kwargs.empty?
326
328
  plugin.use(self, **kwargs)
327
329
  else
328
330
  plugin.use(self)
@@ -338,6 +340,10 @@ module GraphQL
338
340
  # @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
339
341
  # @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
340
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
346
+ end
341
347
  all_types = non_introspection_types.merge(introspection_system.types)
342
348
  visible_types = {}
343
349
  all_types.each do |k, v|
@@ -363,27 +369,36 @@ module GraphQL
363
369
  end
364
370
 
365
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}
366
374
  # @return [Module, nil] A type, or nil if there's no type called `type_name`
367
- def get_type(type_name, context = GraphQL::Query::NullContext.instance)
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)
378
+ end
368
379
  local_entry = own_types[type_name]
369
380
  type_defn = case local_entry
370
381
  when nil
371
382
  nil
372
383
  when Array
373
- visible_t = nil
374
- warden = Warden.from_context(context)
375
- local_entry.each do |t|
376
- if warden.visible_type?(t, context)
377
- if visible_t.nil?
378
- visible_t = t
379
- else
380
- raise DuplicateNamesError.new(
381
- duplicated_name: type_name, duplicated_definition_1: visible_t.inspect, duplicated_definition_2: t.inspect
382
- )
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
383
398
  end
384
399
  end
400
+ visible_t
385
401
  end
386
- visible_t
387
402
  when Module
388
403
  local_entry
389
404
  else
@@ -392,7 +407,7 @@ module GraphQL
392
407
 
393
408
  type_defn ||
394
409
  introspection_system.types[type_name] || # todo context-specific introspection?
395
- (superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context) : nil)
410
+ (superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context, use_visibility_profile) : nil)
396
411
  end
397
412
 
398
413
  # @return [Boolean] Does this schema have _any_ definition for a type named `type_name`, regardless of visibility?
@@ -419,55 +434,127 @@ module GraphQL
419
434
  end
420
435
  end
421
436
 
422
- def new_connections?
423
- !!connections
424
- end
425
-
426
- def query(new_query_object = nil)
427
- 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?
428
447
  if @query_object
429
- 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
430
462
  else
431
- @query_object = new_query_object
432
- add_type_and_traverse(new_query_object, root: true)
433
- nil
463
+ @query_object = new_query_object || lazy_load_block.call
464
+ add_type_and_traverse(@query_object, root: true)
434
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
435
471
  else
436
472
  @query_object || find_inherited_value(:query)
437
473
  end
438
474
  end
439
475
 
440
- def mutation(new_mutation_object = nil)
441
- 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?
442
486
  if @mutation_object
443
- 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
444
501
  else
445
- @mutation_object = new_mutation_object
446
- add_type_and_traverse(new_mutation_object, root: true)
447
- nil
502
+ @mutation_object = new_mutation_object || lazy_load_block.call
503
+ add_type_and_traverse(@mutation_object, root: true)
448
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
449
510
  else
450
511
  @mutation_object || find_inherited_value(:mutation)
451
512
  end
452
513
  end
453
514
 
454
- def subscription(new_subscription_object = nil)
455
- 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?
456
525
  if @subscription_object
457
- 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
458
541
  else
459
- @subscription_object = new_subscription_object
542
+ @subscription_object = new_subscription_object || lazy_load_block.call
460
543
  add_subscription_extension_if_necessary
461
- add_type_and_traverse(new_subscription_object, root: true)
462
- nil
544
+ add_type_and_traverse(@subscription_object, root: true)
463
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
464
552
  else
465
553
  @subscription_object || find_inherited_value(:subscription)
466
554
  end
467
555
  end
468
556
 
469
- # @see [GraphQL::Schema::Warden] Restricted access to root types
470
- # @return [GraphQL::ObjectType, nil]
557
+ # @api private
471
558
  def root_type_for_operation(operation)
472
559
  case operation
473
560
  when "query"
@@ -481,10 +568,16 @@ module GraphQL
481
568
  end
482
569
  end
483
570
 
571
+ # @return [Array<Class>] The root types (query, mutation, subscription) defined for this schema
484
572
  def root_types
485
- @root_types
573
+ if use_visibility_profile?
574
+ [query, mutation, subscription].compact
575
+ else
576
+ @root_types
577
+ end
486
578
  end
487
579
 
580
+ # @api private
488
581
  def warden_class
489
582
  if defined?(@warden_class)
490
583
  @warden_class
@@ -495,12 +588,48 @@ module GraphQL
495
588
  end
496
589
  end
497
590
 
591
+ # @api private
498
592
  attr_writer :warden_class
499
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
617
+ end
618
+ end
619
+
500
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}
501
623
  # @return [Hash<String, Module>] All possible types, if no `type` is given.
502
624
  # @return [Array<Module>] Possible types for `type`, if it's given.
503
- def possible_types(type = nil, context = GraphQL::Query::NullContext.instance)
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
504
633
  if type
505
634
  # TODO duck-typing `.possible_types` would probably be nicer here
506
635
  if type.kind.union?
@@ -518,7 +647,7 @@ module GraphQL
518
647
  introspection_system.possible_types[type] ||
519
648
  (
520
649
  superclass.respond_to?(:possible_types) ?
521
- superclass.possible_types(type, context) :
650
+ superclass.possible_types(type, context, use_visibility_profile) :
522
651
  EMPTY_ARRAY
523
652
  )
524
653
  end
@@ -565,7 +694,7 @@ module GraphQL
565
694
  # and generally speaking, we won't inherit any values.
566
695
  # So optimize the most common case -- don't create a duplicate Hash.
567
696
  inherited_value = find_inherited_value(:references_to, EMPTY_HASH)
568
- if inherited_value.any?
697
+ if !inherited_value.empty?
569
698
  inherited_value.merge(own_references_to)
570
699
  else
571
700
  own_references_to
@@ -573,9 +702,8 @@ module GraphQL
573
702
  end
574
703
  end
575
704
 
576
- def type_from_ast(ast_node, context: nil)
577
- type_owner = context ? context.warden : self
578
- 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)
579
707
  end
580
708
 
581
709
  def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext.instance)
@@ -605,20 +733,27 @@ module GraphQL
605
733
  type.fields(context)
606
734
  end
607
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
608
739
  def introspection(new_introspection_namespace = nil)
609
740
  if new_introspection_namespace
610
741
  @introspection = new_introspection_namespace
611
742
  # reset this cached value:
612
743
  @introspection_system = nil
744
+ introspection_system
745
+ @introspection
613
746
  else
614
747
  @introspection || find_inherited_value(:introspection)
615
748
  end
616
749
  end
617
750
 
751
+ # @return [Schema::IntrospectionSystem] Based on {introspection}
618
752
  def introspection_system
619
753
  if !@introspection_system
620
754
  @introspection_system = Schema::IntrospectionSystem.new(self)
621
755
  @introspection_system.resolve_late_bindings
756
+ self.visibility&.introspection_system_configured(@introspection_system)
622
757
  end
623
758
  @introspection_system
624
759
  end
@@ -722,6 +857,7 @@ module GraphQL
722
857
  res[:errors]
723
858
  end
724
859
 
860
+ # @param new_query_class [Class<GraphQL::Query>] A subclass to use when executing queries
725
861
  def query_class(new_query_class = NOT_CONFIGURED)
726
862
  if NOT_CONFIGURED.equal?(new_query_class)
727
863
  @query_class || (superclass.respond_to?(:query_class) ? superclass.query_class : GraphQL::Query)
@@ -732,13 +868,11 @@ module GraphQL
732
868
 
733
869
  attr_writer :validate_max_errors
734
870
 
735
- def validate_max_errors(new_validate_max_errors = nil)
736
- if new_validate_max_errors
737
- @validate_max_errors = new_validate_max_errors
738
- elsif defined?(@validate_max_errors)
739
- @validate_max_errors
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)
740
874
  else
741
- find_inherited_value(:validate_max_errors)
875
+ @validate_max_errors = new_validate_max_errors
742
876
  end
743
877
  end
744
878
 
@@ -769,16 +903,6 @@ module GraphQL
769
903
  @analysis_engine || find_inherited_value(:analysis_engine, self.default_analysis_engine)
770
904
  end
771
905
 
772
- def using_ast_analysis?
773
- true
774
- end
775
-
776
- def interpreter?
777
- true
778
- end
779
-
780
- attr_writer :interpreter
781
-
782
906
  def error_bubbling(new_error_bubbling = nil)
783
907
  if !new_error_bubbling.nil?
784
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")
@@ -856,7 +980,7 @@ module GraphQL
856
980
  # @param new_extra_types [Module] Type definitions to include in printing and introspection, even though they aren't referenced in the schema
857
981
  # @return [Array<Module>] Type definitions added to this schema
858
982
  def extra_types(*new_extra_types)
859
- if new_extra_types.any?
983
+ if !new_extra_types.empty?
860
984
  new_extra_types = new_extra_types.flatten
861
985
  @own_extra_types ||= []
862
986
  @own_extra_types.concat(new_extra_types)
@@ -873,11 +997,18 @@ module GraphQL
873
997
  end
874
998
  end
875
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
876
1007
  def orphan_types(*new_orphan_types)
877
- if new_orphan_types.any?
1008
+ if !new_orphan_types.empty?
878
1009
  new_orphan_types = new_orphan_types.flatten
879
1010
  non_object_types = new_orphan_types.reject { |ot| ot.is_a?(Class) && ot < GraphQL::Schema::Object }
880
- if non_object_types.any?
1011
+ if !non_object_types.empty?
881
1012
  raise ArgumentError, <<~ERR
882
1013
  Only object type classes should be added as `orphan_types(...)`.
883
1014
 
@@ -887,13 +1018,14 @@ module GraphQL
887
1018
  To add other types to your schema, you might want `extra_types`: https://graphql-ruby.org/schema/definition.html#extra-types
888
1019
  ERR
889
1020
  end
890
- add_type_and_traverse(new_orphan_types, root: false)
1021
+ add_type_and_traverse(new_orphan_types, root: false) unless use_visibility_profile?
891
1022
  own_orphan_types.concat(new_orphan_types.flatten)
1023
+ self.visibility&.orphan_types_configured(new_orphan_types)
892
1024
  end
893
1025
 
894
1026
  inherited_ot = find_inherited_value(:orphan_types, nil)
895
1027
  if inherited_ot
896
- if own_orphan_types.any?
1028
+ if !own_orphan_types.empty?
897
1029
  inherited_ot + own_orphan_types
898
1030
  else
899
1031
  inherited_ot
@@ -919,6 +1051,8 @@ module GraphQL
919
1051
  end
920
1052
  end
921
1053
 
1054
+
1055
+ # @param new_default_logger [#log] Something to use for logging messages
922
1056
  def default_logger(new_default_logger = NOT_CONFIGURED)
923
1057
  if NOT_CONFIGURED.equal?(new_default_logger)
924
1058
  if defined?(@default_logger)
@@ -939,6 +1073,7 @@ module GraphQL
939
1073
  end
940
1074
  end
941
1075
 
1076
+ # @param new_context_class [Class<GraphQL::Query::Context>] A subclass to use when executing queries
942
1077
  def context_class(new_context_class = nil)
943
1078
  if new_context_class
944
1079
  @context_class = new_context_class
@@ -947,6 +1082,20 @@ module GraphQL
947
1082
  end
948
1083
  end
949
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
950
1099
  def rescue_from(*err_classes, &handler_block)
951
1100
  err_classes.each do |err_class|
952
1101
  Execution::Errors.register_rescue_from(err_class, error_handlers[:subclass_handlers], handler_block)
@@ -1013,8 +1162,24 @@ module GraphQL
1013
1162
  end
1014
1163
  end
1015
1164
 
1016
- def resolve_type(type, obj, ctx)
1017
- raise GraphQL::RequiredImplementationMissingError, "#{self.name}.resolve_type(type, obj, ctx) must be implemented to use Union types, Interface types, or `loads:` (tried to resolve: #{type.name})"
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})"
1018
1183
  end
1019
1184
  # rubocop:enable Lint/DuplicateMethods
1020
1185
 
@@ -1029,15 +1194,45 @@ module GraphQL
1029
1194
  child_class.own_trace_modes[name] = child_class.build_trace_mode(name)
1030
1195
  end
1031
1196
  child_class.singleton_class.prepend(ResolveTypeWithType)
1032
- super
1033
- end
1034
1197
 
1035
- def object_from_id(node_id, ctx)
1036
- 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
1037
1203
  end
1038
1204
 
1039
- def id_from_object(object, type, ctx)
1040
- 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}`)"
1041
1236
  end
1042
1237
 
1043
1238
  def visible?(member, ctx)
@@ -1053,6 +1248,10 @@ module GraphQL
1053
1248
  Member::HasDirectives.get_directives(self, @own_schema_directives, :schema_directives)
1054
1249
  end
1055
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
1056
1255
  # This hook is called when an object fails an `authorized?` check.
1057
1256
  # You might report to your bug tracker here, so you can correct
1058
1257
  # the field resolvers not to return unauthorized objects.
@@ -1088,6 +1287,16 @@ module GraphQL
1088
1287
  unauthorized_object(unauthorized_error)
1089
1288
  end
1090
1289
 
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
1091
1300
  def type_error(type_error, ctx)
1092
1301
  case type_error
1093
1302
  when GraphQL::InvalidNullError
@@ -1126,12 +1335,12 @@ module GraphQL
1126
1335
  # Add several directives at once
1127
1336
  # @param new_directives [Class]
1128
1337
  def directives(*new_directives)
1129
- if new_directives.any?
1338
+ if !new_directives.empty?
1130
1339
  new_directives.flatten.each { |d| directive(d) }
1131
1340
  end
1132
1341
 
1133
1342
  inherited_dirs = find_inherited_value(:directives, default_directives)
1134
- if own_directives.any?
1343
+ if !own_directives.empty?
1135
1344
  inherited_dirs.merge(own_directives)
1136
1345
  else
1137
1346
  inherited_dirs
@@ -1142,7 +1351,11 @@ module GraphQL
1142
1351
  # @param new_directive [Class]
1143
1352
  # @return void
1144
1353
  def directive(new_directive)
1145
- 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
1146
1359
  end
1147
1360
 
1148
1361
  def default_directives
@@ -1179,6 +1392,7 @@ module GraphQL
1179
1392
  # @param mode [Symbol] Trace module will only be used for this trade mode
1180
1393
  # @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
1181
1394
  # @return [void]
1395
+ # @see GraphQL::Tracing::Trace for available tracing methods
1182
1396
  def trace_with(trace_mod, mode: :default, **options)
1183
1397
  if mode.is_a?(Array)
1184
1398
  mode.each { |m| trace_with(trace_mod, mode: m, **options) }
@@ -1256,6 +1470,8 @@ module GraphQL
1256
1470
  trace_class_for_mode.new(**trace_options)
1257
1471
  end
1258
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
1259
1475
  def query_analyzer(new_analyzer)
1260
1476
  own_query_analyzers << new_analyzer
1261
1477
  end
@@ -1264,6 +1480,8 @@ module GraphQL
1264
1480
  find_inherited_value(:query_analyzers, EMPTY_ARRAY) + own_query_analyzers
1265
1481
  end
1266
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
1267
1485
  def multiplex_analyzer(new_analyzer)
1268
1486
  own_multiplex_analyzers << new_analyzer
1269
1487
  end
@@ -1336,7 +1554,8 @@ module GraphQL
1336
1554
 
1337
1555
  # @api private
1338
1556
  def add_subscription_extension_if_necessary
1339
- if !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
1340
1559
  @subscription_extension_added = true
1341
1560
  subscription.all_field_definitions.each do |field|
1342
1561
  if !field.extensions.any? { |ext| ext.is_a?(Subscriptions::DefaultSubscriptionResolveExtension) }
@@ -1346,6 +1565,11 @@ module GraphQL
1346
1565
  end
1347
1566
  end
1348
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]
1349
1573
  def query_stack_error(query, err)
1350
1574
  query.context.errors.push(GraphQL::ExecutionError.new("This query is too large to execute."))
1351
1575
  end
@@ -1404,11 +1628,34 @@ module GraphQL
1404
1628
  end
1405
1629
  end
1406
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
+
1407
1645
  private
1408
1646
 
1409
1647
  def add_trace_options_for(mode, new_options)
1410
- t_opts = trace_options_for(mode)
1411
- t_opts.merge!(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
1412
1659
  nil
1413
1660
  end
1414
1661
 
@@ -1485,7 +1732,7 @@ module GraphQL
1485
1732
  end
1486
1733
 
1487
1734
  def own_references_to
1488
- @own_references_to ||= {}.tap(&:compare_by_identity)
1735
+ @own_references_to ||= {}.compare_by_identity
1489
1736
  end
1490
1737
 
1491
1738
  def non_introspection_types
@@ -1501,7 +1748,7 @@ module GraphQL
1501
1748
  end
1502
1749
 
1503
1750
  def own_possible_types
1504
- @own_possible_types ||= {}.tap(&:compare_by_identity)
1751
+ @own_possible_types ||= {}.compare_by_identity
1505
1752
  end
1506
1753
 
1507
1754
  def own_union_memberships
@@ -1552,5 +1799,12 @@ module GraphQL
1552
1799
 
1553
1800
  # Install these here so that subclasses will also install it.
1554
1801
  self.connections = GraphQL::Pagination::Connections.new(schema: self)
1802
+
1803
+ # @api private
1804
+ module DefaultTraceClass
1805
+ end
1555
1806
  end
1556
1807
  end
1808
+
1809
+ require "graphql/schema/loader"
1810
+ require "graphql/schema/printer"