Skip to content

Commit bb67584

Browse files
Add support for resolveObject to query execution
1 parent 3413c59 commit bb67584

3 files changed

Lines changed: 63 additions & 8 deletions

File tree

package-lock.json

Lines changed: 11 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"node": ">=6"
3535
},
3636
"dependencies": {
37+
"@apollographql/apollo-tools": "file:../apollo-tooling/packages/apollo-tools",
3738
"apollo-cache-control": "file:packages/apollo-cache-control",
3839
"apollo-datasource": "file:packages/apollo-datasource",
3940
"apollo-datasource-rest": "file:packages/apollo-datasource-rest",
@@ -94,7 +95,7 @@
9495
"express": "4.16.4",
9596
"fibers": "3.0.0",
9697
"form-data": "2.3.3",
97-
"graphql": "14.0.2",
98+
"graphql": "file:../graphql-js/dist",
9899
"graphql-subscriptions": "1.0.0",
99100
"graphql-tag": "2.10.0",
100101
"graphql-tools": "4.0.3",

packages/graphql-extensions/src/index.ts

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import {
88
GraphQLResolveInfo,
99
ExecutionArgs,
1010
DocumentNode,
11+
ResponsePath,
12+
FieldNode,
1113
} from 'graphql';
1214

1315
import { Request } from 'apollo-server-env';
@@ -19,6 +21,8 @@ import {
1921
} from 'apollo-server-core/dist/requestPipelineAPI';
2022
export { GraphQLResponse };
2123

24+
import { GraphQLObjectResolver } from '@apollographql/apollo-tools';
25+
2226
export type EndHandler = (...errors: Array<Error>) => void;
2327
// A StartHandlerInvoker is a function that, given a specific GraphQLExtension,
2428
// finds a specific StartHandler on that extension and calls it with appropriate
@@ -199,6 +203,15 @@ function wrapField(field: GraphQLField<any, any>): void {
199203
const fieldResolver = field.resolve;
200204

201205
field.resolve = (source, args, context, info) => {
206+
// This is a bit of a hack, but since `ResponsePath` is a linked list,
207+
// a new object gets created every time a path segment is added.
208+
// So we can use that to share our `whenObjectResolved` promise across
209+
// all field resolvers for the same object.
210+
const parentPath = info.path.prev as ResponsePath & {
211+
__fields: Record<string, ReadonlyArray<FieldNode>>;
212+
__whenObjectResolved: Promise<any>;
213+
};
214+
202215
const extensionStack = context && context._extensionStack;
203216
const handler =
204217
(extensionStack &&
@@ -207,12 +220,45 @@ function wrapField(field: GraphQLField<any, any>): void {
207220
/* do nothing */
208221
});
209222

210-
// If no resolver has been defined for a field, use the default field resolver
211-
// (which matches the behavior of graphql-js when there is no explicit resolve function defined).
223+
const resolveObject: GraphQLObjectResolver<
224+
any,
225+
any
226+
> = (info.parentType as any).resolveObject;
227+
228+
let whenObjectResolved: Promise<any> | undefined;
229+
230+
if (parentPath && resolveObject) {
231+
whenObjectResolved = parentPath.__whenObjectResolved;
232+
if (!whenObjectResolved) {
233+
whenObjectResolved = (async () => {
234+
return resolveObject(source, context, info);
235+
})();
236+
parentPath.__whenObjectResolved = whenObjectResolved;
237+
}
238+
}
239+
212240
try {
213-
const result = (fieldResolver ||
241+
// If no resolver has been defined for a field, use either the configured
242+
// field resolver or the default field resolver
243+
// (which matches the behavior of graphql-js when there is no explicit
244+
// resolve function defined).
245+
// XXX: Can't this be pulled up to the top of `wrapField` and only
246+
// assigned once? It seems `extensionStack.fieldResolver` isn't set
247+
// anywhere?
248+
const actualFieldResolver =
249+
fieldResolver ||
214250
(extensionStack && extensionStack.fieldResolver) ||
215-
defaultFieldResolver)(source, args, context, info);
251+
defaultFieldResolver;
252+
253+
let result: any;
254+
if (whenObjectResolved) {
255+
result = whenObjectResolved.then((resolvedObject: any) => {
256+
return actualFieldResolver(resolvedObject, args, context, info);
257+
});
258+
} else {
259+
result = actualFieldResolver(source, args, context, info);
260+
}
261+
216262
// Call the stack's handlers either immediately (if result is not a
217263
// Promise) or once the Promise is done. Then return that same
218264
// maybe-Promise value.

0 commit comments

Comments
 (0)