@@ -37,173 +37,107 @@ def run_all(schema, query_options, context: {}, max_complexity: schema.max_compl
3737 multiplex . current_trace . execute_multiplex ( multiplex : multiplex ) do
3838 schema = multiplex . schema
3939 queries = multiplex . queries
40- query_instrumenters = schema . instrumenters [ :query ]
41- multiplex_instrumenters = schema . instrumenters [ :multiplex ]
4240 lazies_at_depth = Hash . new { |h , k | h [ k ] = [ ] }
41+ multiplex_analyzers = schema . multiplex_analyzers
42+ if multiplex . max_complexity
43+ multiplex_analyzers += [ GraphQL ::Analysis ::AST ::MaxQueryComplexity ]
44+ end
4345
44- # First, run multiplex instrumentation, then query instrumentation for each query
45- call_hooks ( multiplex_instrumenters , multiplex , :before_multiplex , :after_multiplex ) do
46- each_query_call_hooks ( query_instrumenters , queries ) do
47- schema = multiplex . schema
48- multiplex_analyzers = schema . multiplex_analyzers
49- queries = multiplex . queries
50- if multiplex . max_complexity
51- multiplex_analyzers += [ GraphQL ::Analysis ::AST ::MaxQueryComplexity ]
52- end
53-
54- schema . analysis_engine . analyze_multiplex ( multiplex , multiplex_analyzers )
55- begin
56- # Since this is basically the batching context,
57- # share it for a whole multiplex
58- multiplex . context [ :interpreter_instance ] ||= multiplex . schema . query_execution_strategy . new
59- # Do as much eager evaluation of the query as possible
60- results = [ ]
61- queries . each_with_index do |query , idx |
62- multiplex . dataloader . append_job {
63- operation = query . selected_operation
64- result = if operation . nil? || !query . valid? || query . context . errors . any?
65- NO_OPERATION
66- else
67- begin
68- # Although queries in a multiplex _share_ an Interpreter instance,
69- # they also have another item of state, which is private to that query
70- # in particular, assign it here:
71- runtime = Runtime . new ( query : query , lazies_at_depth : lazies_at_depth )
72- query . context . namespace ( :interpreter_runtime ) [ :runtime ] = runtime
73-
74- query . current_trace . execute_query ( query : query ) do
75- runtime . run_eager
76- end
77- rescue GraphQL ::ExecutionError => err
78- query . context . errors << err
79- NO_OPERATION
80- end
46+ schema . analysis_engine . analyze_multiplex ( multiplex , multiplex_analyzers )
47+ begin
48+ # Since this is basically the batching context,
49+ # share it for a whole multiplex
50+ multiplex . context [ :interpreter_instance ] ||= multiplex . schema . query_execution_strategy . new
51+ # Do as much eager evaluation of the query as possible
52+ results = [ ]
53+ queries . each_with_index do |query , idx |
54+ multiplex . dataloader . append_job {
55+ operation = query . selected_operation
56+ result = if operation . nil? || !query . valid? || query . context . errors . any?
57+ NO_OPERATION
58+ else
59+ begin
60+ # Although queries in a multiplex _share_ an Interpreter instance,
61+ # they also have another item of state, which is private to that query
62+ # in particular, assign it here:
63+ runtime = Runtime . new ( query : query , lazies_at_depth : lazies_at_depth )
64+ query . context . namespace ( :interpreter_runtime ) [ :runtime ] = runtime
65+
66+ query . current_trace . execute_query ( query : query ) do
67+ runtime . run_eager
8168 end
82- results [ idx ] = result
83- }
69+ rescue GraphQL ::ExecutionError => err
70+ query . context . errors << err
71+ NO_OPERATION
72+ end
8473 end
74+ results [ idx ] = result
75+ }
76+ end
8577
86- multiplex . dataloader . run
78+ multiplex . dataloader . run
8779
88- # Then, work through lazy results in a breadth-first way
89- multiplex . dataloader . append_job {
90- query = multiplex . queries . length == 1 ? multiplex . queries [ 0 ] : nil
91- queries = multiplex ? multiplex . queries : [ query ]
92- final_values = queries . map do |query |
93- runtime = query . context . namespace ( :interpreter_runtime ) [ :runtime ]
94- # it might not be present if the query has an error
95- runtime ? runtime . final_result : nil
96- end
97- final_values . compact!
98- multiplex . current_trace . execute_query_lazy ( multiplex : multiplex , query : query ) do
99- Interpreter ::Resolve . resolve_each_depth ( lazies_at_depth , multiplex . dataloader )
100- end
80+ # Then, work through lazy results in a breadth-first way
81+ multiplex . dataloader . append_job {
82+ query = multiplex . queries . length == 1 ? multiplex . queries [ 0 ] : nil
83+ queries = multiplex ? multiplex . queries : [ query ]
84+ final_values = queries . map do |query |
85+ runtime = query . context . namespace ( :interpreter_runtime ) [ :runtime ]
86+ # it might not be present if the query has an error
87+ runtime ? runtime . final_result : nil
88+ end
89+ final_values . compact!
90+ multiplex . current_trace . execute_query_lazy ( multiplex : multiplex , query : query ) do
91+ Interpreter ::Resolve . resolve_each_depth ( lazies_at_depth , multiplex . dataloader )
92+ end
93+ }
94+ multiplex . dataloader . run
95+
96+ # Then, find all errors and assign the result to the query object
97+ results . each_with_index do |data_result , idx |
98+ query = queries [ idx ]
99+ # Assign the result so that it can be accessed in instrumentation
100+ query . result_values = if data_result . equal? ( NO_OPERATION )
101+ if !query . valid? || query . context . errors . any?
102+ # A bit weird, but `Query#static_errors` _includes_ `query.context.errors`
103+ { "errors" => query . static_errors . map ( &:to_h ) }
104+ else
105+ data_result
106+ end
107+ else
108+ result = {
109+ "data" => query . context . namespace ( :interpreter_runtime ) [ :runtime ] . final_result
101110 }
102- multiplex . dataloader . run
103-
104- # Then, find all errors and assign the result to the query object
105- results . each_with_index do |data_result , idx |
106- query = queries [ idx ]
107- # Assign the result so that it can be accessed in instrumentation
108- query . result_values = if data_result . equal? ( NO_OPERATION )
109- if !query . valid? || query . context . errors . any?
110- # A bit weird, but `Query#static_errors` _includes_ `query.context.errors`
111- { "errors" => query . static_errors . map ( &:to_h ) }
112- else
113- data_result
114- end
115- else
116- result = {
117- "data" => query . context . namespace ( :interpreter_runtime ) [ :runtime ] . final_result
118- }
119111
120- if query . context . errors . any?
121- error_result = query . context . errors . map ( &:to_h )
122- result [ "errors" ] = error_result
123- end
124-
125- result
126- end
127- if query . context . namespace? ( :__query_result_extensions__ )
128- query . result_values [ "extensions" ] = query . context . namespace ( :__query_result_extensions__ )
129- end
130- # Get the Query::Result, not the Hash
131- results [ idx ] = query . result
112+ if query . context . errors . any?
113+ error_result = query . context . errors . map ( &:to_h )
114+ result [ "errors" ] = error_result
132115 end
133116
134- results
135- rescue Exception
136- # TODO rescue at a higher level so it will catch errors in analysis, too
137- # Assign values here so that the query's `@executed` becomes true
138- queries . map { |q | q . result_values ||= { } }
139- raise
140- ensure
141- queries . map { |query |
142- runtime = query . context . namespace ( :interpreter_runtime ) [ :runtime ]
143- if runtime
144- runtime . delete_all_interpreter_context
145- end
146- }
117+ result
118+ end
119+ if query . context . namespace? ( :__query_result_extensions__ )
120+ query . result_values [ "extensions" ] = query . context . namespace ( :__query_result_extensions__ )
147121 end
122+ # Get the Query::Result, not the Hash
123+ results [ idx ] = query . result
148124 end
149- end
150- end
151- end
152-
153- private
154125
155- # Call the before_ hooks of each query,
156- # Then yield if no errors.
157- # `call_hooks` takes care of appropriate cleanup.
158- def each_query_call_hooks ( instrumenters , queries , i = 0 )
159- if i >= queries . length
160- yield
161- else
162- query = queries [ i ]
163- call_hooks ( instrumenters , query , :before_query , :after_query ) {
164- each_query_call_hooks ( instrumenters , queries , i + 1 ) {
165- yield
126+ results
127+ rescue Exception
128+ # TODO rescue at a higher level so it will catch errors in analysis, too
129+ # Assign values here so that the query's `@executed` becomes true
130+ queries . map { |q | q . result_values ||= { } }
131+ raise
132+ ensure
133+ queries . map { |query |
134+ runtime = query . context . namespace ( :interpreter_runtime ) [ :runtime ]
135+ if runtime
136+ runtime . delete_all_interpreter_context
137+ end
166138 }
167- }
168- end
169- end
170-
171- # Call each before hook, and if they all succeed, yield.
172- # If they don't all succeed, call after_ for each one that succeeded.
173- def call_hooks ( instrumenters , object , before_hook_name , after_hook_name )
174- begin
175- successful = [ ]
176- instrumenters . each do |instrumenter |
177- instrumenter . public_send ( before_hook_name , object )
178- successful << instrumenter
179- end
180-
181- # if any before hooks raise an exception, quit calling before hooks,
182- # but call the after hooks on anything that succeeded but also
183- # raise the exception that came from the before hook.
184- rescue GraphQL ::ExecutionError => err
185- object . context . errors << err
186- rescue => e
187- raise call_after_hooks ( successful , object , after_hook_name , e )
188- end
189-
190- begin
191- yield # Call the user code
192- ensure
193- ex = call_after_hooks ( successful , object , after_hook_name , nil )
194- raise ex if ex
195- end
196- end
197-
198- def call_after_hooks ( instrumenters , object , after_hook_name , ex )
199- instrumenters . reverse_each do |instrumenter |
200- begin
201- instrumenter . public_send ( after_hook_name , object )
202- rescue => e
203- ex = e
204139 end
205140 end
206- ex
207141 end
208142 end
209143
0 commit comments