Skip to content

Commit

Permalink
Allow SchemaDirectiveVisitors to provide GraphQLDirective declarations.
Browse files Browse the repository at this point in the history
  • Loading branch information
benjamn committed Mar 14, 2018
1 parent 67c625d commit 99fd3b7
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 7 deletions.
56 changes: 50 additions & 6 deletions src/schemaVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,17 @@ export class SchemaDirectiveVisitor extends SchemaVisitor {
// object, or just use the default empty object.
public context: { [key: string]: any };

// Override this method to return a custom GraphQLDirective (or modify one
// already present in the schema) to enforce argument types, provide default
// argument values, or specify schema locations where this @directive may
// appear. By default, any declaration found in the schema will be returned.
public static getDirectiveDeclaration(
directiveName: string,
previousDeclaration?: GraphQLDirective,
): GraphQLDirective {
return previousDeclaration;
}

// Call SchemaDirectiveVisitor.visitSchemaDirectives to visit every
// @directive in the schema and create an appropriate SchemaDirectiveVisitor
// instance to visit the object decorated by the @directive.
Expand Down Expand Up @@ -320,12 +331,8 @@ export class SchemaDirectiveVisitor extends SchemaVisitor {
// If the schema declares any directives for public consumption, record
// them here so that we can properly coerce arguments when/if we encounter
// an occurrence of the directive while walking the schema below.
const declaredDirectives: {
[directiveName: string]: GraphQLDirective,
} = Object.create(null);
schema.getDirectives().forEach(decl => {
declaredDirectives[decl.name] = decl;
});
const declaredDirectives =
this.getDeclaredDirectives(schema, directiveVisitors);

// Map from directive names to lists of SchemaDirectiveVisitor instances
// created while visiting the schema.
Expand Down Expand Up @@ -405,6 +412,43 @@ export class SchemaDirectiveVisitor extends SchemaVisitor {
return createdVisitors;
}

protected static getDeclaredDirectives(
schema: GraphQLSchema,
directiveVisitors: {
[directiveName: string]: typeof SchemaDirectiveVisitor
},
) {
const declaredDirectives: {
[directiveName: string]: GraphQLDirective,
} = Object.create(null);

schema.getDirectives().forEach(decl => {
declaredDirectives[decl.name] = decl;
});

// If the visitor subclass overrides getDirectiveDeclaration, and it
// returns a non-null GraphQLDirective, use that instead of any directive
// declared in the schema itself. Reasoning: if a SchemaDirectiveVisitor
// goes to the trouble of implementing getDirectiveDeclaration, it should
// be able to rely on that implementation.
each(directiveVisitors, (visitorClass, directiveName) => {
const decl = visitorClass.getDirectiveDeclaration(
directiveName,
// Give the getDirectiveDeclaration method a chance to look at the
// existing declaration, in case it only needs to make minor tweaks.
// Ironically, this grants the SchemaDirectiveVisitor subclass the
// ability to visit/inspect/modify directive declarations found in
// the schema, even though they can't be decorated with @directives!
declaredDirectives[directiveName] || null,
);
if (decl) {
declaredDirectives[directiveName] = decl;
}
});

return declaredDirectives;
}

// Mark the constructor protected to enforce passing SchemaDirectiveVisitor
// subclasses (not instances) to visitSchemaDirectives.
protected constructor(config: {
Expand Down
18 changes: 17 additions & 1 deletion src/test/testDirectives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
GraphQLInputObjectType,
GraphQLObjectType,
GraphQLSchema,
GraphQLDirective,
} from 'graphql';

const typeDefs = `
Expand Down Expand Up @@ -446,7 +447,7 @@ describe('@directives', () => {

it('can also handle declared arguments', () => {
const schemaText = `
directive @oyez(times: Int = 3) on OBJECT | FIELD_DEFINITION
directive @oyez(times: Int = 5) on OBJECT | FIELD_DEFINITION
schema {
query: Courtroom
Expand Down Expand Up @@ -475,6 +476,21 @@ describe('@directives', () => {

const visitors = SchemaDirectiveVisitor.visitSchemaDirectives(schema, {
oyez: class extends SchemaDirectiveVisitor {
public static getDirectiveDeclaration(
name: string,
prev: GraphQLDirective,
) {
prev.args.some(arg => {
if (arg.name === 'times') {
// Override the default value of the times argument to be 3
// instead of 5.
arg.defaultValue = 3;
return true;
}
});
return prev;
}

public visitObject(object: GraphQLObjectType) {
++this.context.objectCount;
assert.strictEqual(this.args.times, 3);
Expand Down

0 comments on commit 99fd3b7

Please sign in to comment.