Skip to content

Commit

Permalink
allow type resolution with GraphQLObjectTypes (#117)
Browse files Browse the repository at this point in the history
...as opposed to just type names.

This reverts graphql#2905

This change was made upstream to support schema transformation functions like lexicographicallySortSchema which do not modify resolveType methods. Previously, a workaround was included that simply looks up the actual type from the type name returned by resolveType. In actuality, in my opinion, the schema transformation functions should natively or manually modify the resolveType function so it works correctly.

In any case, we restore the workaround here to preserve v14 support.
  • Loading branch information
yaacovCR committed Dec 20, 2021
1 parent 3132731 commit 0b6363a
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 22 deletions.
69 changes: 60 additions & 9 deletions src/execution/__tests__/abstract-test.ts
Expand Up @@ -562,7 +562,66 @@ describe('Execute: Handles execution of abstract types', () => {
});
});

it('resolveType on Interface yields useful error', () => {
it('resolve Interface type using GraphQLObjectType', async () => {
const schema = buildSchema(`
type Query {
pets: [Pet]
}
interface Pet {
name: String
}
type Cat implements Pet {
name: String
meows: Boolean
}
type Dog implements Pet {
name: String
woofs: Boolean
}
`);

const query = `
{
pets {
name
... on Cat {
meows
}
}
}
`;

const rootValue = {
pets: [
{
__typename: 'Cat',
name: 'Garfield',
meows: false,
},
],
};

// FIXME: workaround since we can't inject resolveType into SDL
// @ts-expect-error
assertInterfaceType(schema.getType('Pet')).resolveType = () =>
schema.getType('Cat');

expect(await executeQuery({ schema, query, rootValue })).to.deep.equal({
data: {
pets: [
{
name: 'Garfield',
meows: false,
},
],
},
});
});

it('resolve Interface type using __typename on source object', () => {
const schema = buildSchema(`
type Query {
pet: Pet
Expand Down Expand Up @@ -630,13 +689,5 @@ describe('Execute: Handles execution of abstract types', () => {
expectError({ forTypeName: undefined }).toEqual(
'Abstract type "Pet" must resolve to an Object type at runtime for field "Query.pet" with value { __typename: undefined }, received "[]".',
);

// FIXME: workaround since we can't inject resolveType into SDL
// @ts-expect-error
assertInterfaceType(schema.getType('Pet')).resolveType = () =>
schema.getType('Cat');
expectError({ forTypeName: undefined }).toEqual(
'Support for returning GraphQLObjectType from resolveType was removed in graphql-js@16.0.0 please return type name instead.',
);
});
});
20 changes: 7 additions & 13 deletions src/execution/executor.ts
Expand Up @@ -52,6 +52,7 @@ import {
isAbstractType,
isLeafType,
isListType,
isNamedType,
isNonNullType,
isObjectType,
} from '../type/definition';
Expand Down Expand Up @@ -1439,31 +1440,24 @@ export class Executor {
}

ensureValidRuntimeType(
runtimeTypeName: unknown,
runtimeTypeOrName: unknown,
exeContext: ExecutionContext,
returnType: GraphQLAbstractType,
fieldNodes: ReadonlyArray<FieldNode>,
info: GraphQLResolveInfo,
result: unknown,
): GraphQLObjectType {
if (runtimeTypeName == null) {
if (runtimeTypeOrName == null) {
throw new GraphQLError(
`Abstract type "${returnType.name}" must resolve to an Object type at runtime for field "${info.parentType.name}.${info.fieldName}". Either the "${returnType.name}" type should provide a "resolveType" function or each possible type should provide an "isTypeOf" function.`,
fieldNodes,
);
}

// releases before 16.0.0 supported returning `GraphQLObjectType` from `resolveType`
// TODO: remove in 17.0.0 release
if (
typeof runtimeTypeName === 'object' &&
runtimeTypeName &&
isObjectType(runtimeTypeName)
) {
throw new GraphQLError(
'Support for returning GraphQLObjectType from resolveType was removed in graphql-js@16.0.0 please return type name instead.',
);
}
const runtimeTypeName =
typeof runtimeTypeOrName === 'object' && isNamedType(runtimeTypeOrName)
? runtimeTypeOrName.name
: runtimeTypeOrName;

if (typeof runtimeTypeName !== 'string') {
throw new GraphQLError(
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Expand Up @@ -12,6 +12,7 @@ export {
isEnumType,
isInputObjectType,
isListType,
isNamedType,
isNonNullType,
isInputType,
isLeafType,
Expand Down
15 changes: 15 additions & 0 deletions src/type/definition.ts
Expand Up @@ -6,6 +6,7 @@ import type {
GraphQLInterfaceType,
GraphQLLeafType,
GraphQLList,
GraphQLNamedType,
GraphQLNonNull,
GraphQLNullableType,
GraphQLObjectType,
Expand Down Expand Up @@ -148,3 +149,17 @@ function _isWrappingType(type: { [key: string]: any }) {
export const isWrappingType = memoize1(_isWrappingType) as (type: {
[key: string]: any;
}) => type is GraphQLWrappingType;

function _isNamedType(type: { [key: string]: any }) {
return (
isScalarType(type) ||
isObjectType(type) ||
isInterfaceType(type) ||
isUnionType(type) ||
isEnumType(type) ||
isInputObjectType(type)
);
}
export const isNamedType = memoize1(_isNamedType) as (type: {
[key: string]: any;
}) => type is GraphQLNamedType;
1 change: 1 addition & 0 deletions src/type/index.ts
Expand Up @@ -14,6 +14,7 @@ export {
isEnumType,
isInputObjectType,
isListType,
isNamedType,
isNonNullType,
isInputType,
isLeafType,
Expand Down

0 comments on commit 0b6363a

Please sign in to comment.