UniqueFieldDefinitionNames.js.flow 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. // @flow strict
  2. import { GraphQLError } from '../../error/GraphQLError';
  3. import { type ASTVisitor } from '../../language/visitor';
  4. import {
  5. isObjectType,
  6. isInterfaceType,
  7. isInputObjectType,
  8. } from '../../type/definition';
  9. import { type SDLValidationContext } from '../ValidationContext';
  10. export function duplicateFieldDefinitionNameMessage(
  11. typeName: string,
  12. fieldName: string,
  13. ): string {
  14. return `Field "${typeName}.${fieldName}" can only be defined once.`;
  15. }
  16. export function existedFieldDefinitionNameMessage(
  17. typeName: string,
  18. fieldName: string,
  19. ): string {
  20. return `Field "${typeName}.${fieldName}" already exists in the schema. It cannot also be defined in this type extension.`;
  21. }
  22. /**
  23. * Unique field definition names
  24. *
  25. * A GraphQL complex type is only valid if all its fields are uniquely named.
  26. */
  27. export function UniqueFieldDefinitionNames(
  28. context: SDLValidationContext,
  29. ): ASTVisitor {
  30. const schema = context.getSchema();
  31. const existingTypeMap = schema ? schema.getTypeMap() : Object.create(null);
  32. const knownFieldNames = Object.create(null);
  33. return {
  34. InputObjectTypeDefinition: checkFieldUniqueness,
  35. InputObjectTypeExtension: checkFieldUniqueness,
  36. InterfaceTypeDefinition: checkFieldUniqueness,
  37. InterfaceTypeExtension: checkFieldUniqueness,
  38. ObjectTypeDefinition: checkFieldUniqueness,
  39. ObjectTypeExtension: checkFieldUniqueness,
  40. };
  41. function checkFieldUniqueness(node) {
  42. const typeName = node.name.value;
  43. if (!knownFieldNames[typeName]) {
  44. knownFieldNames[typeName] = Object.create(null);
  45. }
  46. if (node.fields) {
  47. const fieldNames = knownFieldNames[typeName];
  48. for (const fieldDef of node.fields) {
  49. const fieldName = fieldDef.name.value;
  50. if (hasField(existingTypeMap[typeName], fieldName)) {
  51. context.reportError(
  52. new GraphQLError(
  53. existedFieldDefinitionNameMessage(typeName, fieldName),
  54. fieldDef.name,
  55. ),
  56. );
  57. } else if (fieldNames[fieldName]) {
  58. context.reportError(
  59. new GraphQLError(
  60. duplicateFieldDefinitionNameMessage(typeName, fieldName),
  61. [fieldNames[fieldName], fieldDef.name],
  62. ),
  63. );
  64. } else {
  65. fieldNames[fieldName] = fieldDef.name;
  66. }
  67. }
  68. }
  69. return false;
  70. }
  71. }
  72. function hasField(type, fieldName) {
  73. if (isObjectType(type) || isInterfaceType(type) || isInputObjectType(type)) {
  74. return type.getFields()[fieldName];
  75. }
  76. return false;
  77. }