knownDirectivesRule function

Visitor knownDirectivesRule(
  1. SDLValidationCtx context
)

Known directives

A GraphQL document is only valid if all @directives are known by the schema and legally positioned.

See https://spec.graphql.org/draft/#sec-Directives-Are-Defined

Implementation

Visitor knownDirectivesRule(
  SDLValidationCtx context,
) {
  final visitor = TypedVisitor();
  final locationsMap = <String, List<DirectiveLocation>>{};

  final schema = context.schema;
  final definedDirectives =
      schema != null ? schema.directives : GraphQLDirective.specifiedDirectives;
  for (final directive in definedDirectives) {
    locationsMap[directive.name] = directive.locations;
  }

  final astDefinitions = context.document.definitions;
  for (final def in astDefinitions.whereType<DirectiveDefinitionNode>()) {
    locationsMap[def.name.value] =
        def.locations.map(mapDirectiveLocation).toList();
  }

  visitor.add<DirectiveNode>((node) {
    final name = node.name.value;
    final locations = locationsMap[name];

    final _errorLocations =
        GraphQLErrorLocation.firstFromNodes([node, node.name]);

    if (locations == null) {
      context.reportError(
        GraphQLError(
          'Unknown directive "@${name}".',
          locations: _errorLocations,
          extensions: _knownDirectivesSpec.extensions(),
        ),
      );
      return;
    }

    final candidateLocation = getDirectiveLocationForASTPath(
      visitor.ancestors(),
    );
    if (candidateLocation != null && !locations.contains(candidateLocation)) {
      context.reportError(
        GraphQLError(
          'Directive "@${name}" may not be used on ${candidateLocation.toJson()}.',
          locations: _errorLocations,
          extensions: _misplacedDirectivesSpec.extensions(),
        ),
      );
    }
  });

  return visitor;
}