UniqueFieldDefinitionNamesRule.js.flow 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  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. /**
  11. * Unique field definition names
  12. *
  13. * A GraphQL complex type is only valid if all its fields are uniquely named.
  14. */
  15. export function UniqueFieldDefinitionNamesRule(
  16. context: SDLValidationContext,
  17. ): ASTVisitor {
  18. const schema = context.getSchema();
  19. const existingTypeMap = schema ? schema.getTypeMap() : Object.create(null);
  20. const knownFieldNames = Object.create(null);
  21. return {
  22. InputObjectTypeDefinition: checkFieldUniqueness,
  23. InputObjectTypeExtension: checkFieldUniqueness,
  24. InterfaceTypeDefinition: checkFieldUniqueness,
  25. InterfaceTypeExtension: checkFieldUniqueness,
  26. ObjectTypeDefinition: checkFieldUniqueness,
  27. ObjectTypeExtension: checkFieldUniqueness,
  28. };
  29. function checkFieldUniqueness(node) {
  30. const typeName = node.name.value;
  31. if (!knownFieldNames[typeName]) {
  32. knownFieldNames[typeName] = Object.create(null);
  33. }
  34. // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
  35. const fieldNodes = node.fields ?? [];
  36. const fieldNames = knownFieldNames[typeName];
  37. for (const fieldDef of fieldNodes) {
  38. const fieldName = fieldDef.name.value;
  39. if (hasField(existingTypeMap[typeName], fieldName)) {
  40. context.reportError(
  41. new GraphQLError(
  42. `Field "${typeName}.${fieldName}" already exists in the schema. It cannot also be defined in this type extension.`,
  43. fieldDef.name,
  44. ),
  45. );
  46. } else if (fieldNames[fieldName]) {
  47. context.reportError(
  48. new GraphQLError(
  49. `Field "${typeName}.${fieldName}" can only be defined once.`,
  50. [fieldNames[fieldName], fieldDef.name],
  51. ),
  52. );
  53. } else {
  54. fieldNames[fieldName] = fieldDef.name;
  55. }
  56. }
  57. return false;
  58. }
  59. }
  60. function hasField(type, fieldName) {
  61. if (isObjectType(type) || isInterfaceType(type) || isInputObjectType(type)) {
  62. return type.getFields()[fieldName];
  63. }
  64. return false;
  65. }