Skip to content

Allow to override default type resolver #1332

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/execution/__tests__/executor-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { execute } from '../execute';
import { Kind, parse } from '../../language';
import {
GraphQLSchema,
GraphQLInterfaceType,
GraphQLObjectType,
GraphQLList,
GraphQLBoolean,
Expand Down Expand Up @@ -1029,4 +1030,47 @@ describe('Execute: Handles basic execution tasks', () => {
const result = execute({ schema, document, fieldResolver });
expect(result).to.deep.equal({ data: { foo: 'foo' } });
});

it('uses a custom type resolver', () => {
const document = parse('{ foo { bar } }');

const fooInterface = new GraphQLInterfaceType({
name: 'FooInterface',
fields: {
bar: { type: GraphQLString },
},
});

const fooObject = new GraphQLObjectType({
name: 'FooObject',
interfaces: [fooInterface],
fields: {
bar: { type: GraphQLString },
},
});

const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
foo: { type: fooInterface },
},
}),
types: [fooObject],
});

let possibleTypes;
function typeResolver(source, context, info, abstractType) {
// Resolver should be able to figure out all possible types on its own
possibleTypes = info.schema.getPossibleTypes(abstractType);

return 'FooObject';
}

const rootValue = { foo: { bar: 'bar' } };
const result = execute({ schema, document, rootValue, typeResolver });

expect(result).to.deep.equal({ data: { foo: { bar: 'bar' } } });
expect(possibleTypes).to.deep.equal([fooObject]);
});
});
31 changes: 21 additions & 10 deletions src/execution/execute.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import type {
GraphQLField,
GraphQLFieldResolver,
GraphQLResolveInfo,
GraphQLTypeResolver,
ResponsePath,
GraphQLList,
} from '../type/definition';
Expand Down Expand Up @@ -102,6 +103,7 @@ export type ExecutionContext = {|
operation: OperationDefinitionNode,
variableValues: { [variable: string]: mixed },
fieldResolver: GraphQLFieldResolver<any, any>,
typeResolver: GraphQLTypeResolver<any, any>,
errors: Array<GraphQLError>,
|};

Expand All @@ -124,6 +126,7 @@ export type ExecutionArgs = {|
variableValues?: ?{ [variable: string]: mixed },
operationName?: ?string,
fieldResolver?: ?GraphQLFieldResolver<any, any>,
typeResolver?: ?GraphQLTypeResolver<any, any>,
|};

/**
Expand Down Expand Up @@ -151,6 +154,7 @@ declare function execute(
variableValues?: ?{ [variable: string]: mixed },
operationName?: ?string,
fieldResolver?: ?GraphQLFieldResolver<any, any>,
typeResolver?: ?GraphQLTypeResolver<any, any>,
): MaybePromise<ExecutionResult>;
export function execute(
argsOrSchema,
Expand All @@ -160,6 +164,7 @@ export function execute(
variableValues,
operationName,
fieldResolver,
typeResolver,
) {
/* eslint-enable no-redeclare */
// Extract arguments from object args if provided.
Expand All @@ -172,6 +177,7 @@ export function execute(
argsOrSchema.variableValues,
argsOrSchema.operationName,
argsOrSchema.fieldResolver,
argsOrSchema.typeResolver,
)
: executeImpl(
argsOrSchema,
Expand All @@ -181,6 +187,7 @@ export function execute(
variableValues,
operationName,
fieldResolver,
typeResolver,
);
}

Expand All @@ -192,6 +199,7 @@ function executeImpl(
variableValues,
operationName,
fieldResolver,
typeResolver,
) {
// If arguments are missing or incorrect, throw an error.
assertValidExecutionArguments(schema, document, variableValues);
Expand All @@ -206,6 +214,7 @@ function executeImpl(
variableValues,
operationName,
fieldResolver,
typeResolver,
);

// Return early errors if execution context failed.
Expand Down Expand Up @@ -301,6 +310,7 @@ export function buildExecutionContext(
rawVariableValues: ?ObjMap<mixed>,
operationName: ?string,
fieldResolver: ?GraphQLFieldResolver<any, any>,
typeResolver?: ?GraphQLTypeResolver<any, any>,
): $ReadOnlyArray<GraphQLError> | ExecutionContext {
const errors: Array<GraphQLError> = [];
let operation: OperationDefinitionNode | void;
Expand Down Expand Up @@ -371,6 +381,7 @@ export function buildExecutionContext(
operation,
variableValues,
fieldResolver: fieldResolver || defaultFieldResolver,
typeResolver: typeResolver || defaultTypeResolver,
errors,
};
}
Expand Down Expand Up @@ -991,9 +1002,9 @@ function completeAbstractValue(
path: ResponsePath,
result: mixed,
): MaybePromise<ObjMap<mixed>> {
const runtimeType = returnType.resolveType
? returnType.resolveType(result, exeContext.contextValue, info)
: defaultResolveTypeFn(result, exeContext.contextValue, info, returnType);
const resolveTypeFn = returnType.resolveType || exeContext.typeResolver;
const contextValue = exeContext.contextValue;
const runtimeType = resolveTypeFn(result, contextValue, info, returnType);

if (isPromise(runtimeType)) {
return runtimeType.then(resolvedRuntimeType =>
Expand Down Expand Up @@ -1174,12 +1185,12 @@ function _collectSubfields(
* Otherwise, test each possible type for the abstract type by calling
* isTypeOf for the object being coerced, returning the first type that matches.
*/
function defaultResolveTypeFn(
value: mixed,
contextValue: mixed,
info: GraphQLResolveInfo,
abstractType: GraphQLAbstractType,
): MaybePromise<?GraphQLObjectType | string> {
export const defaultTypeResolver: GraphQLTypeResolver<any, *> = function(
value,
contextValue,
info,
abstractType,
) {
// First, look for `__typename`.
if (
value !== null &&
Expand Down Expand Up @@ -1216,7 +1227,7 @@ function defaultResolveTypeFn(
}
});
}
}
};

/**
* If a resolve function is not given, then a default resolve behavior is used
Expand Down
7 changes: 6 additions & 1 deletion src/execution/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@
* @flow strict
*/

export { execute, defaultFieldResolver, responsePathAsArray } from './execute';
export {
execute,
defaultFieldResolver,
defaultTypeResolver,
responsePathAsArray,
} from './execute';
export { getDirectiveValues } from './values';

export type { ExecutionArgs, ExecutionResult } from './execute';
16 changes: 15 additions & 1 deletion src/graphql.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import { validate } from './validation/validate';
import { execute } from './execution/execute';
import type { ObjMap } from './jsutils/ObjMap';
import type { Source } from './language/source';
import type { GraphQLFieldResolver } from './type/definition';
import type {
GraphQLFieldResolver,
GraphQLTypeResolver,
} from './type/definition';
import type { GraphQLSchema } from './type/schema';
import type { ExecutionResult } from './execution/execute';
import type { MaybePromise } from './jsutils/MaybePromise';
Expand Down Expand Up @@ -61,6 +64,7 @@ export type GraphQLArgs = {|
variableValues?: ?ObjMap<mixed>,
operationName?: ?string,
fieldResolver?: ?GraphQLFieldResolver<any, any>,
typeResolver?: ?GraphQLTypeResolver<any, any>,
|};
declare function graphql(GraphQLArgs, ..._: []): Promise<ExecutionResult>;
/* eslint-disable no-redeclare */
Expand All @@ -72,6 +76,7 @@ declare function graphql(
variableValues?: ?ObjMap<mixed>,
operationName?: ?string,
fieldResolver?: ?GraphQLFieldResolver<any, any>,
typeResolver?: ?GraphQLTypeResolver<any, any>,
): Promise<ExecutionResult>;
export function graphql(
argsOrSchema,
Expand All @@ -81,6 +86,7 @@ export function graphql(
variableValues,
operationName,
fieldResolver,
typeResolver,
) {
/* eslint-enable no-redeclare */
// Always return a Promise for a consistent API.
Expand All @@ -96,6 +102,7 @@ export function graphql(
argsOrSchema.variableValues,
argsOrSchema.operationName,
argsOrSchema.fieldResolver,
argsOrSchema.typeResolver,
)
: graphqlImpl(
argsOrSchema,
Expand All @@ -105,6 +112,7 @@ export function graphql(
variableValues,
operationName,
fieldResolver,
typeResolver,
),
),
);
Expand All @@ -126,6 +134,7 @@ declare function graphqlSync(
variableValues?: ?ObjMap<mixed>,
operationName?: ?string,
fieldResolver?: ?GraphQLFieldResolver<any, any>,
typeResolver?: ?GraphQLTypeResolver<any, any>,
): ExecutionResult;
export function graphqlSync(
argsOrSchema,
Expand All @@ -135,6 +144,7 @@ export function graphqlSync(
variableValues,
operationName,
fieldResolver,
typeResolver,
) {
/* eslint-enable no-redeclare */
// Extract arguments from object args if provided.
Expand All @@ -148,6 +158,7 @@ export function graphqlSync(
argsOrSchema.variableValues,
argsOrSchema.operationName,
argsOrSchema.fieldResolver,
argsOrSchema.typeResolver,
)
: graphqlImpl(
argsOrSchema,
Expand All @@ -157,6 +168,7 @@ export function graphqlSync(
variableValues,
operationName,
fieldResolver,
typeResolver,
);

// Assert that the execution was synchronous.
Expand All @@ -175,6 +187,7 @@ function graphqlImpl(
variableValues,
operationName,
fieldResolver,
typeResolver,
): MaybePromise<ExecutionResult> {
// Validate Schema
const schemaValidationErrors = validateSchema(schema);
Expand Down Expand Up @@ -205,5 +218,6 @@ function graphqlImpl(
variableValues,
operationName,
fieldResolver,
typeResolver,
);
}
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ export type {
export {
execute,
defaultFieldResolver,
defaultTypeResolver,
responsePathAsArray,
getDirectiveValues,
} from './execution';
Expand Down
1 change: 1 addition & 0 deletions src/type/definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,7 @@ export type GraphQLTypeResolver<TSource, TContext> = (
value: TSource,
context: TContext,
info: GraphQLResolveInfo,
abstractType: GraphQLAbstractType,
) => MaybePromise<?GraphQLObjectType | string>;

export type GraphQLIsTypeOfFn<TSource, TContext> = (
Expand Down