| // @flow strict |
| |
| import { GraphQLError } from '../../error/GraphQLError'; |
| import { type ASTVisitor } from '../../language/visitor'; |
| import { isEnumType } from '../../type/definition'; |
| |
| import { type SDLValidationContext } from '../ValidationContext'; |
| |
| /** |
| * Unique enum value names |
| * |
| * A GraphQL enum type is only valid if all its values are uniquely named. |
| */ |
| export function UniqueEnumValueNamesRule( |
| context: SDLValidationContext, |
| ): ASTVisitor { |
| const schema = context.getSchema(); |
| const existingTypeMap = schema ? schema.getTypeMap() : Object.create(null); |
| const knownValueNames = Object.create(null); |
| |
| return { |
| EnumTypeDefinition: checkValueUniqueness, |
| EnumTypeExtension: checkValueUniqueness, |
| }; |
| |
| function checkValueUniqueness(node) { |
| const typeName = node.name.value; |
| |
| if (!knownValueNames[typeName]) { |
| knownValueNames[typeName] = Object.create(null); |
| } |
| |
| /* istanbul ignore next (See https://github.com/graphql/graphql-js/issues/2203) */ |
| const valueNodes = node.values ?? []; |
| const valueNames = knownValueNames[typeName]; |
| |
| for (const valueDef of valueNodes) { |
| const valueName = valueDef.name.value; |
| |
| const existingType = existingTypeMap[typeName]; |
| if (isEnumType(existingType) && existingType.getValue(valueName)) { |
| context.reportError( |
| new GraphQLError( |
| `Enum value "${typeName}.${valueName}" already exists in the schema. It cannot also be defined in this type extension.`, |
| valueDef.name, |
| ), |
| ); |
| } else if (valueNames[valueName]) { |
| context.reportError( |
| new GraphQLError( |
| `Enum value "${typeName}.${valueName}" can only be defined once.`, |
| [valueNames[valueName], valueDef.name], |
| ), |
| ); |
| } else { |
| valueNames[valueName] = valueDef.name; |
| } |
| } |
| |
| return false; |
| } |
| } |