graphql 2.6.13 → 2.6.14
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/max_query_complexity.rb +1 -1
- data/lib/graphql/execution/field_resolve_step.rb +243 -157
- data/lib/graphql/execution/input_values.rb +3 -1
- data/lib/graphql/execution/prepare_object_step.rb +37 -13
- data/lib/graphql/language/lexer.rb +1 -1
- data/lib/graphql/language/parser.rb +5 -1
- data/lib/graphql/pagination/relation_connection.rb +21 -10
- data/lib/graphql/query/partial.rb +1 -2
- data/lib/graphql/schema/addition.rb +55 -48
- data/lib/graphql/schema/argument.rb +14 -3
- data/lib/graphql/schema/build_from_definition.rb +435 -173
- data/lib/graphql/schema/directive.rb +16 -0
- data/lib/graphql/schema/field/connection_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +1 -1
- data/lib/graphql/schema/member/build_type.rb +5 -2
- data/lib/graphql/schema/member/has_arguments.rb +0 -4
- data/lib/graphql/schema/member/has_directives.rb +7 -1
- data/lib/graphql/schema/member/has_interfaces.rb +0 -2
- data/lib/graphql/schema/object.rb +11 -3
- data/lib/graphql/schema.rb +4 -2
- 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: 33d067b80170a8be730630eb60106962726899c114a50d397559d5ccbb274dd4
|
|
4
|
+
data.tar.gz: 5d5d69c98b1461f7e9d50e45b615a6078155c3b15e1af0dfc51d46d49364064d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b77a04ebe1d0da2e9edd7e5af4f8218f6c25676893af1985cde457b99696345751c620fd803fedd78910bf9a8978aa34418898014c7393ea2f0e35b36b22594e
|
|
7
|
+
data.tar.gz: 3d3888e23a431bdbd67075eeacbdb082a461c46573946a8d46b228c2b53a686ccd5ba57f25919290e05240a60371063efa80eb8946459b5549c494e54fe5b407
|
|
@@ -8,7 +8,7 @@ module GraphQL
|
|
|
8
8
|
def result
|
|
9
9
|
return if subject.max_complexity.nil?
|
|
10
10
|
|
|
11
|
-
total_complexity =
|
|
11
|
+
total_complexity = super
|
|
12
12
|
|
|
13
13
|
if total_complexity > subject.max_complexity
|
|
14
14
|
GraphQL::AnalysisError.new("Query has complexity of #{total_complexity}, which exceeds max complexity of #{subject.max_complexity}")
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: true
|
|
3
3
|
module GraphQL
|
|
4
4
|
module Execution
|
|
5
5
|
class FieldResolveStep
|
|
@@ -26,6 +26,8 @@ module GraphQL
|
|
|
26
26
|
@finish_extension_idx = nil
|
|
27
27
|
@was_scoped = nil
|
|
28
28
|
@pending_steps = nil
|
|
29
|
+
@field_authorization_objects = @field_authorization_results = @field_authorization_errors = nil
|
|
30
|
+
@field_authorization_next_idx = @field_authorization_pending = nil
|
|
29
31
|
@arguments_without_loads = @post_processors = @directive_finalizers = nil
|
|
30
32
|
end
|
|
31
33
|
|
|
@@ -94,6 +96,12 @@ module GraphQL
|
|
|
94
96
|
finish_extensions
|
|
95
97
|
elsif @field_results
|
|
96
98
|
build_results
|
|
99
|
+
elsif @field_authorization_results
|
|
100
|
+
if @field_authorization_next_idx < @field_authorization_objects.size
|
|
101
|
+
authorize_next_field_object
|
|
102
|
+
else
|
|
103
|
+
finish_field_authorizations
|
|
104
|
+
end
|
|
97
105
|
elsif @arguments
|
|
98
106
|
execute_field
|
|
99
107
|
else
|
|
@@ -118,7 +126,7 @@ module GraphQL
|
|
|
118
126
|
@runner.add_finalizer(@selections_step.query, result, key, err)
|
|
119
127
|
if !err.is_a?(GraphQL::Execution::Skip)
|
|
120
128
|
field_type = return_type
|
|
121
|
-
should_propagate_null = field_type.non_null?
|
|
129
|
+
should_propagate_null = field_type.non_null? #: bool
|
|
122
130
|
while (should_propagate_null == false && field_type.kind.wraps?)
|
|
123
131
|
field_type = field_type.of_type
|
|
124
132
|
should_propagate_null = field_type.non_null?
|
|
@@ -131,10 +139,10 @@ module GraphQL
|
|
|
131
139
|
end
|
|
132
140
|
|
|
133
141
|
def propagate_nulls
|
|
134
|
-
propagating_null = true
|
|
142
|
+
propagating_null = true #: bool
|
|
135
143
|
highest_nulled_depth = path.size
|
|
136
|
-
highest_list_depth = nil
|
|
137
|
-
current_field_step = self
|
|
144
|
+
highest_list_depth = nil #: Integer?
|
|
145
|
+
current_field_step = self #: FieldResolveStep?
|
|
138
146
|
while current_field_step && current_field_step.field_definition
|
|
139
147
|
return_type = current_field_step.field_definition.type
|
|
140
148
|
if propagating_null && return_type.non_null?
|
|
@@ -151,7 +159,7 @@ module GraphQL
|
|
|
151
159
|
end
|
|
152
160
|
|
|
153
161
|
if highest_list_depth.nil? || highest_nulled_depth <= highest_list_depth
|
|
154
|
-
kill_field_step = self
|
|
162
|
+
kill_field_step = self #: FieldResolveStep?
|
|
155
163
|
while kill_field_step && kill_field_step.field_definition && highest_nulled_depth <= kill_field_step.path.size
|
|
156
164
|
kill_field_step.selections_step.killed = true
|
|
157
165
|
kill_field_step = kill_field_step.selections_step.field_resolve_step
|
|
@@ -243,153 +251,30 @@ module GraphQL
|
|
|
243
251
|
end
|
|
244
252
|
|
|
245
253
|
if @runner.authorizes?(@field_definition, ctx)
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
field_authed = @field_definition.authorized?(o, @arguments, ctx)
|
|
255
|
-
if @runner.resolves_lazies && @runner.lazy?(field_authed)
|
|
256
|
-
# TODO batch this properly...
|
|
257
|
-
field_authed = sync(field_authed)
|
|
258
|
-
end
|
|
259
|
-
rescue GraphQL::UnauthorizedFieldError => field_auth_err
|
|
260
|
-
err = field_auth_err
|
|
261
|
-
err.field ||= @field_definition
|
|
262
|
-
field_authed = false
|
|
263
|
-
end
|
|
264
|
-
|
|
265
|
-
if field_authed
|
|
266
|
-
authorized_results << @results[i]
|
|
267
|
-
authorized_objects << o
|
|
268
|
-
else
|
|
269
|
-
begin
|
|
270
|
-
err ||= GraphQL::UnauthorizedFieldError.new(object: o, type: @parent_type, context: ctx, field: @field_definition)
|
|
271
|
-
new_obj = query.schema.unauthorized_field(err)
|
|
272
|
-
if !new_obj.nil?
|
|
273
|
-
authorized_objects << new_obj
|
|
274
|
-
authorized_results << @results[i]
|
|
275
|
-
end
|
|
276
|
-
rescue GraphQL::ExecutionError => exec_err
|
|
277
|
-
add_graphql_error(@results[i], key, exec_err)
|
|
278
|
-
end
|
|
279
|
-
end
|
|
280
|
-
i += 1
|
|
281
|
-
end
|
|
282
|
-
|
|
283
|
-
if authorized_objects.size == 0
|
|
254
|
+
@field_authorization_objects = objects
|
|
255
|
+
@field_authorization_results = Array.new(objects.size)
|
|
256
|
+
@field_authorization_next_idx = 0
|
|
257
|
+
if objects.size > 1 && !@runner.dataloader.is_a?(GraphQL::Dataloader::NullDataloader)
|
|
258
|
+
# Enqueue this step once per object, so Dataloader sources loaded by `authorized?` batch across objects.
|
|
259
|
+
# Each call authorizes the next object; see `authorize_next_field_object`.
|
|
260
|
+
@field_authorization_pending = objects.size
|
|
261
|
+
objects.size.times { @runner.dataloader.append_job(self) }
|
|
284
262
|
return
|
|
285
263
|
end
|
|
286
|
-
|
|
264
|
+
objects.size.times { |idx| authorize_field_at(idx, ctx) }
|
|
265
|
+
authorized_objects = collect_field_authorizations
|
|
266
|
+
return if authorized_objects.nil?
|
|
287
267
|
else
|
|
288
268
|
authorized_objects = objects
|
|
289
269
|
end
|
|
290
270
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
query.current_trace.begin_execute_field(@field_definition, authorized_objects, @arguments, query)
|
|
296
|
-
|
|
297
|
-
if @runner.uses_runtime_directives
|
|
298
|
-
if @ast_nodes.nil? || @ast_nodes.size == 1
|
|
299
|
-
directives = if !@ast_node.directives.empty?
|
|
300
|
-
@ast_node.directives
|
|
301
|
-
else
|
|
302
|
-
nil
|
|
303
|
-
end
|
|
304
|
-
else
|
|
305
|
-
directives = nil
|
|
306
|
-
@ast_nodes.each do |n|
|
|
307
|
-
if (d = n.directives).any? # rubocop:disable Development/NoneWithoutBlockCop
|
|
308
|
-
directives ||= []
|
|
309
|
-
directives.concat(d)
|
|
310
|
-
end
|
|
311
|
-
end
|
|
312
|
-
end
|
|
313
|
-
|
|
314
|
-
if directives
|
|
315
|
-
directives.each do |dir_node|
|
|
316
|
-
if (dir_defn = @runner.runtime_directives[dir_node.name])
|
|
317
|
-
dir_args, errors = @runner.input_values[query].argument_values(dir_defn, dir_node.arguments, self) # rubocop:disable Development/ContextIsPassedCop
|
|
318
|
-
if errors
|
|
319
|
-
@results.each { |r| r.delete(@key) }
|
|
320
|
-
errors.each { |e| e.ast_node = dir_node }
|
|
321
|
-
build_errors_result(errors, nil)
|
|
322
|
-
return
|
|
323
|
-
else
|
|
324
|
-
begin
|
|
325
|
-
dir_defn.validate!(dir_args, query.context)
|
|
326
|
-
if !(result = dir_defn.resolve_field(ast_nodes, @parent_type, field_definition, authorized_objects, dir_args, ctx)).nil?
|
|
327
|
-
if result.is_a?(Finalizer)
|
|
328
|
-
result.path = path
|
|
329
|
-
@directive_finalizers ||= []
|
|
330
|
-
@directive_finalizers << result
|
|
331
|
-
end
|
|
332
|
-
|
|
333
|
-
if result.is_a?(PostProcessor)
|
|
334
|
-
@post_processors ||= []
|
|
335
|
-
@post_processors << result
|
|
336
|
-
end
|
|
337
|
-
|
|
338
|
-
if result.is_a?(HaltExecution)
|
|
339
|
-
@directive_finalizers&.each { |f|
|
|
340
|
-
@selections_step.results.each { |r| @runner.add_finalizer(query, r, key, f) }
|
|
341
|
-
}
|
|
342
|
-
return
|
|
343
|
-
end
|
|
344
|
-
end
|
|
345
|
-
rescue GraphQL::RuntimeError => err
|
|
346
|
-
err.ast_node = dir_node
|
|
347
|
-
raise
|
|
348
|
-
end
|
|
349
|
-
end
|
|
350
|
-
end
|
|
351
|
-
end
|
|
352
|
-
end
|
|
353
|
-
end
|
|
354
|
-
|
|
355
|
-
has_extensions = @field_definition.extensions.size > 0
|
|
356
|
-
if has_extensions
|
|
357
|
-
@extended = GraphQL::Schema::Field::ExtendedState.new(@arguments, authorized_objects)
|
|
358
|
-
@field_results = @field_definition.run_next_extensions_before_resolve(authorized_objects, @arguments, ctx, @extended) do |objs, args|
|
|
359
|
-
if (added_extras = @extended.added_extras)
|
|
360
|
-
args = args.dup
|
|
361
|
-
added_extras.each { |e| args.delete(e) }
|
|
362
|
-
end
|
|
363
|
-
resolve_batch(objs, ctx, args)
|
|
364
|
-
end
|
|
365
|
-
@finish_extension_idx = 0
|
|
366
|
-
else
|
|
367
|
-
@field_results = resolve_batch(authorized_objects, ctx, @arguments)
|
|
368
|
-
end
|
|
369
|
-
|
|
370
|
-
query.current_trace.end_execute_field(@field_definition, authorized_objects, @arguments, query, @field_results)
|
|
371
|
-
|
|
372
|
-
if any_lazy_results?
|
|
373
|
-
@runner.dataloader.lazy_at_depth(path.size, self)
|
|
374
|
-
elsif @pending_steps.nil? || @pending_steps.empty?
|
|
375
|
-
if has_extensions
|
|
376
|
-
finish_extensions
|
|
377
|
-
else
|
|
378
|
-
build_results
|
|
379
|
-
end
|
|
380
|
-
end
|
|
381
|
-
rescue GraphQL::ExecutionError => err
|
|
382
|
-
build_errors_result(nil, err)
|
|
383
|
-
rescue StandardError => stderr
|
|
384
|
-
begin
|
|
385
|
-
@selections_step.query.handle_or_reraise(stderr, field: @field_definition, arguments: @arguments, object: nil)
|
|
386
|
-
rescue GraphQL::ExecutionError => err
|
|
387
|
-
add_graphql_error(@results[0], key, err)
|
|
388
|
-
end
|
|
271
|
+
execute_authorized_field(authorized_objects)
|
|
272
|
+
rescue StandardError => err
|
|
273
|
+
handle_execute_field_error(err)
|
|
389
274
|
end
|
|
390
275
|
|
|
391
276
|
def any_lazy_results?
|
|
392
|
-
lazies = false
|
|
277
|
+
lazies = false #: bool
|
|
393
278
|
if @runner.resolves_lazies # TODO extract this
|
|
394
279
|
@field_results.each do |field_result|
|
|
395
280
|
if @runner.lazy?(field_result)
|
|
@@ -621,7 +506,7 @@ module GraphQL
|
|
|
621
506
|
# Preserve existing messages for single-level lists.
|
|
622
507
|
element_type = return_type.of_type if list_type.of_type.list?
|
|
623
508
|
end
|
|
624
|
-
err = @parent_type
|
|
509
|
+
err = @parent_type.const_get(:InvalidNullError).new(@parent_type, @field_definition, ast_nodes, is_from_array: is_from_array, path: path, element_type: element_type)
|
|
625
510
|
nn_result = @runner.schema.type_error(err, @selections_step.query.context)
|
|
626
511
|
if nn_result.nil?
|
|
627
512
|
propagate_nulls
|
|
@@ -635,6 +520,207 @@ module GraphQL
|
|
|
635
520
|
|
|
636
521
|
private
|
|
637
522
|
|
|
523
|
+
def execute_authorized_field(authorized_objects)
|
|
524
|
+
query = @selections_step.query
|
|
525
|
+
ctx = query.context
|
|
526
|
+
|
|
527
|
+
if @parent_type.default_relay? && SCOPED_RELAY_FIELD_NAMES.include?(@field_definition.original_name) && authorized_objects.all? { |o| o.respond_to?(:was_authorized_by_scope_items?) && o.was_authorized_by_scope_items? }
|
|
528
|
+
@was_scoped = true
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
query.current_trace.begin_execute_field(@field_definition, authorized_objects, @arguments, query)
|
|
532
|
+
|
|
533
|
+
if @runner.uses_runtime_directives
|
|
534
|
+
if @ast_nodes.nil? || @ast_nodes.size == 1
|
|
535
|
+
directives = if !@ast_node.directives.empty?
|
|
536
|
+
@ast_node.directives
|
|
537
|
+
else
|
|
538
|
+
nil
|
|
539
|
+
end
|
|
540
|
+
else
|
|
541
|
+
directives = nil #: Array[untyped]?
|
|
542
|
+
@ast_nodes.each do |n|
|
|
543
|
+
if (d = n.directives).any? # rubocop:disable Development/NoneWithoutBlockCop
|
|
544
|
+
directives ||= []
|
|
545
|
+
directives.concat(d)
|
|
546
|
+
end
|
|
547
|
+
end
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
if directives
|
|
551
|
+
directives.each do |dir_node|
|
|
552
|
+
if (dir_defn = @runner.runtime_directives[dir_node.name])
|
|
553
|
+
dir_args, errors = @runner.input_values[query].argument_values(dir_defn, dir_node.arguments, self) # rubocop:disable Development/ContextIsPassedCop
|
|
554
|
+
if errors
|
|
555
|
+
@results.each { |r| r.delete(@key) }
|
|
556
|
+
errors.each { |e| e.ast_node = dir_node }
|
|
557
|
+
build_errors_result(errors, nil)
|
|
558
|
+
return
|
|
559
|
+
else
|
|
560
|
+
begin
|
|
561
|
+
dir_defn.validate!(dir_args, query.context)
|
|
562
|
+
if !(result = dir_defn.resolve_field(ast_nodes, @parent_type, field_definition, authorized_objects, dir_args, ctx)).nil?
|
|
563
|
+
if result.is_a?(Finalizer)
|
|
564
|
+
result.path = path
|
|
565
|
+
@directive_finalizers ||= []
|
|
566
|
+
@directive_finalizers << result
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
if result.is_a?(PostProcessor)
|
|
570
|
+
@post_processors ||= []
|
|
571
|
+
@post_processors << result
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
if result.is_a?(HaltExecution)
|
|
575
|
+
@directive_finalizers&.each { |f|
|
|
576
|
+
@selections_step.results.each { |r| @runner.add_finalizer(query, r, key, f) }
|
|
577
|
+
}
|
|
578
|
+
return
|
|
579
|
+
end
|
|
580
|
+
end
|
|
581
|
+
rescue GraphQL::RuntimeError => err
|
|
582
|
+
err.ast_node = dir_node
|
|
583
|
+
raise
|
|
584
|
+
end
|
|
585
|
+
end
|
|
586
|
+
end
|
|
587
|
+
end
|
|
588
|
+
end
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
has_extensions = @field_definition.extensions.size > 0
|
|
592
|
+
if has_extensions
|
|
593
|
+
@extended = GraphQL::Schema::Field::ExtendedState.new(@arguments, authorized_objects)
|
|
594
|
+
@field_results = @field_definition.run_next_extensions_before_resolve(authorized_objects, @arguments, ctx, @extended) do |objs, args|
|
|
595
|
+
if (added_extras = @extended.added_extras)
|
|
596
|
+
args = args.dup
|
|
597
|
+
added_extras.each { |e| args.delete(e) }
|
|
598
|
+
end
|
|
599
|
+
resolve_batch(objs, ctx, args)
|
|
600
|
+
end
|
|
601
|
+
@finish_extension_idx = 0
|
|
602
|
+
else
|
|
603
|
+
@field_results = resolve_batch(authorized_objects, ctx, @arguments)
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
query.current_trace.end_execute_field(@field_definition, authorized_objects, @arguments, query, @field_results)
|
|
607
|
+
|
|
608
|
+
if any_lazy_results?
|
|
609
|
+
@runner.dataloader.lazy_at_depth(path.size, self)
|
|
610
|
+
elsif @pending_steps.nil? || @pending_steps.empty?
|
|
611
|
+
if has_extensions
|
|
612
|
+
finish_extensions
|
|
613
|
+
else
|
|
614
|
+
build_results
|
|
615
|
+
end
|
|
616
|
+
end
|
|
617
|
+
rescue StandardError => err
|
|
618
|
+
handle_execute_field_error(err)
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
def handle_execute_field_error(err)
|
|
622
|
+
if @field_authorization_objects
|
|
623
|
+
clear_field_authorizations
|
|
624
|
+
end
|
|
625
|
+
if err.is_a?(GraphQL::ExecutionError)
|
|
626
|
+
build_errors_result(nil, err)
|
|
627
|
+
else
|
|
628
|
+
begin
|
|
629
|
+
@selections_step.query.handle_or_reraise(err, field: @field_definition, arguments: @arguments, object: nil)
|
|
630
|
+
rescue GraphQL::ExecutionError => exec_err
|
|
631
|
+
add_graphql_error(@results[0], key, exec_err)
|
|
632
|
+
end
|
|
633
|
+
end
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
# Stores the (possibly synced) +authorized?+ result for the object at +idx+.
|
|
637
|
+
# A GraphQL::UnauthorizedFieldError raised by +authorized?+ is stored in +@field_authorization_errors+.
|
|
638
|
+
def authorize_field_at(idx, ctx)
|
|
639
|
+
field_authed = @field_definition.authorized?(@field_authorization_objects[idx], @arguments, ctx)
|
|
640
|
+
if @runner.resolves_lazies && @runner.lazy?(field_authed)
|
|
641
|
+
field_authed = sync(field_authed)
|
|
642
|
+
end
|
|
643
|
+
@field_authorization_results[idx] = field_authed
|
|
644
|
+
rescue GraphQL::UnauthorizedFieldError => field_auth_err
|
|
645
|
+
field_auth_err.field ||= @field_definition
|
|
646
|
+
(@field_authorization_errors ||= {})[idx] = field_auth_err
|
|
647
|
+
end
|
|
648
|
+
|
|
649
|
+
# Runs once for each time this step was enqueued for field authorization.
|
|
650
|
+
# The index is taken before +authorized?+ runs, so it's still correct if this Fiber pauses.
|
|
651
|
+
# When the last object is done, the step is enqueued again to call +finish_field_authorizations+.
|
|
652
|
+
def authorize_next_field_object
|
|
653
|
+
idx = @field_authorization_next_idx
|
|
654
|
+
@field_authorization_next_idx += 1
|
|
655
|
+
begin
|
|
656
|
+
authorize_field_at(idx, @selections_step.query.context)
|
|
657
|
+
rescue StandardError => err
|
|
658
|
+
(@field_authorization_errors ||= {})[idx] = err
|
|
659
|
+
end
|
|
660
|
+
@field_authorization_pending -= 1
|
|
661
|
+
if @field_authorization_pending == 0
|
|
662
|
+
@runner.add_step(self)
|
|
663
|
+
end
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
def finish_field_authorizations
|
|
667
|
+
if (errors = @field_authorization_errors)
|
|
668
|
+
@field_authorization_objects.size.times do |idx|
|
|
669
|
+
err = errors[idx]
|
|
670
|
+
if err && !err.is_a?(GraphQL::UnauthorizedFieldError)
|
|
671
|
+
raise err
|
|
672
|
+
end
|
|
673
|
+
end
|
|
674
|
+
end
|
|
675
|
+
authorized_objects = collect_field_authorizations
|
|
676
|
+
if authorized_objects
|
|
677
|
+
execute_authorized_field(authorized_objects)
|
|
678
|
+
end
|
|
679
|
+
rescue StandardError => err
|
|
680
|
+
handle_execute_field_error(err)
|
|
681
|
+
end
|
|
682
|
+
|
|
683
|
+
def clear_field_authorizations
|
|
684
|
+
@field_authorization_objects = @field_authorization_results = @field_authorization_errors = nil
|
|
685
|
+
@field_authorization_next_idx = @field_authorization_pending = nil
|
|
686
|
+
end
|
|
687
|
+
|
|
688
|
+
# Applies +unauthorized_field+ for denied objects, in object order.
|
|
689
|
+
# Returns the objects to resolve, or +nil+ if none are left.
|
|
690
|
+
def collect_field_authorizations
|
|
691
|
+
objects = @field_authorization_objects
|
|
692
|
+
auth_results = @field_authorization_results
|
|
693
|
+
errors = @field_authorization_errors
|
|
694
|
+
clear_field_authorizations
|
|
695
|
+
ctx = @selections_step.query.context
|
|
696
|
+
authorized_objects = []
|
|
697
|
+
authorized_results = []
|
|
698
|
+
objects.each_with_index do |o, i|
|
|
699
|
+
err = errors && errors[i]
|
|
700
|
+
if !err && auth_results[i]
|
|
701
|
+
authorized_results << @results[i]
|
|
702
|
+
authorized_objects << o
|
|
703
|
+
else
|
|
704
|
+
begin
|
|
705
|
+
err ||= GraphQL::UnauthorizedFieldError.new(object: o, type: @parent_type, context: ctx, field: @field_definition)
|
|
706
|
+
new_obj = ctx.schema.unauthorized_field(err)
|
|
707
|
+
if !new_obj.nil?
|
|
708
|
+
authorized_objects << new_obj
|
|
709
|
+
authorized_results << @results[i]
|
|
710
|
+
end
|
|
711
|
+
rescue GraphQL::ExecutionError => exec_err
|
|
712
|
+
add_graphql_error(@results[i], key, exec_err)
|
|
713
|
+
end
|
|
714
|
+
end
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
if authorized_objects.size == 0
|
|
718
|
+
return nil
|
|
719
|
+
end
|
|
720
|
+
@results = authorized_results
|
|
721
|
+
authorized_objects
|
|
722
|
+
end
|
|
723
|
+
|
|
638
724
|
def build_graphql_result(graphql_result, key, field_result, return_type, is_nn, is_list, is_from_array) # rubocop:disable Metrics/ParameterLists
|
|
639
725
|
if field_result.nil?
|
|
640
726
|
if is_nn
|
|
@@ -712,25 +798,25 @@ module GraphQL
|
|
|
712
798
|
when :resolve_batch
|
|
713
799
|
begin
|
|
714
800
|
method_receiver.public_send(@field_definition.execution_mode_key, objects, context, **args_hash)
|
|
715
|
-
rescue GraphQL::ExecutionError =>
|
|
716
|
-
error_instance_array(objects.size,
|
|
717
|
-
rescue StandardError =>
|
|
801
|
+
rescue GraphQL::ExecutionError => batch_err
|
|
802
|
+
error_instance_array(objects.size, batch_err)
|
|
803
|
+
rescue StandardError => batch_stderr
|
|
718
804
|
begin
|
|
719
|
-
context.query.handle_or_reraise(
|
|
720
|
-
rescue GraphQL::ExecutionError =>
|
|
721
|
-
error_instance_array(objects.size,
|
|
805
|
+
context.query.handle_or_reraise(batch_stderr, field: @field_definition, arguments: @arguments, object: nil)
|
|
806
|
+
rescue GraphQL::ExecutionError => batch_err
|
|
807
|
+
error_instance_array(objects.size, batch_err)
|
|
722
808
|
end
|
|
723
809
|
end
|
|
724
810
|
when :resolve_static
|
|
725
811
|
result = begin
|
|
726
812
|
method_receiver.public_send(@field_definition.execution_mode_key, context, **args_hash)
|
|
727
|
-
rescue GraphQL::ExecutionError =>
|
|
728
|
-
|
|
729
|
-
rescue StandardError =>
|
|
813
|
+
rescue GraphQL::ExecutionError => static_err
|
|
814
|
+
static_err
|
|
815
|
+
rescue StandardError => static_stderr
|
|
730
816
|
begin
|
|
731
|
-
context.query.handle_or_reraise(
|
|
732
|
-
rescue GraphQL::ExecutionError =>
|
|
733
|
-
|
|
817
|
+
context.query.handle_or_reraise(static_stderr, field: @field_definition, arguments: @arguments, object: nil)
|
|
818
|
+
rescue GraphQL::ExecutionError => static_err
|
|
819
|
+
static_err
|
|
734
820
|
end
|
|
735
821
|
end
|
|
736
822
|
Array.new(objects.size, result)
|
|
@@ -183,7 +183,9 @@ module GraphQL
|
|
|
183
183
|
arg_value = begin
|
|
184
184
|
argument_definition.prepare_value(nil, arg_value, context: @query.context)
|
|
185
185
|
rescue StandardError => err
|
|
186
|
-
@runner.schema.handle_or_reraise(@query.context, err, object: nil, arguments: argument_values, field: field_resolve_step&.field_definition)
|
|
186
|
+
handled_err = @runner.schema.handle_or_reraise(@query.context, err, object: nil, arguments: argument_values, field: field_resolve_step&.field_definition)
|
|
187
|
+
raise handled_err if handled_err.is_a?(GraphQL::RuntimeError)
|
|
188
|
+
handled_err
|
|
187
189
|
end
|
|
188
190
|
end
|
|
189
191
|
|
|
@@ -14,6 +14,7 @@ module GraphQL
|
|
|
14
14
|
@resolved_type = nil
|
|
15
15
|
@authorized_value = nil
|
|
16
16
|
@authorization_error = nil
|
|
17
|
+
@execution_error = nil
|
|
17
18
|
@key = key
|
|
18
19
|
@next_step = :resolve_type
|
|
19
20
|
@is_from_array = is_from_array
|
|
@@ -24,7 +25,12 @@ module GraphQL
|
|
|
24
25
|
if @authorized_value
|
|
25
26
|
query = @field_resolve_step.selections_step.query
|
|
26
27
|
query.current_trace.begin_authorized(@resolved_type, @object, query.context)
|
|
27
|
-
|
|
28
|
+
begin
|
|
29
|
+
@authorized_value = @runner.schema.sync_lazy(@authorized_value)
|
|
30
|
+
rescue StandardError => err
|
|
31
|
+
@authorized_value = nil
|
|
32
|
+
handle_authorization_error(err, query)
|
|
33
|
+
end
|
|
28
34
|
query.current_trace.end_authorized(@resolved_type, @object, query.context, @authorized_value)
|
|
29
35
|
elsif @resolved_type
|
|
30
36
|
ctx = @field_resolve_step.selections_step.query.context
|
|
@@ -90,8 +96,9 @@ module GraphQL
|
|
|
90
96
|
query.current_trace.begin_authorized(@resolved_type, @object, query.context)
|
|
91
97
|
@authorized_value = @resolved_type.authorized?(@object, query.context)
|
|
92
98
|
query.current_trace.end_authorized(@resolved_type, @object, query.context, @authorized_value)
|
|
93
|
-
rescue
|
|
94
|
-
@
|
|
99
|
+
rescue StandardError => err
|
|
100
|
+
@authorized_value = nil
|
|
101
|
+
handle_authorization_error(err, query)
|
|
95
102
|
end
|
|
96
103
|
|
|
97
104
|
if @runner.resolves_lazies && @runner.lazy?(@authorized_value)
|
|
@@ -100,19 +107,14 @@ module GraphQL
|
|
|
100
107
|
else
|
|
101
108
|
create_result
|
|
102
109
|
end
|
|
103
|
-
rescue GraphQL::RuntimeError => err
|
|
104
|
-
@graphql_result[@key] = add_field_error(err)
|
|
105
|
-
rescue StandardError => err
|
|
106
|
-
query ||= @field_resolve_step.selections_step.query
|
|
107
|
-
begin
|
|
108
|
-
query.handle_or_reraise(err, field: @field_resolve_step.field_definition, arguments: @field_resolve_step.arguments, object: @object) # rubocop:disable Development/ContextIsPassedCop
|
|
109
|
-
rescue GraphQL::RuntimeError => err
|
|
110
|
-
@graphql_result[@key] = add_field_error(err)
|
|
111
|
-
end
|
|
112
110
|
end
|
|
113
111
|
|
|
114
112
|
def create_result
|
|
115
|
-
if
|
|
113
|
+
if @execution_error
|
|
114
|
+
# `.authorized?` raised (or its lazy value raised) an error other than UnauthorizedError:
|
|
115
|
+
# it isn't an authorization decision, so don't call `unauthorized_object`.
|
|
116
|
+
@graphql_result[@key] = add_field_error(@execution_error)
|
|
117
|
+
elsif !@authorized_value
|
|
116
118
|
@authorization_error ||= GraphQL::UnauthorizedError.new(object: @object, type: @resolved_type, context: @field_resolve_step.selections_step.query.context)
|
|
117
119
|
end
|
|
118
120
|
|
|
@@ -147,6 +149,28 @@ module GraphQL
|
|
|
147
149
|
|
|
148
150
|
@field_resolve_step.authorized_finished(self)
|
|
149
151
|
end
|
|
152
|
+
|
|
153
|
+
private
|
|
154
|
+
|
|
155
|
+
# Errors raised by `.authorized?` (or by syncing its lazy return value):
|
|
156
|
+
#
|
|
157
|
+
# - `UnauthorizedError` is equivalent to returning `false`, so it goes to `unauthorized_object`
|
|
158
|
+
# - Other `GraphQL::RuntimeError`s are added to the response for this field
|
|
159
|
+
# - Other errors go to `rescue_from` handlers, which might return a `GraphQL::RuntimeError`, or else re-raise
|
|
160
|
+
def handle_authorization_error(err, query)
|
|
161
|
+
case err
|
|
162
|
+
when GraphQL::UnauthorizedError
|
|
163
|
+
@authorization_error = err
|
|
164
|
+
when GraphQL::RuntimeError
|
|
165
|
+
@execution_error = err
|
|
166
|
+
else
|
|
167
|
+
begin
|
|
168
|
+
@authorized_value = query.handle_or_reraise(err, field: @field_resolve_step.field_definition, arguments: @field_resolve_step.arguments, object: @object) # rubocop:disable Development/ContextIsPassedCop
|
|
169
|
+
rescue GraphQL::RuntimeError => runtime_err
|
|
170
|
+
handle_authorization_error(runtime_err, query)
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
150
174
|
end
|
|
151
175
|
end
|
|
152
176
|
end
|
|
@@ -197,7 +197,7 @@ module GraphQL
|
|
|
197
197
|
raise GraphQL::ParseError.new(message, line, col, @string, filename: @filename)
|
|
198
198
|
end
|
|
199
199
|
|
|
200
|
-
IGNORE_REGEXP = /[, \
|
|
200
|
+
IGNORE_REGEXP = /[, \r\n\t\uFEFF]+/
|
|
201
201
|
COMMENT_REGEXP = /\#[^\n]*/
|
|
202
202
|
IDENTIFIER_REGEXP = /[_A-Za-z][_0-9A-Za-z]*/
|
|
203
203
|
INT_REGEXP = /-?(?:[0]|[1-9][0-9]*)/
|
|
@@ -370,7 +370,11 @@ module GraphQL
|
|
|
370
370
|
input_fields_definition = parse_input_object_field_definitions
|
|
371
371
|
InputObjectTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, fields: input_fields_definition, filename: @filename, source: self)
|
|
372
372
|
else
|
|
373
|
-
|
|
373
|
+
expected_tokens = [:SCHEMA, :SCALAR, :TYPE, :ENUM, :INPUT, :UNION, :INTERFACE, :DIRECTIVE]
|
|
374
|
+
if desc.nil?
|
|
375
|
+
expected_tokens.unshift(:QUERY, :MUTATION, :SUBSCRIPTION, :FRAGMENT, :LCURLY, :EXTEND, :STRING)
|
|
376
|
+
end
|
|
377
|
+
expect_one_of(expected_tokens)
|
|
374
378
|
end
|
|
375
379
|
end
|
|
376
380
|
end
|