11package graphql.execution.instrumentation.dataloader
22
3+ import com.fasterxml.jackson.databind.ObjectMapper
34import graphql.Directives
45import graphql.GraphQL
56import graphql.execution.instrumentation.Instrumentation
@@ -61,19 +62,23 @@ class DataLoaderPerformanceData {
6162 ]]
6263 ]
6364
64- static def query = """
65+ static String getQuery (boolean enableDeferred ) {
66+ return """
6567 query {
6668 shops {
6769 id name
68- departments {
69- id name
70- products {
70+ ... @defer(if: $enableDeferred ) {
71+ departments {
7172 id name
73+ products {
74+ id name
75+ }
7276 }
73- }
77+ }
7478 }
7579 }
7680 """
81+ }
7782
7883 static def expectedExpensiveData = [
7984 shops : [[name : " Shop 1" ,
@@ -162,7 +167,8 @@ class DataLoaderPerformanceData {
162167 assert incrementalResultsItems. any { it == [path : [], data : [expensiveShops : [[id : " exshop-1" , name : " ExShop 1" ], [id : " exshop-2" , name : " ExShop 2" ], [id : " exshop-3" , name : " ExShop 3" ]]]] }
163168 }
164169
165- static def expensiveQuery = """
170+ static String getExpensiveQuery (boolean deferredEnabled ) {
171+ return """
166172 query {
167173 shops {
168174 name
@@ -171,180 +177,59 @@ class DataLoaderPerformanceData {
171177 products {
172178 name
173179 }
174- expensiveProducts {
175- name
176- }
177- }
178- expensiveDepartments {
179- name
180- products {
181- name
182- }
183- expensiveProducts {
184- name
185- }
186- }
187- }
188- expensiveShops {
189- name
190- departments {
191- name
192- products {
193- name
194- }
195- expensiveProducts {
196- name
197- }
180+ ... @defer(if: $deferredEnabled ) {
181+ expensiveProducts {
182+ name
183+ }
184+ }
198185 }
199- expensiveDepartments {
200- name
201- products {
202- name
203- }
204- expensiveProducts {
186+ ... @defer(if: $deferredEnabled ) {
187+ expensiveDepartments {
205188 name
206- }
207- }
208- }
209- }
210- """
211-
212- static def expectedInitialDeferredData = [
213- data : [
214- shops : [
215- [id : " shop-1" , name : " Shop 1" ],
216- [id : " shop-2" , name : " Shop 2" ],
217- [id : " shop-3" , name : " Shop 3" ],
218- ]
219- ],
220- hasNext : true
221- ]
222-
223- static def expectedListOfDeferredData = [
224- [
225- hasNext : true ,
226- incremental : [[
227- path : [" shops" , 0 ],
228- data : [
229- departments : [
230- [id : " department-1" , name : " Department 1" , products : [[id : " product-1" , name : " Product 1" ]]],
231- [id : " department-2" , name : " Department 2" , products : [[id : " product-2" , name : " Product 2" ]]],
232- [id : " department-3" , name : " Department 3" , products : [[id : " product-3" , name : " Product 3" ]]]
233- ]
234- ]
235- ]],
236- ],
237- [
238- hasNext : true ,
239- incremental : [[
240- path : [" shops" , 1 ],
241- data : [
242- departments : [
243- [id : " department-4" , name : " Department 4" , products : [[id : " product-4" , name : " Product 4" ]]],
244- [id : " department-5" , name : " Department 5" , products : [[id : " product-5" , name : " Product 5" ]]],
245- [id : " department-6" , name : " Department 6" , products : [[id : " product-6" , name : " Product 6" ]]]
246- ]
247- ],
248- ]],
249- ],
250- [
251- hasNext : false ,
252- incremental : [[
253- path : [" shops" , 2 ],
254- data : [
255- departments : [
256- [id : " department-7" , name : " Department 7" , products : [[id : " product-7" , name : " Product 7" ]]],
257- [id : " department-8" , name : " Department 8" , products : [[id : " product-8" , name : " Product 8" ]]],
258- [id : " department-9" , name : " Department 9" , products : [[id : " product-9" , name : " Product 9" ]]]
259- ]
260- ]
261- ]],
262- ]
263- ]
264-
265-
266- static def deferredQuery = """
267- query {
268- shops {
269- id name
270- ... @defer {
271- departments {
272- id name
273189 products {
274- id name
190+ name
275191 }
276- }
277- }
278- }
279- }
280- """
281-
282- static def expensiveDeferredQuery = """
283- query {
284- shops {
285- id name
286- ... @defer {
287- departments {
288- name
289- ... @defer {
290- products {
291- name
292- }
293- }
294- ... @defer {
192+ ... @defer(if: $deferredEnabled ) {
295193 expensiveProducts {
296194 name
297195 }
298196 }
299197 }
300198 }
301- ... @defer {
302- expensiveDepartments {
199+ }
200+ ... @defer(if: $deferredEnabled ) {
201+ expensiveShops {
202+ name
203+ departments {
303204 name
304205 products {
305206 name
306207 }
307- expensiveProducts {
208+ ... @defer(if: $deferredEnabled ) {
209+ expensiveProducts {
210+ name
211+ }
212+ }
213+ }
214+ ... @defer(if: $deferredEnabled ) {
215+ expensiveDepartments {
308216 name
217+ products {
218+ name
219+ }
220+ ... @defer(if: $deferredEnabled ) {
221+ expensiveProducts {
222+ name
223+ }
224+ }
309225 }
310- }
311- }
312- }
313- ... @defer {
314- expensiveShops {
315- id name
226+ }
316227 }
317228 }
318229 }
319- """
230+ """
320231
321- static def expectedExpensiveDeferredData = [
322- [[id : " exshop-1" , name : " ExShop 1" ], [id : " exshop-2" , name : " ExShop 2" ], [id : " exshop-3" , name : " ExShop 3" ]],
323- [[name : " Department 1" ,products :null , expensiveProducts :null ], [name : " Department 2" ,products :null , expensiveProducts :null ], [name : " Department 3" ,products :null , expensiveProducts :null ]],
324- [[name : " Department 1" , products : [[name : " Product 1" ]], expensiveProducts : [[name : " Product 1" ]]], [name : " Department 2" , products : [[name : " Product 2" ]], expensiveProducts : [[name : " Product 2" ]]], [name : " Department 3" , products : [[name : " Product 3" ]], expensiveProducts : [[name : " Product 3" ]]]],
325- [[name : " Department 4" ,products :null , expensiveProducts :null ], [name : " Department 5" ,products :null , expensiveProducts :null ], [name : " Department 6" ,products :null , expensiveProducts :null ]],
326- [[name : " Department 4" , products : [[name : " Product 4" ]], expensiveProducts : [[name : " Product 4" ]]], [name : " Department 5" , products : [[name : " Product 5" ]], expensiveProducts : [[name : " Product 5" ]]], [name : " Department 6" , products : [[name : " Product 6" ]], expensiveProducts : [[name : " Product 6" ]]]],
327- [[name : " Department 7" ,products :null , expensiveProducts :null ], [name : " Department 8" ,products :null , expensiveProducts :null ], [name : " Department 9" ,products :null , expensiveProducts :null ]],
328- [[name : " Department 7" , products : [[name : " Product 7" ]], expensiveProducts : [[name : " Product 7" ]]], [name : " Department 8" , products : [[name : " Product 8" ]], expensiveProducts : [[name : " Product 8" ]]], [name : " Department 9" , products : [[name : " Product 9" ]], expensiveProducts : [[name : " Product 9" ]]]],
329- [[name : " Product 1" ]],
330- [[name : " Product 1" ]],
331- [[name : " Product 2" ]],
332- [[name : " Product 2" ]],
333- [[name : " Product 3" ]],
334- [[name : " Product 3" ]],
335- [[name : " Product 4" ]],
336- [[name : " Product 4" ]],
337- [[name : " Product 5" ]],
338- [[name : " Product 5" ]],
339- [[name : " Product 6" ]],
340- [[name : " Product 6" ]],
341- [[name : " Product 7" ]],
342- [[name : " Product 7" ]],
343- [[name : " Product 8" ]],
344- [[name : " Product 8" ]],
345- [[name : " Product 9" ]],
346- [[name : " Product 9" ]],
347- ]
232+ }
348233
349234 static List<Map<String , Object > > getIncrementalResults (IncrementalExecutionResult initialResult ) {
350235 Publisher<DelayedIncrementalPartialResult > deferredResultStream = initialResult. incrementalItemPublisher
@@ -356,4 +241,48 @@ class DataLoaderPerformanceData {
356241 return subscriber. getEvents()
357242 .collect { it. toSpecification() }
358243 }
244+
245+
246+ static Map<String , Object > combineExecutionResults (Map<String , Object > initialResult , List<Map<String , Object > > incrementalResults ) {
247+ Map<String , Object > combinedResult = deepClone(initialResult, Map . class)
248+
249+ incrementalResults
250+ // groovy's flatMap
251+ .collectMany { (List ) it. incremental }
252+ .each { result ->
253+ def parent = findByPath((Map ) combinedResult. data, (List ) result. path)
254+ if (parent instanceof Map ) {
255+ parent. putAll((Map ) result. data)
256+ } else if (parent instanceof List ) {
257+ parent. addAll(result. data)
258+ } else {
259+ throw new RuntimeException (" Unexpected parent type: ${ parent.getClass()} " )
260+ }
261+
262+ if (combinedResult. errors != null && ! result. errors. isEmpty()) {
263+ if (combinedResult. errors == null ) {
264+ combinedResult. errors = []
265+ }
266+
267+ combinedResult. errors. addAll(result. errors)
268+ }
269+ }
270+
271+ combinedResult. remove(" hasNext" )
272+
273+ combinedResult
274+ }
275+
276+ private static ObjectMapper objectMapper = new ObjectMapper ()
277+ private static <T> T deepClone (Object obj , Class<T> clazz ) {
278+ return objectMapper. readValue(objectMapper. writeValueAsString(obj), clazz)
279+ }
280+
281+ private static Object findByPath (Map<String , Object > data , List<Object > path ) {
282+ def current = data
283+ path. each { key ->
284+ current = current[key]
285+ }
286+ current
287+ }
359288}
0 commit comments