Skip to content

Commit a276ba8

Browse files
committed
fix(pruneSchema): respect directive definitions in extensions
1 parent e07bb6d commit a276ba8

File tree

3 files changed

+88
-22
lines changed

3 files changed

+88
-22
lines changed

.changeset/pretty-foxes-check.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@graphql-tools/utils": patch
3+
---
4+
5+
Respect directive extensions on \`pruneSchema\`

packages/utils/src/prune.ts

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import {
2-
ASTNode,
32
getNamedType,
43
GraphQLFieldMap,
54
GraphQLSchema,
@@ -11,6 +10,7 @@ import {
1110
isSpecifiedScalarType,
1211
isUnionType,
1312
} from 'graphql';
13+
import { DirectableGraphQLObject } from './get-directives.js';
1414
import { getImplementingTypes } from './get-implementing-types.js';
1515
import { MapperKind } from './Interfaces.js';
1616
import { mapSchema } from './mapSchema.js';
@@ -152,12 +152,7 @@ function visitQueue(
152152
if (isEnumType(type)) {
153153
// Visit enum values directives argument types
154154
queue.push(
155-
...type.getValues().flatMap(value => {
156-
if (value.astNode) {
157-
return getDirectivesArgumentsTypeNames(schema, value.astNode);
158-
}
159-
return [];
160-
}),
155+
...type.getValues().flatMap(value => getDirectivesArgumentsTypeNames(schema, value)),
161156
);
162157
}
163158
// Visit interfaces this type is implementing if they haven't been visited yet
@@ -180,9 +175,7 @@ function visitQueue(
180175
queue.push(
181176
...field.args.flatMap(arg => {
182177
const typeNames = [getNamedType(arg.type).name];
183-
if (arg.astNode) {
184-
typeNames.push(...getDirectivesArgumentsTypeNames(schema, arg.astNode));
185-
}
178+
typeNames.push(...getDirectivesArgumentsTypeNames(schema, arg));
186179
return typeNames;
187180
}),
188181
);
@@ -192,9 +185,7 @@ function visitQueue(
192185

193186
queue.push(namedType.name);
194187

195-
if (field.astNode) {
196-
queue.push(...getDirectivesArgumentsTypeNames(schema, field.astNode));
197-
}
188+
queue.push(...getDirectivesArgumentsTypeNames(schema, field));
198189

199190
// Interfaces returned on fields need to be revisited to add their implementations
200191
if (isInterfaceType(namedType) && !(namedType.name in revisit)) {
@@ -203,9 +194,7 @@ function visitQueue(
203194
}
204195
}
205196

206-
if (type.astNode) {
207-
queue.push(...getDirectivesArgumentsTypeNames(schema, type.astNode));
208-
}
197+
queue.push(...getDirectivesArgumentsTypeNames(schema, type));
209198

210199
visited.add(typeName); // Mark as visited (and therefore it is used and should be kept)
211200
}
@@ -215,10 +204,30 @@ function visitQueue(
215204

216205
function getDirectivesArgumentsTypeNames(
217206
schema: GraphQLSchema,
218-
astNode: Extract<ASTNode, { readonly directives?: any }>,
207+
directableObj: DirectableGraphQLObject,
219208
) {
220-
return (astNode.directives ?? []).flatMap(
221-
directive =>
222-
schema.getDirective(directive.name.value)?.args.map(arg => getNamedType(arg.type).name) ?? [],
223-
);
209+
const argTypeNames = new Set<string>();
210+
if (directableObj.astNode?.directives) {
211+
for (const directiveNode of directableObj.astNode.directives) {
212+
const directive = schema.getDirective(directiveNode.name.value);
213+
if (directive?.args) {
214+
for (const arg of directive.args) {
215+
const argType = getNamedType(arg.type);
216+
argTypeNames.add(argType.name);
217+
}
218+
}
219+
}
220+
}
221+
if (directableObj.extensions?.['directives']) {
222+
for (const directiveName in directableObj.extensions['directives']) {
223+
const directive = schema.getDirective(directiveName);
224+
if (directive?.args) {
225+
for (const arg of directive.args) {
226+
const argType = getNamedType(arg.type);
227+
argTypeNames.add(argType.name);
228+
}
229+
}
230+
}
231+
}
232+
return [...argTypeNames];
224233
}

packages/utils/tests/prune.test.ts

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1-
import { buildSchema, GraphQLNamedType } from 'graphql';
1+
import {
2+
buildSchema,
3+
DirectiveLocation,
4+
GraphQLDirective,
5+
GraphQLEnumType,
6+
GraphQLNamedType,
7+
GraphQLObjectType,
8+
GraphQLSchema,
9+
GraphQLString,
10+
} from 'graphql';
211
import { PruneSchemaFilter } from '../src/index.js';
312
import { pruneSchema } from '../src/prune.js';
413

@@ -487,6 +496,49 @@ describe('pruneSchema', () => {
487496
}
488497
`);
489498

499+
const result = pruneSchema(schema);
500+
expect(result.getType('DirectiveArg')).toBeDefined();
501+
});
502+
test('does not remove type used in argument definition directive argument from extensions', () => {
503+
const enumType = new GraphQLEnumType({
504+
name: 'DirectiveArg',
505+
values: {
506+
VALUE: {
507+
value: 'VALUE',
508+
},
509+
},
510+
});
511+
const schema = new GraphQLSchema({
512+
query: new GraphQLObjectType({
513+
name: 'Query',
514+
fields: {
515+
foo: {
516+
type: GraphQLString,
517+
extensions: {
518+
directives: {
519+
bar: [
520+
{
521+
arg: 'VALUE',
522+
},
523+
],
524+
},
525+
},
526+
},
527+
},
528+
}),
529+
directives: [
530+
new GraphQLDirective({
531+
name: 'bar',
532+
locations: [DirectiveLocation.FIELD],
533+
args: {
534+
arg: {
535+
type: enumType,
536+
},
537+
},
538+
}),
539+
],
540+
});
541+
490542
const result = pruneSchema(schema);
491543
expect(result.getType('DirectiveArg')).toBeDefined();
492544
});

0 commit comments

Comments
 (0)