1- import { getNamedType , GraphQLOutputType , GraphQLList , GraphQLSchema , FieldNode } from 'graphql' ;
1+ import { GraphQLSchema , FieldNode } from 'graphql' ;
22
33import DataLoader from 'dataloader' ;
44
5+ import { SubschemaConfig , Transformer , getExecutor , validateRequest , DelegationContext } from '@graphql-tools/delegate' ;
56import {
6- SubschemaConfig ,
7- Transformer ,
8- createRequestFromInfo ,
9- getDelegationContext ,
10- getDelegatingOperation ,
11- getExecutor ,
12- validateRequest ,
13- Receiver ,
14- externalValueFromResult ,
15- } from '@graphql-tools/delegate' ;
16- import { isAsyncIterable , relocatedError } from '@graphql-tools/utils' ;
7+ AsyncExecutionResult ,
8+ ExecutionPatchResult ,
9+ ExecutionResult ,
10+ isAsyncIterable ,
11+ mapAsyncIterator ,
12+ Request ,
13+ splitAsyncIterator ,
14+ } from '@graphql-tools/utils' ;
1715
1816import { BatchDelegateOptions } from './types' ;
1917
@@ -22,48 +20,21 @@ const cache1: WeakMap<
2220 WeakMap < GraphQLSchema | SubschemaConfig , Record < string , DataLoader < any , any > > >
2321> = new WeakMap ( ) ;
2422
25- function createBatchFn < K = any > ( options : BatchDelegateOptions ) {
23+ function createBatchFn < K = any > ( options : BatchDelegateOptions , request : Request , delegationContext : DelegationContext ) {
2624 const argsFromKeys = options . argsFromKeys ?? ( ( keys : ReadonlyArray < K > ) => ( { ids : keys } ) ) ;
27- const { lazyOptionsFn } = options ;
2825
29- return async ( keys : ReadonlyArray < K > ) => {
30- const {
31- context,
32- info,
33- operationName,
34- operation = getDelegatingOperation ( info . parentType , info . schema ) ,
35- fieldName = info . fieldName ,
36- returnType = new GraphQLList ( getNamedType ( options . info . returnType ) as GraphQLOutputType ) ,
37- selectionSet,
38- fieldNodes,
39- binding,
40- skipValidation,
41- } = options ;
42-
43- if ( operation !== 'query' && operation !== 'mutation' ) {
44- throw new Error ( `Batch delegation not possible for operation '${ operation } '.` ) ;
45- }
26+ const { binding, skipValidation } = options ;
4627
47- const request = createRequestFromInfo ( {
48- info,
49- operation,
50- fieldName,
51- selectionSet,
52- fieldNodes,
53- operationName,
54- } ) ;
28+ const { fieldName, context, info } = delegationContext ;
5529
56- const delegationContext = getDelegationContext ( {
57- request,
58- args : argsFromKeys ( keys ) ,
59- onLocatedError : originalError => relocatedError ( originalError , originalError . path . slice ( 1 ) ) ,
60- ...( lazyOptionsFn == null ? options : lazyOptionsFn ( options ) ) ,
61- operation,
62- fieldName,
63- returnType,
64- } ) ;
65-
66- const transformer = new Transformer ( delegationContext , binding ) ;
30+ return async ( keys : ReadonlyArray < K > ) => {
31+ const transformer = new Transformer (
32+ {
33+ ...delegationContext ,
34+ args : argsFromKeys ( keys ) ,
35+ } ,
36+ binding
37+ ) ;
6738
6839 const processedRequest = transformer . transformRequest ( request ) ;
6940
@@ -79,40 +50,38 @@ function createBatchFn<K = any>(options: BatchDelegateOptions) {
7950 info,
8051 } ) ;
8152
53+ const numKeys = keys . length ;
8254 if ( isAsyncIterable ( batchResult ) ) {
83- // TODO: split the asyncIterable and make a new receiver from each of them, return the Receiver instead of the
84- // initial value, so that the correct info can be used to instantiate the Receiver
85- const receiver = new Receiver ( batchResult , delegationContext , executionResult =>
86- transformer . transformResult ( executionResult )
87- ) ;
88-
89- const batchValue = await receiver . getInitialValue ( ) ;
90-
91- return Array . isArray ( batchValue ) ? batchValue : keys . map ( ( ) => batchValue ) ;
55+ const mappedBatchResult = mapAsyncIterator ( batchResult , result => transformer . transformResult ( result ) ) ;
56+ return splitAsyncIterator ( mappedBatchResult , numKeys , result => splitAsyncResult ( result , fieldName ) ) ;
9257 }
9358
94- // TODO: split the batchedResult and return the result instead of the value, so the correct info
95- // can be used to instantiate the value
96- const batchValue = externalValueFromResult ( transformer . transformResult ( batchResult ) , delegationContext ) ;
97-
98- return Array . isArray ( batchValue ) ? batchValue : keys . map ( ( ) => batchValue ) ;
59+ return splitResult ( transformer . transformResult ( batchResult ) , fieldName , numKeys ) ;
9960 } ;
10061}
10162
102- export function getLoader < K = any , V = any , C = K > ( options : BatchDelegateOptions ) : DataLoader < K , V , C > {
63+ export function getLoader < K = any , C = K > (
64+ options : BatchDelegateOptions ,
65+ request : Request ,
66+ delegationContext : DelegationContext
67+ ) : DataLoader < K , ExecutionResult | AsyncIterableIterator < AsyncExecutionResult > , C > {
10368 const fieldName = options . fieldName ?? options . info . fieldName ;
10469
105- let cache2 : WeakMap < GraphQLSchema | SubschemaConfig , Record < string , DataLoader < K , V , C > > > = cache1 . get (
106- options . info . fieldNodes
107- ) ;
70+ let cache2 : WeakMap <
71+ GraphQLSchema | SubschemaConfig ,
72+ Record < string , DataLoader < K , ExecutionResult | AsyncIterableIterator < AsyncExecutionResult > , C > >
73+ > = cache1 . get ( options . info . fieldNodes ) ;
10874
10975 if ( cache2 === undefined ) {
11076 cache2 = new WeakMap ( ) ;
11177 cache1 . set ( options . info . fieldNodes , cache2 ) ;
11278 const loaders = Object . create ( null ) ;
11379 cache2 . set ( options . schema , loaders ) ;
114- const batchFn = createBatchFn ( options ) ;
115- const loader = new DataLoader < K , V , C > ( keys => batchFn ( keys ) , options . dataLoaderOptions ) ;
80+ const batchFn = createBatchFn ( options , request , delegationContext ) ;
81+ const loader = new DataLoader < K , ExecutionResult | AsyncIterableIterator < AsyncExecutionResult > , C > (
82+ keys => batchFn ( keys ) ,
83+ options . dataLoaderOptions
84+ ) ;
11685 loaders [ fieldName ] = loader ;
11786 return loader ;
11887 }
@@ -122,19 +91,96 @@ export function getLoader<K = any, V = any, C = K>(options: BatchDelegateOptions
12291 if ( loaders === undefined ) {
12392 loaders = Object . create ( null ) ;
12493 cache2 . set ( options . schema , loaders ) ;
125- const batchFn = createBatchFn ( options ) ;
126- const loader = new DataLoader < K , V , C > ( keys => batchFn ( keys ) , options . dataLoaderOptions ) ;
94+ const batchFn = createBatchFn ( options , request , delegationContext ) ;
95+ const loader = new DataLoader < K , ExecutionResult | AsyncIterableIterator < AsyncExecutionResult > , C > (
96+ keys => batchFn ( keys ) ,
97+ options . dataLoaderOptions
98+ ) ;
12799 loaders [ fieldName ] = loader ;
128100 return loader ;
129101 }
130102
131103 let loader = loaders [ fieldName ] ;
132104
133105 if ( loader === undefined ) {
134- const batchFn = createBatchFn ( options ) ;
135- loader = new DataLoader < K , V , C > ( keys => batchFn ( keys ) , options . dataLoaderOptions ) ;
106+ const batchFn = createBatchFn ( options , request , delegationContext ) ;
107+ loader = new DataLoader < K , ExecutionResult | AsyncIterableIterator < AsyncExecutionResult > , C > (
108+ keys => batchFn ( keys ) ,
109+ options . dataLoaderOptions
110+ ) ;
136111 loaders [ fieldName ] = loader ;
137112 }
138113
139114 return loader ;
140115}
116+
117+ function splitResult ( result : ExecutionResult , fieldName : string , numItems : number ) : Array < ExecutionResult > {
118+ const { data, errors } = result ;
119+ const fieldData = data ?. [ fieldName ] ;
120+
121+ if ( fieldData === undefined ) {
122+ if ( errors === undefined ) {
123+ return Array ( numItems ) . fill ( { } ) ;
124+ }
125+
126+ return Array ( numItems ) . fill ( { errors } ) ;
127+ }
128+
129+ return fieldData . map ( ( value : any ) => ( {
130+ data : {
131+ [ fieldName ] : value ,
132+ } ,
133+ errors,
134+ } ) ) ;
135+ }
136+
137+ function splitAsyncResult ( result : AsyncExecutionResult , fieldName : string ) : [ [ number , AsyncExecutionResult ] ] {
138+ const { data, errors, path } = result as ExecutionPatchResult ;
139+
140+ if ( path === undefined || path . length === 0 ) {
141+ const fieldData = data ?. [ fieldName ] ;
142+ if ( fieldData !== undefined ) {
143+ return fieldData . map ( ( value : any , index : number ) => [
144+ index ,
145+ {
146+ data : {
147+ [ fieldName ] : value ,
148+ } ,
149+ errors,
150+ } ,
151+ ] ) ;
152+ }
153+ } else if ( path [ 0 ] === fieldName ) {
154+ const index = path [ 1 ] as number ;
155+
156+ if ( path . length === 2 ) {
157+ return [
158+ [
159+ index ,
160+ {
161+ ...result ,
162+ data : {
163+ [ fieldName ] : data ,
164+ } ,
165+ errors,
166+ } ,
167+ ] ,
168+ ] ;
169+ }
170+
171+ const newPath = [ fieldName , ...path . slice ( 2 ) ] ;
172+ return [
173+ [
174+ index ,
175+ {
176+ ...result ,
177+ data,
178+ errors,
179+ path : newPath ,
180+ } ,
181+ ] ,
182+ ] ;
183+ }
184+
185+ return [ [ undefined , result ] ] ;
186+ }
0 commit comments