graphql 2.3.19 → 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/graphql/schema/enum.rb +2 -0
- data/lib/graphql/schema/input_object.rb +20 -7
- data/lib/graphql/schema/printer.rb +1 -0
- data/lib/graphql/schema/visibility/migration.rb +1 -0
- data/lib/graphql/schema/warden.rb +63 -1
- data/lib/graphql/schema.rb +129 -10
- 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/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/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 26e43b0bc48317698ed17f8a11498c19a7a4a0df9d33fdc9785edc47ae1147b6
|
4
|
+
data.tar.gz: 74402e930ebe03a451bc2bdb82285642aeeba7222ab491dd9329400fc852eb41
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f1d97c4397ca8410f6b62c3ea2de9a0a4b18ca610adcb92de9676c5377b00a071cbff4fbd0396760eea9ab359e4b0b8cdfff1630c9fa83de51cfd31b0b96307f
|
7
|
+
data.tar.gz: d9250a9ad1d57f40e7b0151484f77a66e11a361f8b0f50938f42219eb8322a9b4dc59c59102f4b49f62a28e90f03b8f9e0cf5b2799b5fd5080dbcf443cebac15
|
data/lib/graphql/schema/enum.rb
CHANGED
@@ -133,12 +133,14 @@ module GraphQL
|
|
133
133
|
end
|
134
134
|
# Add a method access
|
135
135
|
method_name = argument_defn.keyword
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
136
|
+
suppress_redefinition_warning do
|
137
|
+
class_eval <<-RUBY, __FILE__, __LINE__
|
138
|
+
def #{method_name}
|
139
|
+
self[#{method_name.inspect}]
|
140
|
+
end
|
141
|
+
alias_method :#{method_name}, :#{method_name}
|
142
|
+
RUBY
|
143
|
+
end
|
142
144
|
argument_defn
|
143
145
|
end
|
144
146
|
|
@@ -163,7 +165,7 @@ module GraphQL
|
|
163
165
|
|
164
166
|
# Inject missing required arguments
|
165
167
|
missing_required_inputs = ctx.types.arguments(self).reduce({}) do |m, (argument)|
|
166
|
-
if !input.key?(argument.graphql_name) && argument.type.non_null? && types.argument(self, argument.graphql_name)
|
168
|
+
if !input.key?(argument.graphql_name) && argument.type.non_null? && !argument.default_value? && types.argument(self, argument.graphql_name)
|
167
169
|
m[argument.graphql_name] = nil
|
168
170
|
end
|
169
171
|
|
@@ -243,6 +245,17 @@ module GraphQL
|
|
243
245
|
|
244
246
|
result
|
245
247
|
end
|
248
|
+
|
249
|
+
private
|
250
|
+
|
251
|
+
# Suppress redefinition warning for objectId arguments
|
252
|
+
def suppress_redefinition_warning
|
253
|
+
verbose = $VERBOSE
|
254
|
+
$VERBOSE = nil
|
255
|
+
yield
|
256
|
+
ensure
|
257
|
+
$VERBOSE = verbose
|
258
|
+
end
|
246
259
|
end
|
247
260
|
|
248
261
|
private
|
@@ -96,6 +96,7 @@ module GraphQL
|
|
96
96
|
end
|
97
97
|
warden_ctx = GraphQL::Query::Context.new(query: context.query, values: warden_ctx_vals)
|
98
98
|
warden_ctx.warden = GraphQL::Schema::Warden.new(schema: warden_schema, context: warden_ctx)
|
99
|
+
warden_ctx.warden.skip_warning = true
|
99
100
|
warden_ctx.types = @warden_types = warden_ctx.warden.visibility_profile
|
100
101
|
end
|
101
102
|
end
|
@@ -19,6 +19,10 @@ module GraphQL
|
|
19
19
|
PassThruWarden
|
20
20
|
end
|
21
21
|
|
22
|
+
def self.use(schema)
|
23
|
+
# no-op
|
24
|
+
end
|
25
|
+
|
22
26
|
# @param visibility_method [Symbol] a Warden method to call for this entry
|
23
27
|
# @param entry [Object, Array<Object>] One or more definitions for a given name in a GraphQL Schema
|
24
28
|
# @param context [GraphQL::Query::Context]
|
@@ -73,6 +77,9 @@ module GraphQL
|
|
73
77
|
@visibility_profile = Warden::VisibilityProfile.new(self)
|
74
78
|
end
|
75
79
|
|
80
|
+
# No-op, but for compatibility:
|
81
|
+
attr_writer :skip_warning
|
82
|
+
|
76
83
|
# @api private
|
77
84
|
module NullVisibilityProfile
|
78
85
|
def self.new(context:, schema:)
|
@@ -187,7 +194,7 @@ module GraphQL
|
|
187
194
|
@mutation = @schema.mutation
|
188
195
|
@subscription = @schema.subscription
|
189
196
|
@context = context
|
190
|
-
@visibility_cache = read_through { |m| schema
|
197
|
+
@visibility_cache = read_through { |m| check_visible(schema, m) }
|
191
198
|
# Initialize all ivars to improve object shape consistency:
|
192
199
|
@types = @visible_types = @reachable_types = @visible_parent_fields =
|
193
200
|
@visible_possible_types = @visible_fields = @visible_arguments = @visible_enum_arrays =
|
@@ -195,8 +202,11 @@ module GraphQL
|
|
195
202
|
@visible_and_reachable_type = @unions = @unfiltered_interfaces =
|
196
203
|
@reachable_type_set = @visibility_profile =
|
197
204
|
nil
|
205
|
+
@skip_warning = schema.plugins.any? { |(plugin, _opts)| plugin == GraphQL::Schema::Warden }
|
198
206
|
end
|
199
207
|
|
208
|
+
attr_writer :skip_warning
|
209
|
+
|
200
210
|
# @return [Hash<String, GraphQL::BaseType>] Visible types in the schema
|
201
211
|
def types
|
202
212
|
@types ||= begin
|
@@ -465,6 +475,58 @@ module GraphQL
|
|
465
475
|
Hash.new { |h, k| h[k] = yield(k) }.compare_by_identity
|
466
476
|
end
|
467
477
|
|
478
|
+
def check_visible(schema, member)
|
479
|
+
if schema.visible?(member, @context)
|
480
|
+
true
|
481
|
+
elsif @skip_warning
|
482
|
+
false
|
483
|
+
else
|
484
|
+
member_s = member.respond_to?(:path) ? member.path : member.inspect
|
485
|
+
member_type = case member
|
486
|
+
when Module
|
487
|
+
if member.respond_to?(:kind)
|
488
|
+
member.kind.name.downcase
|
489
|
+
else
|
490
|
+
""
|
491
|
+
end
|
492
|
+
when GraphQL::Schema::Field
|
493
|
+
"field"
|
494
|
+
when GraphQL::Schema::EnumValue
|
495
|
+
"enum value"
|
496
|
+
when GraphQL::Schema::Argument
|
497
|
+
"argument"
|
498
|
+
else
|
499
|
+
""
|
500
|
+
end
|
501
|
+
|
502
|
+
schema_s = schema.name ? "#{schema.name}'s" : ""
|
503
|
+
schema_name = schema.name ? "#{schema.name}" : "your schema"
|
504
|
+
warn(ADD_WARDEN_WARNING % { schema_s: schema_s, schema_name: schema_name, member: member_s, member_type: member_type })
|
505
|
+
@skip_warning = true # only warn once per query
|
506
|
+
# If there's no schema name, add the backtrace for additional context:
|
507
|
+
if schema_s == ""
|
508
|
+
puts caller.map { |l| " #{l}"}
|
509
|
+
end
|
510
|
+
false
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
ADD_WARDEN_WARNING = <<~WARNING
|
515
|
+
DEPRECATION: %{schema_s} "%{member}" %{member_type} returned `false` for `.visible?` but `GraphQL::Schema::Visibility` isn't configured yet.
|
516
|
+
|
517
|
+
Address this warning by adding:
|
518
|
+
|
519
|
+
use GraphQL::Schema::Visibility
|
520
|
+
|
521
|
+
to the definition for %{schema_name}. (Future GraphQL-Ruby versions won't check `.visible?` methods by default.)
|
522
|
+
|
523
|
+
Alternatively, for legacy behavior, add:
|
524
|
+
|
525
|
+
use GraphQL::Schema::Warden # legacy visibility behavior
|
526
|
+
|
527
|
+
For more information see: https://graphql-ruby.org/authorization/visibility.html
|
528
|
+
WARNING
|
529
|
+
|
468
530
|
def reachable_type_set
|
469
531
|
return @reachable_type_set if @reachable_type_set
|
470
532
|
|
data/lib/graphql/schema.rb
CHANGED
@@ -431,6 +431,14 @@ module GraphQL
|
|
431
431
|
end
|
432
432
|
end
|
433
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.
|
434
442
|
def query(new_query_object = nil, &lazy_load_block)
|
435
443
|
if new_query_object || block_given?
|
436
444
|
if @query_object
|
@@ -450,6 +458,14 @@ module GraphQL
|
|
450
458
|
end
|
451
459
|
end
|
452
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.
|
453
469
|
def mutation(new_mutation_object = nil, &lazy_load_block)
|
454
470
|
if new_mutation_object || block_given?
|
455
471
|
if @mutation_object
|
@@ -469,6 +485,14 @@ module GraphQL
|
|
469
485
|
end
|
470
486
|
end
|
471
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.
|
472
496
|
def subscription(new_subscription_object = nil, &lazy_load_block)
|
473
497
|
if new_subscription_object || block_given?
|
474
498
|
if @subscription_object
|
@@ -492,8 +516,7 @@ module GraphQL
|
|
492
516
|
end
|
493
517
|
end
|
494
518
|
|
495
|
-
# @
|
496
|
-
# @return [GraphQL::ObjectType, nil]
|
519
|
+
# @api private
|
497
520
|
def root_type_for_operation(operation)
|
498
521
|
case operation
|
499
522
|
when "query"
|
@@ -507,6 +530,7 @@ module GraphQL
|
|
507
530
|
end
|
508
531
|
end
|
509
532
|
|
533
|
+
# @return [Array<Class>] The root types (query, mutation, subscription) defined for this schema
|
510
534
|
def root_types
|
511
535
|
if use_visibility_profile?
|
512
536
|
[query, mutation, subscription].compact
|
@@ -515,6 +539,7 @@ module GraphQL
|
|
515
539
|
end
|
516
540
|
end
|
517
541
|
|
542
|
+
# @api private
|
518
543
|
def warden_class
|
519
544
|
if defined?(@warden_class)
|
520
545
|
@warden_class
|
@@ -525,6 +550,7 @@ module GraphQL
|
|
525
550
|
end
|
526
551
|
end
|
527
552
|
|
553
|
+
# @api private
|
528
554
|
attr_writer :warden_class
|
529
555
|
|
530
556
|
# @api private
|
@@ -786,6 +812,7 @@ module GraphQL
|
|
786
812
|
res[:errors]
|
787
813
|
end
|
788
814
|
|
815
|
+
# @param new_query_class [Class<GraphQL::Query>] A subclass to use when executing queries
|
789
816
|
def query_class(new_query_class = NOT_CONFIGURED)
|
790
817
|
if NOT_CONFIGURED.equal?(new_query_class)
|
791
818
|
@query_class || (superclass.respond_to?(:query_class) ? superclass.query_class : GraphQL::Query)
|
@@ -971,6 +998,8 @@ module GraphQL
|
|
971
998
|
end
|
972
999
|
end
|
973
1000
|
|
1001
|
+
|
1002
|
+
# @param new_default_logger [#log] Something to use for logging messages
|
974
1003
|
def default_logger(new_default_logger = NOT_CONFIGURED)
|
975
1004
|
if NOT_CONFIGURED.equal?(new_default_logger)
|
976
1005
|
if defined?(@default_logger)
|
@@ -991,6 +1020,7 @@ module GraphQL
|
|
991
1020
|
end
|
992
1021
|
end
|
993
1022
|
|
1023
|
+
# @param new_context_class [Class<GraphQL::Query::Context>] A subclass to use when executing queries
|
994
1024
|
def context_class(new_context_class = nil)
|
995
1025
|
if new_context_class
|
996
1026
|
@context_class = new_context_class
|
@@ -999,6 +1029,20 @@ module GraphQL
|
|
999
1029
|
end
|
1000
1030
|
end
|
1001
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
|
1002
1046
|
def rescue_from(*err_classes, &handler_block)
|
1003
1047
|
err_classes.each do |err_class|
|
1004
1048
|
Execution::Errors.register_rescue_from(err_class, error_handlers[:subclass_handlers], handler_block)
|
@@ -1065,8 +1109,24 @@ module GraphQL
|
|
1065
1109
|
end
|
1066
1110
|
end
|
1067
1111
|
|
1068
|
-
|
1069
|
-
|
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})"
|
1070
1130
|
end
|
1071
1131
|
# rubocop:enable Lint/DuplicateMethods
|
1072
1132
|
|
@@ -1089,12 +1149,37 @@ module GraphQL
|
|
1089
1149
|
super
|
1090
1150
|
end
|
1091
1151
|
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
def
|
1097
|
-
|
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}`)"
|
1098
1183
|
end
|
1099
1184
|
|
1100
1185
|
def visible?(member, ctx)
|
@@ -1149,6 +1234,16 @@ module GraphQL
|
|
1149
1234
|
unauthorized_object(unauthorized_error)
|
1150
1235
|
end
|
1151
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
|
1152
1247
|
def type_error(type_error, ctx)
|
1153
1248
|
case type_error
|
1154
1249
|
when GraphQL::InvalidNullError
|
@@ -1244,6 +1339,7 @@ module GraphQL
|
|
1244
1339
|
# @param mode [Symbol] Trace module will only be used for this trade mode
|
1245
1340
|
# @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
|
1246
1341
|
# @return [void]
|
1342
|
+
# @see GraphQL::Tracing::Trace for available tracing methods
|
1247
1343
|
def trace_with(trace_mod, mode: :default, **options)
|
1248
1344
|
if mode.is_a?(Array)
|
1249
1345
|
mode.each { |m| trace_with(trace_mod, mode: m, **options) }
|
@@ -1321,6 +1417,8 @@ module GraphQL
|
|
1321
1417
|
trace_class_for_mode.new(**trace_options)
|
1322
1418
|
end
|
1323
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
|
1324
1422
|
def query_analyzer(new_analyzer)
|
1325
1423
|
own_query_analyzers << new_analyzer
|
1326
1424
|
end
|
@@ -1329,6 +1427,8 @@ module GraphQL
|
|
1329
1427
|
find_inherited_value(:query_analyzers, EMPTY_ARRAY) + own_query_analyzers
|
1330
1428
|
end
|
1331
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
|
1332
1432
|
def multiplex_analyzer(new_analyzer)
|
1333
1433
|
own_multiplex_analyzers << new_analyzer
|
1334
1434
|
end
|
@@ -1412,6 +1512,11 @@ module GraphQL
|
|
1412
1512
|
end
|
1413
1513
|
end
|
1414
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]
|
1415
1520
|
def query_stack_error(query, err)
|
1416
1521
|
query.context.errors.push(GraphQL::ExecutionError.new("This query is too large to execute."))
|
1417
1522
|
end
|
@@ -1470,6 +1575,20 @@ module GraphQL
|
|
1470
1575
|
end
|
1471
1576
|
end
|
1472
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
|
+
|
1473
1592
|
private
|
1474
1593
|
|
1475
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
|
@@ -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
|
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.0
|
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-10-
|
11
|
+
date: 2024-10-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: base64
|