Skip to content

Commit d32f2e1

Browse files
committed
add support for __directive meta-field
analogue to __type
1 parent bd2fe71 commit d32f2e1

File tree

9 files changed

+113
-4
lines changed

9 files changed

+113
-4
lines changed

src/__tests__/starWarsIntrospection-test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,5 +362,47 @@ describe('Star Wars Introspection Tests', () => {
362362
},
363363
});
364364
});
365+
366+
it('Allows querying the schema for directives', () => {
367+
const data = queryStarWars(`
368+
{
369+
__directive(name: "skip") {
370+
name
371+
args {
372+
name
373+
type {
374+
kind
375+
name
376+
ofType {
377+
kind
378+
name
379+
}
380+
}
381+
defaultValue
382+
}
383+
}
384+
}
385+
`);
386+
387+
expect(data).to.deep.equal({
388+
__directive: {
389+
name: 'skip',
390+
args: [
391+
{
392+
name: 'if',
393+
type: {
394+
kind: 'NON_NULL',
395+
name: null,
396+
ofType: {
397+
kind: 'SCALAR',
398+
name: 'Boolean',
399+
},
400+
},
401+
defaultValue: null,
402+
},
403+
],
404+
},
405+
});
406+
});
365407
});
366408
});

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export {
8484
// Meta-field definitions.
8585
SchemaMetaFieldDef,
8686
TypeMetaFieldDef,
87+
DirectiveMetaFieldDef,
8788
TypeNameMetaFieldDef,
8889
// Predicates
8990
isSchema,

src/type/__tests__/introspection-test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1656,6 +1656,32 @@ describe('Introspection', () => {
16561656
});
16571657
});
16581658

1659+
it('fails as expected on the __directive root field without an arg', () => {
1660+
const schema = buildSchema(`
1661+
type Query {
1662+
someField: String
1663+
}
1664+
`);
1665+
1666+
const source = `
1667+
{
1668+
__directive {
1669+
name
1670+
}
1671+
}
1672+
`;
1673+
1674+
expectJSON(graphqlSync({ schema, source })).toDeepEqual({
1675+
errors: [
1676+
{
1677+
message:
1678+
'Argument "<meta>.__directive(name:)" of type "String!" is required, but it was not provided.',
1679+
locations: [{ line: 3, column: 9 }],
1680+
},
1681+
],
1682+
});
1683+
});
1684+
16591685
it('exposes descriptions', () => {
16601686
const schema = buildSchema(`
16611687
"""Enum description"""

src/type/__tests__/schema-test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
} from '../definition.js';
1919
import { GraphQLDirective } from '../directives.js';
2020
import {
21+
DirectiveMetaFieldDef,
2122
SchemaMetaFieldDef,
2223
TypeMetaFieldDef,
2324
TypeNameMetaFieldDef,
@@ -413,6 +414,7 @@ describe('Type System: Schema', () => {
413414
expectField(catOrDog, '__typename').to.equal(TypeNameMetaFieldDef);
414415

415416
expectField(queryType, '__type').to.equal(TypeMetaFieldDef);
417+
expectField(queryType, '__directive').to.equal(DirectiveMetaFieldDef);
416418
expectField(queryType, '__schema').to.equal(SchemaMetaFieldDef);
417419
});
418420

src/type/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ export {
182182
// Meta-field definitions.
183183
SchemaMetaFieldDef,
184184
TypeMetaFieldDef,
185+
DirectiveMetaFieldDef,
185186
TypeNameMetaFieldDef,
186187
} from './introspection.js';
187188

src/type/introspection.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,28 @@ export const TypeMetaFieldDef: GraphQLField<unknown, unknown> = {
546546
astNode: undefined,
547547
};
548548

549+
export const DirectiveMetaFieldDef: GraphQLField<unknown, unknown> = {
550+
name: '__directive',
551+
type: __Directive,
552+
description: 'Request information for a single directive.',
553+
args: [
554+
{
555+
name: 'name',
556+
description: undefined,
557+
type: new GraphQLNonNull(GraphQLString),
558+
defaultValue: undefined,
559+
deprecationReason: undefined,
560+
extensions: Object.create(null),
561+
astNode: undefined,
562+
},
563+
],
564+
resolve: (_source, { name }, _context, { schema }) =>
565+
schema.getDirective(name),
566+
deprecationReason: undefined,
567+
extensions: Object.create(null),
568+
astNode: undefined,
569+
};
570+
549571
export const TypeNameMetaFieldDef: GraphQLField<unknown, unknown> = {
550572
name: '__typename',
551573
type: new GraphQLNonNull(GraphQLString),

src/type/schema.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import type { GraphQLDirective } from './directives.js';
3232
import { isDirective, specifiedDirectives } from './directives.js';
3333
import {
3434
__Schema,
35+
DirectiveMetaFieldDef,
3536
SchemaMetaFieldDef,
3637
TypeMetaFieldDef,
3738
TypeNameMetaFieldDef,
@@ -340,13 +341,14 @@ export class GraphQLSchema {
340341
/**
341342
* This method looks up the field on the given type definition.
342343
* It has special casing for the three introspection fields, `__schema`,
343-
* `__type` and `__typename`.
344+
* `__type`, `__directive` and `__typename`.
344345
*
345346
* `__typename` is special because it can always be queried as a field, even
346347
* in situations where no other fields are allowed, like on a Union.
347348
*
348-
* `__schema` and `__type` could get automatically added to the query type,
349-
* but that would require mutating type definitions, which would cause issues.
349+
* `__schema`, `__type`, and `__directive` could get automatically added to the
350+
* query type, but that would require mutating type definitions, which would
351+
* cause issues.
350352
*/
351353
getField(
352354
parentType: GraphQLCompositeType,
@@ -361,6 +363,10 @@ export class GraphQLSchema {
361363
return this.getQueryType() === parentType
362364
? TypeMetaFieldDef
363365
: undefined;
366+
case DirectiveMetaFieldDef.name:
367+
return this.getQueryType() === parentType
368+
? DirectiveMetaFieldDef
369+
: undefined;
364370
case TypeNameMetaFieldDef.name:
365371
return TypeNameMetaFieldDef;
366372
}

src/utilities/__tests__/TypeInfo-test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ describe('visitWithTypeInfo', () => {
160160
{
161161
__typename
162162
__type(name: "Cat") { __typename }
163+
__directive(name: "skip") { __typename }
163164
__schema {
164165
__typename # in object type
165166
}
@@ -174,6 +175,7 @@ describe('visitWithTypeInfo', () => {
174175
}
175176
pet {
176177
__type # unknown
178+
__directive # unknown
177179
__schema # unknown
178180
}
179181
}
@@ -195,6 +197,8 @@ describe('visitWithTypeInfo', () => {
195197
['QueryRoot', '__typename'],
196198
['QueryRoot', '__type'],
197199
['__Type', '__typename'],
200+
['QueryRoot', '__directive'],
201+
['__Directive', '__typename'],
198202
['QueryRoot', '__schema'],
199203
['__Schema', '__typename'],
200204
['QueryRoot', 'humanOrAlien'],
@@ -206,6 +210,7 @@ describe('visitWithTypeInfo', () => {
206210
['QueryRoot', 'pet'],
207211
['Pet', undefined],
208212
['Pet', undefined],
213+
['Pet', undefined],
209214
]);
210215
});
211216

src/validation/rules/MaxIntrospectionDepthRule.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,11 @@ export function MaxIntrospectionDepthRule(
7777

7878
return {
7979
Field(node) {
80-
if (node.name.value === '__schema' || node.name.value === '__type') {
80+
if (
81+
node.name.value === '__schema' ||
82+
node.name.value === '__type' ||
83+
node.name.value === '__directive'
84+
) {
8185
if (checkDepth(node)) {
8286
context.reportError(
8387
new GraphQLError('Maximum introspection depth exceeded', {

0 commit comments

Comments
 (0)