Skip to content

Commit 028edb0

Browse files
committed
Add option printDirective to printSchema
Resolves graphql#2020 I initially thought about implementing this as graphql#2020 (comment), but really could not think of a good use case where a directive should be printed in a modified way. Thus, I went for a simpler signature: ```ts shouldPrintDirective?: (directiveNode: DirectiveNode) => boolean ``` This is only a partial implementation for now, there are more possible locations in a schema that can have a directive. Before I finish the implementation, I would like to validate the direction is right.
1 parent 30b4469 commit 028edb0

File tree

3 files changed

+151
-11
lines changed

3 files changed

+151
-11
lines changed

src/execution/__tests__/directives-test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ describe('Execute: handles directives', () => {
174174
data: { a: 'a', b: 'b' },
175175
});
176176
});
177+
177178
it('unless false includes inline fragment', () => {
178179
const result = executeTestQuery(`
179180
query {
@@ -188,6 +189,7 @@ describe('Execute: handles directives', () => {
188189
data: { a: 'a', b: 'b' },
189190
});
190191
});
192+
191193
it('unless true includes inline fragment', () => {
192194
const result = executeTestQuery(`
193195
query {
@@ -234,6 +236,7 @@ describe('Execute: handles directives', () => {
234236
data: { a: 'a', b: 'b' },
235237
});
236238
});
239+
237240
it('unless false includes anonymous inline fragment', () => {
238241
const result = executeTestQuery(`
239242
query Q {
@@ -248,6 +251,7 @@ describe('Execute: handles directives', () => {
248251
data: { a: 'a', b: 'b' },
249252
});
250253
});
254+
251255
it('unless true includes anonymous inline fragment', () => {
252256
const result = executeTestQuery(`
253257
query {

src/utilities/__tests__/printSchema-test.ts

Lines changed: 94 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,19 @@ import {
2121
} from '../../type/definition';
2222

2323
import { buildSchema } from '../buildASTSchema';
24-
import { printSchema, printIntrospectionSchema } from '../printSchema';
24+
import type { PrintSchemaOptions } from '../printSchema';
25+
import {
26+
printSchema,
27+
printIntrospectionSchema,
28+
} from '../printSchema';
2529

26-
function expectPrintedSchema(schema: GraphQLSchema) {
27-
const schemaText = printSchema(schema);
30+
function expectPrintedSchema(
31+
schema: GraphQLSchema,
32+
options?: PrintSchemaOptions,
33+
) {
34+
const schemaText = printSchema(schema, options);
2835
// keep printSchema and buildSchema in sync
29-
expect(printSchema(buildSchema(schemaText))).to.equal(schemaText);
36+
expect(printSchema(buildSchema(schemaText), options)).to.equal(schemaText);
3037
return expect(schemaText);
3138
}
3239

@@ -260,6 +267,16 @@ describe('Type System Printer', () => {
260267
`);
261268
});
262269

270+
it('Omits schema of common names', () => {
271+
const schema = new GraphQLSchema({
272+
query: new GraphQLObjectType({ name: 'Query', fields: {} }),
273+
});
274+
275+
expectPrintedSchema(schema).to.equal(dedent`
276+
type Query
277+
`);
278+
});
279+
263280
it('Prints schema with description', () => {
264281
const schema = new GraphQLSchema({
265282
description: 'Schema description.',
@@ -318,6 +335,79 @@ describe('Type System Printer', () => {
318335
`);
319336
});
320337

338+
it('Prints schema with directives', () => {
339+
const schema = buildSchema(`
340+
schema @foo {
341+
query: Query
342+
}
343+
344+
directive @foo on SCHEMA
345+
346+
type Query
347+
`);
348+
349+
expectPrintedSchema(schema, { shouldPrintDirective: () => true }).to
350+
.equal(dedent`
351+
schema @foo {
352+
query: Query
353+
}
354+
355+
directive @foo on SCHEMA
356+
357+
type Query
358+
`);
359+
});
360+
361+
it('Includes directives conditionally', () => {
362+
const schema = buildSchema(`
363+
schema @foo @bar {
364+
query: Query
365+
}
366+
367+
directive @foo on SCHEMA
368+
369+
directive @bar on SCHEMA
370+
371+
type Query
372+
`);
373+
374+
expectPrintedSchema(schema, {
375+
shouldPrintDirective: (directive) => directive.name.value === 'foo',
376+
}).to.equal(dedent`
377+
schema @foo {
378+
query: Query
379+
}
380+
381+
directive @foo on SCHEMA
382+
383+
directive @bar on SCHEMA
384+
385+
type Query
386+
`);
387+
388+
expectPrintedSchema(schema, { shouldPrintDirective: () => true }).to
389+
.equal(dedent`
390+
schema @foo @bar {
391+
query: Query
392+
}
393+
394+
directive @foo on SCHEMA
395+
396+
directive @bar on SCHEMA
397+
398+
type Query
399+
`);
400+
401+
expectPrintedSchema(schema, { shouldPrintDirective: () => false }).to
402+
.equal(dedent`
403+
directive @foo on SCHEMA
404+
405+
directive @bar on SCHEMA
406+
407+
type Query
408+
`);
409+
});
410+
321411
it('Print Interface', () => {
322412
const FooType = new GraphQLInterfaceType({
323413
name: 'Foo',

src/utilities/printSchema.ts

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,39 @@ import {
3434
isInputObjectType,
3535
} from '../type/definition';
3636

37+
import type { DirectiveNode } from '../language/ast';
38+
3739
import { astFromValue } from './astFromValue';
3840

39-
export function printSchema(schema: GraphQLSchema): string {
41+
export interface PrintSchemaOptions {
42+
/**
43+
* Should the given directive node be included in the print output?
44+
*/
45+
shouldPrintDirective?: (directiveNode: DirectiveNode) => boolean;
46+
}
47+
48+
export function printSchema(
49+
schema: GraphQLSchema,
50+
options?: PrintSchemaOptions,
51+
): string {
4052
return printFilteredSchema(
4153
schema,
4254
(n) => !isSpecifiedDirective(n),
4355
isDefinedType,
56+
options,
4457
);
4558
}
4659

47-
export function printIntrospectionSchema(schema: GraphQLSchema): string {
48-
return printFilteredSchema(schema, isSpecifiedDirective, isIntrospectionType);
60+
export function printIntrospectionSchema(
61+
schema: GraphQLSchema,
62+
options?: PrintSchemaOptions,
63+
): string {
64+
return printFilteredSchema(
65+
schema,
66+
isSpecifiedDirective,
67+
isIntrospectionType,
68+
options,
69+
);
4970
}
5071

5172
function isDefinedType(type: GraphQLNamedType): boolean {
@@ -56,21 +77,41 @@ function printFilteredSchema(
5677
schema: GraphQLSchema,
5778
directiveFilter: (type: GraphQLDirective) => boolean,
5879
typeFilter: (type: GraphQLNamedType) => boolean,
80+
options?: PrintSchemaOptions,
5981
): string {
6082
const directives = schema.getDirectives().filter(directiveFilter);
6183
const types = Object.values(schema.getTypeMap()).filter(typeFilter);
6284

6385
return [
64-
printSchemaDefinition(schema),
86+
printSchemaDefinition(schema, options),
6587
...directives.map((directive) => printDirective(directive)),
6688
...types.map((type) => printType(type)),
6789
]
6890
.filter(Boolean)
6991
.join('\n\n');
7092
}
7193

72-
function printSchemaDefinition(schema: GraphQLSchema): Maybe<string> {
73-
if (schema.description == null && isSchemaOfCommonNames(schema)) {
94+
function printSchemaDefinition(
95+
schema: GraphQLSchema,
96+
options?: PrintSchemaOptions,
97+
): Maybe<string> {
98+
const directives: Array<string> = [];
99+
const printDirectives = options?.shouldPrintDirective;
100+
if (printDirectives) {
101+
[schema.astNode, ...schema.extensionASTNodes].forEach((node) => {
102+
node?.directives?.forEach((directive) => {
103+
if (printDirectives(directive)) {
104+
directives.push(print(directive));
105+
}
106+
});
107+
});
108+
}
109+
110+
if (
111+
schema.description == null &&
112+
directives.length === 0 &&
113+
isSchemaOfCommonNames(schema)
114+
) {
74115
return;
75116
}
76117

@@ -91,7 +132,12 @@ function printSchemaDefinition(schema: GraphQLSchema): Maybe<string> {
91132
operationTypes.push(` subscription: ${subscriptionType.name}`);
92133
}
93134

94-
return printDescription(schema) + `schema {\n${operationTypes.join('\n')}\n}`;
135+
return (
136+
printDescription(schema) +
137+
['schema', directives.join(' '), `{\n${operationTypes.join('\n')}\n}`]
138+
.filter(Boolean)
139+
.join(' ')
140+
);
95141
}
96142

97143
/**

0 commit comments

Comments
 (0)