Skip to content

Commit ebc31f3

Browse files
committed
fix: check the actual GraphQLDirective instance in isSpecifiedDirective
1 parent e2457b3 commit ebc31f3

File tree

5 files changed

+146
-3
lines changed

5 files changed

+146
-3
lines changed

src/type/__tests__/predicate-test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,5 +700,13 @@ describe('Directive predicates', () => {
700700
it('returns false for custom directive', () => {
701701
expect(isSpecifiedDirective(Directive)).to.equal(false);
702702
});
703+
704+
it('returns false for the directives with the same name as specified directives', () => {
705+
const FakeSkipDirective = new GraphQLDirective({
706+
name: 'skip',
707+
locations: [DirectiveLocation.QUERY],
708+
});
709+
expect(isSpecifiedDirective(FakeSkipDirective)).to.equal(false);
710+
});
703711
});
704712
});

src/type/directives.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,5 +233,7 @@ export const specifiedDirectives: ReadonlyArray<GraphQLDirective> =
233233
]);
234234

235235
export function isSpecifiedDirective(directive: GraphQLDirective): boolean {
236-
return specifiedDirectives.some(({ name }) => name === directive.name);
236+
return specifiedDirectives.some(
237+
(specifiedDirective) => specifiedDirective === directive,
238+
);
237239
}

src/utilities/buildClientSchema.ts

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ import {
2424
GraphQLScalarType,
2525
GraphQLUnionType,
2626
isInputType,
27+
isNamedType,
2728
isOutputType,
2829
} from '../type/definition';
29-
import { GraphQLDirective } from '../type/directives';
30+
import { GraphQLDirective, specifiedDirectives } from '../type/directives';
3031
import { introspectionTypes, TypeKind } from '../type/introspection';
3132
import { specifiedScalarTypes } from '../type/scalars';
3233
import type { GraphQLSchemaValidationOptions } from '../type/schema';
@@ -381,6 +382,66 @@ export function buildClientSchema(
381382
};
382383
}
383384

385+
function getSpecifiedDirectiveFromIntrospection(
386+
directiveIntrospection: IntrospectionDirective,
387+
): GraphQLDirective | undefined {
388+
const possibleSpecifiedDirective = specifiedDirectives.find(
389+
(dir) => dir.name === directiveIntrospection.name,
390+
);
391+
if (possibleSpecifiedDirective == null) {
392+
return;
393+
}
394+
395+
for (const location of directiveIntrospection.locations) {
396+
if (!possibleSpecifiedDirective.locations.includes(location)) {
397+
return;
398+
}
399+
}
400+
401+
for (const arg of directiveIntrospection.args) {
402+
const possibleArg = possibleSpecifiedDirective.args.find(
403+
(a) => a.name === arg.name,
404+
);
405+
if (possibleArg == null) {
406+
return;
407+
}
408+
const argType = getType(arg.type);
409+
// Is same type
410+
let currentType = argType;
411+
let expectedType = possibleArg.type;
412+
// eslint-disable-next-line no-constant-condition
413+
while (true) {
414+
if (currentType instanceof GraphQLNonNull) {
415+
if (expectedType instanceof GraphQLNonNull) {
416+
currentType = currentType.ofType;
417+
expectedType = expectedType.ofType;
418+
continue;
419+
} else {
420+
return;
421+
}
422+
}
423+
if (currentType instanceof GraphQLList) {
424+
if (expectedType instanceof GraphQLList) {
425+
currentType = currentType.ofType;
426+
expectedType = expectedType.ofType;
427+
continue;
428+
} else {
429+
return;
430+
}
431+
}
432+
if (!isNamedType(currentType) || !isNamedType(expectedType)) {
433+
return;
434+
}
435+
if (currentType !== expectedType) {
436+
return;
437+
}
438+
break;
439+
}
440+
}
441+
442+
return possibleSpecifiedDirective;
443+
}
444+
384445
function buildDirective(
385446
directiveIntrospection: IntrospectionDirective,
386447
): GraphQLDirective {
@@ -396,6 +457,12 @@ export function buildClientSchema(
396457
`Introspection result missing directive locations: ${directiveIntrospectionStr}.`,
397458
);
398459
}
460+
const specifiedDirective = getSpecifiedDirectiveFromIntrospection(
461+
directiveIntrospection,
462+
);
463+
if (specifiedDirective != null) {
464+
return specifiedDirective;
465+
}
399466
return new GraphQLDirective({
400467
name: directiveIntrospection.name,
401468
description: directiveIntrospection.description,

src/utilities/extendSchema.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ import {
6868
GraphQLDirective,
6969
GraphQLOneOfDirective,
7070
GraphQLSpecifiedByDirective,
71+
isSpecifiedDirective,
72+
specifiedDirectives,
7173
} from '../type/directives';
7274
import { introspectionTypes, isIntrospectionType } from '../type/introspection';
7375
import { isSpecifiedScalarType, specifiedScalarTypes } from '../type/scalars';
@@ -237,6 +239,9 @@ export function extendSchemaImpl(
237239
}
238240

239241
function replaceDirective(directive: GraphQLDirective): GraphQLDirective {
242+
if (isSpecifiedDirective(directive)) {
243+
return directive;
244+
}
240245
const config = directive.toConfig();
241246
return new GraphQLDirective({
242247
...config,
@@ -435,7 +440,65 @@ export function extendSchemaImpl(
435440
return getNamedType(node);
436441
}
437442

443+
function findSpecifiedDirectiveNode(node: DirectiveDefinitionNode) {
444+
const possibleDirective = specifiedDirectives.find(
445+
(stdDirective) => stdDirective.name === node.name.value,
446+
);
447+
if (possibleDirective == null) {
448+
return;
449+
}
450+
if (possibleDirective.description !== node.description?.value) {
451+
return;
452+
}
453+
if (possibleDirective.args.length !== node.arguments?.length) {
454+
return;
455+
}
456+
if (node.arguments?.length > 0) {
457+
for (const argNode of node.arguments) {
458+
const argDef = possibleDirective.args.find(
459+
(stdArg) => stdArg.name === argNode.name.value,
460+
);
461+
if (argDef == null) {
462+
return;
463+
}
464+
let type = argDef.type;
465+
let argDefType = argNode.type;
466+
// eslint-disable-next-line no-constant-condition
467+
while (true) {
468+
if (isNonNullType(type)) {
469+
if (argDefType.kind !== Kind.NON_NULL_TYPE) {
470+
return;
471+
}
472+
type = type.ofType;
473+
argDefType = argDefType.type;
474+
continue;
475+
} else if (isListType(type)) {
476+
if (argDefType.kind !== Kind.LIST_TYPE) {
477+
return;
478+
}
479+
type = type.ofType;
480+
argDefType = argDefType.type;
481+
continue;
482+
} else {
483+
if (argDefType.kind !== Kind.NAMED_TYPE) {
484+
return;
485+
}
486+
if (type.name !== argDefType.name.value) {
487+
return;
488+
}
489+
break;
490+
}
491+
}
492+
}
493+
}
494+
return possibleDirective;
495+
}
496+
438497
function buildDirective(node: DirectiveDefinitionNode): GraphQLDirective {
498+
const specifiedDirective = findSpecifiedDirectiveNode(node);
499+
if (specifiedDirective != null) {
500+
return specifiedDirective;
501+
}
439502
return new GraphQLDirective({
440503
name: node.name.value,
441504
description: node.description?.value,

src/utilities/lexicographicSortSchema.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import {
2929
isScalarType,
3030
isUnionType,
3131
} from '../type/definition';
32-
import { GraphQLDirective } from '../type/directives';
32+
import { GraphQLDirective, isSpecifiedDirective } from '../type/directives';
3333
import { isIntrospectionType } from '../type/introspection';
3434
import { GraphQLSchema } from '../type/schema';
3535

@@ -78,6 +78,9 @@ export function lexicographicSortSchema(schema: GraphQLSchema): GraphQLSchema {
7878
}
7979

8080
function sortDirective(directive: GraphQLDirective) {
81+
if (isSpecifiedDirective(directive)) {
82+
return directive;
83+
}
8184
const config = directive.toConfig();
8285
return new GraphQLDirective({
8386
...config,

0 commit comments

Comments
 (0)