Skip to content
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

Extract check for possible extensions into a separate rule #1643

Merged
merged 1 commit into from
Jan 4, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
52 changes: 0 additions & 52 deletions src/utilities/__tests__/extendSchema-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1106,21 +1106,6 @@ describe('extendSchema', () => {
);
});

it('does not allow extending an unknown type', () => {
[
'extend scalar UnknownType @foo',
'extend type UnknownType @foo',
'extend interface UnknownType @foo',
'extend enum UnknownType @foo',
'extend union UnknownType @foo',
'extend input UnknownType @foo',
].forEach(sdl => {
expect(() => extendTestSchema(sdl)).to.throw(
'Cannot extend type "UnknownType" because it does not exist in the existing schema.',
);
});
});

it('maintains configuration of the original schema object', () => {
const testSchemaWithLegacyNames = new GraphQLSchema({
query: new GraphQLObjectType({
Expand Down Expand Up @@ -1163,43 +1148,6 @@ describe('extendSchema', () => {
});
});

it('does not allow extending a mismatch type', () => {
const typeSDL = `
extend type SomeInterface @foo
`;
expect(() => extendTestSchema(typeSDL)).to.throw(
'Cannot extend non-object type "SomeInterface".',
);

const interfaceSDL = `
extend interface Foo @foo
`;
expect(() => extendTestSchema(interfaceSDL)).to.throw(
'Cannot extend non-interface type "Foo".',
);

const enumSDL = `
extend enum Foo @foo
`;
expect(() => extendTestSchema(enumSDL)).to.throw(
'Cannot extend non-enum type "Foo".',
);

const unionSDL = `
extend union Foo @foo
`;
expect(() => extendTestSchema(unionSDL)).to.throw(
'Cannot extend non-union type "Foo".',
);

const inputSDL = `
extend input Foo @foo
`;
expect(() => extendTestSchema(inputSDL)).to.throw(
'Cannot extend non-input object type "Foo".',
);
});

describe('can add additional root operation types', () => {
it('does not automatically include common root type names', () => {
const schema = extendTestSchema(`
Expand Down
56 changes: 0 additions & 56 deletions src/utilities/extendSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import keyValMap from '../jsutils/keyValMap';
import objectValues from '../jsutils/objectValues';
import { ASTDefinitionBuilder } from './buildASTSchema';
import { assertValidSDLExtension } from '../validation/validate';
import { GraphQLError } from '../error/GraphQLError';
import { assertSchema, GraphQLSchema } from '../type/schema';
import { isIntrospectionType } from '../type/introspection';
import { isSpecifiedScalarType } from '../type/scalars';
Expand Down Expand Up @@ -136,19 +135,7 @@ export function extendSchema(
const typeName = def.name.value;
typeDefinitionMap[typeName] = def;
} else if (isTypeExtensionNode(def)) {
// Sanity check that this type extension exists within the
// schema's existing types.
const extendedTypeName = def.name.value;
const existingType = schema.getType(extendedTypeName);
if (!existingType) {
throw new GraphQLError(
`Cannot extend type "${extendedTypeName}" because it does not ` +
'exist in the existing schema.',
[def],
);
}
checkExtensionNode(existingType, def);

const existingTypeExtensions = typeExtensionsMap[extendedTypeName];
typeExtensionsMap[extendedTypeName] = existingTypeExtensions
? existingTypeExtensions.concat([def])
Expand Down Expand Up @@ -540,46 +527,3 @@ export function extendSchema(
return extendNamedType(typeDef);
}
}

function checkExtensionNode(type, node) {
switch (node.kind) {
case Kind.OBJECT_TYPE_EXTENSION:
if (!isObjectType(type)) {
throw new GraphQLError(
`Cannot extend non-object type "${type.name}".`,
[node],
);
}
break;
case Kind.INTERFACE_TYPE_EXTENSION:
if (!isInterfaceType(type)) {
throw new GraphQLError(
`Cannot extend non-interface type "${type.name}".`,
[node],
);
}
break;
case Kind.ENUM_TYPE_EXTENSION:
if (!isEnumType(type)) {
throw new GraphQLError(`Cannot extend non-enum type "${type.name}".`, [
node,
]);
}
break;
case Kind.UNION_TYPE_EXTENSION:
if (!isUnionType(type)) {
throw new GraphQLError(`Cannot extend non-union type "${type.name}".`, [
node,
]);
}
break;
case Kind.INPUT_OBJECT_TYPE_EXTENSION:
if (!isInputObjectType(type)) {
throw new GraphQLError(
`Cannot extend non-input object type "${type.name}".`,
[node],
);
}
break;
}
}
235 changes: 235 additions & 0 deletions src/validation/__tests__/PossibleTypeExtensions-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
/**
* Copyright (c) 2018-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
*/

import { describe, it } from 'mocha';
import { buildSchema } from '../../utilities';
import { expectSDLValidationErrors } from './harness';
import {
PossibleTypeExtensions,
extendingUnknownTypeMessage,
extendingDifferentTypeKindMessage,
} from '../rules/PossibleTypeExtensions';

function expectSDLErrors(sdlStr, schema) {
return expectSDLValidationErrors(schema, PossibleTypeExtensions, sdlStr);
}

function expectValidSDL(sdlStr, schema) {
expectSDLErrors(sdlStr, schema).to.deep.equal([]);
}

function extendingUnknownType(typeName, suggestedTypes, line, column) {
return {
message: extendingUnknownTypeMessage(typeName, suggestedTypes),
locations: [{ line, column }],
};
}

function extendingDifferentTypeKind(typeName, kind, l1, c1, l2, c2) {
const message = extendingDifferentTypeKindMessage(typeName, kind);
const locations = [{ line: l1, column: c1 }];

if (l2 && c2) {
locations.push({ line: l2, column: c2 });
}
return { message, locations };
}

describe('Validate: Possible type extensions', () => {
it('no extensions', () => {
expectValidSDL(`
scalar FooScalar
type FooObject
interface FooInterface
union FooUnion
enum FooEnum
input FooInputObject
`);
});

it('one extension per type', () => {
expectValidSDL(`
scalar FooScalar
type FooObject
interface FooInterface
union FooUnion
enum FooEnum
input FooInputObject

extend scalar FooScalar @dummy
extend type FooObject @dummy
extend interface FooInterface @dummy
extend union FooUnion @dummy
extend enum FooEnum @dummy
extend input FooInputObject @dummy
`);
});

it('many extension per type', () => {
expectValidSDL(`
scalar FooScalar
type FooObject
interface FooInterface
union FooUnion
enum FooEnum
input FooInputObject

extend scalar FooScalar @dummy
extend type FooObject @dummy
extend interface FooInterface @dummy
extend union FooUnion @dummy
extend enum FooEnum @dummy
extend input FooInputObject @dummy

extend scalar FooScalar @dummy
extend type FooObject @dummy
extend interface FooInterface @dummy
extend union FooUnion @dummy
extend enum FooEnum @dummy
extend input FooInputObject @dummy
`);
});

it('extending unknow type', () => {
expectSDLErrors(`
type Known

extend scalar Unknown @dummy
extend type Unknown @dummy
extend interface Unknown @dummy
extend union Unknown @dummy
extend enum Unknown @dummy
extend input Unknown @dummy
`).to.deep.equal([
extendingUnknownType('Unknown', ['Known'], 4, 21),
extendingUnknownType('Unknown', ['Known'], 5, 19),
extendingUnknownType('Unknown', ['Known'], 6, 24),
extendingUnknownType('Unknown', ['Known'], 7, 20),
extendingUnknownType('Unknown', ['Known'], 8, 19),
extendingUnknownType('Unknown', ['Known'], 9, 20),
]);
});

it('doesnot consider non-type definitions', () => {
expectSDLErrors(`
query Foo { __typename }
fragment Foo on Query { __typename }
directive @Foo on SCHEMA

extend scalar Foo @dummy
extend type Foo @dummy
extend interface Foo @dummy
extend union Foo @dummy
extend enum Foo @dummy
extend input Foo @dummy
`).to.deep.equal([
extendingUnknownType('Foo', [], 6, 21),
extendingUnknownType('Foo', [], 7, 19),
extendingUnknownType('Foo', [], 8, 24),
extendingUnknownType('Foo', [], 9, 20),
extendingUnknownType('Foo', [], 10, 19),
extendingUnknownType('Foo', [], 11, 20),
]);
});

it('extending with different kinds', () => {
expectSDLErrors(`
scalar FooScalar
type FooObject
interface FooInterface
union FooUnion
enum FooEnum
input FooInputObject

extend type FooScalar @dummy
extend interface FooObject @dummy
extend union FooInterface @dummy
extend enum FooUnion @dummy
extend input FooEnum @dummy
extend scalar FooInputObject @dummy
`).to.deep.equal([
extendingDifferentTypeKind('FooScalar', 'scalar', 2, 7, 9, 7),
extendingDifferentTypeKind('FooObject', 'object', 3, 7, 10, 7),
extendingDifferentTypeKind('FooInterface', 'interface', 4, 7, 11, 7),
extendingDifferentTypeKind('FooUnion', 'union', 5, 7, 12, 7),
extendingDifferentTypeKind('FooEnum', 'enum', 6, 7, 13, 7),
extendingDifferentTypeKind('FooInputObject', 'input object', 7, 7, 14, 7),
]);
});

it('extending types within existing schema', () => {
const schema = buildSchema(`
scalar FooScalar
type FooObject
interface FooInterface
union FooUnion
enum FooEnum
input FooInputObject
`);
const sdl = `
extend scalar FooScalar @dummy
extend type FooObject @dummy
extend interface FooInterface @dummy
extend union FooUnion @dummy
extend enum FooEnum @dummy
extend input FooInputObject @dummy
`;

expectValidSDL(sdl, schema);
});

it('extending unknown types within existing schema', () => {
const schema = buildSchema('type Known');
const sdl = `
extend scalar Unknown @dummy
extend type Unknown @dummy
extend interface Unknown @dummy
extend union Unknown @dummy
extend enum Unknown @dummy
extend input Unknown @dummy
`;

expectSDLErrors(sdl, schema).to.deep.equal([
extendingUnknownType('Unknown', ['Known'], 2, 21),
extendingUnknownType('Unknown', ['Known'], 3, 19),
extendingUnknownType('Unknown', ['Known'], 4, 24),
extendingUnknownType('Unknown', ['Known'], 5, 20),
extendingUnknownType('Unknown', ['Known'], 6, 19),
extendingUnknownType('Unknown', ['Known'], 7, 20),
]);
});

it('extending types with different kinds within existing schema', () => {
const schema = buildSchema(`
scalar FooScalar
type FooObject
interface FooInterface
union FooUnion
enum FooEnum
input FooInputObject
`);
const sdl = `
extend type FooScalar @dummy
extend interface FooObject @dummy
extend union FooInterface @dummy
extend enum FooUnion @dummy
extend input FooEnum @dummy
extend scalar FooInputObject @dummy
`;

expectSDLErrors(sdl, schema).to.deep.equal([
extendingDifferentTypeKind('FooScalar', 'scalar', 2, 7),
extendingDifferentTypeKind('FooObject', 'object', 3, 7),
extendingDifferentTypeKind('FooInterface', 'interface', 4, 7),
extendingDifferentTypeKind('FooUnion', 'union', 5, 7),
extendingDifferentTypeKind('FooEnum', 'enum', 6, 7),
extendingDifferentTypeKind('FooInputObject', 'input object', 7, 7),
]);
});
});