@@ -31,6 +31,16 @@ def initialize(result_name, parent_result)
3131 # @return [nil, true]
3232 attr_accessor :graphql_non_null_list_items
3333
34+ # @return [nil, true]
35+ attr_accessor :graphql_skip_list_items_that_raise
36+
37+ def has_graphql_graph_parent_that_skips_list_items_that_raise
38+ return @has_graphql_graph_parent_that_skips_list_items_that_raise if defined? ( @has_graphql_graph_parent_that_skips_list_items_that_raise )
39+
40+ @has_graphql_graph_parent_that_skips_list_items_that_raise = graphql_skip_list_items_that_raise ||
41+ !!graphql_parent &.has_graphql_graph_parent_that_skips_list_items_that_raise
42+ end
43+
3444 # @return [Hash] Plain-Ruby result data (`@graphql_metadata` contains Result wrapper objects)
3545 attr_accessor :graphql_result_data
3646 end
@@ -513,13 +523,18 @@ def evaluate_selection_with_args(arguments, field_defn, next_path, ast_node, fie
513523 rescue GraphQL ::ExecutionError => err
514524 err
515525 rescue StandardError => err
516- begin
517- query . handle_or_reraise ( err )
518- rescue GraphQL ::ExecutionError => ex_err
519- ex_err
526+ if selection_result . has_graphql_graph_parent_that_skips_list_items_that_raise
527+ nil
528+ else
529+ begin
530+ query . handle_or_reraise ( err )
531+ rescue GraphQL ::ExecutionError => ex_err
532+ ex_err
533+ end
520534 end
521535 end
522536 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 |
537+ puts ( "return type: #{ return_type . to_type_signature } , skip_nodes_on_raise: #{ return_type . skip_nodes_on_raise? } " )
523538 continue_value = continue_value ( next_path , inner_result , owner_type , field_defn , return_type . non_null? , ast_node , result_name , selection_result )
524539 if HALT != continue_value
525540 continue_field ( next_path , continue_value , owner_type , field_defn , return_type , ast_node , next_selections , false , object , resolved_arguments , result_name , selection_result )
@@ -545,7 +560,27 @@ def dead_result?(selection_result)
545560
546561 def set_result ( selection_result , result_name , value )
547562 if !dead_result? ( selection_result )
548- if value . nil? &&
563+ if value == GraphQL ::Execution ::SKIP
564+ if selection_result . graphql_skip_list_items_that_raise
565+ case selection_result
566+ when GraphQLResultHash then selection_result . delete ( result_name ) # TODO: unify `#delete` interface with `#graphql_skip_at`
567+ when GraphQLResultArray then selection_result . graphql_skip_at ( result_name )
568+ else raise "huh?"
569+ end
570+ else
571+ # Propograte up to find the first list this item can be skiped from.
572+ #
573+ parent = selection_result . graphql_parent
574+ name_in_parent = selection_result . graphql_result_name
575+ if parent . nil? # This is a top-level result hash
576+ @response = nil
577+ else
578+ set_result ( parent , name_in_parent , GraphQL ::Execution ::SKIP )
579+ set_graphql_dead ( selection_result )
580+ end
581+ end
582+
583+ elsif value . nil? &&
549584 ( # there are two conditions under which `nil` is not allowed in the response:
550585 ( selection_result . graphql_non_null_list_items ) || # this value would be written into a list that doesn't allow nils
551586 ( ( nn = selection_result . graphql_non_null_field_names ) && nn . include? ( result_name ) ) # this value would be written into a field that doesn't allow nils
@@ -591,7 +626,11 @@ def set_graphql_dead(selection_result)
591626 def continue_value ( path , value , parent_type , field , is_non_null , ast_node , result_name , selection_result ) # rubocop:disable Metrics/ParameterLists
592627 case value
593628 when nil
594- if is_non_null
629+ if selection_result . has_graphql_graph_parent_that_skips_list_items_that_raise
630+ # This writes the `nil` in, we need to patch it to skip instead.
631+ print ( "skip!" )
632+ set_result ( selection_result , result_name , GraphQL ::Execution ::SKIP )
633+ elsif is_non_null
595634 set_result ( selection_result , result_name , nil ) do
596635 # This block is called if `result_name` is not dead. (Maybe a previous invalid nil caused it be marked dead.)
597636 err = parent_type ::InvalidNullError . new ( parent_type , field , value )
@@ -693,6 +732,8 @@ def continue_value(path, value, parent_type, field, is_non_null, ast_node, resul
693732 #
694733 # @return [Lazy, Array, Hash, Object] Lazy, Array, and Hash are all traversed to resolve lazy values later
695734 def continue_field ( path , value , owner_type , field , current_type , ast_node , next_selections , is_non_null , owner_object , arguments , result_name , selection_result ) # rubocop:disable Metrics/ParameterLists
735+ # puts("current_type: #{current_type.to_type_signature}, skip_nodes_on_raise: #{current_type.skip_nodes_on_raise?} ")
736+
696737 if current_type . non_null?
697738 current_type = current_type . of_type
698739 is_non_null = true
@@ -776,10 +817,13 @@ def continue_field(path, value, owner_type, field, current_type, ast_node, next_
776817 end
777818 when "LIST"
778819 inner_type = current_type . of_type
820+ puts ( "LIST: #{ current_type } , #{ current_type . skip_nodes_on_raise? . inspect } " )
821+ puts ( "Item type: #{ inner_type } , #{ inner_type . skip_nodes_on_raise? . inspect } " )
779822 # This is true for objects, unions, and interfaces
780823 use_dataloader_job = !inner_type . unwrap . kind . input?
781824 response_list = GraphQLResultArray . new ( result_name , selection_result )
782825 response_list . graphql_non_null_list_items = inner_type . non_null?
826+ response_list . graphql_skip_list_items_that_raise = current_type . skip_nodes_on_raise?
783827 set_result ( selection_result , result_name , response_list )
784828 result_was_set = false
785829 idx = 0
0 commit comments