@@ -4,25 +4,25 @@ import {
4
4
GraphQLObjectType ,
5
5
GraphQLList ,
6
6
GraphQLType ,
7
- GraphQLField ,
8
7
GraphQLResolveInfo ,
9
8
getNullableType ,
10
9
getNamedType ,
11
- GraphQLNamedType ,
12
10
GraphQLFieldResolver ,
13
11
GraphQLNullableType ,
14
12
isSchema ,
15
13
isObjectType ,
16
- isUnionType ,
17
14
isInterfaceType ,
18
15
isListType ,
19
16
isEnumType ,
20
17
isAbstractType ,
18
+ GraphQLInterfaceType ,
19
+ GraphQLUnionType ,
20
+ GraphQLTypeResolver ,
21
21
} from 'graphql' ;
22
22
23
23
import { buildSchemaFromTypeDefinitions } from '@graphql-tools/schema' ;
24
24
import { IMocks , IMockServer , IMockFn , IMockOptions , IMockTypeFn } from './types' ;
25
- import { forEachField , ITypeDefinitions } from '@graphql-tools/utils' ;
25
+ import { ITypeDefinitions , mapSchema , MapperKind } from '@graphql-tools/utils' ;
26
26
27
27
/**
28
28
* This function wraps addMocksToSchema for more convenience
@@ -40,7 +40,7 @@ export function mockServer(
40
40
mySchema = schema ;
41
41
}
42
42
43
- addMocksToSchema ( { schema : mySchema , mocks, preserveResolvers } ) ;
43
+ mySchema = addMocksToSchema ( { schema : mySchema , mocks, preserveResolvers } ) ;
44
44
45
45
return { query : ( query , vars ) => graphql ( mySchema , query , { } , { } , vars ) } ;
46
46
}
@@ -64,7 +64,7 @@ defaultMockMap.set('ID', () => uuidv4());
64
64
// TODO allow providing a seed such that lengths of list could be deterministic
65
65
// this could be done by using casual to get a random list length if the casual
66
66
// object is global.
67
- export function addMocksToSchema ( { schema, mocks = { } , preserveResolvers = false } : IMockOptions ) : void {
67
+ export function addMocksToSchema ( { schema, mocks = { } , preserveResolvers = false } : IMockOptions ) : GraphQLSchema {
68
68
if ( ! schema ) {
69
69
throw new Error ( 'Must provide schema to mock' ) ;
70
70
}
@@ -181,43 +181,75 @@ export function addMocksToSchema({ schema, mocks = {}, preserveResolvers = false
181
181
} ;
182
182
} ;
183
183
184
- forEachField ( schema , ( field : GraphQLField < any , any > , typeName : string , fieldName : string ) => {
185
- assignResolveType ( field . type , preserveResolvers ) ;
186
- let mockResolver : GraphQLFieldResolver < any , any > = mockType ( field . type , typeName , fieldName ) ;
187
-
188
- // we have to handle the root mutation and root query types differently,
189
- // because no resolver is called at the root.
190
- const queryType = schema . getQueryType ( ) ;
191
- const isOnQueryType = queryType != null && queryType . name === typeName ;
192
-
193
- const mutationType = schema . getMutationType ( ) ;
194
- const isOnMutationType = mutationType != null && mutationType . name === typeName ;
195
-
196
- if ( isOnQueryType || isOnMutationType ) {
197
- if ( mockFunctionMap . has ( typeName ) ) {
198
- const rootMock = mockFunctionMap . get ( typeName ) ;
199
- // XXX: BUG in here, need to provide proper signature for rootMock.
200
- if ( typeof rootMock ( undefined , { } , { } , { } as any ) [ fieldName ] === 'function' ) {
201
- mockResolver = ( root : any , args : Record < string , any > , context : any , info : GraphQLResolveInfo ) => {
202
- const updatedRoot = root ?? { } ; // TODO: should we clone instead?
203
- updatedRoot [ fieldName ] = rootMock ( root , args , context , info ) [ fieldName ] ;
204
- // XXX this is a bit of a hack to still use mockType, which
205
- // lets you mock lists etc. as well
206
- // otherwise we could just set field.resolve to rootMock()[fieldName]
207
- // it's like pretending there was a resolver that ran before
208
- // the root resolver.
209
- return mockType ( field . type , typeName , fieldName ) ( updatedRoot , args , context , info ) ;
210
- } ;
184
+ return mapSchema ( schema , {
185
+ [ MapperKind . ABSTRACT_TYPE ] : type => {
186
+ const oldResolveType = type . resolveType ;
187
+ if ( preserveResolvers && oldResolveType != null && oldResolveType . length ) {
188
+ return ;
189
+ }
190
+
191
+ // the default `resolveType` always returns null. We add a fallback
192
+ // resolution that works with how unions and interface are mocked
193
+ const resolveType : GraphQLTypeResolver < any , any > = ( data , _context , info : GraphQLResolveInfo ) =>
194
+ info . schema . getType ( data . __typename ) as GraphQLObjectType ;
195
+
196
+ if ( isInterfaceType ( type ) ) {
197
+ return new GraphQLInterfaceType ( {
198
+ ...type . toConfig ( ) ,
199
+ resolveType,
200
+ } ) ;
201
+ } else {
202
+ return new GraphQLUnionType ( {
203
+ ...type . toConfig ( ) ,
204
+ resolveType,
205
+ } ) ;
206
+ }
207
+ } ,
208
+ [ MapperKind . OBJECT_FIELD ] : ( fieldConfig , fieldName , type ) => {
209
+ const typeName = type . name ;
210
+ const fieldType = fieldConfig . type ;
211
+ const fieldResolver = fieldConfig . resolve ;
212
+ const newFieldConfig = {
213
+ ...fieldConfig ,
214
+ } ;
215
+
216
+ let mockResolver : GraphQLFieldResolver < any , any > = mockType ( fieldType , typeName , fieldName ) ;
217
+
218
+ // we have to handle the root mutation and root query types differently,
219
+ // because no resolver is called at the root.
220
+ const queryType = schema . getQueryType ( ) ;
221
+ const isOnQueryType = queryType != null && queryType . name === typeName ;
222
+
223
+ const mutationType = schema . getMutationType ( ) ;
224
+ const isOnMutationType = mutationType != null && mutationType . name === typeName ;
225
+
226
+ if ( isOnQueryType || isOnMutationType ) {
227
+ if ( mockFunctionMap . has ( typeName ) ) {
228
+ const rootMock = mockFunctionMap . get ( typeName ) ;
229
+ // XXX: BUG in here, need to provide proper signature for rootMock.
230
+ if ( typeof rootMock ( undefined , { } , { } , { } as any ) [ fieldName ] === 'function' ) {
231
+ mockResolver = ( root : any , args : Record < string , any > , context : any , info : GraphQLResolveInfo ) => {
232
+ const updatedRoot = root ?? { } ; // TODO: should we clone instead?
233
+ updatedRoot [ fieldName ] = rootMock ( root , args , context , info ) [ fieldName ] ;
234
+ // XXX this is a bit of a hack to still use mockType, which
235
+ // lets you mock lists etc. as well
236
+ // otherwise we could just set field.resolve to rootMock()[fieldName]
237
+ // it's like pretending there was a resolver that ran before
238
+ // the root resolver.
239
+ return mockType ( fieldConfig . type , typeName , fieldName ) ( updatedRoot , args , context , info ) ;
240
+ } ;
241
+ }
211
242
}
212
243
}
213
- }
214
- if ( ! preserveResolvers || ! field . resolve ) {
215
- field . resolve = mockResolver ;
216
- } else {
217
- const oldResolver = field . resolve ;
218
- field . resolve = ( rootObject : any , args : Record < string , any > , context : any , info : GraphQLResolveInfo ) =>
219
- Promise . all ( [ mockResolver ( rootObject , args , context , info ) , oldResolver ( rootObject , args , context , info ) ] ) . then (
220
- values => {
244
+ if ( ! preserveResolvers || ! fieldResolver ) {
245
+ newFieldConfig . resolve = mockResolver ;
246
+ } else {
247
+ const oldResolver = fieldResolver ;
248
+ newFieldConfig . resolve = ( rootObject : any , args : Record < string , any > , context : any , info : GraphQLResolveInfo ) =>
249
+ Promise . all ( [
250
+ mockResolver ( rootObject , args , context , info ) ,
251
+ oldResolver ( rootObject , args , context , info ) ,
252
+ ] ) . then ( values => {
221
253
const [ mockedValue , resolvedValue ] = values ;
222
254
223
255
// In case we couldn't mock
@@ -240,9 +272,11 @@ export function addMocksToSchema({ schema, mocks = {}, preserveResolvers = false
240
272
return copyOwnProps ( emptyObject , resolvedValue , mockedValue ) ;
241
273
}
242
274
return undefined !== resolvedValue ? resolvedValue : mockedValue ;
243
- }
244
- ) ;
245
- }
275
+ } ) ;
276
+ }
277
+
278
+ return newFieldConfig ;
279
+ } ,
246
280
} ) ;
247
281
}
248
282
@@ -294,29 +328,6 @@ function mergeMocks(genericMockFunction: () => any, customMock: any): any {
294
328
return customMock ;
295
329
}
296
330
297
- function getResolveType ( namedFieldType : GraphQLNamedType ) {
298
- if ( isAbstractType ( namedFieldType ) ) {
299
- return namedFieldType . resolveType ;
300
- }
301
- }
302
-
303
- function assignResolveType ( type : GraphQLType , preserveResolvers : boolean ) {
304
- const fieldType = getNullableType ( type ) as GraphQLNullableType ;
305
- const namedFieldType = getNamedType ( fieldType ) ;
306
-
307
- const oldResolveType = getResolveType ( namedFieldType ) ;
308
- if ( preserveResolvers && oldResolveType != null && oldResolveType . length ) {
309
- return ;
310
- }
311
-
312
- if ( isInterfaceType ( namedFieldType ) || isUnionType ( namedFieldType ) ) {
313
- // the default `resolveType` always returns null. We add a fallback
314
- // resolution that works with how unions and interface are mocked
315
- namedFieldType . resolveType = ( data : any , _context : any , info : GraphQLResolveInfo ) =>
316
- info . schema . getType ( data . __typename ) as GraphQLObjectType ;
317
- }
318
- }
319
-
320
331
export function isMockList ( obj : any ) : obj is MockList {
321
332
if ( typeof obj ?. len === 'number' || ( Array . isArray ( obj ?. len ) && typeof obj ?. len [ 0 ] === 'number' ) ) {
322
333
if ( typeof obj . wrappedFunction === 'undefined' || typeof obj . wrappedFunction === 'function' ) {
@@ -377,9 +388,3 @@ export class MockList {
377
388
return Math . floor ( Math . random ( ) * ( high - low + 1 ) + low ) ;
378
389
}
379
390
}
380
-
381
- // retain addMockFunctionsToSchema for backwards compatibility
382
-
383
- export function addMockFunctionsToSchema ( { schema, mocks = { } , preserveResolvers = false } : IMockOptions ) : void {
384
- addMocksToSchema ( { schema, mocks, preserveResolvers } ) ;
385
- }
0 commit comments