Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/type/__tests__/predicate-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -700,5 +700,13 @@ describe('Directive predicates', () => {
it('returns false for custom directive', () => {
expect(isSpecifiedDirective(Directive)).to.equal(false);
});

it('returns false for the directives with the same name as specified directives', () => {
const FakeSkipDirective = new GraphQLDirective({
name: 'skip',
locations: [DirectiveLocation.QUERY],
});
expect(isSpecifiedDirective(FakeSkipDirective)).to.equal(false);
});
});
});
4 changes: 3 additions & 1 deletion src/type/directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,5 +233,7 @@ export const specifiedDirectives: ReadonlyArray<GraphQLDirective> =
]);

export function isSpecifiedDirective(directive: GraphQLDirective): boolean {
return specifiedDirectives.some(({ name }) => name === directive.name);
return specifiedDirectives.some(
(specifiedDirective) => specifiedDirective === directive,
);
}
69 changes: 68 additions & 1 deletion src/utilities/buildClientSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ import {
GraphQLScalarType,
GraphQLUnionType,
isInputType,
isNamedType,
isOutputType,
} from '../type/definition';
import { GraphQLDirective } from '../type/directives';
import { GraphQLDirective, specifiedDirectives } from '../type/directives';
import { introspectionTypes, TypeKind } from '../type/introspection';
import { specifiedScalarTypes } from '../type/scalars';
import type { GraphQLSchemaValidationOptions } from '../type/schema';
Expand Down Expand Up @@ -381,6 +382,66 @@ export function buildClientSchema(
};
}

function getSpecifiedDirectiveFromIntrospection(
directiveIntrospection: IntrospectionDirective,
): GraphQLDirective | undefined {
const possibleSpecifiedDirective = specifiedDirectives.find(
(dir) => dir.name === directiveIntrospection.name,
);
if (possibleSpecifiedDirective == null) {
return;
}

for (const location of directiveIntrospection.locations) {
if (!possibleSpecifiedDirective.locations.includes(location)) {
return;
}
}

for (const arg of directiveIntrospection.args) {
const possibleArg = possibleSpecifiedDirective.args.find(
(a) => a.name === arg.name,
);
if (possibleArg == null) {
return;
}
const argType = getType(arg.type);
// Is same type
let currentType = argType;
let expectedType = possibleArg.type;
// eslint-disable-next-line no-constant-condition
while (true) {
if (currentType instanceof GraphQLNonNull) {
if (expectedType instanceof GraphQLNonNull) {
currentType = currentType.ofType;
expectedType = expectedType.ofType;
continue;
} else {
return;
}
}
if (currentType instanceof GraphQLList) {
if (expectedType instanceof GraphQLList) {
currentType = currentType.ofType;
expectedType = expectedType.ofType;
continue;
} else {
return;
}
}
if (!isNamedType(currentType) || !isNamedType(expectedType)) {
return;
}
if (currentType !== expectedType) {
return;
}
break;
}
}

return possibleSpecifiedDirective;
}

function buildDirective(
directiveIntrospection: IntrospectionDirective,
): GraphQLDirective {
Expand All @@ -396,6 +457,12 @@ export function buildClientSchema(
`Introspection result missing directive locations: ${directiveIntrospectionStr}.`,
);
}
const specifiedDirective = getSpecifiedDirectiveFromIntrospection(
directiveIntrospection,
);
if (specifiedDirective != null) {
return specifiedDirective;
}
return new GraphQLDirective({
name: directiveIntrospection.name,
description: directiveIntrospection.description,
Expand Down
63 changes: 63 additions & 0 deletions src/utilities/extendSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ import {
GraphQLDirective,
GraphQLOneOfDirective,
GraphQLSpecifiedByDirective,
isSpecifiedDirective,
specifiedDirectives,
} from '../type/directives';
import { introspectionTypes, isIntrospectionType } from '../type/introspection';
import { isSpecifiedScalarType, specifiedScalarTypes } from '../type/scalars';
Expand Down Expand Up @@ -237,6 +239,9 @@ export function extendSchemaImpl(
}

function replaceDirective(directive: GraphQLDirective): GraphQLDirective {
if (isSpecifiedDirective(directive)) {
return directive;
}
const config = directive.toConfig();
return new GraphQLDirective({
...config,
Expand Down Expand Up @@ -435,7 +440,65 @@ export function extendSchemaImpl(
return getNamedType(node);
}

function findSpecifiedDirectiveNode(node: DirectiveDefinitionNode) {
const possibleDirective = specifiedDirectives.find(
(stdDirective) => stdDirective.name === node.name.value,
);
if (possibleDirective == null) {
return;
}
if (possibleDirective.description !== node.description?.value) {
return;
}
if (possibleDirective.args.length !== node.arguments?.length) {
return;
}
if (node.arguments?.length > 0) {
for (const argNode of node.arguments) {
const argDef = possibleDirective.args.find(
(stdArg) => stdArg.name === argNode.name.value,
);
if (argDef == null) {
return;
}
let type = argDef.type;
let argDefType = argNode.type;
// eslint-disable-next-line no-constant-condition
while (true) {
if (isNonNullType(type)) {
if (argDefType.kind !== Kind.NON_NULL_TYPE) {
return;
}
type = type.ofType;
argDefType = argDefType.type;
continue;
} else if (isListType(type)) {
if (argDefType.kind !== Kind.LIST_TYPE) {
return;
}
type = type.ofType;
argDefType = argDefType.type;
continue;
} else {
if (argDefType.kind !== Kind.NAMED_TYPE) {
return;
}
if (type.name !== argDefType.name.value) {
return;
}
break;
}
}
}
}
return possibleDirective;
}

function buildDirective(node: DirectiveDefinitionNode): GraphQLDirective {
const specifiedDirective = findSpecifiedDirectiveNode(node);
if (specifiedDirective != null) {
return specifiedDirective;
}
return new GraphQLDirective({
name: node.name.value,
description: node.description?.value,
Expand Down
5 changes: 4 additions & 1 deletion src/utilities/lexicographicSortSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
isScalarType,
isUnionType,
} from '../type/definition';
import { GraphQLDirective } from '../type/directives';
import { GraphQLDirective, isSpecifiedDirective } from '../type/directives';
import { isIntrospectionType } from '../type/introspection';
import { GraphQLSchema } from '../type/schema';

Expand Down Expand Up @@ -78,6 +78,9 @@ export function lexicographicSortSchema(schema: GraphQLSchema): GraphQLSchema {
}

function sortDirective(directive: GraphQLDirective) {
if (isSpecifiedDirective(directive)) {
return directive;
}
const config = directive.toConfig();
return new GraphQLDirective({
...config,
Expand Down
Loading