@@ -75,7 +75,7 @@ def initialize(field_name, value)
75
75
#
76
76
# @return [Object] the initial result, without any defers
77
77
def execute ( ast_operation , root_type , query_object )
78
- collector = query_object . context [ CONTEXT_PATCH_TARGET ]
78
+ collector = query_object . context [ CONTEXT_PATCH_TARGET ] || MergeCollector . new
79
79
irep_root = query_object . internal_representation [ ast_operation . name ]
80
80
81
81
scope = ExecScope . new ( query_object )
@@ -88,81 +88,93 @@ def execute(ast_operation, root_type, query_object)
88
88
)
89
89
90
90
initial_result = resolve_or_defer_frame ( scope , initial_thread , initial_frame )
91
+ resolve_batches ( scope , initial_thread , query_object , initial_result )
91
92
92
- if collector
93
- initial_data = if initial_result == DEFERRED_RESULT
94
- { }
95
- else
96
- initial_result
97
- end
98
- initial_patch = { "data" => initial_data }
93
+ initial_data = if initial_result == DEFERRED_RESULT
94
+ { }
95
+ else
96
+ initial_result
97
+ end
99
98
100
- initial_errors = initial_thread . errors + query_object . context . errors
101
- error_idx = initial_errors . length
99
+ initial_patch = { "data" => initial_data }
102
100
103
- if initial_errors . any?
104
- initial_patch [ "errors" ] = initial_errors . map ( &:to_h )
105
- end
101
+ initial_errors = initial_thread . errors + query_object . context . errors
102
+ error_idx = initial_errors . length
106
103
107
- collector . patch (
108
- path : [ ] ,
109
- value : initial_patch
110
- )
104
+ if initial_errors . any?
105
+ initial_patch [ "errors" ] = initial_errors . map ( &:to_h )
106
+ end
111
107
112
- defers = initial_thread . defers
113
- while defers . any?
114
- next_defers = [ ]
115
- defers . each do |deferral |
116
- deferred_thread = ExecThread . new
117
- case deferral
118
- when ExecFrame
119
- deferred_frame = deferral
120
- deferred_result = resolve_frame ( scope , deferred_thread , deferred_frame )
121
-
122
- when ExecStream
123
- begin
124
- list_frame = deferral . frame
125
- inner_type = deferral . type
126
- item , idx = deferral . enumerator . next
127
- deferred_frame = ExecFrame . new ( {
128
- node : list_frame . node ,
129
- path : list_frame . path + [ idx ] ,
130
- type : inner_type ,
131
- value : item ,
132
- } )
133
- deferred_result = resolve_value ( scope , deferred_thread , deferred_frame , item , inner_type )
134
- deferred_thread . defers << deferral
135
- rescue StopIteration
136
- # The enum is done
137
- end
138
- else
139
- raise ( "Can't continue deferred #{ deferred_frame . class . name } " )
140
- end
108
+ collector . patch (
109
+ path : [ ] ,
110
+ value : initial_patch
111
+ )
141
112
142
- # TODO: Should I patch nil?
143
- if !deferred_result . nil?
144
- collector . patch (
145
- path : [ "data" ] + deferred_frame . path ,
146
- value : deferred_result
147
- )
113
+ defers = initial_thread . defers
114
+ while defers . any?
115
+ next_defers = [ ]
116
+ defers . each do |deferral |
117
+ deferred_thread = ExecThread . new
118
+ case deferral
119
+ when ExecFrame
120
+ deferred_frame = deferral
121
+ deferred_result = resolve_frame ( scope , deferred_thread , deferred_frame )
122
+ if deferred_result . is_a? ( GraphQL ::Execution ::Batch ::BatchResolve )
123
+ # TODO: this will miss nested resolves
124
+ res = { }
125
+ resolve_batches ( scope , deferred_thread , query_object , res )
126
+ deferred_result = get_in ( res , deferred_frame . path )
148
127
end
128
+ when ExecStream
129
+ begin
130
+ list_frame = deferral . frame
131
+ inner_type = deferral . type
132
+ item , idx = deferral . enumerator . next
133
+ deferred_frame = ExecFrame . new ( {
134
+ node : list_frame . node ,
135
+ path : list_frame . path + [ idx ] ,
136
+ type : inner_type ,
137
+ value : item ,
138
+ } )
139
+ field_defn = scope . get_field ( list_frame . type , list_frame . node . definition_name )
140
+ deferred_result = resolve_value ( scope , deferred_thread , deferred_frame , item , field_defn , inner_type )
141
+
142
+ # TODO: deep merge ??
143
+ res = { }
144
+ resolve_batches ( scope , deferred_thread , query_object , res )
145
+ if res . any?
146
+ deferred_result = get_in ( res , deferred_frame . path )
147
+ end
149
148
150
- deferred_thread . errors . each do |deferred_error |
151
- collector . patch (
152
- path : [ "errors" , error_idx ] ,
153
- value : deferred_error . to_h
154
- )
155
- error_idx += 1
149
+ deferred_thread . defers << deferral
150
+ rescue StopIteration
151
+ # The enum is done
156
152
end
157
- next_defers . push ( *deferred_thread . defers )
153
+ else
154
+ raise ( "Can't continue deferred #{ deferred_frame . class . name } " )
155
+ end
156
+
157
+ # TODO: Should I patch nil?
158
+ if !deferred_result . nil?
159
+ collector . patch (
160
+ path : [ "data" ] . concat ( deferred_frame . path ) ,
161
+ value : deferred_result
162
+ )
158
163
end
159
- defers = next_defers
164
+
165
+ deferred_thread . errors . each do |deferred_error |
166
+ collector . patch (
167
+ path : [ "errors" , error_idx ] ,
168
+ value : deferred_error . to_h
169
+ )
170
+ error_idx += 1
171
+ end
172
+ next_defers . push ( *deferred_thread . defers )
160
173
end
161
- else
162
- query_object . context . errors . concat ( initial_thread . errors )
174
+ defers = next_defers
163
175
end
164
176
165
- initial_result
177
+ initial_data
166
178
end
167
179
168
180
private
@@ -197,6 +209,7 @@ def resolve_frame(scope, thread, frame)
197
209
thread ,
198
210
frame ,
199
211
field_result ,
212
+ field_defn ,
200
213
return_type_defn ,
201
214
)
202
215
else
@@ -250,7 +263,9 @@ def resolve_selections(scope, thread, outer_frame)
250
263
# This value has already been resolved in another type branch
251
264
end
252
265
253
- if inner_result != DEFERRED_RESULT
266
+ if inner_result == DEFERRED_RESULT || inner_result . is_a? ( GraphQL ::Execution ::Batch ::BatchResolve )
267
+ # This result was dealt with by the thread
268
+ else
254
269
GraphQL ::Execution ::MergeBranchResult . merge ( selection_result , { selection_name => inner_result } )
255
270
end
256
271
end
@@ -319,13 +334,22 @@ def resolve_field_frame(scope, thread, frame, field_defn)
319
334
# expected to be an instance of `type_defn`.
320
335
# This coerces terminals and recursively resolves non-terminals (object, list, non-null).
321
336
# @return [Object] the response-ready version of `value`
322
- def resolve_value ( scope , thread , frame , value , type_defn )
323
- if value . nil? || value . is_a? ( GraphQL ::ExecutionError )
337
+ def resolve_value ( scope , thread , frame , value , field_defn , type_defn )
338
+ case value
339
+ when nil , GraphQL ::ExecutionError
324
340
if type_defn . kind . non_null?
325
341
raise InnerInvalidNullError . new ( frame . node . ast_node . name , value )
326
342
else
327
343
nil
328
344
end
345
+ when GraphQL ::Execution ::Batch ::BatchResolve
346
+ scope . query . accumulator . register (
347
+ field_defn . batch_loader . func ,
348
+ field_defn . batch_loader . args ,
349
+ frame ,
350
+ value
351
+ )
352
+ value
329
353
else
330
354
case type_defn . kind
331
355
when GraphQL ::TypeKinds ::SCALAR , GraphQL ::TypeKinds ::ENUM
@@ -337,11 +361,11 @@ def resolve_value(scope, thread, frame, value, type_defn)
337
361
if !resolved_type . is_a? ( GraphQL ::ObjectType ) || !possible_types . include? ( resolved_type )
338
362
raise GraphQL ::UnresolvedTypeError . new ( frame . node . definition_name , type_defn , frame . node . parent . return_type , resolved_type , possible_types )
339
363
else
340
- resolve_value ( scope , thread , frame , value , resolved_type )
364
+ resolve_value ( scope , thread , frame , value , field_defn , resolved_type )
341
365
end
342
366
when GraphQL ::TypeKinds ::NON_NULL
343
367
wrapped_type = type_defn . of_type
344
- resolve_value ( scope , thread , frame , value , wrapped_type )
368
+ resolve_value ( scope , thread , frame , value , field_defn , wrapped_type )
345
369
when GraphQL ::TypeKinds ::LIST
346
370
wrapped_type = type_defn . of_type
347
371
items_enumerator = value . map . with_index
@@ -361,7 +385,7 @@ def resolve_value(scope, thread, frame, value, type_defn)
361
385
type : wrapped_type ,
362
386
value : item ,
363
387
} )
364
- resolve_value ( scope , thread , inner_frame , item , wrapped_type )
388
+ resolve_value ( scope , thread , inner_frame , item , field_defn , wrapped_type )
365
389
end
366
390
resolved_values
367
391
end
@@ -378,6 +402,33 @@ def resolve_value(scope, thread, frame, value, type_defn)
378
402
end
379
403
end
380
404
end
405
+
406
+ def set_in ( data , path , value )
407
+ path = path . dup
408
+ last = path . pop
409
+ path . each do |key |
410
+ data = data [ key ] ||= { }
411
+ end
412
+ data [ last ] = value
413
+ end
414
+
415
+ def get_in ( data , path )
416
+ path . each do |key |
417
+ data = data [ key ]
418
+ end
419
+ data
420
+ end
421
+
422
+ def resolve_batches ( scope , thread , query_object , merge_target )
423
+ while query_object . accumulator . any?
424
+ query_object . accumulator . resolve_all do |frame , value |
425
+ # TODO cache on frame
426
+ field_defn = scope . get_field ( frame . type , frame . node . definition_name )
427
+ finished_value = resolve_value ( scope , thread , frame , value , field_defn , field_defn . type )
428
+ set_in ( merge_target , frame . path , finished_value )
429
+ end
430
+ end
431
+ end
381
432
end
382
433
end
383
434
end
0 commit comments