Skip to content

Commit 2a383bc

Browse files
committed
mutable => immutable
= addResolversToSchema and other decorators = addMocksToSchema
1 parent 55f4742 commit 2a383bc

26 files changed

+460
-392
lines changed

packages/merge/src/merge-schemas.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,11 @@ function makeSchema(
7474
}: { resolvers: IResolvers; typeDefs: string | DocumentNode; extensions: SchemaExtensions },
7575
config: MergeSchemasConfig
7676
) {
77-
const schema = typeof typeDefs === 'string' ? buildSchema(typeDefs, config) : buildASTSchema(typeDefs, config);
77+
let schema = typeof typeDefs === 'string' ? buildSchema(typeDefs, config) : buildASTSchema(typeDefs, config);
7878

7979
// add resolvers
8080
if (resolvers) {
81-
addResolversToSchema({
81+
schema = addResolversToSchema({
8282
schema,
8383
resolvers,
8484
resolverValidationOptions: {
@@ -90,7 +90,7 @@ function makeSchema(
9090

9191
// use logger
9292
if (config.logger) {
93-
addErrorLoggingToSchema(schema, config.logger);
93+
schema = addErrorLoggingToSchema(schema, config.logger);
9494
}
9595

9696
// use schema directives

packages/mock/src/mocking.ts

Lines changed: 78 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,25 @@ import {
44
GraphQLObjectType,
55
GraphQLList,
66
GraphQLType,
7-
GraphQLField,
87
GraphQLResolveInfo,
98
getNullableType,
109
getNamedType,
11-
GraphQLNamedType,
1210
GraphQLFieldResolver,
1311
GraphQLNullableType,
1412
isSchema,
1513
isObjectType,
16-
isUnionType,
1714
isInterfaceType,
1815
isListType,
1916
isEnumType,
2017
isAbstractType,
18+
GraphQLInterfaceType,
19+
GraphQLUnionType,
20+
GraphQLTypeResolver,
2121
} from 'graphql';
2222

2323
import { buildSchemaFromTypeDefinitions } from '@graphql-tools/schema';
2424
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';
2626

2727
/**
2828
* This function wraps addMocksToSchema for more convenience
@@ -40,7 +40,7 @@ export function mockServer(
4040
mySchema = schema;
4141
}
4242

43-
addMocksToSchema({ schema: mySchema, mocks, preserveResolvers });
43+
mySchema = addMocksToSchema({ schema: mySchema, mocks, preserveResolvers });
4444

4545
return { query: (query, vars) => graphql(mySchema, query, {}, {}, vars) };
4646
}
@@ -64,7 +64,7 @@ defaultMockMap.set('ID', () => uuidv4());
6464
// TODO allow providing a seed such that lengths of list could be deterministic
6565
// this could be done by using casual to get a random list length if the casual
6666
// object is global.
67-
export function addMocksToSchema({ schema, mocks = {}, preserveResolvers = false }: IMockOptions): void {
67+
export function addMocksToSchema({ schema, mocks = {}, preserveResolvers = false }: IMockOptions): GraphQLSchema {
6868
if (!schema) {
6969
throw new Error('Must provide schema to mock');
7070
}
@@ -181,43 +181,75 @@ export function addMocksToSchema({ schema, mocks = {}, preserveResolvers = false
181181
};
182182
};
183183

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+
}
211242
}
212243
}
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 => {
221253
const [mockedValue, resolvedValue] = values;
222254

223255
// In case we couldn't mock
@@ -240,9 +272,11 @@ export function addMocksToSchema({ schema, mocks = {}, preserveResolvers = false
240272
return copyOwnProps(emptyObject, resolvedValue, mockedValue);
241273
}
242274
return undefined !== resolvedValue ? resolvedValue : mockedValue;
243-
}
244-
);
245-
}
275+
});
276+
}
277+
278+
return newFieldConfig;
279+
},
246280
});
247281
}
248282

@@ -294,29 +328,6 @@ function mergeMocks(genericMockFunction: () => any, customMock: any): any {
294328
return customMock;
295329
}
296330

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-
320331
export function isMockList(obj: any): obj is MockList {
321332
if (typeof obj?.len === 'number' || (Array.isArray(obj?.len) && typeof obj?.len[0] === 'number')) {
322333
if (typeof obj.wrappedFunction === 'undefined' || typeof obj.wrappedFunction === 'function') {
@@ -377,9 +388,3 @@ export class MockList {
377388
return Math.floor(Math.random() * (high - low + 1) + low);
378389
}
379390
}
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

Comments
 (0)