UniqueEnumValueNamesRule.js.flow 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. // @flow strict
  2. import { GraphQLError } from '../../error/GraphQLError';
  3. import type { ASTVisitor } from '../../language/visitor';
  4. import { isEnumType } from '../../type/definition';
  5. import type { SDLValidationContext } from '../ValidationContext';
  6. /**
  7. * Unique enum value names
  8. *
  9. * A GraphQL enum type is only valid if all its values are uniquely named.
  10. */
  11. export function UniqueEnumValueNamesRule(
  12. context: SDLValidationContext,
  13. ): ASTVisitor {
  14. const schema = context.getSchema();
  15. const existingTypeMap = schema ? schema.getTypeMap() : Object.create(null);
  16. const knownValueNames = Object.create(null);
  17. return {
  18. EnumTypeDefinition: checkValueUniqueness,
  19. EnumTypeExtension: checkValueUniqueness,
  20. };
  21. function checkValueUniqueness(node) {
  22. const typeName = node.name.value;
  23. if (!knownValueNames[typeName]) {
  24. knownValueNames[typeName] = Object.create(null);
  25. }
  26. // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
  27. const valueNodes = node.values ?? [];
  28. const valueNames = knownValueNames[typeName];
  29. for (const valueDef of valueNodes) {
  30. const valueName = valueDef.name.value;
  31. const existingType = existingTypeMap[typeName];
  32. if (isEnumType(existingType) && existingType.getValue(valueName)) {
  33. context.reportError(
  34. new GraphQLError(
  35. `Enum value "${typeName}.${valueName}" already exists in the schema. It cannot also be defined in this type extension.`,
  36. valueDef.name,
  37. ),
  38. );
  39. } else if (valueNames[valueName]) {
  40. context.reportError(
  41. new GraphQLError(
  42. `Enum value "${typeName}.${valueName}" can only be defined once.`,
  43. [valueNames[valueName], valueDef.name],
  44. ),
  45. );
  46. } else {
  47. valueNames[valueName] = valueDef.name;
  48. }
  49. }
  50. return false;
  51. }
  52. }