KnownDirectives.js.flow 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. // @flow strict
  2. import { GraphQLError } from '../../error/GraphQLError';
  3. import { Kind } from '../../language/kinds';
  4. import { type ASTVisitor } from '../../language/visitor';
  5. import { DirectiveLocation } from '../../language/directiveLocation';
  6. import { specifiedDirectives } from '../../type/directives';
  7. import {
  8. type ValidationContext,
  9. type SDLValidationContext,
  10. } from '../ValidationContext';
  11. export function unknownDirectiveMessage(directiveName: string): string {
  12. return `Unknown directive "${directiveName}".`;
  13. }
  14. export function misplacedDirectiveMessage(
  15. directiveName: string,
  16. location: string,
  17. ): string {
  18. return `Directive "${directiveName}" may not be used on ${location}.`;
  19. }
  20. /**
  21. * Known directives
  22. *
  23. * A GraphQL document is only valid if all `@directives` are known by the
  24. * schema and legally positioned.
  25. */
  26. export function KnownDirectives(
  27. context: ValidationContext | SDLValidationContext,
  28. ): ASTVisitor {
  29. const locationsMap = Object.create(null);
  30. const schema = context.getSchema();
  31. const definedDirectives = schema
  32. ? schema.getDirectives()
  33. : specifiedDirectives;
  34. for (const directive of definedDirectives) {
  35. locationsMap[directive.name] = directive.locations;
  36. }
  37. const astDefinitions = context.getDocument().definitions;
  38. for (const def of astDefinitions) {
  39. if (def.kind === Kind.DIRECTIVE_DEFINITION) {
  40. locationsMap[def.name.value] = def.locations.map(name => name.value);
  41. }
  42. }
  43. return {
  44. Directive(node, key, parent, path, ancestors) {
  45. const name = node.name.value;
  46. const locations = locationsMap[name];
  47. if (!locations) {
  48. context.reportError(
  49. new GraphQLError(unknownDirectiveMessage(name), node),
  50. );
  51. return;
  52. }
  53. const candidateLocation = getDirectiveLocationForASTPath(ancestors);
  54. if (candidateLocation && locations.indexOf(candidateLocation) === -1) {
  55. context.reportError(
  56. new GraphQLError(
  57. misplacedDirectiveMessage(name, candidateLocation),
  58. node,
  59. ),
  60. );
  61. }
  62. },
  63. };
  64. }
  65. function getDirectiveLocationForASTPath(ancestors) {
  66. const appliedTo = ancestors[ancestors.length - 1];
  67. if (!Array.isArray(appliedTo)) {
  68. switch (appliedTo.kind) {
  69. case Kind.OPERATION_DEFINITION:
  70. switch (appliedTo.operation) {
  71. case 'query':
  72. return DirectiveLocation.QUERY;
  73. case 'mutation':
  74. return DirectiveLocation.MUTATION;
  75. case 'subscription':
  76. return DirectiveLocation.SUBSCRIPTION;
  77. }
  78. break;
  79. case Kind.FIELD:
  80. return DirectiveLocation.FIELD;
  81. case Kind.FRAGMENT_SPREAD:
  82. return DirectiveLocation.FRAGMENT_SPREAD;
  83. case Kind.INLINE_FRAGMENT:
  84. return DirectiveLocation.INLINE_FRAGMENT;
  85. case Kind.FRAGMENT_DEFINITION:
  86. return DirectiveLocation.FRAGMENT_DEFINITION;
  87. case Kind.VARIABLE_DEFINITION:
  88. return DirectiveLocation.VARIABLE_DEFINITION;
  89. case Kind.SCHEMA_DEFINITION:
  90. case Kind.SCHEMA_EXTENSION:
  91. return DirectiveLocation.SCHEMA;
  92. case Kind.SCALAR_TYPE_DEFINITION:
  93. case Kind.SCALAR_TYPE_EXTENSION:
  94. return DirectiveLocation.SCALAR;
  95. case Kind.OBJECT_TYPE_DEFINITION:
  96. case Kind.OBJECT_TYPE_EXTENSION:
  97. return DirectiveLocation.OBJECT;
  98. case Kind.FIELD_DEFINITION:
  99. return DirectiveLocation.FIELD_DEFINITION;
  100. case Kind.INTERFACE_TYPE_DEFINITION:
  101. case Kind.INTERFACE_TYPE_EXTENSION:
  102. return DirectiveLocation.INTERFACE;
  103. case Kind.UNION_TYPE_DEFINITION:
  104. case Kind.UNION_TYPE_EXTENSION:
  105. return DirectiveLocation.UNION;
  106. case Kind.ENUM_TYPE_DEFINITION:
  107. case Kind.ENUM_TYPE_EXTENSION:
  108. return DirectiveLocation.ENUM;
  109. case Kind.ENUM_VALUE_DEFINITION:
  110. return DirectiveLocation.ENUM_VALUE;
  111. case Kind.INPUT_OBJECT_TYPE_DEFINITION:
  112. case Kind.INPUT_OBJECT_TYPE_EXTENSION:
  113. return DirectiveLocation.INPUT_OBJECT;
  114. case Kind.INPUT_VALUE_DEFINITION: {
  115. const parentNode = ancestors[ancestors.length - 3];
  116. return parentNode.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION
  117. ? DirectiveLocation.INPUT_FIELD_DEFINITION
  118. : DirectiveLocation.ARGUMENT_DEFINITION;
  119. }
  120. }
  121. }
  122. }