graphql 2.5.4 → 2.5.11
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/analysis/query_complexity.rb +2 -2
- data/lib/graphql/dataloader/active_record_association_source.rb +14 -2
- data/lib/graphql/dataloader/null_dataloader.rb +7 -0
- data/lib/graphql/execution/interpreter/resolve.rb +3 -3
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +11 -1
- data/lib/graphql/execution/interpreter/runtime.rb +136 -49
- data/lib/graphql/execution/interpreter.rb +1 -1
- data/lib/graphql/language/lexer.rb +9 -2
- data/lib/graphql/language/nodes.rb +5 -1
- data/lib/graphql/language/parser.rb +1 -0
- data/lib/graphql/query/context.rb +1 -1
- data/lib/graphql/query/partial.rb +179 -0
- data/lib/graphql/query.rb +55 -43
- data/lib/graphql/schema/addition.rb +3 -1
- data/lib/graphql/schema/argument.rb +5 -0
- data/lib/graphql/schema/build_from_definition.rb +5 -1
- data/lib/graphql/schema/directive.rb +33 -1
- data/lib/graphql/schema/field.rb +8 -0
- data/lib/graphql/schema/input_object.rb +28 -21
- data/lib/graphql/schema/member/has_arguments.rb +2 -2
- data/lib/graphql/schema/member/has_interfaces.rb +2 -2
- data/lib/graphql/schema/member/type_system_helpers.rb +16 -2
- data/lib/graphql/schema/ractor_shareable.rb +79 -0
- data/lib/graphql/schema/scalar.rb +1 -6
- data/lib/graphql/schema/timeout.rb +19 -2
- data/lib/graphql/schema/validator/required_validator.rb +15 -6
- data/lib/graphql/schema/visibility/migration.rb +2 -2
- data/lib/graphql/schema/visibility/profile.rb +74 -10
- data/lib/graphql/schema/visibility.rb +30 -17
- data/lib/graphql/schema/warden.rb +13 -5
- data/lib/graphql/schema.rb +53 -22
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +6 -2
- data/lib/graphql/tracing/notifications_trace.rb +3 -1
- data/lib/graphql/tracing/null_trace.rb +1 -1
- data/lib/graphql/tracing/perfetto_trace.rb +1 -1
- data/lib/graphql/type_kinds.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +1 -1
- metadata +19 -3
data/lib/graphql/query.rb
CHANGED
@@ -10,12 +10,47 @@ module GraphQL
|
|
10
10
|
autoload :Context, "graphql/query/context"
|
11
11
|
autoload :Fingerprint, "graphql/query/fingerprint"
|
12
12
|
autoload :NullContext, "graphql/query/null_context"
|
13
|
+
autoload :Partial, "graphql/query/partial"
|
13
14
|
autoload :Result, "graphql/query/result"
|
14
15
|
autoload :Variables, "graphql/query/variables"
|
15
16
|
autoload :InputValidationResult, "graphql/query/input_validation_result"
|
16
17
|
autoload :VariableValidationError, "graphql/query/variable_validation_error"
|
17
18
|
autoload :ValidationPipeline, "graphql/query/validation_pipeline"
|
18
19
|
|
20
|
+
# Code shared with {Partial}
|
21
|
+
module Runnable
|
22
|
+
def after_lazy(value, &block)
|
23
|
+
if !defined?(@runtime_instance)
|
24
|
+
@runtime_instance = context.namespace(:interpreter_runtime)[:runtime]
|
25
|
+
end
|
26
|
+
|
27
|
+
if @runtime_instance
|
28
|
+
@runtime_instance.minimal_after_lazy(value, &block)
|
29
|
+
else
|
30
|
+
@schema.after_lazy(value, &block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Node-level cache for calculating arguments. Used during execution and query analysis.
|
35
|
+
# @param ast_node [GraphQL::Language::Nodes::AbstractNode]
|
36
|
+
# @param definition [GraphQL::Schema::Field]
|
37
|
+
# @param parent_object [GraphQL::Schema::Object]
|
38
|
+
# @return [Hash{Symbol => Object}]
|
39
|
+
def arguments_for(ast_node, definition, parent_object: nil)
|
40
|
+
arguments_cache.fetch(ast_node, definition, parent_object)
|
41
|
+
end
|
42
|
+
|
43
|
+
def arguments_cache
|
44
|
+
@arguments_cache ||= Execution::Interpreter::ArgumentsCache.new(self)
|
45
|
+
end
|
46
|
+
|
47
|
+
# @api private
|
48
|
+
def handle_or_reraise(err)
|
49
|
+
@schema.handle_or_reraise(context, err)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
include Runnable
|
19
54
|
class OperationNameMissingError < GraphQL::ExecutionError
|
20
55
|
def initialize(name)
|
21
56
|
msg = if name.nil?
|
@@ -198,19 +233,10 @@ module GraphQL
|
|
198
233
|
# @return [GraphQL::Execution::Lookahead]
|
199
234
|
def lookahead
|
200
235
|
@lookahead ||= begin
|
201
|
-
|
202
|
-
if ast_node.nil?
|
236
|
+
if selected_operation.nil?
|
203
237
|
GraphQL::Execution::Lookahead::NULL_LOOKAHEAD
|
204
238
|
else
|
205
|
-
root_type
|
206
|
-
when nil, "query"
|
207
|
-
types.query_root # rubocop:disable Development/ContextIsPassedCop
|
208
|
-
when "mutation"
|
209
|
-
types.mutation_root # rubocop:disable Development/ContextIsPassedCop
|
210
|
-
when "subscription"
|
211
|
-
types.subscription_root # rubocop:disable Development/ContextIsPassedCop
|
212
|
-
end
|
213
|
-
GraphQL::Execution::Lookahead.new(query: self, root_type: root_type, ast_nodes: [ast_node])
|
239
|
+
GraphQL::Execution::Lookahead.new(query: self, root_type: root_type, ast_nodes: [selected_operation])
|
214
240
|
end
|
215
241
|
end
|
216
242
|
end
|
@@ -236,6 +262,18 @@ module GraphQL
|
|
236
262
|
with_prepared_ast { @operations }
|
237
263
|
end
|
238
264
|
|
265
|
+
# Run subtree partials of this query and return their results.
|
266
|
+
# Each partial is identified with a `path:` and `object:`
|
267
|
+
# where the path references a field in the AST and the object will be treated
|
268
|
+
# as the return value from that field. Subfields of the field named by `path`
|
269
|
+
# will be executed with `object` as the starting point
|
270
|
+
# @param partials_hashes [Array<Hash{Symbol => Object}>] Hashes with `path:` and `object:` keys
|
271
|
+
# @return [Array<GraphQL::Query::Result>]
|
272
|
+
def run_partials(partials_hashes)
|
273
|
+
partials = partials_hashes.map { |partial_options| Partial.new(query: self, **partial_options) }
|
274
|
+
Execution::Interpreter.run_all(@schema, partials, context: @context)
|
275
|
+
end
|
276
|
+
|
239
277
|
# Get the result for this query, executing it once
|
240
278
|
# @return [GraphQL::Query::Result] A Hash-like GraphQL response, with `"data"` and/or `"errors"` keys
|
241
279
|
def result
|
@@ -278,19 +316,6 @@ module GraphQL
|
|
278
316
|
end
|
279
317
|
end
|
280
318
|
|
281
|
-
# Node-level cache for calculating arguments. Used during execution and query analysis.
|
282
|
-
# @param ast_node [GraphQL::Language::Nodes::AbstractNode]
|
283
|
-
# @param definition [GraphQL::Schema::Field]
|
284
|
-
# @param parent_object [GraphQL::Schema::Object]
|
285
|
-
# @return [Hash{Symbol => Object}]
|
286
|
-
def arguments_for(ast_node, definition, parent_object: nil)
|
287
|
-
arguments_cache.fetch(ast_node, definition, parent_object)
|
288
|
-
end
|
289
|
-
|
290
|
-
def arguments_cache
|
291
|
-
@arguments_cache ||= Execution::Interpreter::ArgumentsCache.new(self)
|
292
|
-
end
|
293
|
-
|
294
319
|
# A version of the given query string, with:
|
295
320
|
# - Variables inlined to the query
|
296
321
|
# - Strings replaced with `<REDACTED>`
|
@@ -357,17 +382,21 @@ module GraphQL
|
|
357
382
|
|
358
383
|
def root_type_for_operation(op_type)
|
359
384
|
case op_type
|
360
|
-
when "query"
|
385
|
+
when "query", nil
|
361
386
|
types.query_root # rubocop:disable Development/ContextIsPassedCop
|
362
387
|
when "mutation"
|
363
388
|
types.mutation_root # rubocop:disable Development/ContextIsPassedCop
|
364
389
|
when "subscription"
|
365
390
|
types.subscription_root # rubocop:disable Development/ContextIsPassedCop
|
366
391
|
else
|
367
|
-
raise ArgumentError, "unexpected root type name: #{op_type.inspect}; expected 'query', 'mutation', or 'subscription'"
|
392
|
+
raise ArgumentError, "unexpected root type name: #{op_type.inspect}; expected nil, 'query', 'mutation', or 'subscription'"
|
368
393
|
end
|
369
394
|
end
|
370
395
|
|
396
|
+
def root_type
|
397
|
+
root_type_for_operation(selected_operation.operation_type)
|
398
|
+
end
|
399
|
+
|
371
400
|
def types
|
372
401
|
@visibility_profile || warden.visibility_profile
|
373
402
|
end
|
@@ -400,23 +429,6 @@ module GraphQL
|
|
400
429
|
with_prepared_ast { @subscription }
|
401
430
|
end
|
402
431
|
|
403
|
-
# @api private
|
404
|
-
def handle_or_reraise(err)
|
405
|
-
schema.handle_or_reraise(context, err)
|
406
|
-
end
|
407
|
-
|
408
|
-
def after_lazy(value, &block)
|
409
|
-
if !defined?(@runtime_instance)
|
410
|
-
@runtime_instance = context.namespace(:interpreter_runtime)[:runtime]
|
411
|
-
end
|
412
|
-
|
413
|
-
if @runtime_instance
|
414
|
-
@runtime_instance.minimal_after_lazy(value, &block)
|
415
|
-
else
|
416
|
-
@schema.after_lazy(value, &block)
|
417
|
-
end
|
418
|
-
end
|
419
|
-
|
420
432
|
attr_reader :logger
|
421
433
|
|
422
434
|
private
|
@@ -258,7 +258,9 @@ module GraphQL
|
|
258
258
|
# We can get these now; we'll have to get late-bound types later
|
259
259
|
if interface_type.is_a?(Module) && type.is_a?(Class)
|
260
260
|
implementers = @possible_types[interface_type] ||= []
|
261
|
-
implementers
|
261
|
+
if !implementers.include?(type)
|
262
|
+
implementers << type
|
263
|
+
end
|
262
264
|
end
|
263
265
|
when String, Schema::LateBoundType
|
264
266
|
interface_type = interface_type_membership
|
@@ -212,6 +212,11 @@ module GraphQL
|
|
212
212
|
@statically_coercible = !requires_parent_object
|
213
213
|
end
|
214
214
|
|
215
|
+
def freeze
|
216
|
+
statically_coercible?
|
217
|
+
super
|
218
|
+
end
|
219
|
+
|
215
220
|
# Apply the {prepare} configuration to `value`, using methods from `obj`.
|
216
221
|
# Used by the runtime.
|
217
222
|
# @api private
|
@@ -187,6 +187,10 @@ module GraphQL
|
|
187
187
|
|
188
188
|
object_types.each do |t|
|
189
189
|
t.interfaces.each do |int_t|
|
190
|
+
if int_t.is_a?(LateBoundType)
|
191
|
+
int_t = types[int_t.graphql_name]
|
192
|
+
t.implements(int_t)
|
193
|
+
end
|
190
194
|
int_t.orphan_types(t)
|
191
195
|
end
|
192
196
|
end
|
@@ -519,7 +523,7 @@ module GraphQL
|
|
519
523
|
|
520
524
|
def define_field_resolve_method(owner, method_name, field_name)
|
521
525
|
owner.define_method(method_name) { |**args|
|
522
|
-
field_instance =
|
526
|
+
field_instance = context.types.field(owner, field_name)
|
523
527
|
context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context)
|
524
528
|
}
|
525
529
|
end
|
@@ -9,6 +9,7 @@ module GraphQL
|
|
9
9
|
class Directive < GraphQL::Schema::Member
|
10
10
|
extend GraphQL::Schema::Member::HasArguments
|
11
11
|
extend GraphQL::Schema::Member::HasArguments::HasDirectiveArguments
|
12
|
+
extend GraphQL::Schema::Member::HasValidators
|
12
13
|
|
13
14
|
class << self
|
14
15
|
# Directives aren't types, they don't have kinds.
|
@@ -75,6 +76,10 @@ module GraphQL
|
|
75
76
|
yield
|
76
77
|
end
|
77
78
|
|
79
|
+
def validate!(arguments, context)
|
80
|
+
Schema::Validator.validate!(validators, self, context, arguments)
|
81
|
+
end
|
82
|
+
|
78
83
|
def on_field?
|
79
84
|
locations.include?(FIELD)
|
80
85
|
end
|
@@ -111,6 +116,9 @@ module GraphQL
|
|
111
116
|
# @return [GraphQL::Interpreter::Arguments]
|
112
117
|
attr_reader :arguments
|
113
118
|
|
119
|
+
class InvalidArgumentError < GraphQL::Error
|
120
|
+
end
|
121
|
+
|
114
122
|
def initialize(owner, **arguments)
|
115
123
|
@owner = owner
|
116
124
|
assert_valid_owner
|
@@ -119,7 +127,31 @@ module GraphQL
|
|
119
127
|
# - lazy resolution
|
120
128
|
# Probably, those won't be needed here, since these are configuration arguments,
|
121
129
|
# not runtime arguments.
|
122
|
-
|
130
|
+
context = Query::NullContext.instance
|
131
|
+
self.class.all_argument_definitions.each do |arg_defn|
|
132
|
+
if arguments.key?(arg_defn.keyword)
|
133
|
+
value = arguments[arg_defn.keyword]
|
134
|
+
# This is a Ruby-land value; convert it to graphql for validation
|
135
|
+
graphql_value = begin
|
136
|
+
arg_defn.type.unwrap.coerce_isolated_result(value)
|
137
|
+
rescue GraphQL::Schema::Enum::UnresolvedValueError
|
138
|
+
# Let validation handle this
|
139
|
+
value
|
140
|
+
end
|
141
|
+
else
|
142
|
+
value = graphql_value = nil
|
143
|
+
end
|
144
|
+
|
145
|
+
result = arg_defn.type.validate_input(graphql_value, context)
|
146
|
+
if !result.valid?
|
147
|
+
raise InvalidArgumentError, "@#{graphql_name}.#{arg_defn.graphql_name} on #{owner.path} is invalid (#{value.inspect}): #{result.problems.first["explanation"]}"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
self.class.validate!(arguments, context)
|
151
|
+
@arguments = self.class.coerce_arguments(nil, arguments, context)
|
152
|
+
if @arguments.is_a?(GraphQL::ExecutionError)
|
153
|
+
raise @arguments
|
154
|
+
end
|
123
155
|
end
|
124
156
|
|
125
157
|
def graphql_name
|
data/lib/graphql/schema/field.rb
CHANGED
@@ -176,30 +176,37 @@ module GraphQL
|
|
176
176
|
return GraphQL::Query::InputValidationResult.from_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
|
177
177
|
end
|
178
178
|
|
179
|
-
# Inject missing required arguments
|
180
|
-
missing_required_inputs = ctx.types.arguments(self).reduce({}) do |m, (argument)|
|
181
|
-
if !input.key?(argument.graphql_name) && argument.type.non_null? && !argument.default_value? && types.argument(self, argument.graphql_name)
|
182
|
-
m[argument.graphql_name] = nil
|
183
|
-
end
|
184
|
-
|
185
|
-
m
|
186
|
-
end
|
187
179
|
|
188
180
|
result = nil
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
181
|
+
|
182
|
+
|
183
|
+
input.each do |argument_name, value|
|
184
|
+
argument = types.argument(self, argument_name)
|
185
|
+
if argument.nil? && ctx.is_a?(Query::NullContext) && argument_name.is_a?(Symbol)
|
186
|
+
# Validating definition directive arguments which come in as Symbols
|
187
|
+
argument = types.arguments(self).find { |arg| arg.keyword == argument_name }
|
188
|
+
end
|
189
|
+
# Items in the input that are unexpected
|
190
|
+
if argument.nil?
|
191
|
+
result ||= Query::InputValidationResult.new
|
192
|
+
result.add_problem("Field is not defined on #{self.graphql_name}", [argument_name])
|
193
|
+
else
|
194
|
+
# Items in the input that are expected, but have invalid values
|
195
|
+
argument_result = argument.type.validate_input(value, ctx)
|
196
|
+
if !argument_result.valid?
|
199
197
|
result ||= Query::InputValidationResult.new
|
200
|
-
|
201
|
-
|
202
|
-
|
198
|
+
result.merge_result!(argument_name, argument_result)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# Check for missing non-null arguments
|
204
|
+
ctx.types.arguments(self).each do |argument|
|
205
|
+
if !input.key?(argument.graphql_name) && argument.type.non_null? && !argument.default_value?
|
206
|
+
result ||= Query::InputValidationResult.new
|
207
|
+
argument_result = argument.type.validate_input(nil, ctx)
|
208
|
+
if !argument_result.valid?
|
209
|
+
result.merge_result!(argument.graphql_name, argument_result)
|
203
210
|
end
|
204
211
|
end
|
205
212
|
end
|
@@ -370,8 +370,8 @@ module GraphQL
|
|
370
370
|
end
|
371
371
|
|
372
372
|
passes_possible_types_check = if context.types.loadable?(arg_loads_type, context)
|
373
|
-
if arg_loads_type.kind.
|
374
|
-
# This union is used in `loads:` but not otherwise visible to this query
|
373
|
+
if arg_loads_type.kind.abstract?
|
374
|
+
# This union/interface is used in `loads:` but not otherwise visible to this query
|
375
375
|
context.types.loadable_possible_types(arg_loads_type, context).include?(application_object_type)
|
376
376
|
else
|
377
377
|
true
|
@@ -8,8 +8,8 @@ module GraphQL
|
|
8
8
|
new_memberships = []
|
9
9
|
new_interfaces.each do |int|
|
10
10
|
if int.is_a?(Module)
|
11
|
-
unless int.include?(GraphQL::Schema::Interface)
|
12
|
-
raise "#{int} cannot be implemented since it's not a GraphQL Interface. Use `include` for plain Ruby modules."
|
11
|
+
unless int.include?(GraphQL::Schema::Interface) && !int.is_a?(Class)
|
12
|
+
raise "#{int.respond_to?(:graphql_name) ? "#{int.graphql_name} (#{int})" : int.inspect} cannot be implemented since it's not a GraphQL Interface. Use `include` for plain Ruby modules."
|
13
13
|
end
|
14
14
|
|
15
15
|
new_memberships << int.type_membership_class.new(int, self, **options)
|
@@ -12,12 +12,26 @@ module GraphQL
|
|
12
12
|
|
13
13
|
# @return [Schema::NonNull] Make a non-null-type representation of this type
|
14
14
|
def to_non_null_type
|
15
|
-
@to_non_null_type
|
15
|
+
@to_non_null_type || begin
|
16
|
+
t = GraphQL::Schema::NonNull.new(self)
|
17
|
+
if frozen?
|
18
|
+
t
|
19
|
+
else
|
20
|
+
@to_non_null_type = t
|
21
|
+
end
|
22
|
+
end
|
16
23
|
end
|
17
24
|
|
18
25
|
# @return [Schema::List] Make a list-type representation of this type
|
19
26
|
def to_list_type
|
20
|
-
@to_list_type
|
27
|
+
@to_list_type || begin
|
28
|
+
t = GraphQL::Schema::List.new(self)
|
29
|
+
if frozen?
|
30
|
+
t
|
31
|
+
else
|
32
|
+
@to_list_type = t
|
33
|
+
end
|
34
|
+
end
|
21
35
|
end
|
22
36
|
|
23
37
|
# @return [Boolean] true if this is a non-nullable type. A nullable list of non-nullables is considered nullable.
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Schema
|
4
|
+
module RactorShareable
|
5
|
+
def self.extended(schema_class)
|
6
|
+
schema_class.extend(SchemaExtension)
|
7
|
+
schema_class.freeze_schema
|
8
|
+
end
|
9
|
+
|
10
|
+
module SchemaExtension
|
11
|
+
|
12
|
+
def freeze_error_handlers(handlers)
|
13
|
+
handlers[:subclass_handlers].default_proc = nil
|
14
|
+
handlers[:subclass_handlers].each do |_class, subclass_handlers|
|
15
|
+
freeze_error_handlers(subclass_handlers)
|
16
|
+
end
|
17
|
+
Ractor.make_shareable(handlers)
|
18
|
+
end
|
19
|
+
|
20
|
+
def freeze_schema
|
21
|
+
# warm some ivars:
|
22
|
+
default_analysis_engine
|
23
|
+
default_execution_strategy
|
24
|
+
GraphQL.default_parser
|
25
|
+
default_logger
|
26
|
+
freeze_error_handlers(error_handlers)
|
27
|
+
# TODO: this freezes errors of parent classes which could cause trouble
|
28
|
+
parent_class = superclass
|
29
|
+
while parent_class.respond_to?(:error_handlers)
|
30
|
+
freeze_error_handlers(parent_class.error_handlers)
|
31
|
+
parent_class = parent_class.superclass
|
32
|
+
end
|
33
|
+
|
34
|
+
own_tracers.freeze
|
35
|
+
@frozen_tracers = tracers.freeze
|
36
|
+
own_trace_modes.each do |m|
|
37
|
+
trace_options_for(m)
|
38
|
+
build_trace_mode(m)
|
39
|
+
end
|
40
|
+
build_trace_mode(:default)
|
41
|
+
Ractor.make_shareable(@trace_options_for_mode)
|
42
|
+
Ractor.make_shareable(own_trace_modes)
|
43
|
+
Ractor.make_shareable(own_multiplex_analyzers)
|
44
|
+
@frozen_multiplex_analyzers = Ractor.make_shareable(multiplex_analyzers)
|
45
|
+
Ractor.make_shareable(own_query_analyzers)
|
46
|
+
@frozen_query_analyzers = Ractor.make_shareable(query_analyzers)
|
47
|
+
Ractor.make_shareable(own_plugins)
|
48
|
+
own_plugins.each do |(plugin, options)|
|
49
|
+
Ractor.make_shareable(plugin)
|
50
|
+
Ractor.make_shareable(options)
|
51
|
+
end
|
52
|
+
@frozen_plugins = Ractor.make_shareable(plugins)
|
53
|
+
Ractor.make_shareable(own_references_to)
|
54
|
+
@frozen_directives = Ractor.make_shareable(directives)
|
55
|
+
|
56
|
+
Ractor.make_shareable(visibility)
|
57
|
+
Ractor.make_shareable(introspection_system)
|
58
|
+
extend(FrozenMethods)
|
59
|
+
|
60
|
+
Ractor.make_shareable(self)
|
61
|
+
superclass.respond_to?(:freeze_schema) && superclass.freeze_schema
|
62
|
+
end
|
63
|
+
|
64
|
+
module FrozenMethods
|
65
|
+
def tracers; @frozen_tracers; end
|
66
|
+
def multiplex_analyzers; @frozen_multiplex_analyzers; end
|
67
|
+
def query_analyzers; @frozen_query_analyzers; end
|
68
|
+
def plugins; @frozen_plugins; end
|
69
|
+
def directives; @frozen_directives; end
|
70
|
+
|
71
|
+
# This actually accumulates info during execution...
|
72
|
+
# How to support it?
|
73
|
+
def lazy?(_obj); false; end
|
74
|
+
def sync_lazy(obj); obj; end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -50,12 +50,7 @@ module GraphQL
|
|
50
50
|
end
|
51
51
|
|
52
52
|
if coerced_result.nil?
|
53
|
-
|
54
|
-
""
|
55
|
-
else
|
56
|
-
" #{GraphQL::Language.serialize(value)}"
|
57
|
-
end
|
58
|
-
Query::InputValidationResult.from_problem("Could not coerce value#{str_value} to #{graphql_name}")
|
53
|
+
Query::InputValidationResult.from_problem("Could not coerce value #{GraphQL::Language.serialize(value)} to #{graphql_name}")
|
59
54
|
elsif coerced_result.is_a?(GraphQL::CoercionError)
|
60
55
|
Query::InputValidationResult.from_problem(coerced_result.message, message: coerced_result.message, extensions: coerced_result.extensions)
|
61
56
|
else
|
@@ -71,15 +71,23 @@ module GraphQL
|
|
71
71
|
def execute_field(query:, field:, **_rest)
|
72
72
|
timeout_state = query.context.namespace(@timeout).fetch(:state)
|
73
73
|
# If the `:state` is `false`, then `max_seconds(query)` opted out of timeout for this query.
|
74
|
-
if timeout_state
|
74
|
+
if timeout_state == false
|
75
|
+
super
|
76
|
+
elsif Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) > timeout_state.fetch(:timeout_at)
|
75
77
|
error = GraphQL::Schema::Timeout::TimeoutError.new(field)
|
76
78
|
# Only invoke the timeout callback for the first timeout
|
77
79
|
if !timeout_state[:timed_out]
|
78
80
|
timeout_state[:timed_out] = true
|
79
81
|
@timeout.handle_timeout(error, query)
|
82
|
+
timeout_state = query.context.namespace(@timeout).fetch(:state)
|
80
83
|
end
|
81
84
|
|
82
|
-
|
85
|
+
# `handle_timeout` may have set this to be `false`
|
86
|
+
if timeout_state != false
|
87
|
+
error
|
88
|
+
else
|
89
|
+
super
|
90
|
+
end
|
83
91
|
else
|
84
92
|
super
|
85
93
|
end
|
@@ -102,6 +110,15 @@ module GraphQL
|
|
102
110
|
# override to do something interesting
|
103
111
|
end
|
104
112
|
|
113
|
+
# Call this method (eg, from {#handle_timeout}) to disable timeout tracking
|
114
|
+
# for the given query.
|
115
|
+
# @param query [GraphQL::Query]
|
116
|
+
# @return [void]
|
117
|
+
def disable_timeout(query)
|
118
|
+
query.context.namespace(self)[:state] = false
|
119
|
+
nil
|
120
|
+
end
|
121
|
+
|
105
122
|
# This error is raised when a query exceeds `max_seconds`.
|
106
123
|
# Since it's a child of {GraphQL::ExecutionError},
|
107
124
|
# its message will be added to the response's `errors` key.
|
@@ -96,26 +96,35 @@ module GraphQL
|
|
96
96
|
end
|
97
97
|
|
98
98
|
def build_message(context)
|
99
|
-
argument_definitions =
|
99
|
+
argument_definitions = context.types.arguments(@validated)
|
100
|
+
|
100
101
|
required_names = @one_of.map do |arg_keyword|
|
101
102
|
if arg_keyword.is_a?(Array)
|
102
|
-
names = arg_keyword.map { |arg|
|
103
|
+
names = arg_keyword.map { |arg| arg_keyword_to_graphql_name(argument_definitions, arg) }
|
104
|
+
names.compact! # hidden arguments are `nil`
|
103
105
|
"(" + names.join(" and ") + ")"
|
104
106
|
else
|
105
|
-
|
107
|
+
arg_keyword_to_graphql_name(argument_definitions, arg_keyword)
|
106
108
|
end
|
107
109
|
end
|
110
|
+
required_names.compact! # remove entries for hidden arguments
|
111
|
+
|
108
112
|
|
109
|
-
|
113
|
+
case required_names.size
|
114
|
+
when 0
|
115
|
+
# The required definitions were hidden from the client.
|
116
|
+
# Another option here would be to raise an error in the application....
|
117
|
+
"%{validated} is missing a required argument."
|
118
|
+
when 1
|
110
119
|
"%{validated} must include the following argument: #{required_names.first}."
|
111
120
|
else
|
112
121
|
"%{validated} must include exactly one of the following arguments: #{required_names.join(", ")}."
|
113
122
|
end
|
114
123
|
end
|
115
124
|
|
116
|
-
def
|
125
|
+
def arg_keyword_to_graphql_name(argument_definitions, arg_keyword)
|
117
126
|
argument_definition = argument_definitions.find { |defn| defn.keyword == arg_keyword }
|
118
|
-
argument_definition
|
127
|
+
argument_definition&.graphql_name
|
119
128
|
end
|
120
129
|
end
|
121
130
|
end
|
@@ -76,10 +76,10 @@ module GraphQL
|
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
79
|
-
def initialize(context:, schema:, name: nil)
|
79
|
+
def initialize(context:, schema:, name: nil, visibility:)
|
80
80
|
@name = name
|
81
81
|
@skip_error = context[:skip_visibility_migration_error] || context.is_a?(Query::NullContext) || context.is_a?(Hash)
|
82
|
-
@profile_types = GraphQL::Schema::Visibility::Profile.new(context: context, schema: schema)
|
82
|
+
@profile_types = GraphQL::Schema::Visibility::Profile.new(context: context, schema: schema, visibility: visibility)
|
83
83
|
if !@skip_error
|
84
84
|
context[:visibility_migration_running] = true
|
85
85
|
warden_ctx_vals = context.to_h.dup
|