Skip to content

Commit

Permalink
promisify isTypeOf and resolveType
Browse files Browse the repository at this point in the history
  • Loading branch information
dherault committed Dec 15, 2016
1 parent ae33a3c commit 655ead9
Showing 1 changed file with 91 additions and 60 deletions.
151 changes: 91 additions & 60 deletions src/execution/execute.js
Original file line number Diff line number Diff line change
Expand Up @@ -940,40 +940,53 @@ function completeAbstractValue(
path: ResponsePath,
result: mixed
): mixed {
let runtimeType = returnType.resolveType ?
returnType.resolveType(result, exeContext.contextValue, info) :
defaultResolveTypeFn(result, exeContext.contextValue, info, returnType);
const runtimeTypePromise = returnType.resolveType ?
Promise.resolve(returnType.resolveType(
result,
exeContext.contextValue,
info
)) :
resolveFirstValidType(
result,
exeContext.contextValue,
info,
info.schema.getPossibleTypes(returnType)
);

// If resolveType returns a string, we assume it's a GraphQLObjectType name.
if (typeof runtimeType === 'string') {
runtimeType = exeContext.schema.getType(runtimeType);
}
return runtimeTypePromise.then(type => {
let runtimeType = type;

if (!(runtimeType instanceof GraphQLObjectType)) {
throw new GraphQLError(
`Abstract type ${returnType.name} must resolve to an Object type at ` +
`runtime for field ${info.parentType.name}.${info.fieldName} with ` +
`value "${String(result)}", received "${String(runtimeType)}".`,
fieldNodes
);
}
// If resolveType returns a string, we assume it's a GraphQLObjectType name.
if (typeof runtimeType === 'string') {
runtimeType = exeContext.schema.getType(runtimeType);
}

if (!exeContext.schema.isPossibleType(returnType, runtimeType)) {
throw new GraphQLError(
`Runtime Object type "${runtimeType.name}" is not a possible type ` +
`for "${returnType.name}".`,
fieldNodes
);
}
if (!(runtimeType instanceof GraphQLObjectType)) {
throw new GraphQLError(
`Abstract type ${returnType.name} must resolve to an Object type at ` +
`runtime for field ${info.parentType.name}.${info.fieldName} with ` +
`value "${String(result)}", received "${String(runtimeType)}".`,
fieldNodes
);
}

return completeObjectValue(
exeContext,
runtimeType,
fieldNodes,
info,
path,
result
);
if (!exeContext.schema.isPossibleType(returnType, runtimeType)) {
throw new GraphQLError(
`Runtime Object type "${runtimeType.name}" is not a possible type ` +
`for "${returnType.name}".`,
fieldNodes
);
}

return completeObjectValue(
exeContext,
runtimeType,
fieldNodes,
info,
path,
result
);
});
}

/**
Expand All @@ -990,51 +1003,69 @@ function completeObjectValue(
// If there is an isTypeOf predicate function, call it with the
// current result. If isTypeOf returns false, then raise an error rather
// than continuing execution.
if (returnType.isTypeOf &&
!returnType.isTypeOf(result, exeContext.contextValue, info)) {
throw new GraphQLError(
`Expected value of type "${returnType.name}" but got: ${String(result)}.`,
fieldNodes
);
}

// Collect sub-fields to execute to complete this value.
let subFieldNodes = Object.create(null);
const visitedFragmentNames = Object.create(null);
for (let i = 0; i < fieldNodes.length; i++) {
const selectionSet = fieldNodes[i].selectionSet;
if (selectionSet) {
subFieldNodes = collectFields(
exeContext,
returnType,
selectionSet,
subFieldNodes,
visitedFragmentNames
return Promise.resolve(returnType.isTypeOf ?
returnType.isTypeOf(result, exeContext.contextValue, info) :
true
)
.then(isTypeOfResult => {
if (!isTypeOfResult) {
throw new GraphQLError(
`Expected value of type "${returnType.name}" ` +
`but got: ${String(result)}.`,
fieldNodes
);
}
}

return executeFields(exeContext, returnType, result, path, subFieldNodes);
// Collect sub-fields to execute to complete this value.
let subFieldNodes = Object.create(null);
const visitedFragmentNames = Object.create(null);
for (let i = 0; i < fieldNodes.length; i++) {
const selectionSet = fieldNodes[i].selectionSet;
if (selectionSet) {
subFieldNodes = collectFields(
exeContext,
returnType,
selectionSet,
subFieldNodes,
visitedFragmentNames
);
}
}

return executeFields(exeContext, returnType, result, path, subFieldNodes);
});
}

/**
* If a resolveType function is not given, then a default resolve behavior is
* used which tests each possible type for the abstract type by calling
* isTypeOf for the object being coerced, returning the first type that matches.
*/
function defaultResolveTypeFn(
function resolveFirstValidType(
value: mixed,
context: mixed,
info: GraphQLResolveInfo,
abstractType: GraphQLAbstractType
): ?GraphQLObjectType {
const possibleTypes = info.schema.getPossibleTypes(abstractType);
for (let i = 0; i < possibleTypes.length; i++) {
const type = possibleTypes[i];
if (type.isTypeOf && type.isTypeOf(value, context, info)) {
possibleTypes: Array<GraphQLObjectType>,
i: number = 0,
): Promise<?GraphQLObjectType | ?string> {
if (i >= possibleTypes.length) {
return Promise.resolve(null);
}

const type = possibleTypes[i];

if (!type.isTypeOf) {
return resolveFirstValidType(value, context, info, possibleTypes, i + 1);
}

return Promise.resolve(type.isTypeOf(value, context, info))
.then(result => {
if (result) {
return type;
}
}

return resolveFirstValidType(value, context, info, possibleTypes, i + 1);
});
}

/**
Expand Down

0 comments on commit 655ead9

Please sign in to comment.