graphql 2.3.18 → 2.4.1
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/graphql/dataloader/async_dataloader.rb +3 -2
- data/lib/graphql/dataloader/source.rb +1 -1
- data/lib/graphql/dataloader.rb +31 -10
- data/lib/graphql/query/null_context.rb +3 -5
- data/lib/graphql/query.rb +49 -16
- data/lib/graphql/schema/always_visible.rb +6 -3
- data/lib/graphql/schema/argument.rb +1 -0
- data/lib/graphql/schema/build_from_definition.rb +1 -0
- data/lib/graphql/schema/enum.rb +19 -3
- data/lib/graphql/schema/enum_value.rb +1 -1
- data/lib/graphql/schema/input_object.rb +20 -7
- data/lib/graphql/schema/member/has_arguments.rb +2 -2
- data/lib/graphql/schema/member/has_fields.rb +2 -2
- data/lib/graphql/schema/printer.rb +1 -0
- data/lib/graphql/schema/validator/required_validator.rb +28 -4
- data/lib/graphql/schema/visibility/migration.rb +34 -35
- data/lib/graphql/schema/visibility/{subset.rb → profile.rb} +37 -19
- data/lib/graphql/schema/visibility.rb +57 -12
- data/lib/graphql/schema/warden.rb +87 -21
- data/lib/graphql/schema.rb +177 -41
- 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/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/variables_are_input_types.rb +10 -1
- data/lib/graphql/static_validation/validation_context.rb +15 -0
- data/lib/graphql/testing/helpers.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- metadata +3 -3
data/lib/graphql/schema.rb
CHANGED
@@ -317,6 +317,9 @@ module GraphQL
|
|
317
317
|
GraphQL::StaticValidation::Validator.new(schema: self)
|
318
318
|
end
|
319
319
|
|
320
|
+
# Add `plugin` to this schema
|
321
|
+
# @param plugin [#use] A Schema plugin
|
322
|
+
# @return void
|
320
323
|
def use(plugin, **kwargs)
|
321
324
|
if kwargs.any?
|
322
325
|
plugin.use(self, **kwargs)
|
@@ -334,8 +337,9 @@ module GraphQL
|
|
334
337
|
# @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
|
335
338
|
# @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
|
336
339
|
def types(context = GraphQL::Query::NullContext.instance)
|
337
|
-
if
|
338
|
-
|
340
|
+
if use_visibility_profile?
|
341
|
+
types = Visibility::Profile.from_context(context, self)
|
342
|
+
return types.all_types_h
|
339
343
|
end
|
340
344
|
all_types = non_introspection_types.merge(introspection_system.types)
|
341
345
|
visible_types = {}
|
@@ -362,17 +366,19 @@ module GraphQL
|
|
362
366
|
end
|
363
367
|
|
364
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}
|
365
371
|
# @return [Module, nil] A type, or nil if there's no type called `type_name`
|
366
|
-
def get_type(type_name, context = GraphQL::Query::NullContext.instance)
|
367
|
-
if
|
368
|
-
return Visibility::
|
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)
|
369
375
|
end
|
370
376
|
local_entry = own_types[type_name]
|
371
377
|
type_defn = case local_entry
|
372
378
|
when nil
|
373
379
|
nil
|
374
380
|
when Array
|
375
|
-
if context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::
|
381
|
+
if context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile)
|
376
382
|
local_entry
|
377
383
|
else
|
378
384
|
visible_t = nil
|
@@ -398,7 +404,7 @@ module GraphQL
|
|
398
404
|
|
399
405
|
type_defn ||
|
400
406
|
introspection_system.types[type_name] || # todo context-specific introspection?
|
401
|
-
(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)
|
402
408
|
end
|
403
409
|
|
404
410
|
# @return [Boolean] Does this schema have _any_ definition for a type named `type_name`, regardless of visibility?
|
@@ -425,12 +431,20 @@ module GraphQL
|
|
425
431
|
end
|
426
432
|
end
|
427
433
|
|
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.
|
428
442
|
def query(new_query_object = nil, &lazy_load_block)
|
429
443
|
if new_query_object || block_given?
|
430
444
|
if @query_object
|
431
445
|
dup_defn = new_query_object || yield
|
432
446
|
raise GraphQL::Error, "Second definition of `query(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@query_object.inspect}"
|
433
|
-
elsif
|
447
|
+
elsif use_visibility_profile?
|
434
448
|
@query_object = block_given? ? lazy_load_block : new_query_object
|
435
449
|
else
|
436
450
|
@query_object = new_query_object || lazy_load_block.call
|
@@ -444,12 +458,20 @@ module GraphQL
|
|
444
458
|
end
|
445
459
|
end
|
446
460
|
|
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.
|
447
469
|
def mutation(new_mutation_object = nil, &lazy_load_block)
|
448
470
|
if new_mutation_object || block_given?
|
449
471
|
if @mutation_object
|
450
472
|
dup_defn = new_mutation_object || yield
|
451
473
|
raise GraphQL::Error, "Second definition of `mutation(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@mutation_object.inspect}"
|
452
|
-
elsif
|
474
|
+
elsif use_visibility_profile?
|
453
475
|
@mutation_object = block_given? ? lazy_load_block : new_mutation_object
|
454
476
|
else
|
455
477
|
@mutation_object = new_mutation_object || lazy_load_block.call
|
@@ -463,12 +485,20 @@ module GraphQL
|
|
463
485
|
end
|
464
486
|
end
|
465
487
|
|
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.
|
466
496
|
def subscription(new_subscription_object = nil, &lazy_load_block)
|
467
497
|
if new_subscription_object || block_given?
|
468
498
|
if @subscription_object
|
469
499
|
dup_defn = new_subscription_object || yield
|
470
500
|
raise GraphQL::Error, "Second definition of `subscription(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@subscription_object.inspect}"
|
471
|
-
elsif
|
501
|
+
elsif use_visibility_profile?
|
472
502
|
@subscription_object = block_given? ? lazy_load_block : new_subscription_object
|
473
503
|
add_subscription_extension_if_necessary
|
474
504
|
else
|
@@ -486,8 +516,7 @@ module GraphQL
|
|
486
516
|
end
|
487
517
|
end
|
488
518
|
|
489
|
-
# @
|
490
|
-
# @return [GraphQL::ObjectType, nil]
|
519
|
+
# @api private
|
491
520
|
def root_type_for_operation(operation)
|
492
521
|
case operation
|
493
522
|
when "query"
|
@@ -501,14 +530,16 @@ module GraphQL
|
|
501
530
|
end
|
502
531
|
end
|
503
532
|
|
533
|
+
# @return [Array<Class>] The root types (query, mutation, subscription) defined for this schema
|
504
534
|
def root_types
|
505
|
-
if
|
535
|
+
if use_visibility_profile?
|
506
536
|
[query, mutation, subscription].compact
|
507
537
|
else
|
508
538
|
@root_types
|
509
539
|
end
|
510
540
|
end
|
511
541
|
|
542
|
+
# @api private
|
512
543
|
def warden_class
|
513
544
|
if defined?(@warden_class)
|
514
545
|
@warden_class
|
@@ -519,39 +550,46 @@ module GraphQL
|
|
519
550
|
end
|
520
551
|
end
|
521
552
|
|
553
|
+
# @api private
|
522
554
|
attr_writer :warden_class
|
523
555
|
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
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
|
529
562
|
else
|
530
|
-
GraphQL::Schema::Visibility::
|
563
|
+
GraphQL::Schema::Visibility::Profile
|
531
564
|
end
|
532
565
|
end
|
533
566
|
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
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?
|
541
577
|
else
|
542
578
|
false
|
543
579
|
end
|
544
580
|
end
|
545
581
|
|
546
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}
|
547
585
|
# @return [Hash<String, Module>] All possible types, if no `type` is given.
|
548
586
|
# @return [Array<Module>] Possible types for `type`, if it's given.
|
549
|
-
def possible_types(type = nil, context = GraphQL::Query::NullContext.instance)
|
550
|
-
if
|
587
|
+
def possible_types(type = nil, context = GraphQL::Query::NullContext.instance, use_visibility_profile = use_visibility_profile?)
|
588
|
+
if use_visibility_profile
|
551
589
|
if type
|
552
|
-
return Visibility::
|
590
|
+
return Visibility::Profile.from_context(context, self).possible_types(type)
|
553
591
|
else
|
554
|
-
raise "Schema.possible_types is not implemented for `
|
592
|
+
raise "Schema.possible_types is not implemented for `use_visibility_profile?`"
|
555
593
|
end
|
556
594
|
end
|
557
595
|
if type
|
@@ -571,7 +609,7 @@ module GraphQL
|
|
571
609
|
introspection_system.possible_types[type] ||
|
572
610
|
(
|
573
611
|
superclass.respond_to?(:possible_types) ?
|
574
|
-
superclass.possible_types(type, context) :
|
612
|
+
superclass.possible_types(type, context, use_visibility_profile) :
|
575
613
|
EMPTY_ARRAY
|
576
614
|
)
|
577
615
|
end
|
@@ -774,6 +812,7 @@ module GraphQL
|
|
774
812
|
res[:errors]
|
775
813
|
end
|
776
814
|
|
815
|
+
# @param new_query_class [Class<GraphQL::Query>] A subclass to use when executing queries
|
777
816
|
def query_class(new_query_class = NOT_CONFIGURED)
|
778
817
|
if NOT_CONFIGURED.equal?(new_query_class)
|
779
818
|
@query_class || (superclass.respond_to?(:query_class) ? superclass.query_class : GraphQL::Query)
|
@@ -927,7 +966,7 @@ module GraphQL
|
|
927
966
|
To add other types to your schema, you might want `extra_types`: https://graphql-ruby.org/schema/definition.html#extra-types
|
928
967
|
ERR
|
929
968
|
end
|
930
|
-
add_type_and_traverse(new_orphan_types, root: false) unless
|
969
|
+
add_type_and_traverse(new_orphan_types, root: false) unless use_visibility_profile?
|
931
970
|
own_orphan_types.concat(new_orphan_types.flatten)
|
932
971
|
end
|
933
972
|
|
@@ -959,6 +998,8 @@ module GraphQL
|
|
959
998
|
end
|
960
999
|
end
|
961
1000
|
|
1001
|
+
|
1002
|
+
# @param new_default_logger [#log] Something to use for logging messages
|
962
1003
|
def default_logger(new_default_logger = NOT_CONFIGURED)
|
963
1004
|
if NOT_CONFIGURED.equal?(new_default_logger)
|
964
1005
|
if defined?(@default_logger)
|
@@ -979,6 +1020,7 @@ module GraphQL
|
|
979
1020
|
end
|
980
1021
|
end
|
981
1022
|
|
1023
|
+
# @param new_context_class [Class<GraphQL::Query::Context>] A subclass to use when executing queries
|
982
1024
|
def context_class(new_context_class = nil)
|
983
1025
|
if new_context_class
|
984
1026
|
@context_class = new_context_class
|
@@ -987,6 +1029,20 @@ module GraphQL
|
|
987
1029
|
end
|
988
1030
|
end
|
989
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
|
990
1046
|
def rescue_from(*err_classes, &handler_block)
|
991
1047
|
err_classes.each do |err_class|
|
992
1048
|
Execution::Errors.register_rescue_from(err_class, error_handlers[:subclass_handlers], handler_block)
|
@@ -1053,8 +1109,24 @@ module GraphQL
|
|
1053
1109
|
end
|
1054
1110
|
end
|
1055
1111
|
|
1056
|
-
|
1057
|
-
|
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})"
|
1058
1130
|
end
|
1059
1131
|
# rubocop:enable Lint/DuplicateMethods
|
1060
1132
|
|
@@ -1069,15 +1141,45 @@ module GraphQL
|
|
1069
1141
|
child_class.own_trace_modes[name] = child_class.build_trace_mode(name)
|
1070
1142
|
end
|
1071
1143
|
child_class.singleton_class.prepend(ResolveTypeWithType)
|
1072
|
-
super
|
1073
|
-
end
|
1074
1144
|
|
1075
|
-
|
1076
|
-
|
1145
|
+
if use_visibility_profile?
|
1146
|
+
vis = self.visibility
|
1147
|
+
child_class.visibility = vis.dup_for(child_class)
|
1148
|
+
end
|
1149
|
+
super
|
1077
1150
|
end
|
1078
1151
|
|
1079
|
-
|
1080
|
-
|
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}`)"
|
1081
1183
|
end
|
1082
1184
|
|
1083
1185
|
def visible?(member, ctx)
|
@@ -1132,6 +1234,16 @@ module GraphQL
|
|
1132
1234
|
unauthorized_object(unauthorized_error)
|
1133
1235
|
end
|
1134
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
|
1135
1247
|
def type_error(type_error, ctx)
|
1136
1248
|
case type_error
|
1137
1249
|
when GraphQL::InvalidNullError
|
@@ -1186,7 +1298,7 @@ module GraphQL
|
|
1186
1298
|
# @param new_directive [Class]
|
1187
1299
|
# @return void
|
1188
1300
|
def directive(new_directive)
|
1189
|
-
if
|
1301
|
+
if use_visibility_profile?
|
1190
1302
|
own_directives[new_directive.graphql_name] = new_directive
|
1191
1303
|
else
|
1192
1304
|
add_type_and_traverse(new_directive, root: false)
|
@@ -1227,6 +1339,7 @@ module GraphQL
|
|
1227
1339
|
# @param mode [Symbol] Trace module will only be used for this trade mode
|
1228
1340
|
# @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
|
1229
1341
|
# @return [void]
|
1342
|
+
# @see GraphQL::Tracing::Trace for available tracing methods
|
1230
1343
|
def trace_with(trace_mod, mode: :default, **options)
|
1231
1344
|
if mode.is_a?(Array)
|
1232
1345
|
mode.each { |m| trace_with(trace_mod, mode: m, **options) }
|
@@ -1304,6 +1417,8 @@ module GraphQL
|
|
1304
1417
|
trace_class_for_mode.new(**trace_options)
|
1305
1418
|
end
|
1306
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
|
1307
1422
|
def query_analyzer(new_analyzer)
|
1308
1423
|
own_query_analyzers << new_analyzer
|
1309
1424
|
end
|
@@ -1312,6 +1427,8 @@ module GraphQL
|
|
1312
1427
|
find_inherited_value(:query_analyzers, EMPTY_ARRAY) + own_query_analyzers
|
1313
1428
|
end
|
1314
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
|
1315
1432
|
def multiplex_analyzer(new_analyzer)
|
1316
1433
|
own_multiplex_analyzers << new_analyzer
|
1317
1434
|
end
|
@@ -1395,6 +1512,11 @@ module GraphQL
|
|
1395
1512
|
end
|
1396
1513
|
end
|
1397
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]
|
1398
1520
|
def query_stack_error(query, err)
|
1399
1521
|
query.context.errors.push(GraphQL::ExecutionError.new("This query is too large to execute."))
|
1400
1522
|
end
|
@@ -1453,6 +1575,20 @@ module GraphQL
|
|
1453
1575
|
end
|
1454
1576
|
end
|
1455
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
|
+
|
1456
1592
|
private
|
1457
1593
|
|
1458
1594
|
def add_trace_options_for(mode, new_options)
|
@@ -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
|
)
|
@@ -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
|
@@ -23,8 +23,18 @@ module GraphQL
|
|
23
23
|
type_name = fragment_node.type.name
|
24
24
|
type = @types.type(type_name)
|
25
25
|
if type.nil?
|
26
|
+
@all_possible_fragment_type_names ||= begin
|
27
|
+
names = []
|
28
|
+
context.types.all_types.each do |type|
|
29
|
+
if type.kind.fields?
|
30
|
+
names << type.graphql_name
|
31
|
+
end
|
32
|
+
end
|
33
|
+
names
|
34
|
+
end
|
35
|
+
|
26
36
|
add_error(GraphQL::StaticValidation::FragmentTypesExistError.new(
|
27
|
-
"No such type #{type_name}, so it can't be a fragment condition",
|
37
|
+
"No such type #{type_name}, so it can't be a fragment condition#{context.did_you_mean_suggestion(type_name, @all_possible_fragment_type_names)}",
|
28
38
|
nodes: fragment_node,
|
29
39
|
type: type_name
|
30
40
|
))
|
@@ -7,8 +7,17 @@ module GraphQL
|
|
7
7
|
type = context.query.types.type(type_name)
|
8
8
|
|
9
9
|
if type.nil?
|
10
|
+
@all_possible_input_type_names ||= begin
|
11
|
+
names = []
|
12
|
+
context.types.all_types.each { |(t)|
|
13
|
+
if t.kind.input?
|
14
|
+
names << t.graphql_name
|
15
|
+
end
|
16
|
+
}
|
17
|
+
names
|
18
|
+
end
|
10
19
|
add_error(GraphQL::StaticValidation::VariablesAreInputTypesError.new(
|
11
|
-
"#{type_name} isn't a defined input type (on $#{node.name})",
|
20
|
+
"#{type_name} isn't a defined input type (on $#{node.name})#{context.did_you_mean_suggestion(type_name, @all_possible_input_type_names)}",
|
12
21
|
nodes: node,
|
13
22
|
name: node.name,
|
14
23
|
type: type_name
|
@@ -48,6 +48,21 @@ module GraphQL
|
|
48
48
|
def schema_directives
|
49
49
|
@schema_directives ||= schema.directives
|
50
50
|
end
|
51
|
+
|
52
|
+
def did_you_mean_suggestion(name, options)
|
53
|
+
if did_you_mean = schema.did_you_mean
|
54
|
+
suggestions = did_you_mean::SpellChecker.new(dictionary: options).correct(name)
|
55
|
+
case suggestions.size
|
56
|
+
when 0
|
57
|
+
""
|
58
|
+
when 1
|
59
|
+
" (Did you mean `#{suggestions.first}`?)"
|
60
|
+
else
|
61
|
+
last_sugg = suggestions.pop
|
62
|
+
" (Did you mean #{suggestions.map {|s| "`#{s}`"}.join(", ")} or `#{last_sugg}`?)"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
51
66
|
end
|
52
67
|
end
|
53
68
|
end
|
@@ -92,7 +92,7 @@ module GraphQL
|
|
92
92
|
end
|
93
93
|
graphql_result
|
94
94
|
else
|
95
|
-
unfiltered_type = Schema::Visibility::
|
95
|
+
unfiltered_type = Schema::Visibility::Profile.null_profile(schema: schema, context: context).type(type_name)
|
96
96
|
if unfiltered_type
|
97
97
|
raise TypeNotVisibleError.new(type_name: type_name)
|
98
98
|
else
|
data/lib/graphql/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-11-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: base64
|
@@ -503,7 +503,7 @@ files:
|
|
503
503
|
- lib/graphql/schema/validator/required_validator.rb
|
504
504
|
- lib/graphql/schema/visibility.rb
|
505
505
|
- lib/graphql/schema/visibility/migration.rb
|
506
|
-
- lib/graphql/schema/visibility/
|
506
|
+
- lib/graphql/schema/visibility/profile.rb
|
507
507
|
- lib/graphql/schema/warden.rb
|
508
508
|
- lib/graphql/schema/wrapper.rb
|
509
509
|
- lib/graphql/static_validation.rb
|