@@ -30,6 +30,18 @@ def initialize(result_name, parent_result)
3030 attr_accessor :graphql_non_null_field_names
3131 # @return [nil, true]
3232 attr_accessor :graphql_non_null_list_items
33+ # True when this result is a List that was marked `skip_items_on_raise: true`.
34+ # Descendants of this result will have `can_be_skipped?` be true.
35+ # @return [nil, true]
36+ attr_accessor :graphql_skip_list_items_that_raise
37+
38+ # A result can be skipped when it's an ancestor of an item in a List marked `skip_items_on_raise: true`.
39+ # @return [Boolean]
40+ def can_be_skipped?
41+ return @can_be_skipped if defined? ( @can_be_skipped )
42+
43+ @can_be_skipped = graphql_skip_list_items_that_raise || !!( graphql_parent && graphql_parent . can_be_skipped? )
44+ end
3345
3446 # @return [Hash] Plain-Ruby result data (`@graphql_metadata` contains Result wrapper objects)
3547 attr_accessor :graphql_result_data
@@ -513,10 +525,14 @@ def evaluate_selection_with_args(arguments, field_defn, next_path, ast_node, fie
513525 rescue GraphQL ::ExecutionError => err
514526 err
515527 rescue StandardError => err
516- begin
517- query . handle_or_reraise ( err )
518- rescue GraphQL ::ExecutionError => ex_err
519- ex_err
528+ if selection_result . can_be_skipped?
529+ context . skip_from_parent_list # Skip silently without informing the user's `rescue_from` block.
530+ else
531+ begin
532+ query . handle_or_reraise ( err )
533+ rescue GraphQL ::ExecutionError => ex_err
534+ ex_err
535+ end
520536 end
521537 end
522538 after_lazy ( app_result , owner : owner_type , field : field_defn , path : next_path , ast_node : ast_node , owner_object : object , arguments : resolved_arguments , result_name : result_name , result : selection_result ) do |inner_result |
@@ -566,6 +582,19 @@ def set_result(selection_result, result_name, value)
566582 set_result ( parent , name_in_parent , nil )
567583 set_graphql_dead ( selection_result )
568584 end
585+ elsif value == GraphQL ::Execution ::SKIP_FROM_PARENT_LIST
586+ if selection_result . graphql_skip_list_items_that_raise # This is the first list that this item can be skipped from.
587+ unless selection_result . is_a? ( GraphQLResultArray )
588+ raise "Invariant: unexpected result class #{ selection_result . class } (#{ selection_result . inspect } )"
589+ end
590+
591+ selection_result . graphql_skip_at ( result_name )
592+ else # Propograte up to find the first list this item can be skipped from.
593+ parent = selection_result . graphql_parent
594+ name_in_parent = selection_result . graphql_result_name
595+ set_result ( parent , name_in_parent , GraphQL ::Execution ::SKIP_FROM_PARENT_LIST )
596+ set_graphql_dead ( selection_result )
597+ end
569598 else
570599 selection_result [ result_name ] = value
571600 end
@@ -647,6 +676,12 @@ def continue_value(path, value, parent_type, field, is_non_null, ast_node, resul
647676 raise "Invariant: unexpected result class #{ selection_result . class } (#{ selection_result . inspect } )"
648677 end
649678 HALT
679+ elsif GraphQL ::Execution ::SKIP_FROM_PARENT_LIST == value
680+ unless selection_result . can_be_skipped?
681+ raise "Cannot skip list items from lists not marked `skip_items_on_raise: true`"
682+ end
683+
684+ set_result ( selection_result , result_name , GraphQL ::Execution ::SKIP_FROM_PARENT_LIST )
650685 else
651686 # What could this actually _be_? Anyhow,
652687 # preserve the default behavior of doing nothing with it.
@@ -780,6 +815,7 @@ def continue_field(path, value, owner_type, field, current_type, ast_node, next_
780815 use_dataloader_job = !inner_type . unwrap . kind . input?
781816 response_list = GraphQLResultArray . new ( result_name , selection_result )
782817 response_list . graphql_non_null_list_items = inner_type . non_null?
818+ response_list . graphql_skip_list_items_that_raise = current_type . skip_nodes_on_raise?
783819 set_result ( selection_result , result_name , response_list )
784820 result_was_set = false
785821 idx = 0
0 commit comments