55# Next thoughts
66#
77# - `continue_field` is probably a step of its own -- that method can somehow be factored out
8- # - UNION/INTERFACE should initialize the ResultHash that the resolved object type will eventually use.
9- # That would simplify the method call a lot. And then it could add a new step itself.
108# - It seems like Dataloader/Lazy will fit in at the queue level, so the flow would be:
119# - Run jobs from queue
1210# - Then, run dataloader/lazies
@@ -45,6 +43,8 @@ def current_object
4543 # @return [GraphQL::Query::Context]
4644 attr_reader :context
4745
46+ attr_reader :dataloader
47+
4848 def initialize ( query :, lazies_at_depth :)
4949 @query = query
5050 @current_trace = query . current_trace
@@ -78,115 +78,6 @@ def inspect
7878 "#<#{ self . class . name } response=#{ @response . inspect } >"
7979 end
8080
81- class ObjectStep
82- def initialize ( runtime , response , runtime_state )
83- @runtime = runtime
84- @response = response
85- @runtime_state = runtime_state
86- end
87-
88- def run
89- @runtime . each_gathered_selections ( @response ) do |selections , is_selection_array , ordered_result_keys |
90- @response . ordered_result_keys ||= ordered_result_keys
91- if is_selection_array
92- this_result = GraphQLResultHash . new (
93- @response . graphql_response_name ,
94- @response . graphql_result_type ,
95- @response . graphql_application_value ,
96- @response . graphql_parent ,
97- @response . graphql_is_non_null_in_parent ,
98- selections ,
99- false ,
100- @response . ast_node ,
101- @response . graphql_arguments ,
102- @response . graphql_field )
103- this_result . ordered_result_keys = ordered_result_keys
104- final_result = @response
105- else
106- this_result = @response
107- final_result = nil
108- end
109- @runtime . evaluate_selections (
110- selections ,
111- this_result ,
112- final_result ,
113- nil ,
114- )
115- end
116- end
117- end
118-
119- class ListStep
120- def initialize ( runtime , runtime_state , response_list , list_object , was_scoped )
121- @runtime = runtime
122- @runtime_state = runtime_state
123- @response_list = response_list
124- @list_object = list_object
125- @was_scoped = was_scoped
126- end
127-
128- def run
129- current_type = @response_list . graphql_result_type
130- inner_type = current_type . of_type
131- # This is true for objects, unions, and interfaces
132- # use_dataloader_job = !inner_type.unwrap.kind.input?
133- inner_type_non_null = inner_type . non_null?
134- idx = nil
135- list_value = begin
136- begin
137- @list_object . each do |inner_value |
138- idx ||= 0
139- this_idx = idx
140- idx += 1
141- # TODO if use_dataloader_job ... ??
142- # Better would be to extract a ListValueStep
143- @runtime . resolve_list_item (
144- inner_value ,
145- inner_type ,
146- inner_type_non_null ,
147- @response_list . ast_node ,
148- @response_list . graphql_field ,
149- @response_list . graphql_application_value ,
150- @response_list . graphql_arguments ,
151- this_idx ,
152- @response_list ,
153- @was_scoped ,
154- @runtime_state
155- )
156- end
157-
158- @response_list
159- rescue NoMethodError => err
160- # Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.)
161- if err . name == :each && ( err . respond_to? ( :receiver ) ? err . receiver == value : true )
162- # This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
163- raise ListResultFailedError . new ( value : @list_object , field : @response_list . graphql_field , path : @runtime . current_path )
164- else
165- # This was some other NoMethodError -- let it bubble to reveal the real error.
166- raise
167- end
168- rescue GraphQL ::ExecutionError , GraphQL ::UnauthorizedError => ex_err
169- ex_err
170- rescue StandardError => err
171- begin
172- @runtime . query . handle_or_reraise ( err )
173- rescue GraphQL ::ExecutionError => ex_err
174- ex_err
175- end
176- end
177- rescue StandardError => err
178- begin
179- @runtime . query . handle_or_reraise ( err )
180- rescue GraphQL ::ExecutionError => ex_err
181- ex_err
182- end
183- end
184- # Detect whether this error came while calling `.each` (before `idx` is set) or while running list *items* (after `idx` is set)
185- error_is_non_null = idx . nil? ? is_non_null : inner_type . non_null?
186- @runtime . continue_value ( list_value , @response_list . graphql_field , error_is_non_null , @response_list . ast_node , @response_list . graphql_result_name , @response_list . graphql_parent )
187- end
188- end
189-
19081 class DirectivesStep
19182 def initialize ( runtime , object , ast_node , next_step )
19283 @runtime = runtime
@@ -197,7 +88,7 @@ def initialize(runtime, object, ast_node, next_step)
19788
19889 def run
19990 @runtime . call_method_on_directives ( :resolve , @object , @ast_node . directives ) do
200- next_step . call
91+ @runtime . run_queue << @next_step
20192 end
20293 end
20394 end
@@ -234,14 +125,14 @@ def run
234125
235126 possible_types = @runtime . query . types . possible_types ( current_type )
236127 if !possible_types . include? ( resolved_type )
237- parent_type = @field . owner_type
128+ field = @response_hash . graphql_field
129+ parent_type = field . owner_type
238130 err_class = current_type ::UnresolvedTypeError
239131 type_error = err_class . new ( resolved_value , field , parent_type , resolved_type , possible_types )
240- @runtime . schema . type_error ( type_error , context )
132+ @runtime . schema . type_error ( type_error , @runtime . context )
241133 @runtime . set_result ( selection_result , result_name , nil , false , is_non_null )
242134 nil
243135 else
244- # TODO create the response_hash ahead of time which contains all this metadata
245136 @runtime . continue_field ( resolved_value , @response_hash . graphql_field , resolved_type , @response_hash . ast_node , @response_hash . graphql_selections , @response_hash . graphql_is_non_null_in_parent , @response_hash . graphql_arguments , @response_hash . graphql_result_name , @response_hash . graphql_parent , @was_scoped , @runtime_state )
246137 end
247138 end
@@ -275,15 +166,14 @@ def run_eager
275166 if object_proxy . nil?
276167 @response = nil
277168 else
278- @response = GraphQLResultHash . new ( nil , root_type , object_proxy , nil , false , selections , is_eager , ast_node , nil , nil )
169+ @response = GraphQLResultHash . new ( self , nil , root_type , object_proxy , nil , false , selections , is_eager , ast_node , nil , nil )
279170 @response . base_path = base_path
280171 runtime_state . current_result = @response
281- obj_step = ObjectStep . new ( self , @response , nil )
282172 if !ast_node . directives . empty?
283173 dir_step = DirectivesStep . new ( self , object , ast_node , obj_step )
284174 @run_queue << dir_step
285175 else
286- @run_queue << obj_step
176+ @run_queue << @response
287177 end
288178 end
289179 when "LIST"
@@ -304,9 +194,9 @@ def run_eager
304194 end
305195 @response = selection_result [ result_name ]
306196 else
307- @response = GraphQLResultArray . new ( nil , root_type , nil , nil , false , selections , false , ast_node , nil , nil )
197+ @response = GraphQLResultArray . new ( self , nil , root_type , object , nil , false , selections , false , ast_node , nil , nil )
308198 @response . base_path = base_path
309- @run_queue << ListStep . new ( self , runtime_state , @response , object , false )
199+ @run_queue << @response
310200 end
311201 when "SCALAR" , "ENUM"
312202 result_name = ast_node . alias || ast_node . name
@@ -329,27 +219,6 @@ def run_eager
329219 @response . base_path = base_path
330220
331221 @run_queue << ResolveTypeStep . new ( self , @response , false )
332-
333- # resolved_type, _resolved_obj = resolve_type(root_type, object)
334- # resolved_type = schema.sync_lazy(resolved_type)
335- # object_proxy = resolved_type.wrap(object, context)
336- # object_proxy = schema.sync_lazy(object_proxy)
337-
338- # each_gathered_selections(@response) do |selections, is_selection_array, ordered_result_keys|
339- # @response.ordered_result_keys ||= ordered_result_keys
340- # if is_selection_array == true
341- # raise "This isn't supported yet"
342- # end
343-
344- # @dataloader.append_job {
345- # evaluate_selections(
346- # selections,
347- # @response,
348- # nil,
349- # runtime_state,
350- # )
351- # }
352- # end
353222 else
354223 raise "Invariant: unsupported type kind for partial execution: #{ root_type . kind . inspect } (#{ root_type } )"
355224 end
@@ -451,51 +320,7 @@ def gather_selections(owner_object, owner_type, selections, selections_to_run, s
451320
452321 # @return [void]
453322 def evaluate_selections ( gathered_selections , selections_result , target_result , runtime_state ) # rubocop:disable Metrics/ParameterLists
454- runtime_state ||= get_current_runtime_state
455- runtime_state . current_result_name = nil
456- runtime_state . current_result = selections_result
457- # This is a less-frequent case; use a fast check since it's often not there.
458- if ( directives = gathered_selections [ :graphql_directives ] )
459- gathered_selections . delete ( :graphql_directives )
460- end
461323
462- call_method_on_directives ( :resolve , selections_result . graphql_application_value , directives ) do
463- finished_jobs = 0
464- enqueued_jobs = gathered_selections . size
465- gathered_selections . each do |result_name , field_ast_nodes_or_ast_node |
466- # Field resolution may pause the fiber,
467- # so it wouldn't get to the `Resolve` call that happens below.
468- # So instead trigger a run from this outer context.
469- if selections_result . graphql_is_eager
470- @dataloader . clear_cache
471- @dataloader . run_isolated {
472- evaluate_selection (
473- result_name , field_ast_nodes_or_ast_node , selections_result
474- )
475- finished_jobs += 1
476- if finished_jobs == enqueued_jobs
477- if target_result
478- selections_result . merge_into ( target_result )
479- end
480- end
481- @dataloader . clear_cache
482- }
483- else
484- @dataloader . append_job {
485- evaluate_selection (
486- result_name , field_ast_nodes_or_ast_node , selections_result
487- )
488- finished_jobs += 1
489- if finished_jobs == enqueued_jobs
490- if target_result
491- selections_result . merge_into ( target_result )
492- end
493- end
494- }
495- end
496- end
497- selections_result
498- end
499324 end
500325
501326 # @return [void]
@@ -586,7 +411,11 @@ def evaluate_selection_with_args(arguments, field_defn, ast_node, field_ast_node
586411 extra_args [ :argument_details ] = :__arguments_add_self
587412 when :parent
588413 parent_result = selection_result . graphql_parent
589- extra_args [ :parent ] = parent_result &.graphql_application_value &.object
414+ if parent_result . is_a? ( GraphQL ::Execution ::Interpreter ::Runtime ::GraphQLResultArray )
415+ parent_result = parent_result . graphql_parent
416+ end
417+ parent_value = parent_result &.graphql_application_value &.object
418+ extra_args [ :parent ] = parent_value
590419 else
591420 extra_args [ extra ] = field_defn . fetch_extra ( extra , context )
592421 end
@@ -844,39 +673,8 @@ def continue_field(value, field, current_type, ast_node, next_selections, is_non
844673 set_result ( selection_result , result_name , r , false , is_non_null )
845674 r
846675 when "UNION" , "INTERFACE"
847- response_hash = GraphQLResultHash . new ( result_name , current_type , value , selection_result , is_non_null , next_selections , false , ast_node , arguments , field )
676+ response_hash = GraphQLResultHash . new ( self , result_name , current_type , value , selection_result , is_non_null , next_selections , false , ast_node , arguments , field )
848677 @run_queue << ResolveTypeStep . new ( self , response_hash , was_scoped )
849- # resolved_type_or_lazy = begin
850- # resolve_type(current_type, value)
851- # rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
852- # return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
853- # rescue StandardError => err
854- # begin
855- # query.handle_or_reraise(err)
856- # rescue GraphQL::ExecutionError => ex_err
857- # return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
858- # end
859- # end
860- # after_lazy(resolved_type_or_lazy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |resolved_type_result, runtime_state|
861- # if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
862- # resolved_type, resolved_value = resolved_type_result
863- # else
864- # resolved_type = resolved_type_result
865- # resolved_value = value
866- # end
867-
868- # possible_types = query.types.possible_types(current_type)
869- # if !possible_types.include?(resolved_type)
870- # parent_type = field.owner_type
871- # err_class = current_type::UnresolvedTypeError
872- # type_error = err_class.new(resolved_value, field, parent_type, resolved_type, possible_types)
873- # schema.type_error(type_error, context)
874- # set_result(selection_result, result_name, nil, false, is_non_null)
875- # nil
876- # else
877- # continue_field(resolved_value, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result, was_scoped, runtime_state)
878- # end
879- # end
880678 when "OBJECT"
881679 object_proxy = begin
882680 was_scoped ? current_type . wrap_scoped ( value , context ) : current_type . wrap ( value , context )
@@ -886,15 +684,15 @@ def continue_field(value, field, current_type, ast_node, next_selections, is_non
886684 after_lazy ( object_proxy , ast_node : ast_node , field : field , owner_object : selection_result . graphql_application_value , arguments : arguments , trace : false , result_name : result_name , result : selection_result , runtime_state : runtime_state ) do |inner_object , runtime_state |
887685 continue_value = continue_value ( inner_object , field , is_non_null , ast_node , result_name , selection_result )
888686 if HALT != continue_value
889- response_hash = GraphQLResultHash . new ( result_name , current_type , continue_value , selection_result , is_non_null , next_selections , false , ast_node , arguments , field )
687+ response_hash = GraphQLResultHash . new ( self , result_name , current_type , continue_value , selection_result , is_non_null , next_selections , false , ast_node , arguments , field )
890688 set_result ( selection_result , result_name , response_hash , true , is_non_null )
891- @run_queue << ObjectStep . new ( self , response_hash , runtime_state )
689+ @run_queue << response_hash
892690 end
893691 end
894692 when "LIST"
895- response_list = GraphQLResultArray . new ( result_name , current_type , selection_result . graphql_application_value , selection_result , is_non_null , next_selections , false , ast_node , arguments , field )
693+ response_list = GraphQLResultArray . new ( self , result_name , current_type , value , selection_result , is_non_null , next_selections , false , ast_node , arguments , field )
896694 set_result ( selection_result , result_name , response_list , true , is_non_null )
897- @run_queue << ListStep . new ( self , runtime_state , response_list , value , was_scoped )
695+ @run_queue << response_list
898696 else
899697 raise "Invariant: Unhandled type kind #{ current_type . kind } (#{ current_type } )"
900698 end
0 commit comments