Skip to content

Commit 2cb8769

Browse files
committed
Enables support for resolver extensions for compatibility with grafast, complexity, etc.
1 parent ad1c323 commit 2cb8769

File tree

2 files changed

+65
-1
lines changed

2 files changed

+65
-1
lines changed

packages/graphql-modules/src/module/resolvers.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,18 @@ export function createResolvers(
9292
resolvers[typeName][fieldName].resolve = resolver;
9393
}
9494

95+
if (isDefined((obj[fieldName] as any).extensions)) {
96+
// some extensions allow to omit the resolve function, e.g. grafast
97+
const defaultResolver = (val: any) => val;
98+
const resolver = wrapResolver({
99+
config,
100+
resolver: (obj[fieldName] as any).resolve || defaultResolver,
101+
middlewareMap,
102+
path,
103+
});
104+
resolvers[typeName][fieldName].resolve = resolver;
105+
}
106+
95107
// { subscribe }
96108
if (isDefined((obj[fieldName] as any).subscribe)) {
97109
const resolver = wrapResolver({
@@ -286,6 +298,21 @@ function addObject({
286298
container[typeName][fieldName].resolve = resolver.resolve;
287299
}
288300

301+
// extensions
302+
if (isDefined(resolver.extensions)) {
303+
if (container[typeName][fieldName].extensions) {
304+
throw new ResolverDuplicatedError(
305+
`Duplicated resolver of "${typeName}.${fieldName}" (extensions method)`,
306+
useLocation({ dirname: config.dirname, id: config.id })
307+
);
308+
}
309+
310+
(resolver.extensions as any)[resolverMetadataProp] = {
311+
moduleId: config.id,
312+
} as ResolverMetadata;
313+
container[typeName][fieldName].extensions = resolver.extensions;
314+
}
315+
289316
// subscribe
290317
if (isDefined(resolver.subscribe)) {
291318
if (container[typeName][fieldName].subscribe) {
@@ -501,10 +528,15 @@ function isResolveFn(value: any): value is ResolveFn {
501528
interface ResolveOptions {
502529
resolve?: ResolveFn;
503530
subscribe?: ResolveFn;
531+
extensions?: Record<string, any>;
504532
}
505533

506534
function isResolveOptions(value: any): value is ResolveOptions {
507-
return isDefined(value.resolve) || isDefined(value.subscribe);
535+
return (
536+
isDefined(value.resolve) ||
537+
isDefined(value.subscribe) ||
538+
isDefined(value.extensions)
539+
);
508540
}
509541

510542
function isScalarResolver(obj: any): obj is GraphQLScalarType {

packages/graphql-modules/tests/bootstrap.spec.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'reflect-metadata';
22
import { makeExecutableSchema } from '@graphql-tools/schema';
33
import { createApplication, createModule, testkit, gql } from '../src';
44
import { NonDocumentNodeError } from '../src/shared/errors';
5+
import { resolve } from 'path';
56

67
test('fail when modules have non-unique ids', async () => {
78
const modFoo = createModule({
@@ -396,3 +397,34 @@ test('fail when modules have non-DocumentNode typeDefs', async () => {
396397
});
397398
}).toThrow(NonDocumentNodeError);
398399
});
400+
401+
test('should allow resolver extensions', async () => {
402+
const m1 = createModule({
403+
id: 'test',
404+
typeDefs: gql`
405+
type Query {
406+
dummy: String!
407+
}
408+
`,
409+
resolvers: {
410+
Query: {
411+
dummy: {
412+
resolve: () => '1',
413+
extensions: {
414+
test: 'test',
415+
},
416+
},
417+
},
418+
},
419+
});
420+
421+
const app = createApplication({
422+
modules: [m1],
423+
});
424+
425+
const schema = app.schema;
426+
expect(
427+
Object.keys(schema.getQueryType()?.getFields().dummy.extensions || {})
428+
.length
429+
).toBe(1);
430+
});

0 commit comments

Comments
 (0)