KnownArgumentNamesRule.js.flow 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. // @flow strict
  2. import didYouMean from '../../jsutils/didYouMean';
  3. import suggestionList from '../../jsutils/suggestionList';
  4. import { GraphQLError } from '../../error/GraphQLError';
  5. import type { ASTVisitor } from '../../language/visitor';
  6. import { Kind } from '../../language/kinds';
  7. import { specifiedDirectives } from '../../type/directives';
  8. import type {
  9. ValidationContext,
  10. SDLValidationContext,
  11. } from '../ValidationContext';
  12. /**
  13. * Known argument names
  14. *
  15. * A GraphQL field is only valid if all supplied arguments are defined by
  16. * that field.
  17. */
  18. export function KnownArgumentNamesRule(context: ValidationContext): ASTVisitor {
  19. return {
  20. // eslint-disable-next-line new-cap
  21. ...KnownArgumentNamesOnDirectivesRule(context),
  22. Argument(argNode) {
  23. const argDef = context.getArgument();
  24. const fieldDef = context.getFieldDef();
  25. const parentType = context.getParentType();
  26. if (!argDef && fieldDef && parentType) {
  27. const argName = argNode.name.value;
  28. const knownArgsNames = fieldDef.args.map((arg) => arg.name);
  29. const suggestions = suggestionList(argName, knownArgsNames);
  30. context.reportError(
  31. new GraphQLError(
  32. `Unknown argument "${argName}" on field "${parentType.name}.${fieldDef.name}".` +
  33. didYouMean(suggestions),
  34. argNode,
  35. ),
  36. );
  37. }
  38. },
  39. };
  40. }
  41. /**
  42. * @internal
  43. */
  44. export function KnownArgumentNamesOnDirectivesRule(
  45. context: ValidationContext | SDLValidationContext,
  46. ): ASTVisitor {
  47. const directiveArgs = Object.create(null);
  48. const schema = context.getSchema();
  49. const definedDirectives = schema
  50. ? schema.getDirectives()
  51. : specifiedDirectives;
  52. for (const directive of definedDirectives) {
  53. directiveArgs[directive.name] = directive.args.map((arg) => arg.name);
  54. }
  55. const astDefinitions = context.getDocument().definitions;
  56. for (const def of astDefinitions) {
  57. if (def.kind === Kind.DIRECTIVE_DEFINITION) {
  58. // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
  59. const argsNodes = def.arguments ?? [];
  60. directiveArgs[def.name.value] = argsNodes.map((arg) => arg.name.value);
  61. }
  62. }
  63. return {
  64. Directive(directiveNode) {
  65. const directiveName = directiveNode.name.value;
  66. const knownArgs = directiveArgs[directiveName];
  67. if (directiveNode.arguments && knownArgs) {
  68. for (const argNode of directiveNode.arguments) {
  69. const argName = argNode.name.value;
  70. if (knownArgs.indexOf(argName) === -1) {
  71. const suggestions = suggestionList(argName, knownArgs);
  72. context.reportError(
  73. new GraphQLError(
  74. `Unknown argument "${argName}" on directive "@${directiveName}".` +
  75. didYouMean(suggestions),
  76. argNode,
  77. ),
  78. );
  79. }
  80. }
  81. }
  82. return false;
  83. },
  84. };
  85. }