KnownArgumentNames.js.flow 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. // @flow strict
  2. import didYouMean from '../../jsutils/didYouMean';
  3. import suggestionList from '../../jsutils/suggestionList';
  4. import { GraphQLError } from '../../error/GraphQLError';
  5. import { Kind } from '../../language/kinds';
  6. import { type ASTVisitor } from '../../language/visitor';
  7. import { specifiedDirectives } from '../../type/directives';
  8. import {
  9. type ValidationContext,
  10. type SDLValidationContext,
  11. } from '../ValidationContext';
  12. export function unknownArgMessage(
  13. argName: string,
  14. fieldName: string,
  15. typeName: string,
  16. suggestedArgs: $ReadOnlyArray<string>,
  17. ): string {
  18. return (
  19. `Unknown argument "${argName}" on field "${fieldName}" of type "${typeName}".` +
  20. didYouMean(suggestedArgs.map(x => `"${x}"`))
  21. );
  22. }
  23. export function unknownDirectiveArgMessage(
  24. argName: string,
  25. directiveName: string,
  26. suggestedArgs: $ReadOnlyArray<string>,
  27. ): string {
  28. return (
  29. `Unknown argument "${argName}" on directive "@${directiveName}".` +
  30. didYouMean(suggestedArgs.map(x => `"${x}"`))
  31. );
  32. }
  33. /**
  34. * Known argument names
  35. *
  36. * A GraphQL field is only valid if all supplied arguments are defined by
  37. * that field.
  38. */
  39. export function KnownArgumentNames(context: ValidationContext): ASTVisitor {
  40. return {
  41. ...KnownArgumentNamesOnDirectives(context),
  42. Argument(argNode) {
  43. const argDef = context.getArgument();
  44. const fieldDef = context.getFieldDef();
  45. const parentType = context.getParentType();
  46. if (!argDef && fieldDef && parentType) {
  47. const argName = argNode.name.value;
  48. const knownArgsNames = fieldDef.args.map(arg => arg.name);
  49. context.reportError(
  50. new GraphQLError(
  51. unknownArgMessage(
  52. argName,
  53. fieldDef.name,
  54. parentType.name,
  55. suggestionList(argName, knownArgsNames),
  56. ),
  57. argNode,
  58. ),
  59. );
  60. }
  61. },
  62. };
  63. }
  64. // @internal
  65. export function KnownArgumentNamesOnDirectives(
  66. context: ValidationContext | SDLValidationContext,
  67. ): ASTVisitor {
  68. const directiveArgs = Object.create(null);
  69. const schema = context.getSchema();
  70. const definedDirectives = schema
  71. ? schema.getDirectives()
  72. : specifiedDirectives;
  73. for (const directive of definedDirectives) {
  74. directiveArgs[directive.name] = directive.args.map(arg => arg.name);
  75. }
  76. const astDefinitions = context.getDocument().definitions;
  77. for (const def of astDefinitions) {
  78. if (def.kind === Kind.DIRECTIVE_DEFINITION) {
  79. directiveArgs[def.name.value] = def.arguments
  80. ? def.arguments.map(arg => arg.name.value)
  81. : [];
  82. }
  83. }
  84. return {
  85. Directive(directiveNode) {
  86. const directiveName = directiveNode.name.value;
  87. const knownArgs = directiveArgs[directiveName];
  88. if (directiveNode.arguments && knownArgs) {
  89. for (const argNode of directiveNode.arguments) {
  90. const argName = argNode.name.value;
  91. if (knownArgs.indexOf(argName) === -1) {
  92. const suggestions = suggestionList(argName, knownArgs);
  93. context.reportError(
  94. new GraphQLError(
  95. unknownDirectiveArgMessage(argName, directiveName, suggestions),
  96. argNode,
  97. ),
  98. );
  99. }
  100. }
  101. }
  102. return false;
  103. },
  104. };
  105. }