Add support for resolveObject to query execution

This commit is contained in:
Martijn Walraven 2018-11-07 03:30:25 -08:00
parent 3413c59ace
commit bb67584a22
3 changed files with 63 additions and 8 deletions

14
package-lock.json generated
View file

@ -3,6 +3,9 @@
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"@apollographql/apollo-tools": {
"version": "file:../apollo-tooling/packages/apollo-tools"
},
"@apollographql/apollo-upload-server": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/@apollographql/apollo-upload-server/-/apollo-upload-server-5.0.3.tgz",
@ -6080,12 +6083,17 @@
"dev": true
},
"graphql": {
"version": "14.0.2",
"resolved": "https://registry.npmjs.org/graphql/-/graphql-14.0.2.tgz",
"integrity": "sha512-gUC4YYsaiSJT1h40krG3J+USGlwhzNTXSb4IOZljn9ag5Tj+RkoXrWp+Kh7WyE3t1NCfab5kzCuxBIvOMERMXw==",
"version": "file:../graphql-js/dist",
"dev": true,
"requires": {
"iterall": "^1.2.2"
},
"dependencies": {
"iterall": {
"version": "1.2.2",
"bundled": true,
"dev": true
}
}
},
"graphql-extensions": {

View file

@ -34,6 +34,7 @@
"node": ">=6"
},
"dependencies": {
"@apollographql/apollo-tools": "file:../apollo-tooling/packages/apollo-tools",
"apollo-cache-control": "file:packages/apollo-cache-control",
"apollo-datasource": "file:packages/apollo-datasource",
"apollo-datasource-rest": "file:packages/apollo-datasource-rest",
@ -94,7 +95,7 @@
"express": "4.16.4",
"fibers": "3.0.0",
"form-data": "2.3.3",
"graphql": "14.0.2",
"graphql": "file:../graphql-js/dist",
"graphql-subscriptions": "1.0.0",
"graphql-tag": "2.10.0",
"graphql-tools": "4.0.3",

View file

@ -8,6 +8,8 @@ import {
GraphQLResolveInfo,
ExecutionArgs,
DocumentNode,
ResponsePath,
FieldNode,
} from 'graphql';
import { Request } from 'apollo-server-env';
@ -19,6 +21,8 @@ import {
} from 'apollo-server-core/dist/requestPipelineAPI';
export { GraphQLResponse };
import { GraphQLObjectResolver } from '@apollographql/apollo-tools';
export type EndHandler = (...errors: Array<Error>) => void;
// A StartHandlerInvoker is a function that, given a specific GraphQLExtension,
// finds a specific StartHandler on that extension and calls it with appropriate
@ -199,6 +203,15 @@ function wrapField(field: GraphQLField<any, any>): void {
const fieldResolver = field.resolve;
field.resolve = (source, args, context, info) => {
// This is a bit of a hack, but since `ResponsePath` is a linked list,
// a new object gets created every time a path segment is added.
// So we can use that to share our `whenObjectResolved` promise across
// all field resolvers for the same object.
const parentPath = info.path.prev as ResponsePath & {
__fields: Record<string, ReadonlyArray<FieldNode>>;
__whenObjectResolved: Promise<any>;
};
const extensionStack = context && context._extensionStack;
const handler =
(extensionStack &&
@ -207,12 +220,45 @@ function wrapField(field: GraphQLField<any, any>): void {
/* do nothing */
});
// If no resolver has been defined for a field, use the default field resolver
// (which matches the behavior of graphql-js when there is no explicit resolve function defined).
const resolveObject: GraphQLObjectResolver<
any,
any
> = (info.parentType as any).resolveObject;
let whenObjectResolved: Promise<any> | undefined;
if (parentPath && resolveObject) {
whenObjectResolved = parentPath.__whenObjectResolved;
if (!whenObjectResolved) {
whenObjectResolved = (async () => {
return resolveObject(source, context, info);
})();
parentPath.__whenObjectResolved = whenObjectResolved;
}
}
try {
const result = (fieldResolver ||
// If no resolver has been defined for a field, use either the configured
// field resolver or the default field resolver
// (which matches the behavior of graphql-js when there is no explicit
// resolve function defined).
// XXX: Can't this be pulled up to the top of `wrapField` and only
// assigned once? It seems `extensionStack.fieldResolver` isn't set
// anywhere?
const actualFieldResolver =
fieldResolver ||
(extensionStack && extensionStack.fieldResolver) ||
defaultFieldResolver)(source, args, context, info);
defaultFieldResolver;
let result: any;
if (whenObjectResolved) {
result = whenObjectResolved.then((resolvedObject: any) => {
return actualFieldResolver(resolvedObject, args, context, info);
});
} else {
result = actualFieldResolver(source, args, context, info);
}
// Call the stack's handlers either immediately (if result is not a
// Promise) or once the Promise is done. Then return that same
// maybe-Promise value.