graphql 2.3.8 → 2.4.0
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.
- checksums.yaml +4 -4
- data/lib/generators/graphql/install_generator.rb +46 -0
- data/lib/generators/graphql/orm_mutations_base.rb +1 -1
- data/lib/generators/graphql/templates/base_resolver.erb +2 -0
- data/lib/generators/graphql/type_generator.rb +1 -1
- data/lib/graphql/analysis.rb +1 -1
- data/lib/graphql/current.rb +52 -0
- data/lib/graphql/dataloader/async_dataloader.rb +3 -2
- data/lib/graphql/dataloader/source.rb +6 -3
- data/lib/graphql/dataloader.rb +35 -11
- data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
- data/lib/graphql/execution/interpreter/resolve.rb +10 -6
- data/lib/graphql/execution/interpreter/runtime.rb +23 -19
- data/lib/graphql/execution/interpreter.rb +2 -0
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/introspection/schema_type.rb +4 -19
- data/lib/graphql/invalid_null_error.rb +1 -1
- data/lib/graphql/language/comment.rb +18 -0
- data/lib/graphql/language/document_from_schema_definition.rb +38 -4
- data/lib/graphql/language/lexer.rb +15 -12
- data/lib/graphql/language/nodes.rb +24 -16
- data/lib/graphql/language/parser.rb +14 -1
- data/lib/graphql/language/printer.rb +23 -7
- data/lib/graphql/language.rb +6 -5
- data/lib/graphql/query/context.rb +4 -2
- data/lib/graphql/query/null_context.rb +1 -5
- data/lib/graphql/query.rb +49 -20
- data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
- data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
- data/lib/graphql/rubocop.rb +2 -0
- data/lib/graphql/schema/addition.rb +1 -0
- data/lib/graphql/schema/always_visible.rb +6 -3
- data/lib/graphql/schema/argument.rb +14 -1
- data/lib/graphql/schema/build_from_definition.rb +9 -1
- data/lib/graphql/schema/directive/flagged.rb +1 -1
- data/lib/graphql/schema/enum.rb +49 -15
- data/lib/graphql/schema/enum_value.rb +9 -1
- data/lib/graphql/schema/field/connection_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +91 -36
- data/lib/graphql/schema/input_object.rb +20 -7
- data/lib/graphql/schema/interface.rb +21 -4
- data/lib/graphql/schema/introspection_system.rb +3 -2
- data/lib/graphql/schema/member/base_dsl_methods.rb +15 -0
- data/lib/graphql/schema/member/has_arguments.rb +9 -5
- data/lib/graphql/schema/member/has_fields.rb +5 -5
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
- data/lib/graphql/schema/printer.rb +1 -0
- data/lib/graphql/schema/resolver.rb +3 -4
- data/lib/graphql/schema/validator/all_validator.rb +2 -0
- data/lib/graphql/schema/validator/required_validator.rb +28 -4
- data/lib/graphql/schema/visibility/migration.rb +186 -0
- data/lib/graphql/schema/visibility/profile.rb +523 -0
- data/lib/graphql/schema/visibility.rb +75 -0
- data/lib/graphql/schema/warden.rb +80 -21
- data/lib/graphql/schema.rb +254 -85
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +2 -1
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +2 -1
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +2 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -0
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +11 -1
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +10 -1
- data/lib/graphql/static_validation/validation_context.rb +15 -0
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +2 -1
- data/lib/graphql/subscriptions.rb +3 -1
- data/lib/graphql/testing/helpers.rb +7 -3
- data/lib/graphql/tracing/notifications_trace.rb +2 -2
- data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +3 -0
- metadata +28 -7
- data/lib/graphql/schema/subset.rb +0 -397
data/lib/graphql/schema.rb
CHANGED
|
@@ -45,7 +45,7 @@ require "graphql/schema/mutation"
|
|
|
45
45
|
require "graphql/schema/has_single_input_argument"
|
|
46
46
|
require "graphql/schema/relay_classic_mutation"
|
|
47
47
|
require "graphql/schema/subscription"
|
|
48
|
-
require "graphql/schema/
|
|
48
|
+
require "graphql/schema/visibility"
|
|
49
49
|
|
|
50
50
|
module GraphQL
|
|
51
51
|
# A GraphQL schema which may be queried with {GraphQL::Query}.
|
|
@@ -162,6 +162,7 @@ module GraphQL
|
|
|
162
162
|
# re-apply them here
|
|
163
163
|
mods = trace_modules_for(:default)
|
|
164
164
|
mods.each { |mod| new_class.include(mod) }
|
|
165
|
+
new_class.include(DefaultTraceClass)
|
|
165
166
|
trace_mode(:default, new_class)
|
|
166
167
|
backtrace_class = Class.new(new_class)
|
|
167
168
|
backtrace_class.include(GraphQL::Backtrace::Trace)
|
|
@@ -204,24 +205,19 @@ module GraphQL
|
|
|
204
205
|
@own_trace_modes ||= {}
|
|
205
206
|
end
|
|
206
207
|
|
|
207
|
-
module DefaultTraceClass
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
private_constant :DefaultTraceClass
|
|
211
|
-
|
|
212
208
|
def build_trace_mode(mode)
|
|
213
209
|
case mode
|
|
214
210
|
when :default
|
|
215
211
|
# 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
|
|
212
|
+
base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode, build: true)) || GraphQL::Tracing::Trace
|
|
213
|
+
const_set(:DefaultTrace, Class.new(base_class) do
|
|
218
214
|
include DefaultTraceClass
|
|
219
|
-
end
|
|
215
|
+
end)
|
|
220
216
|
when :default_backtrace
|
|
221
217
|
schema_base_class = trace_class_for(:default, build: true)
|
|
222
|
-
Class.new(schema_base_class) do
|
|
218
|
+
const_set(:DefaultTraceBacktrace, Class.new(schema_base_class) do
|
|
223
219
|
include(GraphQL::Backtrace::Trace)
|
|
224
|
-
end
|
|
220
|
+
end)
|
|
225
221
|
else
|
|
226
222
|
# First, see if the superclass has a custom-defined class for this.
|
|
227
223
|
# Then, if it doesn't, use this class's default trace
|
|
@@ -321,6 +317,9 @@ module GraphQL
|
|
|
321
317
|
GraphQL::StaticValidation::Validator.new(schema: self)
|
|
322
318
|
end
|
|
323
319
|
|
|
320
|
+
# Add `plugin` to this schema
|
|
321
|
+
# @param plugin [#use] A Schema plugin
|
|
322
|
+
# @return void
|
|
324
323
|
def use(plugin, **kwargs)
|
|
325
324
|
if kwargs.any?
|
|
326
325
|
plugin.use(self, **kwargs)
|
|
@@ -338,6 +337,10 @@ module GraphQL
|
|
|
338
337
|
# @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
|
|
339
338
|
# @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
|
|
340
339
|
def types(context = GraphQL::Query::NullContext.instance)
|
|
340
|
+
if use_visibility_profile?
|
|
341
|
+
types = Visibility::Profile.from_context(context, self)
|
|
342
|
+
return types.all_types_h
|
|
343
|
+
end
|
|
341
344
|
all_types = non_introspection_types.merge(introspection_system.types)
|
|
342
345
|
visible_types = {}
|
|
343
346
|
all_types.each do |k, v|
|
|
@@ -363,14 +366,19 @@ module GraphQL
|
|
|
363
366
|
end
|
|
364
367
|
|
|
365
368
|
# @param type_name [String]
|
|
369
|
+
# @param context [GraphQL::Query::Context] Used for filtering definitions at query-time
|
|
370
|
+
# @param use_visibility_profile Private, for migration to {Schema::Visibility}
|
|
366
371
|
# @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)
|
|
372
|
+
def get_type(type_name, context = GraphQL::Query::NullContext.instance, use_visibility_profile = use_visibility_profile?)
|
|
373
|
+
if use_visibility_profile
|
|
374
|
+
return Visibility::Profile.from_context(context, self).type(type_name)
|
|
375
|
+
end
|
|
368
376
|
local_entry = own_types[type_name]
|
|
369
377
|
type_defn = case local_entry
|
|
370
378
|
when nil
|
|
371
379
|
nil
|
|
372
380
|
when Array
|
|
373
|
-
if context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::
|
|
381
|
+
if context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile)
|
|
374
382
|
local_entry
|
|
375
383
|
else
|
|
376
384
|
visible_t = nil
|
|
@@ -396,7 +404,7 @@ module GraphQL
|
|
|
396
404
|
|
|
397
405
|
type_defn ||
|
|
398
406
|
introspection_system.types[type_name] || # todo context-specific introspection?
|
|
399
|
-
(superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context) : nil)
|
|
407
|
+
(superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context, use_visibility_profile) : nil)
|
|
400
408
|
end
|
|
401
409
|
|
|
402
410
|
# @return [Boolean] Does this schema have _any_ definition for a type named `type_name`, regardless of visibility?
|
|
@@ -423,55 +431,92 @@ module GraphQL
|
|
|
423
431
|
end
|
|
424
432
|
end
|
|
425
433
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
434
|
+
# Get or set the root `query { ... }` object for this schema.
|
|
435
|
+
#
|
|
436
|
+
# @example Using `Types::Query` as the entry-point
|
|
437
|
+
# query { Types::Query }
|
|
438
|
+
#
|
|
439
|
+
# @param new_query_object [Class<GraphQL::Schema::Object>] The root type to use for queries
|
|
440
|
+
# @param lazy_load_block If a block is given, then it will be called when GraphQL-Ruby needs the root query type.
|
|
441
|
+
# @return [Class<GraphQL::Schema::Object>, nil] The configured query root type, if there is one.
|
|
442
|
+
def query(new_query_object = nil, &lazy_load_block)
|
|
443
|
+
if new_query_object || block_given?
|
|
432
444
|
if @query_object
|
|
433
|
-
|
|
445
|
+
dup_defn = new_query_object || yield
|
|
446
|
+
raise GraphQL::Error, "Second definition of `query(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@query_object.inspect}"
|
|
447
|
+
elsif use_visibility_profile?
|
|
448
|
+
@query_object = block_given? ? lazy_load_block : new_query_object
|
|
434
449
|
else
|
|
435
|
-
@query_object = new_query_object
|
|
436
|
-
add_type_and_traverse(
|
|
437
|
-
nil
|
|
450
|
+
@query_object = new_query_object || lazy_load_block.call
|
|
451
|
+
add_type_and_traverse(@query_object, root: true)
|
|
438
452
|
end
|
|
453
|
+
nil
|
|
454
|
+
elsif @query_object.is_a?(Proc)
|
|
455
|
+
@query_object = @query_object.call
|
|
439
456
|
else
|
|
440
457
|
@query_object || find_inherited_value(:query)
|
|
441
458
|
end
|
|
442
459
|
end
|
|
443
460
|
|
|
444
|
-
|
|
445
|
-
|
|
461
|
+
# Get or set the root `mutation { ... }` object for this schema.
|
|
462
|
+
#
|
|
463
|
+
# @example Using `Types::Mutation` as the entry-point
|
|
464
|
+
# mutation { Types::Mutation }
|
|
465
|
+
#
|
|
466
|
+
# @param new_mutation_object [Class<GraphQL::Schema::Object>] The root type to use for mutations
|
|
467
|
+
# @param lazy_load_block If a block is given, then it will be called when GraphQL-Ruby needs the root mutation type.
|
|
468
|
+
# @return [Class<GraphQL::Schema::Object>, nil] The configured mutation root type, if there is one.
|
|
469
|
+
def mutation(new_mutation_object = nil, &lazy_load_block)
|
|
470
|
+
if new_mutation_object || block_given?
|
|
446
471
|
if @mutation_object
|
|
447
|
-
|
|
472
|
+
dup_defn = new_mutation_object || yield
|
|
473
|
+
raise GraphQL::Error, "Second definition of `mutation(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@mutation_object.inspect}"
|
|
474
|
+
elsif use_visibility_profile?
|
|
475
|
+
@mutation_object = block_given? ? lazy_load_block : new_mutation_object
|
|
448
476
|
else
|
|
449
|
-
@mutation_object = new_mutation_object
|
|
450
|
-
add_type_and_traverse(
|
|
451
|
-
nil
|
|
477
|
+
@mutation_object = new_mutation_object || lazy_load_block.call
|
|
478
|
+
add_type_and_traverse(@mutation_object, root: true)
|
|
452
479
|
end
|
|
480
|
+
nil
|
|
481
|
+
elsif @mutation_object.is_a?(Proc)
|
|
482
|
+
@mutation_object = @mutation_object.call
|
|
453
483
|
else
|
|
454
484
|
@mutation_object || find_inherited_value(:mutation)
|
|
455
485
|
end
|
|
456
486
|
end
|
|
457
487
|
|
|
458
|
-
|
|
459
|
-
|
|
488
|
+
# Get or set the root `subscription { ... }` object for this schema.
|
|
489
|
+
#
|
|
490
|
+
# @example Using `Types::Subscription` as the entry-point
|
|
491
|
+
# subscription { Types::Subscription }
|
|
492
|
+
#
|
|
493
|
+
# @param new_subscription_object [Class<GraphQL::Schema::Object>] The root type to use for subscriptions
|
|
494
|
+
# @param lazy_load_block If a block is given, then it will be called when GraphQL-Ruby needs the root subscription type.
|
|
495
|
+
# @return [Class<GraphQL::Schema::Object>, nil] The configured subscription root type, if there is one.
|
|
496
|
+
def subscription(new_subscription_object = nil, &lazy_load_block)
|
|
497
|
+
if new_subscription_object || block_given?
|
|
460
498
|
if @subscription_object
|
|
461
|
-
|
|
499
|
+
dup_defn = new_subscription_object || yield
|
|
500
|
+
raise GraphQL::Error, "Second definition of `subscription(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@subscription_object.inspect}"
|
|
501
|
+
elsif use_visibility_profile?
|
|
502
|
+
@subscription_object = block_given? ? lazy_load_block : new_subscription_object
|
|
503
|
+
add_subscription_extension_if_necessary
|
|
462
504
|
else
|
|
463
|
-
@subscription_object = new_subscription_object
|
|
505
|
+
@subscription_object = new_subscription_object || lazy_load_block.call
|
|
464
506
|
add_subscription_extension_if_necessary
|
|
465
|
-
add_type_and_traverse(
|
|
466
|
-
nil
|
|
507
|
+
add_type_and_traverse(@subscription_object, root: true)
|
|
467
508
|
end
|
|
509
|
+
nil
|
|
510
|
+
elsif @subscription_object.is_a?(Proc)
|
|
511
|
+
@subscription_object = @subscription_object.call
|
|
512
|
+
add_subscription_extension_if_necessary
|
|
513
|
+
@subscription_object
|
|
468
514
|
else
|
|
469
515
|
@subscription_object || find_inherited_value(:subscription)
|
|
470
516
|
end
|
|
471
517
|
end
|
|
472
518
|
|
|
473
|
-
# @
|
|
474
|
-
# @return [GraphQL::ObjectType, nil]
|
|
519
|
+
# @api private
|
|
475
520
|
def root_type_for_operation(operation)
|
|
476
521
|
case operation
|
|
477
522
|
when "query"
|
|
@@ -485,10 +530,16 @@ module GraphQL
|
|
|
485
530
|
end
|
|
486
531
|
end
|
|
487
532
|
|
|
533
|
+
# @return [Array<Class>] The root types (query, mutation, subscription) defined for this schema
|
|
488
534
|
def root_types
|
|
489
|
-
|
|
535
|
+
if use_visibility_profile?
|
|
536
|
+
[query, mutation, subscription].compact
|
|
537
|
+
else
|
|
538
|
+
@root_types
|
|
539
|
+
end
|
|
490
540
|
end
|
|
491
541
|
|
|
542
|
+
# @api private
|
|
492
543
|
def warden_class
|
|
493
544
|
if defined?(@warden_class)
|
|
494
545
|
@warden_class
|
|
@@ -499,34 +550,48 @@ module GraphQL
|
|
|
499
550
|
end
|
|
500
551
|
end
|
|
501
552
|
|
|
553
|
+
# @api private
|
|
502
554
|
attr_writer :warden_class
|
|
503
555
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
556
|
+
# @api private
|
|
557
|
+
def visibility_profile_class
|
|
558
|
+
if defined?(@visibility_profile_class)
|
|
559
|
+
@visibility_profile_class
|
|
560
|
+
elsif superclass.respond_to?(:visibility_profile_class)
|
|
561
|
+
superclass.visibility_profile_class
|
|
509
562
|
else
|
|
510
|
-
GraphQL::Schema::
|
|
563
|
+
GraphQL::Schema::Visibility::Profile
|
|
511
564
|
end
|
|
512
565
|
end
|
|
513
566
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
567
|
+
# @api private
|
|
568
|
+
attr_writer :visibility_profile_class, :use_visibility_profile
|
|
569
|
+
# @api private
|
|
570
|
+
attr_accessor :visibility
|
|
571
|
+
# @api private
|
|
572
|
+
def use_visibility_profile?
|
|
573
|
+
if defined?(@use_visibility_profile)
|
|
574
|
+
@use_visibility_profile
|
|
575
|
+
elsif superclass.respond_to?(:use_visibility_profile?)
|
|
576
|
+
superclass.use_visibility_profile?
|
|
521
577
|
else
|
|
522
578
|
false
|
|
523
579
|
end
|
|
524
580
|
end
|
|
525
581
|
|
|
526
582
|
# @param type [Module] The type definition whose possible types you want to see
|
|
583
|
+
# @param context [GraphQL::Query::Context] used for filtering visible possible types at runtime
|
|
584
|
+
# @param use_visibility_profile Private, for migration to {Schema::Visibility}
|
|
527
585
|
# @return [Hash<String, Module>] All possible types, if no `type` is given.
|
|
528
586
|
# @return [Array<Module>] Possible types for `type`, if it's given.
|
|
529
|
-
def possible_types(type = nil, context = GraphQL::Query::NullContext.instance)
|
|
587
|
+
def possible_types(type = nil, context = GraphQL::Query::NullContext.instance, use_visibility_profile = use_visibility_profile?)
|
|
588
|
+
if use_visibility_profile
|
|
589
|
+
if type
|
|
590
|
+
return Visibility::Profile.from_context(context, self).possible_types(type)
|
|
591
|
+
else
|
|
592
|
+
raise "Schema.possible_types is not implemented for `use_visibility_profile?`"
|
|
593
|
+
end
|
|
594
|
+
end
|
|
530
595
|
if type
|
|
531
596
|
# TODO duck-typing `.possible_types` would probably be nicer here
|
|
532
597
|
if type.kind.union?
|
|
@@ -544,7 +609,7 @@ module GraphQL
|
|
|
544
609
|
introspection_system.possible_types[type] ||
|
|
545
610
|
(
|
|
546
611
|
superclass.respond_to?(:possible_types) ?
|
|
547
|
-
superclass.possible_types(type, context) :
|
|
612
|
+
superclass.possible_types(type, context, use_visibility_profile) :
|
|
548
613
|
EMPTY_ARRAY
|
|
549
614
|
)
|
|
550
615
|
end
|
|
@@ -747,6 +812,7 @@ module GraphQL
|
|
|
747
812
|
res[:errors]
|
|
748
813
|
end
|
|
749
814
|
|
|
815
|
+
# @param new_query_class [Class<GraphQL::Query>] A subclass to use when executing queries
|
|
750
816
|
def query_class(new_query_class = NOT_CONFIGURED)
|
|
751
817
|
if NOT_CONFIGURED.equal?(new_query_class)
|
|
752
818
|
@query_class || (superclass.respond_to?(:query_class) ? superclass.query_class : GraphQL::Query)
|
|
@@ -757,13 +823,11 @@ module GraphQL
|
|
|
757
823
|
|
|
758
824
|
attr_writer :validate_max_errors
|
|
759
825
|
|
|
760
|
-
def validate_max_errors(new_validate_max_errors =
|
|
761
|
-
if new_validate_max_errors
|
|
762
|
-
@validate_max_errors
|
|
763
|
-
elsif defined?(@validate_max_errors)
|
|
764
|
-
@validate_max_errors
|
|
826
|
+
def validate_max_errors(new_validate_max_errors = NOT_CONFIGURED)
|
|
827
|
+
if NOT_CONFIGURED.equal?(new_validate_max_errors)
|
|
828
|
+
defined?(@validate_max_errors) ? @validate_max_errors : find_inherited_value(:validate_max_errors)
|
|
765
829
|
else
|
|
766
|
-
|
|
830
|
+
@validate_max_errors = new_validate_max_errors
|
|
767
831
|
end
|
|
768
832
|
end
|
|
769
833
|
|
|
@@ -794,16 +858,6 @@ module GraphQL
|
|
|
794
858
|
@analysis_engine || find_inherited_value(:analysis_engine, self.default_analysis_engine)
|
|
795
859
|
end
|
|
796
860
|
|
|
797
|
-
def using_ast_analysis?
|
|
798
|
-
true
|
|
799
|
-
end
|
|
800
|
-
|
|
801
|
-
def interpreter?
|
|
802
|
-
true
|
|
803
|
-
end
|
|
804
|
-
|
|
805
|
-
attr_writer :interpreter
|
|
806
|
-
|
|
807
861
|
def error_bubbling(new_error_bubbling = nil)
|
|
808
862
|
if !new_error_bubbling.nil?
|
|
809
863
|
warn("error_bubbling(#{new_error_bubbling.inspect}) is deprecated; the default value of `false` will be the only option in GraphQL-Ruby 3.0")
|
|
@@ -912,7 +966,7 @@ module GraphQL
|
|
|
912
966
|
To add other types to your schema, you might want `extra_types`: https://graphql-ruby.org/schema/definition.html#extra-types
|
|
913
967
|
ERR
|
|
914
968
|
end
|
|
915
|
-
add_type_and_traverse(new_orphan_types, root: false)
|
|
969
|
+
add_type_and_traverse(new_orphan_types, root: false) unless use_visibility_profile?
|
|
916
970
|
own_orphan_types.concat(new_orphan_types.flatten)
|
|
917
971
|
end
|
|
918
972
|
|
|
@@ -944,6 +998,8 @@ module GraphQL
|
|
|
944
998
|
end
|
|
945
999
|
end
|
|
946
1000
|
|
|
1001
|
+
|
|
1002
|
+
# @param new_default_logger [#log] Something to use for logging messages
|
|
947
1003
|
def default_logger(new_default_logger = NOT_CONFIGURED)
|
|
948
1004
|
if NOT_CONFIGURED.equal?(new_default_logger)
|
|
949
1005
|
if defined?(@default_logger)
|
|
@@ -964,6 +1020,7 @@ module GraphQL
|
|
|
964
1020
|
end
|
|
965
1021
|
end
|
|
966
1022
|
|
|
1023
|
+
# @param new_context_class [Class<GraphQL::Query::Context>] A subclass to use when executing queries
|
|
967
1024
|
def context_class(new_context_class = nil)
|
|
968
1025
|
if new_context_class
|
|
969
1026
|
@context_class = new_context_class
|
|
@@ -972,6 +1029,20 @@ module GraphQL
|
|
|
972
1029
|
end
|
|
973
1030
|
end
|
|
974
1031
|
|
|
1032
|
+
# Register a handler for errors raised during execution. The handlers can return a new value or raise a new error.
|
|
1033
|
+
#
|
|
1034
|
+
# @example Handling "not found" with a client-facing error
|
|
1035
|
+
# rescue_from(ActiveRecord::NotFound) { raise GraphQL::ExecutionError, "An object could not be found" }
|
|
1036
|
+
#
|
|
1037
|
+
# @param err_classes [Array<StandardError>] Classes which should be rescued by `handler_block`
|
|
1038
|
+
# @param handler_block The code to run when one of those errors is raised during execution
|
|
1039
|
+
# @yieldparam error [StandardError] An instance of one of the configured `err_classes`
|
|
1040
|
+
# @yieldparam object [Object] The current application object in the query when the error was raised
|
|
1041
|
+
# @yieldparam arguments [GraphQL::Query::Arguments] The current field arguments when the error was raised
|
|
1042
|
+
# @yieldparam context [GraphQL::Query::Context] The context for the currently-running operation
|
|
1043
|
+
# @yieldreturn [Object] Some object to use in the place where this error was raised
|
|
1044
|
+
# @raise [GraphQL::ExecutionError] In the handler, raise to add a client-facing error to the response
|
|
1045
|
+
# @raise [StandardError] In the handler, raise to crash the query with a developer-facing error
|
|
975
1046
|
def rescue_from(*err_classes, &handler_block)
|
|
976
1047
|
err_classes.each do |err_class|
|
|
977
1048
|
Execution::Errors.register_rescue_from(err_class, error_handlers[:subclass_handlers], handler_block)
|
|
@@ -1038,8 +1109,24 @@ module GraphQL
|
|
|
1038
1109
|
end
|
|
1039
1110
|
end
|
|
1040
1111
|
|
|
1041
|
-
|
|
1042
|
-
|
|
1112
|
+
# GraphQL-Ruby calls this method during execution when it needs the application to determine the type to use for an object.
|
|
1113
|
+
#
|
|
1114
|
+
# Usually, this object was returned from a field whose return type is an {GraphQL::Schema::Interface} or a {GraphQL::Schema::Union}.
|
|
1115
|
+
# 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.
|
|
1116
|
+
#
|
|
1117
|
+
# @example Returning a GraphQL type based on the object's class name
|
|
1118
|
+
# class MySchema < GraphQL::Schema
|
|
1119
|
+
# def resolve_type(_abs_type, object, _context)
|
|
1120
|
+
# graphql_type_name = "Types::#{object.class.name}Type"
|
|
1121
|
+
# graphql_type_name.constantize # If this raises a NameError, then come implement special cases in this method
|
|
1122
|
+
# end
|
|
1123
|
+
# end
|
|
1124
|
+
# @param abstract_type [Class, Module, nil] The Interface or Union type which is being resolved, if there is one
|
|
1125
|
+
# @param application_object [Object] The object returned from a field whose type must be determined
|
|
1126
|
+
# @param context [GraphQL::Query::Context] The query context for the currently-executing query
|
|
1127
|
+
# @return [Class<GraphQL::Schema::Object] The Object type definition to use for `obj`
|
|
1128
|
+
def resolve_type(abstract_type, application_object, context)
|
|
1129
|
+
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})"
|
|
1043
1130
|
end
|
|
1044
1131
|
# rubocop:enable Lint/DuplicateMethods
|
|
1045
1132
|
|
|
@@ -1054,15 +1141,45 @@ module GraphQL
|
|
|
1054
1141
|
child_class.own_trace_modes[name] = child_class.build_trace_mode(name)
|
|
1055
1142
|
end
|
|
1056
1143
|
child_class.singleton_class.prepend(ResolveTypeWithType)
|
|
1057
|
-
super
|
|
1058
|
-
end
|
|
1059
1144
|
|
|
1060
|
-
|
|
1061
|
-
|
|
1145
|
+
if use_visibility_profile?
|
|
1146
|
+
vis = self.visibility
|
|
1147
|
+
child_class.visibility = vis.dup_for(child_class)
|
|
1148
|
+
end
|
|
1149
|
+
super
|
|
1062
1150
|
end
|
|
1063
1151
|
|
|
1064
|
-
|
|
1065
|
-
|
|
1152
|
+
# Fetch an object based on an incoming ID and the current context. This method should return an object
|
|
1153
|
+
# from your application, or return `nil` if there is no object or the object shouldn't be available to this operation.
|
|
1154
|
+
#
|
|
1155
|
+
# @example Fetching an object with Rails's GlobalID
|
|
1156
|
+
# def self.object_from_id(object_id, _context)
|
|
1157
|
+
# GlobalID.find(global_id)
|
|
1158
|
+
# # TODO: use `context[:current_user]` to determine if this object is authorized.
|
|
1159
|
+
# end
|
|
1160
|
+
# @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`)
|
|
1161
|
+
# @param context [GraphQL::Query::Context] The context for the currently-executing operation
|
|
1162
|
+
# @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
|
|
1163
|
+
# @see id_from_object which produces these IDs
|
|
1164
|
+
def object_from_id(object_id, context)
|
|
1165
|
+
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}`)"
|
|
1166
|
+
end
|
|
1167
|
+
|
|
1168
|
+
# Return a stable ID string for `object` so that it can be refetched later, using {.object_from_id}.
|
|
1169
|
+
#
|
|
1170
|
+
# {GlobalID}(https://github.com/rails/globalid) and {SQIDs}(https://sqids.org/ruby) can both be used to create IDs.
|
|
1171
|
+
#
|
|
1172
|
+
# @example Using Rails's GlobalID to generate IDs
|
|
1173
|
+
# def self.id_from_object(application_object, graphql_type, context)
|
|
1174
|
+
# application_object.to_gid_param
|
|
1175
|
+
# end
|
|
1176
|
+
#
|
|
1177
|
+
# @param application_object [Object] Some object encountered by GraphQL-Ruby while running a query
|
|
1178
|
+
# @param graphql_type [Class, Module] The type that GraphQL-Ruby is using for `application_object` during this query
|
|
1179
|
+
# @param context [GraphQL::Query::Context] The context for the operation that is currently running
|
|
1180
|
+
# @return [String] A stable identifier which can be passed to {.object_from_id} later to re-fetch `application_object`
|
|
1181
|
+
def id_from_object(application_object, graphql_type, context)
|
|
1182
|
+
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}`)"
|
|
1066
1183
|
end
|
|
1067
1184
|
|
|
1068
1185
|
def visible?(member, ctx)
|
|
@@ -1117,6 +1234,16 @@ module GraphQL
|
|
|
1117
1234
|
unauthorized_object(unauthorized_error)
|
|
1118
1235
|
end
|
|
1119
1236
|
|
|
1237
|
+
# Called at runtime when GraphQL-Ruby encounters a mismatch between the application behavior
|
|
1238
|
+
# and the GraphQL type system.
|
|
1239
|
+
#
|
|
1240
|
+
# The default implementation of this method is to follow the GraphQL specification,
|
|
1241
|
+
# but you can override this to report errors to your bug tracker or customize error handling.
|
|
1242
|
+
# @param type_error [GraphQL::Error] several specific error classes are passed here, see the default implementation for details
|
|
1243
|
+
# @param context [GraphQL::Query::Context] the context for the currently-running operation
|
|
1244
|
+
# @return [void]
|
|
1245
|
+
# @raise [GraphQL::ExecutionError] to return this error to the client
|
|
1246
|
+
# @raise [GraphQL::Error] to crash the query and raise a developer-facing error
|
|
1120
1247
|
def type_error(type_error, ctx)
|
|
1121
1248
|
case type_error
|
|
1122
1249
|
when GraphQL::InvalidNullError
|
|
@@ -1171,7 +1298,11 @@ module GraphQL
|
|
|
1171
1298
|
# @param new_directive [Class]
|
|
1172
1299
|
# @return void
|
|
1173
1300
|
def directive(new_directive)
|
|
1174
|
-
|
|
1301
|
+
if use_visibility_profile?
|
|
1302
|
+
own_directives[new_directive.graphql_name] = new_directive
|
|
1303
|
+
else
|
|
1304
|
+
add_type_and_traverse(new_directive, root: false)
|
|
1305
|
+
end
|
|
1175
1306
|
end
|
|
1176
1307
|
|
|
1177
1308
|
def default_directives
|
|
@@ -1208,6 +1339,7 @@ module GraphQL
|
|
|
1208
1339
|
# @param mode [Symbol] Trace module will only be used for this trade mode
|
|
1209
1340
|
# @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
|
|
1210
1341
|
# @return [void]
|
|
1342
|
+
# @see GraphQL::Tracing::Trace for available tracing methods
|
|
1211
1343
|
def trace_with(trace_mod, mode: :default, **options)
|
|
1212
1344
|
if mode.is_a?(Array)
|
|
1213
1345
|
mode.each { |m| trace_with(trace_mod, mode: m, **options) }
|
|
@@ -1285,6 +1417,8 @@ module GraphQL
|
|
|
1285
1417
|
trace_class_for_mode.new(**trace_options)
|
|
1286
1418
|
end
|
|
1287
1419
|
|
|
1420
|
+
# @param new_analyzer [Class<GraphQL::Analysis::Analyzer>] An analyzer to run on queries to this schema
|
|
1421
|
+
# @see GraphQL::Analysis the analysis system
|
|
1288
1422
|
def query_analyzer(new_analyzer)
|
|
1289
1423
|
own_query_analyzers << new_analyzer
|
|
1290
1424
|
end
|
|
@@ -1293,6 +1427,8 @@ module GraphQL
|
|
|
1293
1427
|
find_inherited_value(:query_analyzers, EMPTY_ARRAY) + own_query_analyzers
|
|
1294
1428
|
end
|
|
1295
1429
|
|
|
1430
|
+
# @param new_analyzer [Class<GraphQL::Analysis::Analyzer>] An analyzer to run on multiplexes to this schema
|
|
1431
|
+
# @see GraphQL::Analysis the analysis system
|
|
1296
1432
|
def multiplex_analyzer(new_analyzer)
|
|
1297
1433
|
own_multiplex_analyzers << new_analyzer
|
|
1298
1434
|
end
|
|
@@ -1365,7 +1501,8 @@ module GraphQL
|
|
|
1365
1501
|
|
|
1366
1502
|
# @api private
|
|
1367
1503
|
def add_subscription_extension_if_necessary
|
|
1368
|
-
|
|
1504
|
+
# TODO: when there's a proper API for extending root types, migrat this to use it.
|
|
1505
|
+
if !defined?(@subscription_extension_added) && @subscription_object.is_a?(Class) && self.subscriptions
|
|
1369
1506
|
@subscription_extension_added = true
|
|
1370
1507
|
subscription.all_field_definitions.each do |field|
|
|
1371
1508
|
if !field.extensions.any? { |ext| ext.is_a?(Subscriptions::DefaultSubscriptionResolveExtension) }
|
|
@@ -1375,6 +1512,11 @@ module GraphQL
|
|
|
1375
1512
|
end
|
|
1376
1513
|
end
|
|
1377
1514
|
|
|
1515
|
+
# Called when execution encounters a `SystemStackError`. By default, it adds a client-facing error to the response.
|
|
1516
|
+
# You could modify this method to report this error to your bug tracker.
|
|
1517
|
+
# @param query [GraphQL::Query]
|
|
1518
|
+
# @param err [SystemStackError]
|
|
1519
|
+
# @return [void]
|
|
1378
1520
|
def query_stack_error(query, err)
|
|
1379
1521
|
query.context.errors.push(GraphQL::ExecutionError.new("This query is too large to execute."))
|
|
1380
1522
|
end
|
|
@@ -1433,11 +1575,34 @@ module GraphQL
|
|
|
1433
1575
|
end
|
|
1434
1576
|
end
|
|
1435
1577
|
|
|
1578
|
+
# Returns `DidYouMean` if it's defined.
|
|
1579
|
+
# Override this to return `nil` if you don't want to use `DidYouMean`
|
|
1580
|
+
def did_you_mean(new_dym = NOT_CONFIGURED)
|
|
1581
|
+
if NOT_CONFIGURED.equal?(new_dym)
|
|
1582
|
+
if defined?(@did_you_mean)
|
|
1583
|
+
@did_you_mean
|
|
1584
|
+
else
|
|
1585
|
+
find_inherited_value(:did_you_mean, defined?(DidYouMean) ? DidYouMean : nil)
|
|
1586
|
+
end
|
|
1587
|
+
else
|
|
1588
|
+
@did_you_mean = new_dym
|
|
1589
|
+
end
|
|
1590
|
+
end
|
|
1591
|
+
|
|
1436
1592
|
private
|
|
1437
1593
|
|
|
1438
1594
|
def add_trace_options_for(mode, new_options)
|
|
1439
|
-
|
|
1440
|
-
|
|
1595
|
+
if mode == :default
|
|
1596
|
+
own_trace_modes.each do |mode_name, t_class|
|
|
1597
|
+
if t_class <= DefaultTraceClass
|
|
1598
|
+
t_opts = trace_options_for(mode_name)
|
|
1599
|
+
t_opts.merge!(new_options)
|
|
1600
|
+
end
|
|
1601
|
+
end
|
|
1602
|
+
else
|
|
1603
|
+
t_opts = trace_options_for(mode)
|
|
1604
|
+
t_opts.merge!(new_options)
|
|
1605
|
+
end
|
|
1441
1606
|
nil
|
|
1442
1607
|
end
|
|
1443
1608
|
|
|
@@ -1514,7 +1679,7 @@ module GraphQL
|
|
|
1514
1679
|
end
|
|
1515
1680
|
|
|
1516
1681
|
def own_references_to
|
|
1517
|
-
@own_references_to ||= {}.
|
|
1682
|
+
@own_references_to ||= {}.compare_by_identity
|
|
1518
1683
|
end
|
|
1519
1684
|
|
|
1520
1685
|
def non_introspection_types
|
|
@@ -1530,7 +1695,7 @@ module GraphQL
|
|
|
1530
1695
|
end
|
|
1531
1696
|
|
|
1532
1697
|
def own_possible_types
|
|
1533
|
-
@own_possible_types ||= {}.
|
|
1698
|
+
@own_possible_types ||= {}.compare_by_identity
|
|
1534
1699
|
end
|
|
1535
1700
|
|
|
1536
1701
|
def own_union_memberships
|
|
@@ -1581,5 +1746,9 @@ module GraphQL
|
|
|
1581
1746
|
|
|
1582
1747
|
# Install these here so that subclasses will also install it.
|
|
1583
1748
|
self.connections = GraphQL::Pagination::Connections.new(schema: self)
|
|
1749
|
+
|
|
1750
|
+
# @api private
|
|
1751
|
+
module DefaultTraceClass
|
|
1752
|
+
end
|
|
1584
1753
|
end
|
|
1585
1754
|
end
|
|
@@ -10,8 +10,9 @@ module GraphQL
|
|
|
10
10
|
elsif parent_defn
|
|
11
11
|
kind_of_node = node_type(parent)
|
|
12
12
|
error_arg_name = parent_name(parent, parent_defn)
|
|
13
|
+
arg_names = context.types.arguments(parent_defn).map(&:graphql_name)
|
|
13
14
|
add_error(GraphQL::StaticValidation::ArgumentsAreDefinedError.new(
|
|
14
|
-
"#{kind_of_node} '#{error_arg_name}' doesn't accept argument '#{node.name}'",
|
|
15
|
+
"#{kind_of_node} '#{error_arg_name}' doesn't accept argument '#{node.name}'#{context.did_you_mean_suggestion(node.name, arg_names)}",
|
|
15
16
|
nodes: node,
|
|
16
17
|
name: error_arg_name,
|
|
17
18
|
type: kind_of_node,
|
|
@@ -10,8 +10,9 @@ module GraphQL
|
|
|
10
10
|
if !@types.directive_exists?(node.name)
|
|
11
11
|
@directives_are_defined_errors_by_name ||= {}
|
|
12
12
|
error = @directives_are_defined_errors_by_name[node.name] ||= begin
|
|
13
|
+
@directive_names ||= @types.directives.map(&:graphql_name)
|
|
13
14
|
err = GraphQL::StaticValidation::DirectivesAreDefinedError.new(
|
|
14
|
-
"Directive @#{node.name} is not defined",
|
|
15
|
+
"Directive @#{node.name} is not defined#{context.did_you_mean_suggestion(node.name, @directive_names)}",
|
|
15
16
|
nodes: [],
|
|
16
17
|
directive: node.name
|
|
17
18
|
)
|
|
@@ -19,6 +19,7 @@ module GraphQL
|
|
|
19
19
|
GraphQL::Schema::Directive::FRAGMENT_DEFINITION => "fragment definitions",
|
|
20
20
|
GraphQL::Schema::Directive::FRAGMENT_SPREAD => "fragment spreads",
|
|
21
21
|
GraphQL::Schema::Directive::INLINE_FRAGMENT => "inline fragments",
|
|
22
|
+
GraphQL::Schema::Directive::VARIABLE_DEFINITION => "variable definitions",
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
SIMPLE_LOCATIONS = {
|
|
@@ -26,6 +27,7 @@ module GraphQL
|
|
|
26
27
|
Nodes::InlineFragment => GraphQL::Schema::Directive::INLINE_FRAGMENT,
|
|
27
28
|
Nodes::FragmentSpread => GraphQL::Schema::Directive::FRAGMENT_SPREAD,
|
|
28
29
|
Nodes::FragmentDefinition => GraphQL::Schema::Directive::FRAGMENT_DEFINITION,
|
|
30
|
+
Nodes::VariableDefinition => GraphQL::Schema::Directive::VARIABLE_DEFINITION,
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
SIMPLE_LOCATION_NODES = SIMPLE_LOCATIONS.keys
|
|
@@ -14,8 +14,9 @@ module GraphQL
|
|
|
14
14
|
node_name: parent_type.graphql_name
|
|
15
15
|
))
|
|
16
16
|
else
|
|
17
|
+
message = "Field '#{node.name}' doesn't exist on type '#{parent_type.graphql_name}'#{context.did_you_mean_suggestion(node.name, context.types.fields(parent_type).map(&:graphql_name))}"
|
|
17
18
|
add_error(GraphQL::StaticValidation::FieldsAreDefinedOnTypeError.new(
|
|
18
|
-
|
|
19
|
+
message,
|
|
19
20
|
nodes: node,
|
|
20
21
|
field: node.name,
|
|
21
22
|
type: parent_type.graphql_name
|
|
@@ -212,6 +212,7 @@ module GraphQL
|
|
|
212
212
|
|
|
213
213
|
def find_conflict(response_key, field1, field2, mutually_exclusive: false)
|
|
214
214
|
return if @conflict_count >= context.max_errors
|
|
215
|
+
return if field1.definition.nil? || field2.definition.nil?
|
|
215
216
|
|
|
216
217
|
node1 = field1.node
|
|
217
218
|
node2 = field2.node
|