graphql 2.3.7 → 2.4.5

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

Potentially problematic release.


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

Files changed (125) 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 +37 -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 +6 -4
  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 +25 -20
  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 +2 -2
  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 +187 -0
  80. data/lib/graphql/schema/visibility/profile.rb +353 -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 +166 -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/event.rb +1 -1
  113. data/lib/graphql/subscriptions.rb +6 -4
  114. data/lib/graphql/testing/helpers.rb +10 -6
  115. data/lib/graphql/tracing/notifications_trace.rb +2 -2
  116. data/lib/graphql/types/relay/connection_behaviors.rb +12 -2
  117. data/lib/graphql/types/relay/edge_behaviors.rb +11 -1
  118. data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
  119. data/lib/graphql/types.rb +18 -11
  120. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  121. data/lib/graphql/version.rb +1 -1
  122. data/lib/graphql.rb +81 -45
  123. metadata +31 -8
  124. data/lib/graphql/language/token.rb +0 -34
  125. 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,9 @@ 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
+
50
+ GraphQL.ensure_eager_load!
49
51
 
50
52
  module GraphQL
51
53
  # A GraphQL schema which may be queried with {GraphQL::Query}.
@@ -162,6 +164,7 @@ module GraphQL
162
164
  # re-apply them here
163
165
  mods = trace_modules_for(:default)
164
166
  mods.each { |mod| new_class.include(mod) }
167
+ new_class.include(DefaultTraceClass)
165
168
  trace_mode(:default, new_class)
166
169
  backtrace_class = Class.new(new_class)
167
170
  backtrace_class.include(GraphQL::Backtrace::Trace)
@@ -204,24 +207,19 @@ module GraphQL
204
207
  @own_trace_modes ||= {}
205
208
  end
206
209
 
207
- module DefaultTraceClass
208
- end
209
-
210
- private_constant :DefaultTraceClass
211
-
212
210
  def build_trace_mode(mode)
213
211
  case mode
214
212
  when :default
215
213
  # 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
214
+ base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode, build: true)) || GraphQL::Tracing::Trace
215
+ const_set(:DefaultTrace, Class.new(base_class) do
218
216
  include DefaultTraceClass
219
- end
217
+ end)
220
218
  when :default_backtrace
221
219
  schema_base_class = trace_class_for(:default, build: true)
222
- Class.new(schema_base_class) do
220
+ const_set(:DefaultTraceBacktrace, Class.new(schema_base_class) do
223
221
  include(GraphQL::Backtrace::Trace)
224
- end
222
+ end)
225
223
  else
226
224
  # First, see if the superclass has a custom-defined class for this.
227
225
  # Then, if it doesn't, use this class's default trace
@@ -237,7 +235,7 @@ module GraphQL
237
235
  add_trace_options_for(mode, default_options)
238
236
 
239
237
  Class.new(base_class) do
240
- mods.any? && include(*mods)
238
+ !mods.empty? && include(*mods)
241
239
  end
242
240
  end
243
241
  end
@@ -321,8 +319,11 @@ module GraphQL
321
319
  GraphQL::StaticValidation::Validator.new(schema: self)
322
320
  end
323
321
 
322
+ # Add `plugin` to this schema
323
+ # @param plugin [#use] A Schema plugin
324
+ # @return void
324
325
  def use(plugin, **kwargs)
325
- if kwargs.any?
326
+ if !kwargs.empty?
326
327
  plugin.use(self, **kwargs)
327
328
  else
328
329
  plugin.use(self)
@@ -338,6 +339,10 @@ module GraphQL
338
339
  # @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
339
340
  # @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
340
341
  def types(context = GraphQL::Query::NullContext.instance)
342
+ if use_visibility_profile?
343
+ types = Visibility::Profile.from_context(context, self)
344
+ return types.all_types_h
345
+ end
341
346
  all_types = non_introspection_types.merge(introspection_system.types)
342
347
  visible_types = {}
343
348
  all_types.each do |k, v|
@@ -363,27 +368,36 @@ module GraphQL
363
368
  end
364
369
 
365
370
  # @param type_name [String]
371
+ # @param context [GraphQL::Query::Context] Used for filtering definitions at query-time
372
+ # @param use_visibility_profile Private, for migration to {Schema::Visibility}
366
373
  # @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)
374
+ def get_type(type_name, context = GraphQL::Query::NullContext.instance, use_visibility_profile = use_visibility_profile?)
375
+ if use_visibility_profile
376
+ return Visibility::Profile.from_context(context, self).type(type_name)
377
+ end
368
378
  local_entry = own_types[type_name]
369
379
  type_defn = case local_entry
370
380
  when nil
371
381
  nil
372
382
  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
- )
383
+ if context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile)
384
+ local_entry
385
+ else
386
+ visible_t = nil
387
+ warden = Warden.from_context(context)
388
+ local_entry.each do |t|
389
+ if warden.visible_type?(t, context)
390
+ if visible_t.nil?
391
+ visible_t = t
392
+ else
393
+ raise DuplicateNamesError.new(
394
+ duplicated_name: type_name, duplicated_definition_1: visible_t.inspect, duplicated_definition_2: t.inspect
395
+ )
396
+ end
383
397
  end
384
398
  end
399
+ visible_t
385
400
  end
386
- visible_t
387
401
  when Module
388
402
  local_entry
389
403
  else
@@ -392,7 +406,7 @@ module GraphQL
392
406
 
393
407
  type_defn ||
394
408
  introspection_system.types[type_name] || # todo context-specific introspection?
395
- (superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context) : nil)
409
+ (superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context, use_visibility_profile) : nil)
396
410
  end
397
411
 
398
412
  # @return [Boolean] Does this schema have _any_ definition for a type named `type_name`, regardless of visibility?
@@ -419,55 +433,127 @@ module GraphQL
419
433
  end
420
434
  end
421
435
 
422
- def new_connections?
423
- !!connections
424
- end
425
-
426
- def query(new_query_object = nil)
427
- if new_query_object
436
+ # Get or set the root `query { ... }` object for this schema.
437
+ #
438
+ # @example Using `Types::Query` as the entry-point
439
+ # query { Types::Query }
440
+ #
441
+ # @param new_query_object [Class<GraphQL::Schema::Object>] The root type to use for queries
442
+ # @param lazy_load_block If a block is given, then it will be called when GraphQL-Ruby needs the root query type.
443
+ # @return [Class<GraphQL::Schema::Object>, nil] The configured query root type, if there is one.
444
+ def query(new_query_object = nil, &lazy_load_block)
445
+ if new_query_object || block_given?
428
446
  if @query_object
429
- raise GraphQL::Error, "Second definition of `query(...)` (#{new_query_object.inspect}) is invalid, already configured with #{@query_object.inspect}"
447
+ dup_defn = new_query_object || yield
448
+ raise GraphQL::Error, "Second definition of `query(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@query_object.inspect}"
449
+ elsif use_visibility_profile?
450
+ if block_given?
451
+ if visibility.preload?
452
+ @query_object = lazy_load_block.call
453
+ self.visibility.query_configured(@query_object)
454
+ else
455
+ @query_object = lazy_load_block
456
+ end
457
+ else
458
+ @query_object = new_query_object
459
+ self.visibility.query_configured(@query_object)
460
+ end
430
461
  else
431
- @query_object = new_query_object
432
- add_type_and_traverse(new_query_object, root: true)
433
- nil
462
+ @query_object = new_query_object || lazy_load_block.call
463
+ add_type_and_traverse(@query_object, root: true)
434
464
  end
465
+ nil
466
+ elsif @query_object.is_a?(Proc)
467
+ @query_object = @query_object.call
468
+ self.visibility&.query_configured(@query_object)
469
+ @query_object
435
470
  else
436
471
  @query_object || find_inherited_value(:query)
437
472
  end
438
473
  end
439
474
 
440
- def mutation(new_mutation_object = nil)
441
- if new_mutation_object
475
+ # Get or set the root `mutation { ... }` object for this schema.
476
+ #
477
+ # @example Using `Types::Mutation` as the entry-point
478
+ # mutation { Types::Mutation }
479
+ #
480
+ # @param new_mutation_object [Class<GraphQL::Schema::Object>] The root type to use for mutations
481
+ # @param lazy_load_block If a block is given, then it will be called when GraphQL-Ruby needs the root mutation type.
482
+ # @return [Class<GraphQL::Schema::Object>, nil] The configured mutation root type, if there is one.
483
+ def mutation(new_mutation_object = nil, &lazy_load_block)
484
+ if new_mutation_object || block_given?
442
485
  if @mutation_object
443
- raise GraphQL::Error, "Second definition of `mutation(...)` (#{new_mutation_object.inspect}) is invalid, already configured with #{@mutation_object.inspect}"
486
+ dup_defn = new_mutation_object || yield
487
+ raise GraphQL::Error, "Second definition of `mutation(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@mutation_object.inspect}"
488
+ elsif use_visibility_profile?
489
+ if block_given?
490
+ if visibility.preload?
491
+ @mutation_object = lazy_load_block.call
492
+ self.visibility.mutation_configured(@mutation_object)
493
+ else
494
+ @mutation_object = lazy_load_block
495
+ end
496
+ else
497
+ @mutation_object = new_mutation_object
498
+ self.visibility.mutation_configured(@mutation_object)
499
+ end
444
500
  else
445
- @mutation_object = new_mutation_object
446
- add_type_and_traverse(new_mutation_object, root: true)
447
- nil
501
+ @mutation_object = new_mutation_object || lazy_load_block.call
502
+ add_type_and_traverse(@mutation_object, root: true)
448
503
  end
504
+ nil
505
+ elsif @mutation_object.is_a?(Proc)
506
+ @mutation_object = @mutation_object.call
507
+ self.visibility&.mutation_configured(@mutation_object)
508
+ @mutation_object
449
509
  else
450
510
  @mutation_object || find_inherited_value(:mutation)
451
511
  end
452
512
  end
453
513
 
454
- def subscription(new_subscription_object = nil)
455
- if new_subscription_object
514
+ # Get or set the root `subscription { ... }` object for this schema.
515
+ #
516
+ # @example Using `Types::Subscription` as the entry-point
517
+ # subscription { Types::Subscription }
518
+ #
519
+ # @param new_subscription_object [Class<GraphQL::Schema::Object>] The root type to use for subscriptions
520
+ # @param lazy_load_block If a block is given, then it will be called when GraphQL-Ruby needs the root subscription type.
521
+ # @return [Class<GraphQL::Schema::Object>, nil] The configured subscription root type, if there is one.
522
+ def subscription(new_subscription_object = nil, &lazy_load_block)
523
+ if new_subscription_object || block_given?
456
524
  if @subscription_object
457
- raise GraphQL::Error, "Second definition of `subscription(...)` (#{new_subscription_object.inspect}) is invalid, already configured with #{@subscription_object.inspect}"
525
+ dup_defn = new_subscription_object || yield
526
+ raise GraphQL::Error, "Second definition of `subscription(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@subscription_object.inspect}"
527
+ elsif use_visibility_profile?
528
+ if block_given?
529
+ if visibility.preload?
530
+ @subscription_object = lazy_load_block.call
531
+ visibility.subscription_configured(@subscription_object)
532
+ else
533
+ @subscription_object = lazy_load_block
534
+ end
535
+ else
536
+ @subscription_object = new_subscription_object
537
+ self.visibility.subscription_configured(@subscription_object)
538
+ end
539
+ add_subscription_extension_if_necessary
458
540
  else
459
- @subscription_object = new_subscription_object
541
+ @subscription_object = new_subscription_object || lazy_load_block.call
460
542
  add_subscription_extension_if_necessary
461
- add_type_and_traverse(new_subscription_object, root: true)
462
- nil
543
+ add_type_and_traverse(@subscription_object, root: true)
463
544
  end
545
+ nil
546
+ elsif @subscription_object.is_a?(Proc)
547
+ @subscription_object = @subscription_object.call
548
+ add_subscription_extension_if_necessary
549
+ self.visibility.subscription_configured(@subscription_object)
550
+ @subscription_object
464
551
  else
465
552
  @subscription_object || find_inherited_value(:subscription)
466
553
  end
467
554
  end
468
555
 
469
- # @see [GraphQL::Schema::Warden] Restricted access to root types
470
- # @return [GraphQL::ObjectType, nil]
556
+ # @api private
471
557
  def root_type_for_operation(operation)
472
558
  case operation
473
559
  when "query"
@@ -481,10 +567,16 @@ module GraphQL
481
567
  end
482
568
  end
483
569
 
570
+ # @return [Array<Class>] The root types (query, mutation, subscription) defined for this schema
484
571
  def root_types
485
- @root_types
572
+ if use_visibility_profile?
573
+ [query, mutation, subscription].compact
574
+ else
575
+ @root_types
576
+ end
486
577
  end
487
578
 
579
+ # @api private
488
580
  def warden_class
489
581
  if defined?(@warden_class)
490
582
  @warden_class
@@ -495,12 +587,48 @@ module GraphQL
495
587
  end
496
588
  end
497
589
 
590
+ # @api private
498
591
  attr_writer :warden_class
499
592
 
593
+ # @api private
594
+ def visibility_profile_class
595
+ if defined?(@visibility_profile_class)
596
+ @visibility_profile_class
597
+ elsif superclass.respond_to?(:visibility_profile_class)
598
+ superclass.visibility_profile_class
599
+ else
600
+ GraphQL::Schema::Visibility::Profile
601
+ end
602
+ end
603
+
604
+ # @api private
605
+ attr_writer :visibility_profile_class, :use_visibility_profile
606
+ # @api private
607
+ attr_accessor :visibility
608
+ # @api private
609
+ def use_visibility_profile?
610
+ if defined?(@use_visibility_profile)
611
+ @use_visibility_profile
612
+ elsif superclass.respond_to?(:use_visibility_profile?)
613
+ superclass.use_visibility_profile?
614
+ else
615
+ false
616
+ end
617
+ end
618
+
500
619
  # @param type [Module] The type definition whose possible types you want to see
620
+ # @param context [GraphQL::Query::Context] used for filtering visible possible types at runtime
621
+ # @param use_visibility_profile Private, for migration to {Schema::Visibility}
501
622
  # @return [Hash<String, Module>] All possible types, if no `type` is given.
502
623
  # @return [Array<Module>] Possible types for `type`, if it's given.
503
- def possible_types(type = nil, context = GraphQL::Query::NullContext.instance)
624
+ def possible_types(type = nil, context = GraphQL::Query::NullContext.instance, use_visibility_profile = use_visibility_profile?)
625
+ if use_visibility_profile
626
+ if type
627
+ return Visibility::Profile.from_context(context, self).possible_types(type)
628
+ else
629
+ raise "Schema.possible_types is not implemented for `use_visibility_profile?`"
630
+ end
631
+ end
504
632
  if type
505
633
  # TODO duck-typing `.possible_types` would probably be nicer here
506
634
  if type.kind.union?
@@ -518,7 +646,7 @@ module GraphQL
518
646
  introspection_system.possible_types[type] ||
519
647
  (
520
648
  superclass.respond_to?(:possible_types) ?
521
- superclass.possible_types(type, context) :
649
+ superclass.possible_types(type, context, use_visibility_profile) :
522
650
  EMPTY_ARRAY
523
651
  )
524
652
  end
@@ -565,7 +693,7 @@ module GraphQL
565
693
  # and generally speaking, we won't inherit any values.
566
694
  # So optimize the most common case -- don't create a duplicate Hash.
567
695
  inherited_value = find_inherited_value(:references_to, EMPTY_HASH)
568
- if inherited_value.any?
696
+ if !inherited_value.empty?
569
697
  inherited_value.merge(own_references_to)
570
698
  else
571
699
  own_references_to
@@ -573,9 +701,8 @@ module GraphQL
573
701
  end
574
702
  end
575
703
 
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)
704
+ def type_from_ast(ast_node, context: self.query_class.new(self, "{ __typename }").context)
705
+ GraphQL::Schema::TypeExpression.build_type(context.query.types, ast_node)
579
706
  end
580
707
 
581
708
  def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext.instance)
@@ -605,20 +732,27 @@ module GraphQL
605
732
  type.fields(context)
606
733
  end
607
734
 
735
+ # Pass a custom introspection module here to use it for this schema.
736
+ # @param new_introspection_namespace [Module] If given, use this module for custom introspection on the schema
737
+ # @return [Module, nil] The configured namespace, if there is one
608
738
  def introspection(new_introspection_namespace = nil)
609
739
  if new_introspection_namespace
610
740
  @introspection = new_introspection_namespace
611
741
  # reset this cached value:
612
742
  @introspection_system = nil
743
+ introspection_system
744
+ @introspection
613
745
  else
614
746
  @introspection || find_inherited_value(:introspection)
615
747
  end
616
748
  end
617
749
 
750
+ # @return [Schema::IntrospectionSystem] Based on {introspection}
618
751
  def introspection_system
619
752
  if !@introspection_system
620
753
  @introspection_system = Schema::IntrospectionSystem.new(self)
621
754
  @introspection_system.resolve_late_bindings
755
+ self.visibility&.introspection_system_configured(@introspection_system)
622
756
  end
623
757
  @introspection_system
624
758
  end
@@ -722,6 +856,7 @@ module GraphQL
722
856
  res[:errors]
723
857
  end
724
858
 
859
+ # @param new_query_class [Class<GraphQL::Query>] A subclass to use when executing queries
725
860
  def query_class(new_query_class = NOT_CONFIGURED)
726
861
  if NOT_CONFIGURED.equal?(new_query_class)
727
862
  @query_class || (superclass.respond_to?(:query_class) ? superclass.query_class : GraphQL::Query)
@@ -732,13 +867,11 @@ module GraphQL
732
867
 
733
868
  attr_writer :validate_max_errors
734
869
 
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
870
+ def validate_max_errors(new_validate_max_errors = NOT_CONFIGURED)
871
+ if NOT_CONFIGURED.equal?(new_validate_max_errors)
872
+ defined?(@validate_max_errors) ? @validate_max_errors : find_inherited_value(:validate_max_errors)
740
873
  else
741
- find_inherited_value(:validate_max_errors)
874
+ @validate_max_errors = new_validate_max_errors
742
875
  end
743
876
  end
744
877
 
@@ -769,16 +902,6 @@ module GraphQL
769
902
  @analysis_engine || find_inherited_value(:analysis_engine, self.default_analysis_engine)
770
903
  end
771
904
 
772
- def using_ast_analysis?
773
- true
774
- end
775
-
776
- def interpreter?
777
- true
778
- end
779
-
780
- attr_writer :interpreter
781
-
782
905
  def error_bubbling(new_error_bubbling = nil)
783
906
  if !new_error_bubbling.nil?
784
907
  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 +979,7 @@ module GraphQL
856
979
  # @param new_extra_types [Module] Type definitions to include in printing and introspection, even though they aren't referenced in the schema
857
980
  # @return [Array<Module>] Type definitions added to this schema
858
981
  def extra_types(*new_extra_types)
859
- if new_extra_types.any?
982
+ if !new_extra_types.empty?
860
983
  new_extra_types = new_extra_types.flatten
861
984
  @own_extra_types ||= []
862
985
  @own_extra_types.concat(new_extra_types)
@@ -873,11 +996,18 @@ module GraphQL
873
996
  end
874
997
  end
875
998
 
999
+ # Tell the schema about these types so that they can be registered as implementations of interfaces in the schema.
1000
+ #
1001
+ # This method must be used when an object type is connected to the schema as an interface implementor but
1002
+ # 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.
1003
+ #
1004
+ # @param new_orphan_types [Array<Class<GraphQL::Schema::Object>>] Object types to register as implementations of interfaces in the schema.
1005
+ # @return [Array<Class<GraphQL::Schema::Object>>] All previously-registered orphan types for this schema
876
1006
  def orphan_types(*new_orphan_types)
877
- if new_orphan_types.any?
1007
+ if !new_orphan_types.empty?
878
1008
  new_orphan_types = new_orphan_types.flatten
879
1009
  non_object_types = new_orphan_types.reject { |ot| ot.is_a?(Class) && ot < GraphQL::Schema::Object }
880
- if non_object_types.any?
1010
+ if !non_object_types.empty?
881
1011
  raise ArgumentError, <<~ERR
882
1012
  Only object type classes should be added as `orphan_types(...)`.
883
1013
 
@@ -887,13 +1017,14 @@ module GraphQL
887
1017
  To add other types to your schema, you might want `extra_types`: https://graphql-ruby.org/schema/definition.html#extra-types
888
1018
  ERR
889
1019
  end
890
- add_type_and_traverse(new_orphan_types, root: false)
1020
+ add_type_and_traverse(new_orphan_types, root: false) unless use_visibility_profile?
891
1021
  own_orphan_types.concat(new_orphan_types.flatten)
1022
+ self.visibility&.orphan_types_configured(new_orphan_types)
892
1023
  end
893
1024
 
894
1025
  inherited_ot = find_inherited_value(:orphan_types, nil)
895
1026
  if inherited_ot
896
- if own_orphan_types.any?
1027
+ if !own_orphan_types.empty?
897
1028
  inherited_ot + own_orphan_types
898
1029
  else
899
1030
  inherited_ot
@@ -919,6 +1050,8 @@ module GraphQL
919
1050
  end
920
1051
  end
921
1052
 
1053
+
1054
+ # @param new_default_logger [#log] Something to use for logging messages
922
1055
  def default_logger(new_default_logger = NOT_CONFIGURED)
923
1056
  if NOT_CONFIGURED.equal?(new_default_logger)
924
1057
  if defined?(@default_logger)
@@ -939,6 +1072,7 @@ module GraphQL
939
1072
  end
940
1073
  end
941
1074
 
1075
+ # @param new_context_class [Class<GraphQL::Query::Context>] A subclass to use when executing queries
942
1076
  def context_class(new_context_class = nil)
943
1077
  if new_context_class
944
1078
  @context_class = new_context_class
@@ -947,6 +1081,20 @@ module GraphQL
947
1081
  end
948
1082
  end
949
1083
 
1084
+ # Register a handler for errors raised during execution. The handlers can return a new value or raise a new error.
1085
+ #
1086
+ # @example Handling "not found" with a client-facing error
1087
+ # rescue_from(ActiveRecord::NotFound) { raise GraphQL::ExecutionError, "An object could not be found" }
1088
+ #
1089
+ # @param err_classes [Array<StandardError>] Classes which should be rescued by `handler_block`
1090
+ # @param handler_block The code to run when one of those errors is raised during execution
1091
+ # @yieldparam error [StandardError] An instance of one of the configured `err_classes`
1092
+ # @yieldparam object [Object] The current application object in the query when the error was raised
1093
+ # @yieldparam arguments [GraphQL::Query::Arguments] The current field arguments when the error was raised
1094
+ # @yieldparam context [GraphQL::Query::Context] The context for the currently-running operation
1095
+ # @yieldreturn [Object] Some object to use in the place where this error was raised
1096
+ # @raise [GraphQL::ExecutionError] In the handler, raise to add a client-facing error to the response
1097
+ # @raise [StandardError] In the handler, raise to crash the query with a developer-facing error
950
1098
  def rescue_from(*err_classes, &handler_block)
951
1099
  err_classes.each do |err_class|
952
1100
  Execution::Errors.register_rescue_from(err_class, error_handlers[:subclass_handlers], handler_block)
@@ -1013,8 +1161,24 @@ module GraphQL
1013
1161
  end
1014
1162
  end
1015
1163
 
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})"
1164
+ # GraphQL-Ruby calls this method during execution when it needs the application to determine the type to use for an object.
1165
+ #
1166
+ # Usually, this object was returned from a field whose return type is an {GraphQL::Schema::Interface} or a {GraphQL::Schema::Union}.
1167
+ # 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.
1168
+ #
1169
+ # @example Returning a GraphQL type based on the object's class name
1170
+ # class MySchema < GraphQL::Schema
1171
+ # def resolve_type(_abs_type, object, _context)
1172
+ # graphql_type_name = "Types::#{object.class.name}Type"
1173
+ # graphql_type_name.constantize # If this raises a NameError, then come implement special cases in this method
1174
+ # end
1175
+ # end
1176
+ # @param abstract_type [Class, Module, nil] The Interface or Union type which is being resolved, if there is one
1177
+ # @param application_object [Object] The object returned from a field whose type must be determined
1178
+ # @param context [GraphQL::Query::Context] The query context for the currently-executing query
1179
+ # @return [Class<GraphQL::Schema::Object] The Object type definition to use for `obj`
1180
+ def resolve_type(abstract_type, application_object, context)
1181
+ 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
1182
  end
1019
1183
  # rubocop:enable Lint/DuplicateMethods
1020
1184
 
@@ -1029,15 +1193,45 @@ module GraphQL
1029
1193
  child_class.own_trace_modes[name] = child_class.build_trace_mode(name)
1030
1194
  end
1031
1195
  child_class.singleton_class.prepend(ResolveTypeWithType)
1032
- super
1033
- end
1034
1196
 
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}`)"
1197
+ if use_visibility_profile?
1198
+ vis = self.visibility
1199
+ child_class.visibility = vis.dup_for(child_class)
1200
+ end
1201
+ super
1037
1202
  end
1038
1203
 
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}`)"
1204
+ # Fetch an object based on an incoming ID and the current context. This method should return an object
1205
+ # from your application, or return `nil` if there is no object or the object shouldn't be available to this operation.
1206
+ #
1207
+ # @example Fetching an object with Rails's GlobalID
1208
+ # def self.object_from_id(object_id, _context)
1209
+ # GlobalID.find(global_id)
1210
+ # # TODO: use `context[:current_user]` to determine if this object is authorized.
1211
+ # end
1212
+ # @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`)
1213
+ # @param context [GraphQL::Query::Context] The context for the currently-executing operation
1214
+ # @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
1215
+ # @see id_from_object which produces these IDs
1216
+ def object_from_id(object_id, context)
1217
+ 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}`)"
1218
+ end
1219
+
1220
+ # Return a stable ID string for `object` so that it can be refetched later, using {.object_from_id}.
1221
+ #
1222
+ # {GlobalID}(https://github.com/rails/globalid) and {SQIDs}(https://sqids.org/ruby) can both be used to create IDs.
1223
+ #
1224
+ # @example Using Rails's GlobalID to generate IDs
1225
+ # def self.id_from_object(application_object, graphql_type, context)
1226
+ # application_object.to_gid_param
1227
+ # end
1228
+ #
1229
+ # @param application_object [Object] Some object encountered by GraphQL-Ruby while running a query
1230
+ # @param graphql_type [Class, Module] The type that GraphQL-Ruby is using for `application_object` during this query
1231
+ # @param context [GraphQL::Query::Context] The context for the operation that is currently running
1232
+ # @return [String] A stable identifier which can be passed to {.object_from_id} later to re-fetch `application_object`
1233
+ def id_from_object(application_object, graphql_type, context)
1234
+ 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
1235
  end
1042
1236
 
1043
1237
  def visible?(member, ctx)
@@ -1053,6 +1247,10 @@ module GraphQL
1053
1247
  Member::HasDirectives.get_directives(self, @own_schema_directives, :schema_directives)
1054
1248
  end
1055
1249
 
1250
+ # Called when a type is needed by name at runtime
1251
+ def load_type(type_name, ctx)
1252
+ get_type(type_name, ctx)
1253
+ end
1056
1254
  # This hook is called when an object fails an `authorized?` check.
1057
1255
  # You might report to your bug tracker here, so you can correct
1058
1256
  # the field resolvers not to return unauthorized objects.
@@ -1088,6 +1286,16 @@ module GraphQL
1088
1286
  unauthorized_object(unauthorized_error)
1089
1287
  end
1090
1288
 
1289
+ # Called at runtime when GraphQL-Ruby encounters a mismatch between the application behavior
1290
+ # and the GraphQL type system.
1291
+ #
1292
+ # The default implementation of this method is to follow the GraphQL specification,
1293
+ # but you can override this to report errors to your bug tracker or customize error handling.
1294
+ # @param type_error [GraphQL::Error] several specific error classes are passed here, see the default implementation for details
1295
+ # @param context [GraphQL::Query::Context] the context for the currently-running operation
1296
+ # @return [void]
1297
+ # @raise [GraphQL::ExecutionError] to return this error to the client
1298
+ # @raise [GraphQL::Error] to crash the query and raise a developer-facing error
1091
1299
  def type_error(type_error, ctx)
1092
1300
  case type_error
1093
1301
  when GraphQL::InvalidNullError
@@ -1126,12 +1334,12 @@ module GraphQL
1126
1334
  # Add several directives at once
1127
1335
  # @param new_directives [Class]
1128
1336
  def directives(*new_directives)
1129
- if new_directives.any?
1337
+ if !new_directives.empty?
1130
1338
  new_directives.flatten.each { |d| directive(d) }
1131
1339
  end
1132
1340
 
1133
1341
  inherited_dirs = find_inherited_value(:directives, default_directives)
1134
- if own_directives.any?
1342
+ if !own_directives.empty?
1135
1343
  inherited_dirs.merge(own_directives)
1136
1344
  else
1137
1345
  inherited_dirs
@@ -1142,7 +1350,11 @@ module GraphQL
1142
1350
  # @param new_directive [Class]
1143
1351
  # @return void
1144
1352
  def directive(new_directive)
1145
- add_type_and_traverse(new_directive, root: false)
1353
+ if use_visibility_profile?
1354
+ own_directives[new_directive.graphql_name] = new_directive
1355
+ else
1356
+ add_type_and_traverse(new_directive, root: false)
1357
+ end
1146
1358
  end
1147
1359
 
1148
1360
  def default_directives
@@ -1179,6 +1391,7 @@ module GraphQL
1179
1391
  # @param mode [Symbol] Trace module will only be used for this trade mode
1180
1392
  # @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
1181
1393
  # @return [void]
1394
+ # @see GraphQL::Tracing::Trace for available tracing methods
1182
1395
  def trace_with(trace_mod, mode: :default, **options)
1183
1396
  if mode.is_a?(Array)
1184
1397
  mode.each { |m| trace_with(trace_mod, mode: m, **options) }
@@ -1256,6 +1469,8 @@ module GraphQL
1256
1469
  trace_class_for_mode.new(**trace_options)
1257
1470
  end
1258
1471
 
1472
+ # @param new_analyzer [Class<GraphQL::Analysis::Analyzer>] An analyzer to run on queries to this schema
1473
+ # @see GraphQL::Analysis the analysis system
1259
1474
  def query_analyzer(new_analyzer)
1260
1475
  own_query_analyzers << new_analyzer
1261
1476
  end
@@ -1264,6 +1479,8 @@ module GraphQL
1264
1479
  find_inherited_value(:query_analyzers, EMPTY_ARRAY) + own_query_analyzers
1265
1480
  end
1266
1481
 
1482
+ # @param new_analyzer [Class<GraphQL::Analysis::Analyzer>] An analyzer to run on multiplexes to this schema
1483
+ # @see GraphQL::Analysis the analysis system
1267
1484
  def multiplex_analyzer(new_analyzer)
1268
1485
  own_multiplex_analyzers << new_analyzer
1269
1486
  end
@@ -1336,7 +1553,8 @@ module GraphQL
1336
1553
 
1337
1554
  # @api private
1338
1555
  def add_subscription_extension_if_necessary
1339
- if !defined?(@subscription_extension_added) && subscription && self.subscriptions
1556
+ # TODO: when there's a proper API for extending root types, migrat this to use it.
1557
+ if !defined?(@subscription_extension_added) && @subscription_object.is_a?(Class) && self.subscriptions
1340
1558
  @subscription_extension_added = true
1341
1559
  subscription.all_field_definitions.each do |field|
1342
1560
  if !field.extensions.any? { |ext| ext.is_a?(Subscriptions::DefaultSubscriptionResolveExtension) }
@@ -1346,6 +1564,11 @@ module GraphQL
1346
1564
  end
1347
1565
  end
1348
1566
 
1567
+ # Called when execution encounters a `SystemStackError`. By default, it adds a client-facing error to the response.
1568
+ # You could modify this method to report this error to your bug tracker.
1569
+ # @param query [GraphQL::Query]
1570
+ # @param err [SystemStackError]
1571
+ # @return [void]
1349
1572
  def query_stack_error(query, err)
1350
1573
  query.context.errors.push(GraphQL::ExecutionError.new("This query is too large to execute."))
1351
1574
  end
@@ -1404,11 +1627,34 @@ module GraphQL
1404
1627
  end
1405
1628
  end
1406
1629
 
1630
+ # Returns `DidYouMean` if it's defined.
1631
+ # Override this to return `nil` if you don't want to use `DidYouMean`
1632
+ def did_you_mean(new_dym = NOT_CONFIGURED)
1633
+ if NOT_CONFIGURED.equal?(new_dym)
1634
+ if defined?(@did_you_mean)
1635
+ @did_you_mean
1636
+ else
1637
+ find_inherited_value(:did_you_mean, defined?(DidYouMean) ? DidYouMean : nil)
1638
+ end
1639
+ else
1640
+ @did_you_mean = new_dym
1641
+ end
1642
+ end
1643
+
1407
1644
  private
1408
1645
 
1409
1646
  def add_trace_options_for(mode, new_options)
1410
- t_opts = trace_options_for(mode)
1411
- t_opts.merge!(new_options)
1647
+ if mode == :default
1648
+ own_trace_modes.each do |mode_name, t_class|
1649
+ if t_class <= DefaultTraceClass
1650
+ t_opts = trace_options_for(mode_name)
1651
+ t_opts.merge!(new_options)
1652
+ end
1653
+ end
1654
+ else
1655
+ t_opts = trace_options_for(mode)
1656
+ t_opts.merge!(new_options)
1657
+ end
1412
1658
  nil
1413
1659
  end
1414
1660
 
@@ -1485,7 +1731,7 @@ module GraphQL
1485
1731
  end
1486
1732
 
1487
1733
  def own_references_to
1488
- @own_references_to ||= {}.tap(&:compare_by_identity)
1734
+ @own_references_to ||= {}.compare_by_identity
1489
1735
  end
1490
1736
 
1491
1737
  def non_introspection_types
@@ -1501,7 +1747,7 @@ module GraphQL
1501
1747
  end
1502
1748
 
1503
1749
  def own_possible_types
1504
- @own_possible_types ||= {}.tap(&:compare_by_identity)
1750
+ @own_possible_types ||= {}.compare_by_identity
1505
1751
  end
1506
1752
 
1507
1753
  def own_union_memberships
@@ -1552,5 +1798,13 @@ module GraphQL
1552
1798
 
1553
1799
  # Install these here so that subclasses will also install it.
1554
1800
  self.connections = GraphQL::Pagination::Connections.new(schema: self)
1801
+
1802
+ # @api private
1803
+ module DefaultTraceClass
1804
+ end
1555
1805
  end
1556
1806
  end
1807
+
1808
+ require "graphql/schema/built_in_types"
1809
+ require "graphql/schema/loader"
1810
+ require "graphql/schema/printer"