import { GraphQLError } from '../../error/GraphQLError.js';
import type { DirectiveNode } from '../../language/ast.js';
import { OperationTypeNode } from '../../language/ast.js';
import { Kind } from '../../language/kinds.js';
import type { ASTVisitor } from '../../language/visitor.js';
import {
GraphQLDeferDirective,
GraphQLStreamDirective,
} from '../../type/directives.js';
import type { ValidationContext } from '../ValidationContext.js';
function ifArgumentCanBeFalse(node: DirectiveNode): boolean {
const ifArgument = node.arguments?.find((arg) => arg.name.value === 'if');
if (!ifArgument) {
return false;
}
if (ifArgument.value.kind === Kind.BOOLEAN) {
if (ifArgument.value.value) {
return false;
}
} else if (ifArgument.value.kind !== Kind.VARIABLE) {
return false;
}
return true;
}
export function DeferStreamDirectiveOnValidOperationsRule(
context: ValidationContext,
): ASTVisitor {
const fragmentsUsedOnSubscriptions = new Set<string>();
return {
OperationDefinition(operation) {
if (operation.operation === OperationTypeNode.SUBSCRIPTION) {
for (const fragment of context.getRecursivelyReferencedFragments(
operation,
)) {
fragmentsUsedOnSubscriptions.add(fragment.name.value);
}
}
},
Directive(node, _key, _parent, _path, ancestors) {
const definitionNode = ancestors[2];
if (
'kind' in definitionNode &&
((definitionNode.kind === Kind.FRAGMENT_DEFINITION &&
fragmentsUsedOnSubscriptions.has(definitionNode.name.value)) ||
(definitionNode.kind === Kind.OPERATION_DEFINITION &&
definitionNode.operation === OperationTypeNode.SUBSCRIPTION))
) {
if (node.name.value === GraphQLDeferDirective.name) {
if (!ifArgumentCanBeFalse(node)) {
context.reportError(
new GraphQLError(
'Defer directive not supported on subscription operations. Disable `@defer` by setting the `if` argument to `false`.',
{ nodes: node },
),
);
}
} else if (node.name.value === GraphQLStreamDirective.name) {
if (!ifArgumentCanBeFalse(node)) {
context.reportError(
new GraphQLError(
'Stream directive not supported on subscription operations. Disable `@defer` by setting the `if` argument to `false`.',
{ nodes: node },
),
);
}
}
}
},
};
}