diff --git a/src/schemaVisitor.ts b/src/schemaVisitor.ts index 9db069accc7..8394d9fb97a 100644 --- a/src/schemaVisitor.ts +++ b/src/schemaVisitor.ts @@ -62,6 +62,11 @@ export abstract class SchemaVisitor { return false; } + if (this === SchemaVisitor) { + // The SchemaVisitor class implements every visitor method. + return true; + } + const stub = SchemaVisitor.prototype[methodName]; if (method === stub) { // If this.prototype[methodName] was just inherited from SchemaVisitor, @@ -642,7 +647,8 @@ export class SchemaDirectiveVisitor extends SchemaVisitor { each(decl.locations, loc => { const visitorMethodName = directiveLocationToVisitorMethodName(loc); - if (! visitorClass.implementsVisitorMethod(visitorMethodName)) { + if (SchemaVisitor.implementsVisitorMethod(visitorMethodName) && + ! visitorClass.implementsVisitorMethod(visitorMethodName)) { // While visitor subclasses may implement extra visitor methods, // it's definitely a mistake if the GraphQLDirective declares itself // applicable to certain schema locations, and the visitor subclass diff --git a/src/test/testDirectives.ts b/src/test/testDirectives.ts index e87a3b99fa1..86c353d9fbd 100644 --- a/src/test/testDirectives.ts +++ b/src/test/testDirectives.ts @@ -1233,4 +1233,30 @@ describe('@directives', () => { Human ); }); + + it('does not enforce query directive locations (issue #680)', () => { + const visited = new Set(); + const schema = makeExecutableSchema({ + typeDefs: ` + directive @hasScope(scope: [String]) on QUERY | FIELD + + type Query @hasScope { + oyez: String + }`, + + schemaDirectives: { + hasScope: class extends SchemaDirectiveVisitor { + public visitObject(object: GraphQLObjectType) { + assert.strictEqual(object.name, 'Query'); + visited.add(object); + } + } + } + }); + + assert.strictEqual(visited.size, 1); + visited.forEach(object => { + assert.strictEqual(schema.getType('Query'), object); + }); + }); });