ProvidedRequiredArguments.js.flow 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // @flow strict
  2. import inspect from '../../jsutils/inspect';
  3. import keyMap from '../../jsutils/keyMap';
  4. import { GraphQLError } from '../../error/GraphQLError';
  5. import { Kind } from '../../language/kinds';
  6. import { print } from '../../language/printer';
  7. import { type ASTVisitor } from '../../language/visitor';
  8. import { specifiedDirectives } from '../../type/directives';
  9. import { isType, isRequiredArgument } from '../../type/definition';
  10. import {
  11. type ValidationContext,
  12. type SDLValidationContext,
  13. } from '../ValidationContext';
  14. export function missingFieldArgMessage(
  15. fieldName: string,
  16. argName: string,
  17. type: string,
  18. ): string {
  19. return `Field "${fieldName}" argument "${argName}" of type "${type}" is required, but it was not provided.`;
  20. }
  21. export function missingDirectiveArgMessage(
  22. directiveName: string,
  23. argName: string,
  24. type: string,
  25. ): string {
  26. return `Directive "@${directiveName}" argument "${argName}" of type "${type}" is required, but it was not provided.`;
  27. }
  28. /**
  29. * Provided required arguments
  30. *
  31. * A field or directive is only valid if all required (non-null without a
  32. * default value) field arguments have been provided.
  33. */
  34. export function ProvidedRequiredArguments(
  35. context: ValidationContext,
  36. ): ASTVisitor {
  37. return {
  38. ...ProvidedRequiredArgumentsOnDirectives(context),
  39. Field: {
  40. // Validate on leave to allow for deeper errors to appear first.
  41. leave(fieldNode) {
  42. const fieldDef = context.getFieldDef();
  43. if (!fieldDef) {
  44. return false;
  45. }
  46. const argNodes = fieldNode.arguments || [];
  47. const argNodeMap = keyMap(argNodes, arg => arg.name.value);
  48. for (const argDef of fieldDef.args) {
  49. const argNode = argNodeMap[argDef.name];
  50. if (!argNode && isRequiredArgument(argDef)) {
  51. context.reportError(
  52. new GraphQLError(
  53. missingFieldArgMessage(
  54. fieldDef.name,
  55. argDef.name,
  56. inspect(argDef.type),
  57. ),
  58. fieldNode,
  59. ),
  60. );
  61. }
  62. }
  63. },
  64. },
  65. };
  66. }
  67. // @internal
  68. export function ProvidedRequiredArgumentsOnDirectives(
  69. context: ValidationContext | SDLValidationContext,
  70. ): ASTVisitor {
  71. const requiredArgsMap = Object.create(null);
  72. const schema = context.getSchema();
  73. const definedDirectives = schema
  74. ? schema.getDirectives()
  75. : specifiedDirectives;
  76. for (const directive of definedDirectives) {
  77. requiredArgsMap[directive.name] = keyMap(
  78. directive.args.filter(isRequiredArgument),
  79. arg => arg.name,
  80. );
  81. }
  82. const astDefinitions = context.getDocument().definitions;
  83. for (const def of astDefinitions) {
  84. if (def.kind === Kind.DIRECTIVE_DEFINITION) {
  85. requiredArgsMap[def.name.value] = keyMap(
  86. def.arguments ? def.arguments.filter(isRequiredArgumentNode) : [],
  87. arg => arg.name.value,
  88. );
  89. }
  90. }
  91. return {
  92. Directive: {
  93. // Validate on leave to allow for deeper errors to appear first.
  94. leave(directiveNode) {
  95. const directiveName = directiveNode.name.value;
  96. const requiredArgs = requiredArgsMap[directiveName];
  97. if (requiredArgs) {
  98. const argNodes = directiveNode.arguments || [];
  99. const argNodeMap = keyMap(argNodes, arg => arg.name.value);
  100. for (const argName of Object.keys(requiredArgs)) {
  101. if (!argNodeMap[argName]) {
  102. const argType = requiredArgs[argName].type;
  103. context.reportError(
  104. new GraphQLError(
  105. missingDirectiveArgMessage(
  106. directiveName,
  107. argName,
  108. isType(argType) ? inspect(argType) : print(argType),
  109. ),
  110. directiveNode,
  111. ),
  112. );
  113. }
  114. }
  115. }
  116. },
  117. },
  118. };
  119. }
  120. function isRequiredArgumentNode(arg) {
  121. return arg.type.kind === Kind.NON_NULL_TYPE && arg.defaultValue == null;
  122. }