Skip to content

Commit

Permalink
Return useful information from SchemaDirectiveVisitor.visitSchema.
Browse files Browse the repository at this point in the history
  • Loading branch information
benjamn committed Mar 14, 2018
1 parent f951c81 commit 547f5d6
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 17 deletions.
47 changes: 33 additions & 14 deletions src/directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,17 +119,30 @@ export class SchemaDirectiveVisitor {
// method is marked as protected.
[directiveName: string]: typeof SchemaDirectiveVisitor
},
) {
): {
// The visitSchema method returns a map from directive names to lists of
// SchemaDirectiveVisitor instances created while visiting the schema.
[directiveName: string]: SchemaDirectiveVisitor[],
} {
// 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: {
[key: string]: GraphQLDirective,
[directiveName: string]: GraphQLDirective,
} = Object.create(null);
schema.getDirectives().forEach(decl => {
declaredDirectives[decl.name] = decl;
});

// Map from directive names to lists of SchemaDirectiveVisitor instances
// created while visiting the schema.
const createdVisitors: {
[directiveName: string]: SchemaDirectiveVisitor[]
} = Object.create(null);
Object.keys(directiveVisitors).forEach(directiveName => {
createdVisitors[directiveName] = [];
});

// Recursive helper for visiting nested schema type objects.
function visit(type: VisitableType) {
if (type instanceof GraphQLSchema) {
Expand Down Expand Up @@ -252,23 +265,23 @@ export class SchemaDirectiveVisitor {
});
}

// Given a schema type, returns an (often empty) array of directives that
// should be applied to the given type.
// Given a schema type, returns an array of SchemaDirectiveVisitor instances
// that should be applied to the given type. This array will often be empty.
function getDirectives(type: VisitableType): SchemaDirectiveVisitor[] {
const directiveInstances: SchemaDirectiveVisitor[] = [];
const visitors: SchemaDirectiveVisitor[] = [];
const directiveNodes = type.astNode && type.astNode.directives;
if (! directiveNodes) {
return directiveInstances;
return visitors;
}

directiveNodes.forEach(directiveNode => {
const name = directiveNode.name.value;
if (! hasOwn.call(directiveVisitors, name)) {
const directiveName = directiveNode.name.value;
if (! hasOwn.call(directiveVisitors, directiveName)) {
return;
}

const directiveClass = directiveVisitors[name];
const decl = declaredDirectives[name];
const visitorClass = directiveVisitors[directiveName];
const decl = declaredDirectives[directiveName];
let args: { [key: string]: any };

if (decl) {
Expand All @@ -290,21 +303,27 @@ export class SchemaDirectiveVisitor {
// created and assigned names. Subclasses can override the constructor
// method, but since the constructor is marked as protected, these are
// the only arguments that will ever be passed.
directiveInstances.push(new directiveClass({
name,
visitors.push(new visitorClass({
name: directiveName,
args,
visitedType: type,
schema
}));
});

return directiveInstances;
if (visitors.length > 0) {
visitors.forEach(visitor => {
createdVisitors[visitor.name].push(visitor);
});
}

return visitors;
}

// Kick everything off by visiting the top-level GraphQLSchema object.
visit(schema);

// TODO Should visitSchema return anything?
return createdVisitors;
}

// Mark the constructor protected to enforce passing SchemaDirectiveVisitor
Expand Down
14 changes: 11 additions & 3 deletions src/test/testDirectives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,11 +402,11 @@ describe('@directives', () => {
lawyers(
# Should @oyez be disallowed here, since it wasn't declared with
# the ARGUMENT_DEFINITION location, or simply ignored?
side: Side @oyez(times: 0)
party: Party @oyez(times: 0)
): [String]
}
enum Side {
enum Party {
DEFENSE
PROSECUTION
}`;
Expand All @@ -415,7 +415,7 @@ describe('@directives', () => {
let objectCount = 0;
let fieldCount = 0;

SchemaDirectiveVisitor.visitSchema(schema, {
const visitors = SchemaDirectiveVisitor.visitSchema(schema, {
oyez: class extends SchemaDirectiveVisitor {
public visitObject(object: GraphQLObjectType) {
++objectCount;
Expand All @@ -435,5 +435,13 @@ describe('@directives', () => {

assert.strictEqual(objectCount, 1);
assert.strictEqual(fieldCount, 2);

assert.deepEqual(Object.keys(visitors), ['oyez']);
assert.deepEqual(
visitors.oyez.map(v => {
return (v.visitedType as GraphQLObjectType | GraphQLField<any, any>).name;
}),
['Courtroom', 'judge', 'marshall'],
);
});
});

0 comments on commit 547f5d6

Please sign in to comment.